1 /*----------------------------------------------------------------------*/
2 /* graphic.c --- xcircuit routines handling rendered graphic elements */
3 /* Copyright (c) 2005 Tim Edwards, MultiGiG, Inc. */
4 /*----------------------------------------------------------------------*/
6 /*----------------------------------------------------------------------*/
7 /* written by Tim Edwards, 7/11/05 */
8 /*----------------------------------------------------------------------*/
16 #include <X11/Intrinsic.h>
17 #include <X11/StringDefs.h>
24 /*----------------------------------------------------------------------*/
26 /*----------------------------------------------------------------------*/
29 #include "colordefs.h"
31 /*----------------------------------------------------------------------*/
32 /* Function prototype declarations */
33 /*----------------------------------------------------------------------*/
34 #include "prototypes.h"
36 /*----------------------------------------------------------------------*/
37 /* Global Variable definitions */
38 /*----------------------------------------------------------------------*/
41 extern Globaldata xobjs
;
42 extern XCWindowData
*areawin
;
43 extern colorindex
*colorlist
;
44 extern int number_colors
;
46 /*----------------------------------------------------------------------*/
47 /* Recursive search for graphic images in an object. */
48 /* Updates list "glist" when any image is found in an object or its */
50 /*----------------------------------------------------------------------*/
52 void count_graphics(objectptr thisobj
, short *glist
)
59 for (ge
= thisobj
->plist
; ge
< thisobj
->plist
+ thisobj
->parts
; ge
++) {
60 if (IS_GRAPHIC(*ge
)) {
62 for (i
= 0; i
< xobjs
.images
; i
++) {
63 iptr
= xobjs
.imagelist
+ i
;
64 if (iptr
->image
== gp
->source
) {
69 else if (IS_OBJINST(*ge
)) {
70 count_graphics(TOOBJINST(ge
)->thisobject
, glist
);
75 /*----------------------------------------------------------------------*/
76 /* Given a list of pages, return a list of indices into the graphics */
77 /* buffer area of each graphic used on any of the indicated pages. */
78 /* The returned list is allocated and it is the responsibility of the */
79 /* calling routine to free it. */
80 /*----------------------------------------------------------------------*/
82 short *collect_graphics(short *pagelist
)
87 glist
= (short *)malloc(xobjs
.images
* sizeof(short));
89 for (i
= 0; i
< xobjs
.images
; i
++) glist
[i
] = 0;
91 for (i
= 0; i
< xobjs
.pages
; i
++)
93 count_graphics(xobjs
.pagelist
[i
]->pageinst
->thisobject
, glist
);
98 /*----------------------------------------------------------------------*/
99 /* Generate the target view of the indicated graphic image, combining */
100 /* the image's scale and rotation and the zoom factor of the current */
103 /* If the graphic is at the wrong scale or rotation but is not redrawn */
104 /* because it is outside the screen viewing area, return FALSE. */
105 /* Otherwise, return TRUE. */
106 /*----------------------------------------------------------------------*/
109 Boolean
transform_graphic(graphicptr gp
)
111 int width
, height
, twidth
, theight
;
112 float scale
, tscale
, rotation
, crot
;
114 int x
, y
, c
, s
, hw
, hh
, thw
, thh
, xorig
, yorig
, xc
, yc
;
115 int screen
= DefaultScreen(dpy
);
116 static GC cmgc
= (GC
)NULL
;
118 tscale
= UTopScale();
119 scale
= gp
->scale
* tscale
;
120 rotation
= gp
->rotation
+ UTopRotation();
122 if (rotation
>= 360.0) rotation
-= 360.0;
123 else if (rotation
< 0.0) rotation
+= 360.0;
125 /* Check if the top-level rotation and scale match the */
126 /* saved target image. If so, then we're done. */
127 if ((rotation
== gp
->trot
) && (scale
== gp
->tscale
)) return TRUE
;
129 cosr
= cos(RADFAC
* rotation
);
130 sinr
= sin(RADFAC
* rotation
);
131 c
= (int)(8192 * cosr
/ scale
);
132 s
= (int)(8192 * sinr
/ scale
);
134 /* Determine the necessary width and height of the pixmap */
135 /* that fits the rotated and scaled image. */
138 if (crot
> 90.0 && crot
< 180.0) crot
= 180.0 - crot
;
139 if (crot
> 270.0 && crot
< 360.0) crot
= 360.0 - crot
;
140 cosr
= cos(RADFAC
* crot
);
141 sinr
= sin(RADFAC
* crot
);
142 width
= gp
->source
->width
* scale
;
143 height
= gp
->source
->height
* scale
;
145 twidth
= (int)(fabs(width
* cosr
+ height
* sinr
));
146 theight
= (int)(fabs(width
* sinr
+ height
* cosr
));
147 if (twidth
& 1) twidth
++;
148 if (theight
& 1) theight
++;
150 /* Check whether this instance is going to be off-screen, */
151 /* to avoid excessive computation. */
153 UTopOffset(&xc
, &yc
);
154 xc
+= (int)((float)gp
->position
.x
* tscale
);
155 yc
= areawin
->height
- yc
;
156 yc
+= (int)((float)gp
->position
.y
* tscale
);
158 if (xc
- (twidth
>> 1) > areawin
->width
) return FALSE
;
159 if (xc
+ (twidth
>> 1) < 0) return FALSE
;
160 if (yc
- (theight
>> 1) > areawin
->height
) return FALSE
;
161 if (yc
+ (theight
>> 1) < 0) return FALSE
;
163 /* Generate the new target image */
164 if (gp
->target
!= NULL
) {
165 if (gp
->target
->data
!= NULL
) {
166 /* Free data first, because we used our own malloc() */
167 free(gp
->target
->data
);
168 gp
->target
->data
= NULL
;
170 XDestroyImage(gp
->target
);
172 if (gp
->clipmask
!= (Pixmap
)NULL
) XFreePixmap(dpy
, gp
->clipmask
);
174 gp
->target
= XCreateImage(dpy
, DefaultVisual(dpy
, screen
),
175 DefaultDepth(dpy
, screen
), ZPixmap
,
176 0, 0, twidth
, theight
, 8, 0);
177 gp
->target
->data
= (char *)malloc(theight
* gp
->target
->bytes_per_line
);
179 if (gp
->target
->data
== (char *)NULL
) {
180 XDestroyImage(gp
->target
);
181 gp
->target
= (XImage
*)NULL
;
182 gp
->clipmask
= (Pixmap
)NULL
;
186 if (rotation
!= 0.0) {
187 gp
->clipmask
= XCreatePixmap(dpy
, areawin
->window
, twidth
, theight
, 1);
188 if (cmgc
== (GC
)NULL
) {
190 values
.foreground
= 0;
191 values
.background
= 0;
192 cmgc
= XCreateGC(dpy
, gp
->clipmask
, GCForeground
| GCBackground
, &values
);
194 XSetForeground(dpy
, cmgc
, 1);
195 XFillRectangle(dpy
, gp
->clipmask
, cmgc
, 0, 0, twidth
, theight
);
196 XSetForeground(dpy
, cmgc
, 0);
199 gp
->clipmask
= (Pixmap
)NULL
;
201 hh
= gp
->source
->height
>> 1;
202 hw
= gp
->source
->width
>> 1;
205 for (y
= -thh
; y
< thh
; y
++) {
206 for (x
= -thw
; x
< thw
; x
++) {
207 xorig
= ((x
* c
+ y
* s
) >> 13) + hw
;
208 yorig
= ((-x
* s
+ y
* c
) >> 13) + hh
;
210 if ((xorig
>= 0) && (yorig
>= 0) &&
211 (xorig
< gp
->source
->width
) && (yorig
< gp
->source
->height
))
212 XPutPixel(gp
->target
, x
+ thw
, y
+ thh
,
213 XGetPixel(gp
->source
, xorig
, yorig
));
214 else if (gp
->clipmask
)
215 XDrawPoint(dpy
, gp
->clipmask
, cmgc
, x
+ thw
, y
+ thh
);
222 #endif /* HAVE_CAIRO */
224 /*----------------------------------------------------------------------*/
225 /* Draw a graphic image by copying from the image to the window. */
226 /* Image is centered on the center point of the graphic image. */
227 /*----------------------------------------------------------------------*/
230 void UDrawGraphic(graphicptr gp
)
234 /* transform to current scale and rotation, if necessary */
235 if (transform_graphic(gp
) == FALSE
) return; /* Graphic off-screen */
237 /* transform to current position */
238 UTransformbyCTM(DCTM
, &(gp
->position
), &ppt
, 1);
240 /* user_to_window(gp->position, &ppt); */
242 ppt
.x
-= (gp
->target
->width
>> 1);
243 ppt
.y
-= (gp
->target
->height
>> 1);
245 if (gp
->clipmask
!= (Pixmap
)NULL
) {
246 if (areawin
->clipped
> 0) {
247 /* Clipmask areawin->clipmask already applied to window. */
248 /* Modify existing clipmask with the graphic's. */
249 XSetFunction(dpy
, areawin
->cmgc
, GXand
);
250 XCopyArea(dpy
, gp
->clipmask
, areawin
->clipmask
, areawin
->cmgc
,
251 0, 0, gp
->target
->width
, gp
->target
->height
, ppt
.x
, ppt
.y
);
252 XSetClipMask(dpy
, areawin
->gc
, areawin
->clipmask
);
253 XSetFunction(dpy
, areawin
->cmgc
, GXcopy
);
256 XSetClipOrigin(dpy
, areawin
->gc
, ppt
.x
, ppt
.y
);
257 XSetClipMask(dpy
, areawin
->gc
, gp
->clipmask
);
261 XPutImage(dpy
, areawin
->window
, areawin
->gc
,
262 gp
->target
, 0, 0, ppt
.x
, ppt
.y
, gp
->target
->width
,
265 if (gp
->clipmask
!= (Pixmap
)NULL
) {
266 if (areawin
->clipped
<= 0) {
267 XSetClipMask(dpy
, areawin
->gc
, None
);
268 XSetClipOrigin(dpy
, areawin
->gc
, 0, 0);
272 #endif /* HAVE_CAIRO */
274 /*----------------------------------------------------------------------*/
275 /* Allocate space for a new graphic source image of size width x height */
276 /*----------------------------------------------------------------------*/
278 Imagedata
*addnewimage(char *name
, int width
, int height
)
281 int screen
= DefaultScreen(dpy
);
283 /* Create the image and store in the global list of images */
287 xobjs
.imagelist
= (Imagedata
*)realloc(xobjs
.imagelist
,
288 xobjs
.images
* sizeof(Imagedata
));
290 xobjs
.imagelist
= (Imagedata
*)malloc(sizeof(Imagedata
));
292 /* Save the image source in a file */
293 iptr
= xobjs
.imagelist
+ xobjs
.images
- 1;
295 iptr
->filename
= strdup(name
);
297 iptr
->filename
= NULL
; /* must be filled in later! */
298 iptr
->refcount
= 0; /* no calls yet */
299 iptr
->image
= xcImageCreate(width
, height
);
304 /*----------------------------------------------------------------------*/
305 /* Create a new graphic image from a PPM file, and position it at the */
306 /* indicated (px, py) coordinate in user space. */
308 /* This should be expanded to incorporate more PPM formats. Also, it */
309 /* needs to be combined with the render.c routines to transform */
310 /* PostScript graphics into an internal XImage for faster rendering. */
311 /*----------------------------------------------------------------------*/
313 graphicptr
new_graphic(objinstptr destinst
, char *filename
, int px
, int py
)
316 objectptr destobject
;
317 objinstptr locdestinst
;
320 int nr
, width
, height
, imax
, x
, y
, i
; /* nf, (jdk) */
321 char id
[5], c
, buf
[128];
323 locdestinst
= (destinst
== NULL
) ? areawin
->topinstance
: destinst
;
324 destobject
= locdestinst
->thisobject
;
326 /* Check the existing list of images. If there is a match, */
327 /* re-use the source; don't load the file again. */
329 for (i
= 0; i
< xobjs
.images
; i
++) {
330 iptr
= xobjs
.imagelist
+ i
;
331 if (!strcmp(iptr
->filename
, filename
)) {
335 if (i
== xobjs
.images
) {
337 fg
= fopen(filename
, "r");
338 if (fg
== NULL
) return NULL
;
340 /* This ONLY handles binary ppm files with max data = 255 */
343 nr
= fscanf(fg
, " %s", buf
);
344 if (nr
<= 0) return NULL
;
346 if (sscanf(buf
, "%s", id
) <= 0)
350 else fgets(buf
, 127, fg
);
352 if ((nr
<= 0) || strncmp(id
, "P6", 2)) return NULL
;
355 nr
= fscanf(fg
, " %s", buf
);
356 if (nr
<= 0) return NULL
;
358 if (sscanf(buf
, "%d", &width
) <= 0)
362 else fgets(buf
, 127, fg
);
364 if (width
<= 0) return NULL
;
367 nr
= fscanf(fg
, " %s", buf
);
368 if (nr
<= 0) return NULL
;
370 if (sscanf(buf
, "%d", &height
) <= 0)
374 else fgets(buf
, 127, fg
);
376 if (height
<= 0) return NULL
;
379 nr
= fscanf(fg
, " %s", buf
);
380 if (nr
<= 0) return NULL
;
382 if (sscanf(buf
, "%d", &imax
) <= 0)
386 else fgets(buf
, 127, fg
);
388 if (imax
!= 255) return NULL
;
392 if (c
== '\n') break;
393 else if (c
== '\0') return NULL
;
396 iptr
= addnewimage(filename
, width
, height
);
398 /* Read the image data from the PPM file */
399 for (y
= 0; y
< height
; y
++)
400 for (x
= 0; x
< width
; x
++) {
405 xcImagePutPixel(iptr
->image
, x
, y
, r
, g
, b
);
410 NEW_GRAPHIC(gp
, destobject
);
413 (*gp
)->position
.x
= px
;
414 (*gp
)->position
.y
= py
;
415 (*gp
)->rotation
= 0.0;
416 (*gp
)->color
= DEFAULTCOLOR
;
417 (*gp
)->passed
= NULL
;
418 (*gp
)->source
= iptr
->image
;
420 (*gp
)->target
= NULL
;
423 (*gp
)->clipmask
= (Pixmap
)NULL
;
424 #endif /* HAVE_CAIRO */
426 calcbboxvalues(locdestinst
, (genericptr
*)gp
);
427 updatepagebounds(destobject
);
428 incr_changes(destobject
);
430 register_for_undo(XCF_Graphic
, UNDO_DONE
, areawin
->topinstance
, *gp
);
435 /*----------------------------------------------------------------------*/
436 /* Create a gradient field graphic */
437 /* For now, gradient field is linear white-to-black size 100x100. */
438 /*----------------------------------------------------------------------*/
440 graphicptr
gradient_field(objinstptr destinst
, int px
, int py
, int c1
, int c2
)
443 objectptr destobject
;
444 objinstptr locdestinst
;
446 int width
, height
, imax
, x
, y
, i
;
447 int r
, g
, b
, rd
, gd
, bd
;
450 locdestinst
= (destinst
== NULL
) ? areawin
->topinstance
: destinst
;
451 destobject
= locdestinst
->thisobject
;
454 if (c1
>= number_colors
) c1
= 1;
456 if (c2
>= number_colors
) c2
= 1;
458 /* Create name "gradientXX" */
461 for (i
= 0; i
< xobjs
.images
; i
++) {
462 iptr
= xobjs
.imagelist
+ i
;
463 if (!strncmp(iptr
->filename
, "gradient", 8)) {
464 if (sscanf(iptr
->filename
+ 8, "%2d", &x
) == 1)
469 sprintf(id
, "gradient%02d", y
);
471 width
= height
= 100; /* Fixed size, at least for now */
473 iptr
= addnewimage(id
, width
, height
);
475 r
= (colorlist
[c1
].color
.red
>> 8);
476 g
= (colorlist
[c1
].color
.green
>> 8);
477 b
= (colorlist
[c1
].color
.blue
>> 8);
479 rd
= (colorlist
[c2
].color
.red
>> 8) - r
;
480 gd
= (colorlist
[c2
].color
.green
>> 8) - g
;
481 bd
= (colorlist
[c2
].color
.blue
>> 8) - b
;
483 for (y
= 0; y
< height
; y
++)
484 for (x
= 0; x
< width
; x
++) {
485 xcImagePutPixel(iptr
->image
, x
, y
,
486 r
+ ((y
* rd
) / (height
- 1)),
487 g
+ ((y
* gd
) / (height
- 1)),
488 b
+ ((y
* bd
) / (height
- 1)));
492 NEW_GRAPHIC(gp
, destobject
);
495 (*gp
)->position
.x
= px
;
496 (*gp
)->position
.y
= py
;
497 (*gp
)->rotation
= 0.0;
498 (*gp
)->color
= DEFAULTCOLOR
;
499 (*gp
)->passed
= NULL
;
500 (*gp
)->source
= iptr
->image
;
502 (*gp
)->target
= NULL
;
505 (*gp
)->clipmask
= (Pixmap
)NULL
;
506 #endif /* HAVE_CAIRO */
508 calcbboxvalues(locdestinst
, (genericptr
*)gp
);
509 updatepagebounds(destobject
);
510 incr_changes(destobject
);
512 register_for_undo(XCF_Graphic
, UNDO_DONE
, areawin
->topinstance
, *gp
);
518 /*----------------------------------------------------------------------*/
519 /* Free memory associated with the XImage structure for a graphic. */
520 /*----------------------------------------------------------------------*/
522 void freeimage(xcImage
*source
)
527 for (i
= 0; i
< xobjs
.images
; i
++) {
528 iptr
= xobjs
.imagelist
+ i
;
529 if (iptr
->image
== source
) {
531 if (iptr
->refcount
<= 0) {
532 xcImageDestroy(iptr
->image
);
533 free(iptr
->filename
);
535 /* Remove this from the list of images */
537 for (j
= i
; j
< xobjs
.images
- 1; j
++)
538 *(xobjs
.imagelist
+ j
) = *(xobjs
.imagelist
+ j
+ 1);
546 /*----------------------------------------------------------------------*/
547 /* Free memory allocated by a graphicptr structure. */
548 /*----------------------------------------------------------------------*/
550 void freegraphic(graphicptr gp
)
553 if (gp
->target
!= NULL
) {
554 if (gp
->target
->data
!= NULL
) {
555 /* Free data first, because we used our own malloc() */
556 free(gp
->target
->data
);
557 gp
->target
->data
= NULL
;
559 XDestroyImage(gp
->target
);
561 if (gp
->clipmask
!= (Pixmap
)NULL
) XFreePixmap(dpy
, gp
->clipmask
);
562 #endif /* HAVE_CAIRO */
563 freeimage(gp
->source
);
566 /*----------------------------------------------------------------------*/
567 /* A light wrapper around XImage, to a generalized xcImage */
568 /*----------------------------------------------------------------------*/
571 xcImage
*xcImageCreate(int width
, int height
)
573 int screen
= DefaultScreen(dpy
);
574 xcImage
*img
= XCreateImage(dpy
, DefaultVisual(dpy
, screen
),
575 DefaultDepth(dpy
, screen
), ZPixmap
, 0, NULL
, width
, height
, 8, 0);
576 img
->data
= (char *) malloc(height
* img
->bytes_per_line
);
580 void xcImageDestroy(xcImage
*img
)
587 int xcImageGetWidth(xcImage
*img
)
592 int xcImageGetHeight(xcImage
*img
)
597 void xcImagePutPixel(xcImage
*img
, int x
, int y
, u_char r
, u_char g
, u_char b
)
607 XPutPixel(img
, x
, y
, pixel
.i
);
610 void xcImageGetPixel(xcImage
*img
, int x
, int y
, u_char
*r
, u_char
*g
,
617 pixel
.i
= XGetPixel(img
, x
, y
);
622 #endif /* HAVE_CAIRO */