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