more fiddling with edgestyles
[swftools.git] / lib / gfxfilter.c
1 /* gfxfilter.c
2
3    Part of the swftools package.
4
5    Copyright (c) 2010 Matthias Kramm <kramm@quiss.org> 
6  
7    This program is free software; you can redistribute it and/or modify
8    it under the terms of the GNU General Public License as published by
9    the Free Software Foundation; either version 2 of the License, or
10    (at your option) any later version.
11
12    This program is distributed in the hope that it will be useful,
13    but WITHOUT ANY WARRANTY; without even the implied warranty of
14    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15    GNU General Public License for more details.
16
17    You should have received a copy of the GNU General Public License
18    along with this program; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA */
20
21 #include <stdlib.h>
22 #include <stdio.h>
23 #include <string.h>
24 #include <assert.h>
25 #include "mem.h"
26 #include "gfxfilter.h"
27 #include "devices/record.h"
28 #include "q.h"
29
30 typedef struct _internal {
31     gfxfilter_t*filter;
32     gfxdevice_t*out;
33
34     /* for two pass filters: */
35     gfxdevice_t*final_out;
36     int pass;
37     int num_passes;
38     gfxdevice_t record;
39     gfxtwopassfilter_t*twopass;
40 } internal_t;
41
42 static int filter_setparameter(gfxdevice_t*dev, const char*key, const char*value)
43 {
44     internal_t*i = (internal_t*)dev->internal;
45     return i->filter->setparameter(i->filter, key, value, i->out);
46 }
47 static void filter_startpage(gfxdevice_t*dev, int width, int height)
48 {
49     internal_t*i = (internal_t*)dev->internal;
50     i->filter->startpage(i->filter, width, height, i->out);
51 }
52 static void filter_startclip(gfxdevice_t*dev, gfxline_t*line)
53 {
54     internal_t*i = (internal_t*)dev->internal;
55     i->filter->startclip(i->filter, line, i->out);
56 }
57 static void filter_endclip(gfxdevice_t*dev)
58 {
59     internal_t*i = (internal_t*)dev->internal;
60     i->filter->endclip(i->filter, i->out);
61 }
62 static void filter_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
63 {
64     internal_t*i = (internal_t*)dev->internal;
65     i->filter->stroke(i->filter, line, width, color, cap_style, joint_style, miterLimit, i->out);
66 }
67 static void filter_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
68 {
69     internal_t*i = (internal_t*)dev->internal;
70     i->filter->fill(i->filter, line, color, i->out);
71 }
72 static void filter_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
73 {
74     internal_t*i = (internal_t*)dev->internal;
75     i->filter->fillbitmap(i->filter, line, img, matrix, cxform, i->out);
76 }
77 static void filter_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
78 {
79     internal_t*i = (internal_t*)dev->internal;
80     i->filter->fillgradient(i->filter, line, gradient, type, matrix, i->out);
81 }
82 static void filter_addfont(gfxdevice_t*dev, gfxfont_t*font)
83 {
84     internal_t*i = (internal_t*)dev->internal;
85     i->filter->addfont(i->filter, font, i->out);
86 }
87 static void filter_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
88 {
89     internal_t*i = (internal_t*)dev->internal;
90     i->filter->drawchar(i->filter, font, glyphnr, color, matrix, i->out);
91 }
92 static void filter_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
93 {
94     internal_t*i = (internal_t*)dev->internal;
95     i->filter->drawlink(i->filter, line, action, i->out);
96 }
97 static void filter_endpage(gfxdevice_t*dev)
98 {
99     internal_t*i = (internal_t*)dev->internal;
100     i->filter->endpage(i->filter, i->out);
101 }
102 static gfxresult_t* filter_finish(gfxdevice_t*dev)
103 {
104     internal_t*i = (internal_t*)dev->internal;
105     gfxresult_t*r;
106     if(i->filter->finish) {
107         r = i->filter->finish(i->filter, i->out);
108     } else {
109         r = i->out->finish(i->out);
110     }
111     if(i->filter->internal) {
112         free(i->filter->internal);
113         i->filter->internal = 0;
114     }
115     free(i->filter);i->filter=0;
116     free(dev->internal);dev->internal=0;free(dev);
117     return r;
118 }
119
120
121 static int passthrough_setparameter(gfxdevice_t*dev, const char*key, const char*value)
122 {
123     internal_t*i = (internal_t*)dev->internal;
124     return i->out->setparameter(i->out, key, value);
125 }
126 static void passthrough_startpage(gfxdevice_t*dev, int width, int height)
127 {
128     internal_t*i = (internal_t*)dev->internal;
129     i->out->startpage(i->out, width, height);
130 }
131 static void passthrough_startclip(gfxdevice_t*dev, gfxline_t*line)
132 {
133     internal_t*i = (internal_t*)dev->internal;
134     i->out->startclip(i->out, line);
135 }
136 static void passthrough_endclip(gfxdevice_t*dev)
137 {
138     internal_t*i = (internal_t*)dev->internal;
139     i->out->endclip(i->out);
140 }
141 static void passthrough_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
142 {
143     internal_t*i = (internal_t*)dev->internal;
144     i->out->stroke(i->out, line, width, color, cap_style, joint_style, miterLimit);
145 }
146 static void passthrough_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
147 {
148     internal_t*i = (internal_t*)dev->internal;
149     i->out->fill(i->out, line, color);
150 }
151 static void passthrough_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
152 {
153     internal_t*i = (internal_t*)dev->internal;
154     i->out->fillbitmap(i->out, line, img, matrix, cxform);
155 }
156 static void passthrough_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
157 {
158     internal_t*i = (internal_t*)dev->internal;
159     i->out->fillgradient(i->out, line, gradient, type, matrix);
160 }
161 static void passthrough_addfont(gfxdevice_t*dev, gfxfont_t*font)
162 {
163     internal_t*i = (internal_t*)dev->internal;
164     i->out->addfont(i->out, font);
165 }
166 static void passthrough_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
167 {
168     internal_t*i = (internal_t*)dev->internal;
169     i->out->drawchar(i->out, font, glyphnr, color, matrix);
170 }
171 static void passthrough_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
172 {
173     internal_t*i = (internal_t*)dev->internal;
174     i->out->drawlink(i->out, line, action);
175 }
176 static void passthrough_endpage(gfxdevice_t*dev)
177 {
178     internal_t*i = (internal_t*)dev->internal;
179     i->out->endpage(i->out);
180 }
181
182 int discard_setparameter(gfxdevice_t*dev, const char*key, const char*value)
183 {
184     return 0;
185 }
186 static void discard_startpage(gfxdevice_t*dev, int width, int height)
187 {
188 }
189 static void discard_startclip(gfxdevice_t*dev, gfxline_t*line)
190 {
191 }
192 static void discard_endclip(gfxdevice_t*dev)
193 {
194 }
195 static void discard_stroke(gfxdevice_t*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
196 {
197 }
198 static void discard_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
199 {
200 }
201 static void discard_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
202 {
203 }
204 static void discard_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
205 {
206 }
207 static void discard_addfont(gfxdevice_t*dev, gfxfont_t*font)
208 {
209 }
210 static void discard_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
211 {
212 }
213 static void discard_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
214 {
215 }
216 static void discard_endpage(gfxdevice_t*dev)
217 {
218 }
219 static gfxresult_t* discard_finish(gfxdevice_t*dev)
220 {
221     return 0;
222 }
223
224 gfxdevice_t*gfxfilter_apply(gfxfilter_t*_filter, gfxdevice_t*out)
225 {
226     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
227     gfxdevice_t*dev = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
228     gfxfilter_t*filter = (gfxfilter_t*)rfx_alloc(sizeof(gfxfilter_t));
229     memcpy(filter, _filter, sizeof(gfxfilter_t));
230     
231     i->out = out;
232     i->filter = filter;
233     i->pass = 1;
234
235     dev->internal = i;
236     dev->name = filter->name?filter->name:"filter";
237     dev->setparameter = filter->setparameter?filter_setparameter:passthrough_setparameter;
238     dev->startpage = filter->startpage?filter_startpage:passthrough_startpage;
239     dev->startclip = filter->startclip?filter_startclip:passthrough_startclip;
240     dev->endclip = filter->endclip?filter_endclip:passthrough_endclip;
241     dev->stroke = filter->stroke?filter_stroke:passthrough_stroke;
242     dev->fill = filter->fill?filter_fill:passthrough_fill;
243     dev->fillbitmap = filter->fillbitmap?filter_fillbitmap:passthrough_fillbitmap;
244     dev->fillgradient = filter->fillgradient?filter_fillgradient:passthrough_fillgradient;
245     dev->addfont = filter->addfont?filter_addfont:passthrough_addfont;
246     dev->drawchar = filter->drawchar?filter_drawchar:passthrough_drawchar;
247     dev->drawlink = filter->drawlink?filter_drawlink:passthrough_drawlink;
248     dev->endpage = filter->endpage?filter_endpage:passthrough_endpage;
249     dev->finish = filter_finish;
250     return dev;
251 }
252
253 static void setup_twopass(gfxdevice_t*dev, gfxfilter_t*filter)
254 {
255     dev->name = filter->name?filter->name:"filter";
256     dev->setparameter = filter->setparameter?filter_setparameter:passthrough_setparameter;
257     dev->startpage = filter->startpage?filter_startpage:passthrough_startpage;
258     dev->startclip = filter->startclip?filter_startclip:passthrough_startclip;
259     dev->endclip = filter->endclip?filter_endclip:passthrough_endclip;
260     dev->stroke = filter->stroke?filter_stroke:passthrough_stroke;
261     dev->fill = filter->fill?filter_fill:passthrough_fill;
262     dev->fillbitmap = filter->fillbitmap?filter_fillbitmap:passthrough_fillbitmap;
263     dev->fillgradient = filter->fillgradient?filter_fillgradient:passthrough_fillgradient;
264     dev->addfont = filter->addfont?filter_addfont:passthrough_addfont;
265     dev->drawchar = filter->drawchar?filter_drawchar:passthrough_drawchar;
266     dev->drawlink = filter->drawlink?filter_drawlink:passthrough_drawlink;
267     dev->endpage = filter->endpage?filter_endpage:passthrough_endpage;
268 }
269
270 static gfxresult_t* twopass_finish(gfxdevice_t*dev)
271 {
272     internal_t*i = (internal_t*)dev->internal;
273
274     gfxresult_t*r;
275     if(i->filter->finish) {
276         r = i->filter->finish(i->filter, i->out);
277     } else {
278         r = i->out->finish(i->out);
279     }
280
281     if(i->pass == i->num_passes) {
282         free(i->twopass);
283         i->twopass = 0;
284         i->filter = 0;
285         free(i);
286         dev->internal=0;
287         free(dev);
288         return r;
289     }
290
291     /* switch to next pass filter */
292     i->filter = &i->twopass->pass2;
293     setup_twopass(dev, i->filter);
294     dev->finish = twopass_finish;
295
296     if(i->pass == i->num_passes-1) {
297         /* we don't record in the final pass- we just stream out to the 
298            next output device */
299         i->out = i->final_out;
300     } else {
301         // switch to a new tempfile- this only happens for 3 passes or more
302         assert(i->num_passes>2);
303         gfxdevice_record_init(&i->record, /*use tempfile*/1);
304         i->out = &i->record;
305     }
306
307     i->pass++;
308     gfxresult_record_replay(r, dev);
309     r->destroy(r);
310
311     return twopass_finish(dev);
312 }
313
314 gfxdevice_t*gfxtwopassfilter_apply(gfxtwopassfilter_t*_twopass, gfxdevice_t*out)
315 {
316     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
317     gfxdevice_t*dev = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
318
319     gfxtwopassfilter_t*twopass = (gfxtwopassfilter_t*)rfx_alloc(sizeof(gfxtwopassfilter_t));
320     memcpy(twopass, _twopass, sizeof(gfxtwopassfilter_t));
321    
322     gfxdevice_record_init(&i->record, /*use tempfile*/1);
323
324     i->out = &i->record;
325     i->final_out = out;
326     i->twopass = twopass;
327     i->pass = 1;
328     i->num_passes = 2;
329     
330     dev->internal = i;
331    
332     i->filter = &twopass->pass1;
333     setup_twopass(dev, i->filter);
334     dev->finish = twopass_finish;
335
336     return dev;
337 }
338
339 gfxfilterchain_t* gfxfilterchain_parse(const char*_filterexpr)
340 {
341     char*filterexpr = strdup(_filterexpr);    
342     char*f = filterexpr;
343     char*end = filterexpr+strlen(filterexpr);
344     dict_t* params = dict_new2(&charptr_type);
345     char*cmd = 0;
346
347     gfxfilterchain_t*chain = 0;
348     gfxfilterchain_t*next = 0;
349
350     if(!*f)
351         return 0;
352
353     while(1) {
354         char* eq = strchr(f, '=');
355         char* colon = strchr(f, ':');
356         char lastitem = 0;
357         if(!colon) {
358             colon = end;
359             lastitem = 1;
360         }
361         *colon = 0;
362         char*newcmd = 0;
363         char param = 0;
364         
365         /* fixme: change this from a dict_t to gfxparams_t? */
366         if(eq && eq < colon) { // parameter
367             *eq = 0;
368             if(!cmd) {
369                 fprintf(stderr, "Error: need a filter before specifying parameters (%s=%s)\n", f, eq+1);
370                 return 0;
371             }
372             dict_put(params, f, strdup(eq+1));
373             param = 1;
374         } else {
375             newcmd = f;
376         }
377         if(!param || lastitem) {
378             if(!cmd && lastitem) 
379                 cmd = newcmd;
380             assert(cmd);
381             gfxfilterbase_t*f = 0;
382             if(!strcmp(cmd, "maketransparent")) {
383                 char*alphastr = dict_lookup(params, "alpha");
384                 int alpha = 255;
385                 if(alphastr) alpha=atoi(alphastr);
386                 f = malloc(sizeof(gfxfilter_t));
387                 gfxfilter_maketransparent_init((gfxfilter_t*)f, alpha);
388             } else if(!strcmp(cmd, "remove_font_transforms")) {
389                 f = malloc(sizeof(gfxtwopassfilter_t));
390                 gfxtwopassfilter_remove_font_transforms_init((gfxtwopassfilter_t*)f);
391             } else if(!strcmp(cmd, "vectors_to_glyphs")) {
392                 f = malloc(sizeof(gfxtwopassfilter_t));
393                 gfxtwopassfilter_vectors_to_glyphs_init((gfxtwopassfilter_t*)f);
394             } else if(!strcmp(cmd, "one_big_font")) {
395                 f = malloc(sizeof(gfxtwopassfilter_t));
396                 gfxtwopassfilter_one_big_font_init((gfxtwopassfilter_t*)f);
397             } else {
398                 fprintf(stderr, "Unknown filter: %s\n", cmd);
399                 return 0;
400             }
401             dict_clear(params);
402             gfxfilterchain_t*n = rfx_calloc(sizeof(gfxfilterchain_t));
403             if(!chain) {
404                 chain = next = n;
405             } else {
406                 next->next = n;
407                 next = n;
408             }
409             n->filter = f;
410
411             cmd = newcmd;
412             if(lastitem) break;
413         }
414         f = colon+1;
415     }
416     dict_destroy(params);
417     return chain;
418 }
419
420 gfxdevice_t* gfxfilterchain_apply(gfxfilterchain_t*chain, gfxdevice_t*dev)
421 {
422     while(chain) {
423         if(chain->filter->type == gfxfilter_onepass) {
424             dev = gfxfilter_apply((gfxfilter_t*)chain->filter, dev);
425         } else if(chain->filter->type == gfxfilter_twopass) {
426             dev = gfxtwopassfilter_apply((gfxtwopassfilter_t*)chain->filter, dev);
427         } else {
428             fprintf(stderr, "Internal error in gfxfilterchain_apply- unknown filter type %d\n", chain->filter->type);
429         }
430         chain = chain->next;
431     }
432     return dev;
433 }
434
435 void gfxfilterchain_destroy(gfxfilterchain_t*chain)
436 {
437     while(chain) {
438         gfxfilterchain_t*next = chain->next;
439         free(chain);
440         chain = next;
441     }
442 }