1 /*  Part of XPCE --- The SWI-Prolog GUI toolkit
2 
3     Author:        Jan Wielemaker and Anjo Anjewierden
4     E-mail:        jan@swi.psy.uva.nl
5     WWW:           http://www.swi.psy.uva.nl/projects/xpce/
6     Copyright (c)  1985-2002, University of Amsterdam
7     All rights reserved.
8 
9     Redistribution and use in source and binary forms, with or without
10     modification, are permitted provided that the following conditions
11     are met:
12 
13     1. Redistributions of source code must retain the above copyright
14        notice, this list of conditions and the following disclaimer.
15 
16     2. Redistributions in binary form must reproduce the above copyright
17        notice, this list of conditions and the following disclaimer in
18        the documentation and/or other materials provided with the
19        distribution.
20 
21     THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
22     "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
23     LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS
24     FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE
25     COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,
26     INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
27     BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
28     LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
29     CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30     LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN
31     ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
32     POSSIBILITY OF SUCH DAMAGE.
33 */
34 
35 #include <h/kernel.h>
36 #include <h/graphics.h>
37 #include <h/unix.h>
38 
39 #include <math.h>
40 #ifndef M_PI
41 #define M_PI 3.14159265358979323846
42 #endif
43 
44 static status drawInImage(Image image, Graphical gr, Point pos);
45 
46 		/********************************
47 		*         CREATE/DESTROY	*
48 		********************************/
49 
50 status
initialiseImage(Image image,SourceSink data,Int w,Int h,Name kind)51 initialiseImage(Image image, SourceSink data, Int w, Int h, Name kind)
52 { Name name = FAIL;
53 
54   if ( isDefault(data) )
55     data = (SourceSink) NIL;
56 
57   if ( notNil(data) && hasGetMethodObject(data, NAME_name) )
58     name = get(data, NAME_name, EAV);
59   if ( !name )
60     name = NIL;
61 
62   assign(image, name,       name);
63   assign(image, background, DEFAULT);
64   assign(image, foreground, DEFAULT);
65   ws_init_image(image);
66 
67   if ( isNil(data) || notDefault(w) || notDefault(h) || notDefault(kind) )
68   { if ( isDefault(w) )    w = toInt(16);
69     if ( isDefault(h) )    h = toInt(16);
70     if ( isDefault(kind) ) kind = NAME_bitmap;
71 
72     assign(image, kind,   kind);
73     assign(image, file,   NIL);
74     assign(image, depth,  kind == NAME_bitmap ? ONE : (Int) DEFAULT);
75     assign(image, size,	  newObject(ClassSize, w, h, EAV));
76     assign(image, access, NAME_both);
77   } else
78   { assign(image, kind,	  NAME_bitmap);
79     assign(image, file,	  data);
80     assign(image, depth,  ONE);
81     assign(image, size,	  newObject(ClassSize, EAV));
82     TRY(loadImage(image, DEFAULT, DEFAULT));
83     assign(image, access, NAME_read);
84   }
85 
86   if ( notNil(name) )
87   { protectObject(image);
88     appendHashTable(ImageTable, name, image);
89   }
90 
91   succeed;
92 }
93 
94 
95 static Image
getLookupImage(Class class,Any from)96 getLookupImage(Class class, Any from)
97 { if ( !isName(from) )
98     from = qadGetv(from, NAME_name, 0, NULL);
99 
100   answer(getMemberHashTable(ImageTable, from));
101 }
102 
103 
104 static status
unlinkImage(Image image)105 unlinkImage(Image image)
106 { XcloseImage(image, DEFAULT);
107   ws_destroy_image(image);
108 
109   if ( notNil(image->bitmap) && image->bitmap->image == image )
110   { BitmapObj bm = image->bitmap;
111 
112     assign(image, bitmap, NIL);
113     freeObject(bm);
114   }
115 
116   if ( notNil(image->name) )
117     deleteHashTable(ImageTable, image->name);
118 
119   succeed;
120 }
121 
122 
123 static BitmapObj
getContainedInImage(Image image)124 getContainedInImage(Image image)
125 { if ( notNil(image->bitmap) )
126     return image->bitmap;
127 
128   fail;
129 }
130 
131 		/********************************
132 		*           CONVERSION		*
133 		********************************/
134 
135 Image
getConvertImage(Class class,Any obj)136 getConvertImage(Class class, Any obj)
137 { Image image;
138   Name name;
139 
140   if ( (image = getConvertObject(class, obj)) )
141   { if ( instanceOfObject(image, ClassImage) )
142       answer(image);
143 
144     obj = image;
145   }
146 
147   if ( instanceOfObject(obj, ClassBitmap) )
148     answer(((BitmapObj)obj)->image);
149 
150   if ( instanceOfObject(obj, ClassRC) )
151   { RC rc = obj;
152 
153     if ( (image = getMemberHashTable(ImageTable, rc->name)) )
154       answer(image);
155 
156     answer(answerObject(ClassImage, obj, EAV));
157   }
158 
159   if ( (name = checkType(obj, TypeName, class)) )
160   { if ( (image = getMemberHashTable(ImageTable, name)) )
161       answer(image);
162     else
163       answer(answerObject(ClassImage, name, EAV));
164   }
165 
166   if ( instanceOfObject(obj, ClassGraphical) )
167   { Graphical gr = obj;
168     Image img;
169 
170     ComputeGraphical(gr);
171     if ( (img = newObject(ClassImage, NIL, gr->area->w, gr->area->h, EAV)) )
172     { drawInImage(img, gr, answerObject(ClassPoint, EAV));
173       answer(img);
174     }
175   }
176 
177   fail;
178 }
179 
180 
181 		/********************************
182 		*            CHANGES		*
183 		********************************/
184 
185 	/* TBD: function! */
186 #define CHANGING_IMAGE(img, code) \
187   { BitmapObj _b = (img)->bitmap; \
188  \
189     code; \
190  \
191     if ( notNil(_b) ) \
192     { Size _s = (img)->size; \
193       Area _a = _b->area; \
194  \
195       if ( _s->w != _a->w || _s->h != _a->h ) \
196       { Int _w = _a->w, _h = _a->h; \
197  \
198 	assign(_a, w, _s->w); \
199 	assign(_a, h, _s->h); \
200 	changedAreaGraphical(_b, _a->x, _a->y, _w, _h); \
201       } \
202     } \
203   }
204 
205 
206 		/********************************
207 		*           LOAD/STORE		*
208 		********************************/
209 
210 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
211 Store/load images to/form file. Format:
212 
213 <image>	::= <pce-slots>
214 	    'X' <image> | 'O'
215 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
216 
217 static status
storeImage(Image image,FileObj file)218 storeImage(Image image, FileObj file)
219 { TRY( storeSlotsObject(image, file) );
220 
221   if ( isNil(image->file) )
222     return ws_store_image(image, file);
223   else
224   { Sputc('O', file->fd);
225     succeed;
226   }
227 }
228 
229 
230 static status
loadFdImage(Image image,IOSTREAM * fd,ClassDef def)231 loadFdImage(Image image, IOSTREAM *fd, ClassDef def)
232 { FileObj file;
233 
234   TRY( loadSlotsObject(image, fd, def) );
235   ws_init_image(image);
236 
237 					/* convert old path-representation */
238   if ( instanceOfObject((file=(FileObj)image->file), ClassFile) &&
239        isAbsoluteFile(file) &&
240        getBaseNameFile(file) == image->name )
241   { assign(file, path, file->name);
242     assign(file, name, image->name);
243   }
244 
245   switch( Sgetc(fd) )
246   { case 'O':				/* no image */
247       break;
248     case 'X':
249       return loadXImage(image, fd);
250     case 'P':
251       return loadPNMImage(image, fd);
252   }
253 
254   succeed;
255 }
256 
257 
258 		/********************************
259 		*          XOPEN/XCLOSE		*
260 		********************************/
261 
262 
263 status
XopenImage(Image image,DisplayObj d)264 XopenImage(Image image, DisplayObj d)
265 { if ( image->bits )			/* built-in.  See stdImage() */
266   { switch(image->bits->type)
267     { case XBM_DATA:
268 	ws_create_image_from_x11_data(image,
269 				      image->bits->bits.xbm,
270 				      valInt(image->size->w),
271 				      valInt(image->size->h));
272         break;
273       case XPM_DATA:
274 	ws_create_image_from_xpm_data(image,
275 				      image->bits->bits.xpm,
276 				      d);
277 	break;
278       default:
279 	assert(0);
280     }
281 			/* Windows already does the registration */
282     if ( getExistingXrefObject(image, d) )
283       succeed;
284   }
285 
286   return ws_open_image(image, d);
287 }
288 
289 
290 status
XcloseImage(Image image,DisplayObj d)291 XcloseImage(Image image, DisplayObj d)
292 { ws_close_image(image, d);
293 
294   succeed;
295 }
296 
297 		 /*******************************
298 		 *	    COLOURMAP		*
299 		 *******************************/
300 
301 static ColourMap
getColourMapImage(Image image)302 getColourMapImage(Image image)
303 { if ( image->kind != NAME_bitmap )
304     return ws_colour_map_for_image(image);
305 
306   fail;
307 }
308 
309 
310 		/********************************
311 		*         FILE OPERATIONS	*
312 		********************************/
313 
314 status
loadImage(Image image,SourceSink file,CharArray path)315 loadImage(Image image, SourceSink file, CharArray path)
316 { status rval;
317 
318   if ( notDefault(file) )
319     assign(image, file, file);
320 
321   if ( isNil(image->file) )
322     fail;
323 
324   if ( instanceOfObject(image->file, ClassFile) )
325   { if ( isDefault(path) )
326       TRY(path = getClassVariableValueObject(image, NAME_path));
327 
328     TRY(send(image->file, NAME_find, path, NAME_read, EAV));
329   }
330 
331   CHANGING_IMAGE(image,
332 		 (rval = ws_load_image_file(image)));
333 
334   return rval;
335 }
336 
337 
338 static status
saveImage(Image image,SourceSink file,Name fmt)339 saveImage(Image image, SourceSink file, Name fmt)
340 { if ( isDefault(file) )
341     file = image->file;
342   if ( isDefault(fmt) )
343     fmt = NAME_xbm;
344 
345   if ( isNil(file) )
346     return errorPce(image, NAME_noFile);
347 
348   return ws_save_image_file(image, file, fmt);
349 }
350 
351 
352 
353 		/********************************
354 		*        EDIT OPERATIONS	*
355 		********************************/
356 
357 static status
verifyAccessImage(Image image,Name sel)358 verifyAccessImage(Image image, Name sel)
359 { if ( image->access != NAME_both )
360     return errorPce(image, NAME_readOnly);
361 
362   if ( isNil(image->display) )
363     assign(image, display, CurrentDisplay(image));
364 
365   openDisplay(image->display);
366 
367   succeed;
368 }
369 
370 
371 static status
changedImageImage(Image image,Int x,Int y,Int w,Int h)372 changedImageImage(Image image, Int x, Int y, Int w, Int h)
373 { if ( notNil(image->bitmap))
374     return changedImageGraphical(image->bitmap, x, y, w, h);
375 
376   succeed;
377 }
378 
379 
380 static status
changedEntireImageImage(Image image)381 changedEntireImageImage(Image image)
382 { if ( notNil(image->bitmap))
383     return changedImageGraphical(image->bitmap, ZERO, ZERO,
384 				 image->size->w, image->size->h);
385 
386   ws_destroy_image(image);		/* remove memory copy */
387 
388   succeed;
389 }
390 
391 
392 static status
inImage(Image image,Int x,Int y)393 inImage(Image image, Int x, Int y)
394 { if ( valInt(x) >= 0 && valInt(y) >= 0 &&
395        valInt(x) < valInt(image->size->w) &&
396        valInt(y) < valInt(image->size->h) )
397     succeed;
398 
399   fail;
400 }
401 
402 
403 static status
clearImage(Image image)404 clearImage(Image image)
405 { TRY( verifyAccessImage(image, NAME_clear) );
406 
407   CHANGING_IMAGE(image,
408 		 if ( image->size->w != ZERO && image->size->h != ZERO &&
409 		      notNil(image->display) &&
410 		      getExistingXrefObject(image, image->display) != NULL )
411 	         { int w = valInt(image->size->w);
412 		   int h = valInt(image->size->h);
413 
414 		   d_image(image, 0, 0, w, h);
415 		   d_modify();
416 		   r_clear(0, 0, w, h);
417 		   d_done();
418 		   changedEntireImageImage(image);
419 		 });
420 
421   succeed;
422 }
423 
424 
425 static status
resizeImage(Image image,Int w,Int h)426 resizeImage(Image image, Int w, Int h)
427 { TRY( verifyAccessImage(image, NAME_resize) );
428 
429   CHANGING_IMAGE(image,
430 		 ws_resize_image(image, w, h));
431 
432   succeed;
433 }
434 
435 
436 static status
copyImage(Image image,Image i2)437 copyImage(Image image, Image i2)
438 { Int w = i2->size->w;
439   Int h = i2->size->h;
440 
441   TRY(verifyAccessImage(image, NAME_copy));
442 
443   CHANGING_IMAGE(image,
444     TRY(resizeImage(image, w, h));
445 
446     d_image(image, 0, 0, valInt(w), valInt(h));
447     d_modify();
448     r_image(i2, 0, 0, 0, 0, valInt(w), valInt(h), OFF);
449     d_done();
450     changedEntireImageImage(image););
451 
452 
453   succeed;
454 }
455 
456 
457 static status
drawInImage(Image image,Graphical gr,Point pos)458 drawInImage(Image image, Graphical gr, Point pos)
459 { Int oldx, oldy;
460   Device dev;
461   int x, y, w, h;
462   int iw = valInt(image->size->w);
463   int ih = valInt(image->size->h);
464   int m;
465   Area a;
466   BoolObj olddisplayed = gr->displayed;
467 
468   TRY(verifyAccessImage(image, NAME_drawIn));
469 
470   if ( notDefault(pos) )
471   { oldx = gr->area->x;
472     oldy = gr->area->y;
473     dev = gr->device;
474     gr->device = NIL;
475     setGraphical(gr, pos->x, pos->y, DEFAULT, DEFAULT);
476   } else
477   { oldx = oldy = DEFAULT;
478     dev = NIL;				/* keep compiler happy */
479   }
480 
481   displayedGraphical(gr, ON);
482   ComputeGraphical(gr);
483   x = valInt(gr->area->x);
484   y = valInt(gr->area->y);
485   w = valInt(gr->area->w);
486   h = valInt(gr->area->h);
487   NormaliseArea(x,y,w,h);
488 
489   if ( x < 0 )				/* clip and normalise the area */
490   { w += x;
491     x = 0;
492   } else if ( x > iw )
493     goto out;
494 
495   if ( y < 0 )
496   { h += y;
497     y = 0;
498   } else if ( y > ih )
499     goto out;
500 
501   if ( w < 0 || h < 0 )
502     goto out;
503 
504   if ( x+w > iw )
505     w = iw - x;
506   if ( y+h > ih )
507     h = ih - y;
508 
509   if ( (m=get_extension_margin_graphical(gr)) )
510   { x -= m; y -= m; w += m*2; h += 2*m;
511   }
512 
513   a = answerObject(ClassArea, toInt(x), toInt(y), toInt(w), toInt(h), EAV);
514   CHANGING_IMAGE(image,
515     d_image(image, x, y, w, h);
516     d_modify();
517     RedrawArea(gr, a);			/* Was gr->area */
518     d_done();
519     if ( notNil(image->bitmap) )
520       changedImageGraphical(image->bitmap,
521 			    toInt(x), toInt(y), toInt(w), toInt(h)););
522   doneObject(a);
523 
524 out:
525   displayedGraphical(gr, olddisplayed);
526   if ( notDefault(oldx) )
527   { setGraphical(gr, oldx, oldy, DEFAULT, DEFAULT);
528     gr->device = dev;
529   }
530 
531   succeed;
532 }
533 
534 		/********************************
535 		*            FILLING		*
536 		********************************/
537 
538 status
fillImage(Image image,Any pattern,Area area)539 fillImage(Image image, Any pattern, Area area)
540 { int x, y, w, h;
541 
542   TRY(verifyAccessImage(image, NAME_fill));
543 
544   if ( isDefault(area) )
545   { x = y = 0;
546     w = valInt(image->size->w);
547     h = valInt(image->size->h);
548   } else
549   { x = valInt(area->x);
550     y = valInt(area->y);
551     w = valInt(area->w);
552     h = valInt(area->h);
553 
554     NormaliseArea(x, y, w, h);
555     if ( x < 0 ) w += x, x = 0;
556     if ( y < 0 ) h += y, y = 0;
557     if ( x+w > valInt(image->size->w) ) w = valInt(image->size->w) - x;
558     if ( y+h > valInt(image->size->h) ) h = valInt(image->size->h) - y;
559   }
560 
561   if ( w > 0 && h > 0 )
562   { CHANGING_IMAGE(image,
563 	  d_image(image, 0, 0, valInt(image->size->w), valInt(image->size->h));
564 	  d_modify();
565 	  r_fill(x, y, w, h, pattern);
566 	  d_done();
567 	  changedEntireImageImage(image));
568   }
569 
570   succeed;
571 }
572 
573 
574 		/********************************
575 		*           PIXELS		*
576 		********************************/
577 
578 
579 static status
pixelImage(Image image,Int X,Int Y,Any val)580 pixelImage(Image image, Int X, Int Y, Any val)
581 { int x = valInt(X);
582   int y = valInt(Y);
583 
584   TRY( verifyAccessImage(image, NAME_pixel) );
585 
586   if ( inImage(image, X, Y) )
587   { if ( (image->kind == NAME_bitmap && !instanceOfObject(val, ClassBool)) ||
588 	 (image->kind == NAME_pixmap && !instanceOfObject(val, ClassColour)) )
589       return errorPce(image, NAME_pixelMismatch, val);
590 
591     CHANGING_IMAGE(image,
592 	  d_image(image, 0, 0, valInt(image->size->w), valInt(image->size->h));
593 	  d_modify();
594 	  r_pixel(x, y, val);
595 	  d_done();
596 	  changedImageImage(image, X, Y, ONE, ONE));
597 
598     succeed;
599   }
600 
601   fail;
602 }
603 
604 
605 static status
setPixelImage(Image image,Int x,Int y)606 setPixelImage(Image image, Int x, Int y)
607 { if ( image->kind == NAME_bitmap )
608     return pixelImage(image, x, y, ON);
609   else
610     return pixelImage(image, x, y, image->foreground);
611 }
612 
613 
614 static status
clearPixelImage(Image image,Int x,Int y)615 clearPixelImage(Image image, Int x, Int y)
616 { if ( image->kind == NAME_bitmap )
617     return pixelImage(image, x, y, OFF);
618   else
619     return pixelImage(image, x, y, image->background);
620 }
621 
622 
623 static status
invertPixelImage(Image image,Int x,Int y)624 invertPixelImage(Image image, Int x, Int y)
625 { TRY(verifyAccessImage(image, NAME_invertPixel));
626 
627   if ( inImage(image, x, y) )
628   { CHANGING_IMAGE(image,
629 	d_image(image, 0, 0, valInt(image->size->w), valInt(image->size->h));
630 	d_modify();
631 	r_complement_pixel(valInt(x), valInt(y));
632 	d_done();
633 	changedImageImage(image, x, y, ONE, ONE));
634   }
635   succeed;
636 }
637 
638 
639 static status
invertImage(Image image)640 invertImage(Image image)
641 { TRY(verifyAccessImage(image, NAME_invert));
642 
643   CHANGING_IMAGE(image,
644 	d_image(image, 0, 0, valInt(image->size->w), valInt(image->size->h));
645 	d_modify();
646 	r_complement(0, 0, valInt(image->size->w), valInt(image->size->h));
647 	d_done();
648 	changedEntireImageImage(image));
649 
650   succeed;
651 }
652 
653 
654 static Any
getPixelImage(Image image,Int x,Int y)655 getPixelImage(Image image, Int x, Int y)
656 { if ( inImage(image, x, y) )
657   { Any result;
658     d_image(image, 0, 0, valInt(image->size->w), valInt(image->size->h));
659 
660     if ( image->kind == NAME_bitmap )
661       result = (r_get_mono_pixel(valInt(x), valInt(y)) ? ON : OFF);
662     else
663     { unsigned long pixel;
664 
665       pixel = r_get_pixel(valInt(x), valInt(y));
666       if ( pixel == NoPixel )
667 	result = FAIL;
668       else
669 	result = ws_pixel_to_colour(image->display, pixel);
670     }
671     d_done();
672 
673     answer(result);
674   }
675 
676   fail;
677 }
678 
679 
680 static status
maskImage(Image image,Image mask)681 maskImage(Image image, Image mask)
682 { assign(image, mask, mask);
683 
684   if ( notNil(image->bitmap) )
685     updateSolidBitmap(image->bitmap);
686 
687   ws_prepare_image_mask(image);
688 
689   return changedEntireImageImage(image);
690 }
691 
692 
693 		/********************************
694 		*      LOGICAL OPERATIONS	*
695 		********************************/
696 
697 static status
opImage(Image image,Image i2,Name op,Point pos)698 opImage(Image image, Image i2, Name op, Point pos)
699 { int x, y;
700 
701   TRY(verifyAccessImage(image, op));
702   if ( notDefault(pos) )
703   { x = valInt(pos->x);
704     y = valInt(pos->y);
705   } else
706     x = y = 0;
707 
708   CHANGING_IMAGE(image,
709 	d_image(image, x, y, valInt(image->size->w), valInt(image->size->h));
710 	d_modify();
711 	r_op_image(i2, 0, 0, x, y, valInt(i2->size->w), valInt(i2->size->h),
712 		   op);
713 	d_done();
714 	changedEntireImageImage(image));
715 
716   succeed;
717 }
718 
719 
720 static status
orImage(Image image,Image i2,Point pos)721 orImage(Image image, Image i2, Point pos)
722 { return opImage(image, i2, NAME_or, pos);
723 }
724 
725 
726 static status
andImage(Image image,Image i2,Point pos)727 andImage(Image image, Image i2, Point pos)
728 { return opImage(image, i2, NAME_and, pos);
729 }
730 
731 
732 static status
xorImage(Image image,Image i2,Point pos)733 xorImage(Image image, Image i2, Point pos)
734 { return opImage(image, i2, NAME_xor, pos);
735 }
736 
737 
738 		/********************************
739 		*        GET SUB IMAGES		*
740 		********************************/
741 
742 static Image
getClipImage(Image image,Area area)743 getClipImage(Image image, Area area)
744 { int x, y, w, h;
745   Image i2;
746 
747   if ( isDefault(area) )
748   { x = y = 0;
749     w = valInt(image->size->w);
750     h = valInt(image->size->h);
751   } else
752   { x = valInt(area->x); y = valInt(area->y);
753     w = valInt(area->w); h = valInt(area->h);
754   }
755 
756   i2 = answerObject(ClassImage, NIL, toInt(w), toInt(h), image->kind, EAV);
757 
758   if ( notNil(image->hot_spot) )
759   { int hx = valInt(image->hot_spot->x) - x;
760     int hy = valInt(image->hot_spot->y) - y;
761 
762     if ( hx >= 0 && hx <= w && hy >= 0 && hy <= h )
763       assign(i2, hot_spot, newObject(ClassPoint, toInt(hx), toInt(hy), EAV));
764   }
765   if ( notNil(image->mask) )
766     assign(i2, mask, getClipImage(image->mask, area));
767 
768   CHANGING_IMAGE(i2,
769     d_image(i2, 0, 0, w, h);
770     d_modify();
771     r_image(image, x, y, 0, 0, w, h, OFF);
772     d_done();
773     changedEntireImageImage(i2););
774 
775   answer(i2);
776 }
777 
778 
779 Image
getMonochromeImage(Image image)780 getMonochromeImage(Image image)
781 { if ( image->kind == NAME_bitmap )
782     answer(image);
783 
784   answer(ws_monochrome_image(image));
785 }
786 
787 
788 static Image
getScaleImage(Image image,Size size)789 getScaleImage(Image image, Size size)
790 { Image i2;
791 
792   if ( equalSize(size, image->size) )	/* just make a copy */
793     return getClipImage(image, DEFAULT);
794   if ( size->w == ZERO || size->h == ZERO )
795     return answerObject(ClassImage, NIL, size->w, size->h, image->kind, EAV);
796 
797   i2 = ws_scale_image(image, valInt(size->w), valInt(size->h));
798 
799   if ( notNil(image->mask) )
800   { Image m = getScaleImage(image->mask, size);
801 
802     if ( m )
803       assign(i2, mask, m);
804   }
805 
806   if ( notNil(image->hot_spot) )
807   { int hx = (valInt(image->hot_spot->x) * valInt(size->w)) /
808 						    valInt(image->size->w);
809     int hy = (valInt(image->hot_spot->y) * valInt(size->h)) /
810 						    valInt(image->size->h);
811     assign(i2, hot_spot, newObject(ClassPoint, toInt(hx), toInt(hy), EAV));
812   }
813 
814   answer(i2);
815 }
816 
817 
818 static Image
getRotateImage(Image image,Real degrees)819 getRotateImage(Image image, Real degrees)
820 { float a = (float)valReal(degrees);
821   Image rimg;
822 
823   a -= (float)(((int)a / 360)*360);
824   if ( a < 0.0 )				/* normalise 0<=a<360 */
825     a += 360.0;
826   else if ( a == 0.0 )				/* just copy */
827     answer(getClipImage(image, DEFAULT));
828 
829   rimg = ws_rotate_image(image, a);
830 
831   if ( rimg )
832   { if ( notNil(image->hot_spot) )
833     { int hx = valInt(image->hot_spot->x);
834       int hy = valInt(image->hot_spot->y);
835       int nhx, nhy;
836       double rads = (a * M_PI) / 180.0;
837 
838       nhx = rfloat((double)hx * cos(rads) + (double)hy * sin(rads));
839       nhy = rfloat((double)hy * cos(rads) - (double)hx * sin(rads));
840 
841       if ( a <= 90.0 )
842       { nhy += rfloat(sin(rads) * (double)valInt(image->size->w));
843       } else if ( a <= 180.0 )
844       { nhx -= rfloat(cos(rads) * (double)valInt(image->size->w));
845 	nhy += valInt(rimg->size->h);
846       } else if ( a <= 270.0 )
847       { nhx += valInt(rimg->size->w);
848 	nhy -= rfloat(cos(rads) * (double)valInt(image->size->h));
849       } else
850       { nhx -= rfloat(sin(rads) * (double)valInt(image->size->h));
851       }
852 
853       assign(rimg, hot_spot, newObject(ClassPoint, toInt(nhx), toInt(nhy), EAV));
854     }
855 
856     if ( notNil(image->mask) )
857       assign(rimg, mask, getRotateImage(image->mask, degrees));
858   }
859 
860   answer(rimg);
861 }
862 
863 
864 		/********************************
865 		*           POSTSCRIPT		*
866 		********************************/
867 
868 static Area
getBoundingBoxImage(Image image)869 getBoundingBoxImage(Image image)
870 { answer(answerObject(ClassArea,
871 		      ZERO, ZERO, image->size->w, image->size->h, EAV));
872 }
873 
874 
875 static Int
getPostscriptDepthImage(Image image)876 getPostscriptDepthImage(Image image)
877 { if ( image->kind == NAME_bitmap )
878     return ONE;
879   if ( valInt(image->depth) < 3 )	/* 1, 2 */
880     return image->depth;
881   if ( valInt(image->depth) < 8 )	/* 3-7 */
882     return toInt(4);
883 
884   return toInt(8);
885 }
886 
887 
888 static Int
getPostscriptFormatImage(Image image)889 getPostscriptFormatImage(Image image)
890 { if ( image->kind == NAME_bitmap )
891     return NAME_monochrome;
892   else
893     return NAME_colour;			/* may also return greyscale */
894 }
895 
896 
897 
898 
899 		/********************************
900 		*       PREDEFINED IMAGES	*
901 		********************************/
902 
903 #if defined(__WINDOWS__) || defined(HAVE_LIBXPM)
904 #define XPM_PCEIMAGE 1			/* use an XPM image */
905 #endif
906 
907 #include "bitmaps/cycle_bm"
908 #include "bitmaps/mark_bm"
909 #include "bitmaps/nomark_bm"
910 #include "bitmaps/pullright_bm"
911 #include "bitmaps/mark_handle_bm"
912 #include "bitmaps/ms_mark.bm"
913 #include "bitmaps/ms_nomark.bm"
914 #include "bitmaps/ms_left_arrow.bm"
915 #include "bitmaps/ol_pulldown.bm"
916 #include "bitmaps/ol_pullright.bm"
917 #include "bitmaps/ol_cycle.bm"
918 #include "bitmaps/cnode.bm"
919 #include "bitmaps/enode.bm"
920 #include "bitmaps/intarrows.bm"
921 
922 static Image
stdImage(Name name,Image * global,char * bits,int w,int h)923 stdImage(Name name, Image *global, char *bits, int w, int h)
924 { Image image = globalObject(name, ClassImage, name, toInt(w), toInt(h), EAV);
925 
926   assign(image, access, NAME_read);
927   image->bits = alloc(sizeof(*image->bits));
928   image->bits->type = XBM_DATA;
929   image->bits->bits.xbm = (unsigned char *)bits;
930   if ( global )
931     *global = image;
932 
933   return image;
934 }
935 
936 
937 static void
stdXPMImage(Name name,Image * global,char ** bits)938 stdXPMImage(Name name, Image *global, char **bits)
939 { int w, h, colours;
940 
941   if ( sscanf(bits[0], "%d %d %d", &w, &h, &colours) == 3 )
942   { Image image = globalObject(name, ClassImage, name, toInt(w), toInt(h), EAV);
943 
944     if ( colours == 2 )
945     { assign(image, depth, ONE);
946       assign(image, kind, NAME_bitmap);
947     } else
948     { assign(image, kind, NAME_pixmap);
949     }
950 
951     assign(image, access, NAME_read);
952     image->bits = alloc(sizeof(*image->bits));
953     image->bits->type = XPM_DATA;
954     image->bits->bits.xpm = bits;
955 
956     if ( global )
957       *global = image;
958   } else
959     Cprintf("Failed to initialise image %s\n", pp(name));
960 }
961 
962 
963 #ifdef XPM_PCEIMAGE
964 #include "bitmaps/swipl48.xpm"
965 #include "bitmaps/hadjusttile.xpm"
966 #include "bitmaps/vadjusttile.xpm"
967 #include "bitmaps/up.xpm"
968 #include "bitmaps/down.xpm"
969 #include "bitmaps/left.xpm"
970 #include "bitmaps/right.xpm"
971 #include "bitmaps/exclamation.xpm"
972 #else
973 #include "bitmaps/pce.bm"
974 #endif
975 
976 #include "bitmaps/white_bm"
977 #include "bitmaps/grey12_bm"
978 #include "bitmaps/grey25_bm"
979 #include "bitmaps/grey50_bm"
980 #include "bitmaps/grey75_bm"
981 #include "bitmaps/black_bm"
982 
983 static void
greyImage(Name name,int grey,Image * global,char * bits,int w,int h)984 greyImage(Name name, int grey, Image *global,
985 	  char *bits, int w, int h)
986 { Image image;
987 
988   image = stdImage(name, global, bits, w, h);
989 
990   attributeObject(image, NAME_postscriptGrey, toInt(grey));
991 }
992 
993 
994 static void
standardImages(void)995 standardImages(void)
996 { greyImage(NAME_whiteImage,  0,  &WHITE_IMAGE,
997 	    white_bm_bits, white_bm_width, white_bm_height);
998   greyImage(NAME_grey12Image, 12, &GREY12_IMAGE,
999 	    grey12_bm_bits, grey12_bm_width, grey12_bm_height);
1000   greyImage(NAME_grey25Image, 25, &GREY25_IMAGE,
1001 	    grey25_bm_bits, grey25_bm_width, grey25_bm_height);
1002   greyImage(NAME_grey50Image, 50, &GREY50_IMAGE,
1003 	    grey50_bm_bits, grey50_bm_width, grey50_bm_height);
1004   greyImage(NAME_grey75Image, 75, &GREY75_IMAGE,
1005 	    grey75_bm_bits, grey75_bm_width, grey75_bm_height);
1006   greyImage(NAME_blackImage, 100, &BLACK_IMAGE,
1007 	    black_bm_bits, black_bm_width, black_bm_height);
1008 
1009   stdImage(NAME_cycleImage, &CYCLE_IMAGE,
1010 	   cycle_bm_bits, cycle_bm_width, cycle_bm_height);
1011   stdImage(NAME_markImage, &MARK_IMAGE,
1012 	   mark_bm_bits, mark_bm_width, mark_bm_height);
1013   stdImage(NAME_nomarkImage, &NOMARK_IMAGE,
1014 	   nomark_bm_bits, nomark_bm_width, nomark_bm_height);
1015   stdImage(NAME_msMarkImage, &MS_MARK_IMAGE,
1016 	   (char *)ms_mark_bits, ms_mark_width, ms_mark_height);
1017   stdImage(NAME_msNomarkImage, &MS_NOMARK_IMAGE,
1018 	   (char *)ms_nomark_bits, ms_nomark_width, ms_nomark_height);
1019   stdImage(NAME_msLeftArrowImage, NULL,
1020 	   (char *)ms_left_arrow_bits, ms_left_arrow_width, ms_left_arrow_height);
1021   stdImage(NAME_pullRightImage, &PULLRIGHT_IMAGE,
1022 	   pullright_bm_bits, pullright_bm_width, pullright_bm_height);
1023   stdImage(NAME_markHandleImage, &MARK_HANDLE_IMAGE,
1024 	   mark_handle_bm_bits, mark_handle_bm_width, mark_handle_bm_height);
1025   stdImage(NAME_olPullrightImage, NULL,
1026 	   ol_pullright_bits, ol_pullright_width, ol_pullright_height);
1027   stdImage(NAME_olPulldownImage, NULL,
1028 	   ol_pulldown_bits, ol_pulldown_width, ol_pulldown_height);
1029   stdImage(NAME_olCycleImage, NULL,
1030 	   ol_cycle_bits, ol_cycle_width, ol_cycle_height);
1031   stdImage(NAME_treeExpandedImage, NULL,
1032 	   (char*)enode_bits, enode_width, enode_height);
1033   stdImage(NAME_treeCollapsedImage, NULL,
1034 	   (char*)cnode_bits, cnode_width, cnode_height);
1035   stdImage(NAME_intItemImage, &INT_ITEM_IMAGE,
1036 	   (char*)intarrows_bits, intarrows_width, intarrows_height);
1037 #ifdef XPM_PCEIMAGE
1038   stdXPMImage(NAME_pceImage,	     NULL,		  swipl48_xpm);
1039   stdXPMImage(NAME_hadjustTileImage, NULL,		  hadjusttile_xpm);
1040   stdXPMImage(NAME_vadjustTileImage, NULL,		  vadjusttile_xpm);
1041   stdXPMImage(NAME_scrollUpImage,    &SCROLL_UP_IMAGE,	  up_xpm);
1042   stdXPMImage(NAME_scrollDownImage,  &SCROLL_DOWN_IMAGE,  down_xpm);
1043   stdXPMImage(NAME_scrollLeftImage,  &SCROLL_LEFT_IMAGE,  left_xpm);
1044   stdXPMImage(NAME_scrollRightImage, &SCROLL_RIGHT_IMAGE, right_xpm);
1045   stdXPMImage(NAME_exclamationImage, &EXCLAMATION_IMAGE,  exclamation_xpm);
1046 #else
1047   stdImage(NAME_pceImage, NULL,
1048 	   pce_bm_bits, pce_bm_width, pce_bm_height);
1049 #endif
1050 
1051   stdImage(NAME_nullImage, &NULL_IMAGE,
1052 	   NULL, 0, 0);
1053 
1054   ws_system_images();			/* make sure system images exist */
1055 }
1056 
1057 
1058 		 /*******************************
1059 		 *	 CLASS DECLARATION	*
1060 		 *******************************/
1061 
1062 /* Type declarations */
1063 
1064 static char *T_load[] =
1065         { "from=[source_sink]", "path=[char_array]" };
1066 static char *T_drawIn[] =
1067         { "graphical", "at=[point]" };
1068 static char *T_fill[] =
1069         { "image", "[area]" };
1070 static char *T_initialise[] =
1071 	{ "name=[source_sink]*", "width=[int]", "height=[int]",
1072 	  "kind=[{bitmap,pixmap}]" };
1073 static char *T_image_atADpointD[] =
1074         { "image", "at=[point]" };
1075 static char *T_save[] =
1076         { "in=[source_sink]", "format=[{xbm,xpm,pnm,pbm,pgm,ppm,jpeg,gif}]" };
1077 static char *T_postscript[] =
1078         { "landscape=[bool]", "maximum_area=[area]" };
1079 static char *T_resize[] =
1080         { "width=int", "height=int" };
1081 static char *T_xAint_yAint[] =
1082         { "x=int", "y=int" };
1083 static char *T_pixel[] =
1084         { "x=int", "y=int", "value=colour|bool" };
1085 #ifdef O_XLI
1086 static char *T_loadXli[] =
1087 	{ "file=file", "bright=[0..]" };
1088 #endif
1089 
1090 /* Instance Variables */
1091 
1092 static vardecl var_image[] =
1093 { IV(NAME_name, "name*", IV_GET,
1094      NAME_name, "Name of the image"),
1095   IV(NAME_kind, "{bitmap,pixmap}", IV_GET,
1096      NAME_colour, "`bitmap' (0 and 1's) or `pixmap' (coloured)"),
1097   IV(NAME_file, "source_sink*", IV_GET,
1098      NAME_file, "Source (file,resource) from which to load"),
1099   IV(NAME_access, "{read,both}", IV_GET,
1100      NAME_permission, "One of {read, both}"),
1101   IV(NAME_background, "[colour|pixmap]", IV_BOTH,
1102      NAME_colour, "Colour of background (pixmap)"),
1103   IV(NAME_foreground, "[colour|pixmap]", IV_BOTH,
1104      NAME_colour, "Colour of foreground (pixmap)"),
1105   IV(NAME_depth, "[int]", IV_GET,
1106      NAME_colour, "Number of bits/pixel"),
1107   IV(NAME_size, "size", IV_GET,
1108      NAME_dimension, "Size of the image in pixels"),
1109   IV(NAME_display, "display*", IV_GET,
1110      NAME_organisation, "X-Display this image belongs to"),
1111   IV(NAME_bitmap, "bitmap*", IV_GET,
1112      NAME_organisation, "Access both and displayed on this bitmap"),
1113   IV(NAME_hotSpot, "point*", IV_BOTH,
1114      NAME_dimension, "Hot-spot position"),
1115   SV(NAME_mask, "image*", IV_GET|IV_STORE, maskImage,
1116      NAME_area, "Image for masked painting"),
1117   IV(NAME_wsRef, "alien:WsRef", IV_NONE,
1118      NAME_storage, "Window System Reference"),
1119   IV(NAME_bits, "alien:char*", IV_NONE,
1120      NAME_storage, "Data for built-in images")
1121 };
1122 
1123 /* Send Methods */
1124 
1125 #ifdef O_XLI
1126 extern status loadXliImage(Image image, FileObj file, Int bright);
1127 #endif
1128 
1129 static senddecl send_image[] =
1130 { SM(NAME_initialise, 4, T_initialise, initialiseImage,
1131      DEFAULT, "Create from name, [width, height, kind]"),
1132   SM(NAME_unlink, 0, NULL, unlinkImage,
1133      DEFAULT, "Destroy private memory and window-system resources"),
1134   SM(NAME_copy, 1, "from=image", copyImage,
1135      NAME_copy, "Copy contents of argument in image"),
1136   SM(NAME_drawIn, 2, T_drawIn, drawInImage,
1137      NAME_copy, "Paint graphical in image [at point]"),
1138   SM(NAME_resize, 2, T_resize, resizeImage,
1139      NAME_dimension, "Resize image to width, height"),
1140   SM(NAME_and, 2, T_image_atADpointD, andImage,
1141      NAME_edit, "Bitwise and with argument"),
1142   SM(NAME_clear, 0, NULL, clearImage,
1143      NAME_edit, "Clear all pixels to 0 or <-background"),
1144   SM(NAME_fill, 2, T_fill, fillImage,
1145      NAME_edit, "Fill rectangular area of image with pattern"),
1146   SM(NAME_invert, 0, NULL, invertImage,
1147      NAME_edit, "Invert all pixels in image"),
1148   SM(NAME_or, 2, T_image_atADpointD, orImage,
1149      NAME_edit, "Bitwise or with argument"),
1150   SM(NAME_xor, 2, T_image_atADpointD, xorImage,
1151      NAME_edit, "Bitwise xor with argument"),
1152   SM(NAME_load, 2, T_load, loadImage,
1153      NAME_file, "Load image from file (searching in path)"),
1154 #ifdef O_XLI
1155   SM(NAME_loadXli, 2, T_loadXli, loadXliImage,
1156      NAME_file, "Load image using xli library"),
1157 #endif
1158   SM(NAME_save, 2, T_save, saveImage,
1159      NAME_file, "Save image to file in specified format"),
1160   SM(NAME_clearPixel, 2, T_xAint_yAint, clearPixelImage,
1161      NAME_pixel, "Clear pixel at x-y (to 0 or background)"),
1162   SM(NAME_invertPixel, 2, T_xAint_yAint, invertPixelImage,
1163      NAME_pixel, "Invert pixel at x-y"),
1164   SM(NAME_pixel, 3, T_pixel, pixelImage,
1165      NAME_pixel, "Set pixel at x-y to bool or colour"),
1166   SM(NAME_setPixel, 2, T_xAint_yAint, setPixelImage,
1167      NAME_pixel, "Set pixel at x-y (to 1 or foreground)"),
1168   SM(NAME_Postscript, 1, "{head,body}", drawPostScriptImage,
1169      NAME_postscript, "Create PostScript"),
1170   SM(NAME_DrawPostScript, 1, "{head,body}", drawPostScriptImage,
1171      NAME_postscript, "Create PostScript"),
1172   SM(NAME_Xclose, 1, "display", XcloseImage,
1173      NAME_x, "Destroy associated window-system resources"),
1174   SM(NAME_Xopen, 1, "display", XopenImage,
1175      NAME_x, "Open X-image")
1176 };
1177 
1178 /* Get Methods */
1179 
1180 static getdecl get_image[] =
1181 { GM(NAME_containedIn, 0, "bitmap", NULL, getContainedInImage,
1182      DEFAULT, "Equivalent to <-bitmap if ot @nil"),
1183   GM(NAME_convert, 1, "image", "bitmap|name|resource|graphical", getConvertImage,
1184      DEFAULT, "Convert bitmap or (file-)name"),
1185   GM(NAME_clip, 1, "image", "[area]", getClipImage,
1186      NAME_copy, "Get a subimage"),
1187   GM(NAME_monochrome, 0, "image", NULL, getMonochromeImage,
1188      NAME_copy, "Get monochrome version of pixmap image"),
1189   GM(NAME_scale, 1, "image", "size", getScaleImage,
1190      NAME_copy, "Get copy with different dimensions"),
1191   GM(NAME_rotate, 1, "image", "degrees=real", getRotateImage,
1192      NAME_copy, "Get anti-clockwise rotated copy"),
1193   GM(NAME_lookup, 1, "image", "name|resource", getLookupImage,
1194      NAME_oms, "Lookup in @images table"),
1195   GM(NAME_pixel, 2, "value=bool|colour", T_xAint_yAint, getPixelImage,
1196      NAME_pixel, "Get 0-1 (image) or colour for x-y"),
1197   GM(NAME_colourMap, 0, "colour_map", NULL, getColourMapImage,
1198      NAME_colour, "New colour_map for best display of image"),
1199   GM(NAME_boundingBox, 0, "area", NULL, getBoundingBoxImage,
1200      NAME_postscript, "BoundingBox for PostScript generation"),
1201   GM(NAME_postscript, 2, "string", T_postscript, getPostscriptObject,
1202      NAME_postscript, "New string holding PostScript description"),
1203   GM(NAME_postscriptDepth, 0, "int", NULL, getPostscriptDepthImage,
1204      NAME_postscript, "Depth for PostScript image to be generated"),
1205   GM(NAME_postscriptFormat, 0, "{monochrome,greyscale,colour}",
1206      NULL, getPostscriptFormatImage,
1207      NAME_postscript, "Format of generated PostScript")
1208 };
1209 
1210 /* Resources */
1211 
1212 static classvardecl rc_image[] =
1213 { RC(NAME_path, "string",
1214      "\".:bitmaps:~/lib/bitmaps:$PCEHOME/bitmaps:" /* concat */
1215      "/usr/include/X11/bitmaps\"",
1216      "Search path for loading images")
1217 };
1218 
1219 /* Class Declaration */
1220 
1221 static Name image_termnames[] = { NAME_name };
1222 
1223 ClassDecl(image_decls,
1224           var_image, send_image, get_image, rc_image,
1225           1, image_termnames,
1226           "$Rev$");
1227 
1228 
1229 status
makeClassImage(Class class)1230 makeClassImage(Class class)
1231 { declareClass(class, &image_decls);
1232 
1233   saveStyleClass(class, NAME_external);
1234   setLoadStoreFunctionClass(class, loadFdImage, storeImage);
1235   cloneStyleClass(class, NAME_none);	/* just copy reference */
1236 
1237   ImageTable = globalObject(NAME_images, ClassHashTable, toInt(32), EAV);
1238   standardImages();
1239 
1240   succeed;
1241 }
1242 
1243