1 
2 /******************************************************************************
3 * MODULE     : picture.cpp
4 * DESCRIPTION: Abstract graphical pictures
5 * COPYRIGHT  : (C) 2013  Joris van der Hoeven
6 *******************************************************************************
7 * This software falls under the GNU general public license version 3 or later.
8 * It comes WITHOUT ANY WARRANTY WHATSOEVER. For details, see the file LICENSE
9 * in the root directory or <http://www.gnu.org/licenses/gpl-3.0.html>.
10 ******************************************************************************/
11 
12 #include "renderer.hpp"
13 #include "analyze.hpp"
14 #include "gui.hpp"
15 #include "image_files.hpp"
16 #include "true_color.hpp"
17 #include "colors.hpp"
18 #include "iterator.hpp"
19 #include "file.hpp"
20 
21 /******************************************************************************
22 * Useful subroutines
23 ******************************************************************************/
24 
mix(color c1,double a1,color c2,double a2)25 color mix (color c1, double a1, color c2, double a2) {
26   return (color) mix (true_color (c1), a1, true_color (c2), a2);
27 }
28 
mix(color c1,double a1,color c2,double a2,color c3,double a3,color c4,double a4)29 color mix (color c1, double a1, color c2, double a2,
30 	   color c3, double a3, color c4, double a4) {
31   return (color) mix (true_color (c1), a1, true_color (c2), a2,
32 		      true_color (c3), a3, true_color (c4), a4);
33 }
34 
35 int
composition_type(composition_mode mode)36 composition_type (composition_mode mode) {
37   switch (mode) {
38   case compose_destination:
39     return 2;
40   case compose_source:
41     return 1;
42   case compose_source_over:
43     return 3;
44   case compose_towards_source:
45     return 3;
46   case compose_alpha_distance:
47     return 3;
48   case compose_add:
49     return 3;
50   case compose_sub:
51     return 3;
52   case compose_mul:
53     return 0;
54   case compose_min:
55     return 0;
56   case compose_max:
57     return 3;
58   default:
59     return 0;
60   }
61 }
62 
63 /******************************************************************************
64 * Default implementations of some virtual routines
65 ******************************************************************************/
66 
67 url
get_name()68 picture_rep::get_name () {
69   return url_none ();
70 }
71 
72 void
translate_origin(int dx,int dy)73 picture_rep::translate_origin (int dx, int dy) {
74   set_origin (get_origin_x () + dx, get_origin_y () + dy);
75 }
76 
77 color
internal_smooth_pixel(double x,double y)78 picture_rep::internal_smooth_pixel (double x, double y) {
79   x -= 0.5; y -= 0.5;
80   int x1= (int) floor (x);
81   int y1= (int) floor (y);
82   int x2= x1 + 1;
83   int y2= y1 + 1;
84   double ix1= x2 - x;
85   double ix2= x - x1;
86   double iy1= y2 - y;
87   double iy2= y - y1;
88   color cx1y1= internal_get_pixel (x1, y1);
89   color cx1y2= internal_get_pixel (x1, y2);
90   color cx2y1= internal_get_pixel (x2, y1);
91   color cx2y2= internal_get_pixel (x2, y2);
92   return mix (cx1y1, ix1*iy1, cx1y2, ix1*iy2,
93 	      cx2y1, ix2*iy1, cx2y2, ix2*iy2);
94 }
95 
96 void
internal_copy_from(int x,int y,picture src,int x1,int y1,int x2,int y2)97 picture_rep::internal_copy_from (int x, int y, picture src,
98                                  int x1, int y1, int x2, int y2)
99 {
100   for (int yy= y1; yy < y2; yy++)
101     for (int xx= x1; xx < x2; xx++)
102       internal_set_pixel (x + xx, y + yy, src->internal_get_pixel (xx, yy));
103 }
104 
105 void
internal_copy_to(int x,int y,picture dest,int x1,int y1,int x2,int y2)106 picture_rep::internal_copy_to (int x, int y, picture dest,
107                                int x1, int y1, int x2, int y2)
108 {
109   for (int yy= y1; yy < y2; yy++)
110     for (int xx= x1; xx < x2; xx++)
111       dest->internal_set_pixel (x + xx, y + yy, internal_get_pixel (xx, yy));
112 }
113 
114 picture
error_picture(int w,int h)115 error_picture (int w, int h) {
116   picture pic= raster_picture (w, h);
117   draw_on (pic, 0x20ff0000, compose_source);
118   return pic;
119 }
120 
121 /******************************************************************************
122 * Cached pictured loading
123 ******************************************************************************/
124 
125 static hashmap<tree,int> picture_count (0);
126 static hashmap<tree,int> picture_blacklist (0);
127 static hashmap<tree,picture> picture_cache;
128 static hashmap<tree,int> picture_stamp (- (int) (((unsigned int) (-1)) >> 1));
129 
130 void
picture_cache_reserve(url file_name,int w,int h)131 picture_cache_reserve (url file_name, int w, int h) {
132   tree key= tuple (file_name->t, as_string (w), as_string (h));
133   picture_count (key) ++;
134   //cout << key << " -> " << picture_count[key] << "\n";
135 }
136 
137 void
picture_cache_release(url file_name,int w,int h)138 picture_cache_release (url file_name, int w, int h) {
139   tree key= tuple (file_name->t, as_string (w), as_string (h));
140   picture_count (key) --;
141   //cout << key << " -> " << picture_count[key] << "\n";
142   if (picture_count [key] <= 0) picture_blacklist (key) ++;
143 }
144 
145 void
picture_cache_clean()146 picture_cache_clean () {
147   static time_t last_gc= 0;
148   if (texmacs_time () - last_gc <= 60000) return;
149   last_gc= texmacs_time ();
150 
151   iterator<tree> it= iterate (picture_blacklist);
152   while (it->busy ()) {
153     tree key= it->next ();
154     if (picture_count [key] <= 0) {
155       picture_count -> reset (key);
156       picture_cache -> reset (key);
157       picture_stamp -> reset (key);
158       //cout << "Removed " << key << "\n";
159     }
160   }
161   picture_blacklist= hashmap<tree,int> ();
162 }
163 
164 static bool
picture_is_cached(url file_name,int w,int h)165 picture_is_cached (url file_name, int w, int h) {
166   tree key= tuple (file_name->t, as_string (w), as_string (h));
167   if (!picture_cache->contains (key)) return false;
168   int loaded= last_modified (file_name, false);
169   int cached= picture_stamp [key];
170   return cached >= loaded;
171 }
172 
173 picture
cached_load_picture(url file_name,int w,int h,bool permanent)174 cached_load_picture (url file_name, int w, int h, bool permanent) {
175   tree key= tuple (file_name->t, as_string (w), as_string (h));
176   if (picture_is_cached (file_name, w, h))
177     return picture_cache [key];
178   //cout << "Loading " << key << "\n";
179   picture pic= load_picture (file_name, w, h);
180   if (permanent || picture_count[key] > 0) {
181     int pic_modif= last_modified (file_name, false);
182     picture_cache (key)= pic;
183     picture_stamp (key)= pic_modif;
184   }
185   return pic;
186 }
187 
188 /******************************************************************************
189 * xpm pictures
190 ******************************************************************************/
191 
192 picture qt_load_xpm (url file_name);
193 
194 picture
load_xpm(url file_name)195 load_xpm (url file_name) {
196   static hashmap<string,picture> cache;
197   string name= as_string (file_name);
198   if (cache->contains (name)) return cache[name];
199 
200 #ifdef QTTEXMACS
201 
202   picture pict= qt_load_xpm (file_name);
203 
204 #else
205 
206   tree t= xpm_load (file_name);
207 
208   // get main info
209   int ok, i=0, j, k, w, h, c, b, x, y;
210   string s= as_string (t[0]);
211   skip_spaces (s, i);
212   ok= read_int (s, i, w);
213   skip_spaces (s, i);
214   ok= read_int (s, i, h) && ok;
215   skip_spaces (s, i);
216   ok= read_int (s, i, c) && ok;
217   skip_spaces (s, i);
218   ok= read_int (s, i, b) && ok;
219   if ((!ok) || (N(t)<(c+1)) || (c<=0)) {
220     failed_error << "file_name= " << file_name << "\n";
221     FAILED ("invalid xpm");
222   }
223 
224   // setup colors
225   string first_name;
226   hashmap<string,color> pmcs(0);
227   for (k=0; k<c; k++) {
228     string s   = as_string (t[k+1]);
229     string name= "";
230     string def = "none";
231     if (N(s)<b) i=N(s);
232     else { name= s(0,b); i=b; }
233     if (k==0) first_name= name;
234 
235     skip_spaces (s, i);
236     if ((i<N(s)) && (s[i]=='s')) {
237       i++;
238       skip_spaces (s, i);
239       while ((i<N(s)) && (s[i]!=' ') && (s[i]!='\t')) i++;
240       skip_spaces (s, i);
241     }
242     if ((i<N(s)) && (s[i]=='c')) {
243       i++;
244       skip_spaces (s, i);
245       j=i;
246       while ((i<N(s)) && (s[i]!=' ') && (s[i]!='\t')) i++;
247       def= locase_all (s (j, i));
248     }
249     pmcs(name)= xpm_color (def);
250   }
251 
252   // setup pixmap
253   picture pict= raster_picture (w, h);
254   draw_on (pict, 0x00646464, compose_source);
255   for (y=0; y<h; y++) {
256     if (N(t) < (y+c+1)) s= "";
257     else s= as_string (t[y+c+1]);
258     for (x=0; x<w; x++) {
259       string name;
260       if (N(s)<(b*(x+1))) name= first_name;
261       else name= s (b*x, b*(x+1));
262       color pmc= pmcs[name];
263       if (!pmcs->contains (name)) pmc= pmcs[first_name];
264       pict->set_pixel (x, h-1-y, pmc);
265     }
266   }
267   pict= as_native_picture (pict);
268 
269 #endif
270 
271   cache (name)= pict;
272   return pict;
273 }
274 
275 /******************************************************************************
276 * Generation of encapsulated postscript
277 ******************************************************************************/
278 
279 string
picture_as_eps(picture pic,int dpi)280 picture_as_eps (picture pic, int dpi) {
281   static const char* d= "0123456789ABCDEF";
282   int w_pt= pic->get_width (), h_pt= pic->get_height ();
283   int ox= pic->get_origin_x (), oy= pic->get_origin_y ();
284   string r;
285 
286   string sw= as_string (w_pt);
287   string sh= as_string (h_pt);
288   r << "%!PS-Adobe-3.0 EPSF-3.0\n%%Creator: TeXmacs\n%%BoundingBox: 0 0 "
289     << sw << " " << sh
290     << "\n\n% Created by picture_as_eps ()\n\n%%BeginProlog\nsave\n"
291     << "countdictstack\nmark\nnewpath\n/showpage {} def\n/setpagedevice "
292     << "{pop} def\n%%EndProlog\n%%Page 1 1\n"
293     << "/max { 2 copy lt { exch } if pop } bind def\n"
294     << "/ImageWidth " << sw
295     << " def\n/ImageHeight " << sh << " def\nImageWidth ImageHeight max "
296     << "ImageWidth ImageHeight max scale\n\n/ImageDatas\n\tcurrentfile\n\t"
297     << "<< /Filter /ASCIIHexDecode >>\n\t/ReusableStreamDecode\n\tfilter\n";
298 
299   int v, i= 0, j= 0, k= 0, l= 0;
300   bool alpha= false;
301   for (j=0; j < h_pt; j++)
302     for (i=0; i < w_pt; i++) {
303       color col= pic->get_pixel (i - ox, h_pt - 1 - j - oy);
304       int rr, gg, bb, aa;
305       get_rgb_color (col, rr, gg, bb, aa);
306       if (aa != 255) alpha= true;
307     }
308 
309   string mask;
310   for (j= 0; j < h_pt; j++) {
311     for (i=0; i < w_pt; i++) {
312       l++;
313       color col= pic->get_pixel (i - ox, h_pt - 1 - j - oy);
314       int rr, gg, bb, aa;
315       get_rgb_color (col, rr, gg, bb, aa);
316       rr= (rr * aa + 255 * (255 - aa)) / 255;
317       gg= (gg * aa + 255 * (255 - aa)) / 255;
318       bb= (bb * aa + 255 * (255 - aa)) / 255;
319       r << as_hexadecimal (rr, 2);
320       r << as_hexadecimal (gg, 2);
321       r << as_hexadecimal (bb, 2);
322       if (l > 12) {
323         r << "\n";
324         l= 0;
325       }
326     }
327     if (alpha) {
328       v = 0;
329       for (i=0; i < w_pt; i++) {
330         color col= pic->get_pixel (i - ox, h_pt - 1 - j - oy);
331         int rr, gg, bb, aa;
332         get_rgb_color (col, rr, gg, bb, aa);
333         v += (aa <= 32) << (3 - i % 4);
334         if (i % 4 == 3 || i + 1 == w_pt) {
335           mask << d[v];
336           v= 0;
337           k++;
338           // Padding of the image data mask
339           if (i + 1 == w_pt && k % 2 == 1) {
340             mask << d[0];
341             k++;
342           }
343           // Code layout
344           if (k >= 78) {
345             mask << "\n";
346             k= 0;
347           }
348         }
349       }
350     }
351   }
352   r << ">\ndef\n\n";
353 
354   if (alpha) {
355     r << "/MaskDatas\n\tcurrentfile\n\t<< /Filter /ASCIIHexDecode >>\n"
356       << "\t/ReusableStreamDecode\n\tfilter\n"
357       << mask
358       << ">\ndef\n\n"
359       << "/TheMask\n<<\n\t/ImageType\t1\n\t/Width\t\tImageWidth\n\t/Height\t"
360       << "\tImageHeight\n\t/BitsPerComponent 1\n\t/Decode [ 0 1 ]\n\t"
361       << "/ImageMatrix [ ImageWidth 0 0 ImageWidth neg 0 ImageHeight ]\n\t"
362       << "/DataSource MaskDatas\n>> def\n\n";
363   }
364   r << "/TheImage\n<<\n\t/ImageType\t1\n\t/Width\t\tImageWidth\n\t/Height\t"
365     << "\tImageHeight\n\t/BitsPerComponent 8\n\t/Decode [ 0 1 0 1 0 1 ]\n\t"
366     << "/ImageMatrix [ ImageWidth 0 0 ImageWidth neg 0 ImageHeight ]\n\t"
367     << "/DataSource ImageDatas\n>> def\n\n"
368     << "/DeviceRGB setcolorspace\n";
369   if (alpha) {
370     r << "<<\n\t/ImageType 3\n\t/InterleaveType 3\n\t/DataDict TheImage\n"
371       << "\t/MaskDict TheMask\n>>";
372   }
373   else {
374     r << "\tTheImage";
375   }
376   r << "\nimage\nshowpage\n%%Trailer\ncleartomark\ncountdictstack\n"
377     << "exch sub { end } repeat\nrestore\n%%EOF\n";
378 
379   return r;
380 }
381