added filter support to ruby module
[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
29 typedef struct _internal {
30     gfxfilter_t*filter;
31     gfxdevice_t*out;
32
33     /* for two pass filters: */
34     gfxdevice_t*final_out;
35     int pass;
36     int num_passes;
37     gfxdevice_t record;
38     gfxtwopassfilter_t*twopass;
39 } internal_t;
40
41 static int filter_setparameter(gfxdevice_t*dev, const char*key, const char*value)
42 {
43     internal_t*i = (internal_t*)dev->internal;
44     return i->filter->setparameter(i->filter, key, value, i->out);
45 }
46 static void filter_startpage(gfxdevice_t*dev, int width, int height)
47 {
48     internal_t*i = (internal_t*)dev->internal;
49     i->filter->startpage(i->filter, width, height, i->out);
50 }
51 static void filter_startclip(gfxdevice_t*dev, gfxline_t*line)
52 {
53     internal_t*i = (internal_t*)dev->internal;
54     i->filter->startclip(i->filter, line, i->out);
55 }
56 static void filter_endclip(gfxdevice_t*dev)
57 {
58     internal_t*i = (internal_t*)dev->internal;
59     i->filter->endclip(i->filter, i->out);
60 }
61 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)
62 {
63     internal_t*i = (internal_t*)dev->internal;
64     i->filter->stroke(i->filter, line, width, color, cap_style, joint_style, miterLimit, i->out);
65 }
66 static void filter_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
67 {
68     internal_t*i = (internal_t*)dev->internal;
69     i->filter->fill(i->filter, line, color, i->out);
70 }
71 static void filter_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
72 {
73     internal_t*i = (internal_t*)dev->internal;
74     i->filter->fillbitmap(i->filter, line, img, matrix, cxform, i->out);
75 }
76 static void filter_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
77 {
78     internal_t*i = (internal_t*)dev->internal;
79     i->filter->fillgradient(i->filter, line, gradient, type, matrix, i->out);
80 }
81 static void filter_addfont(gfxdevice_t*dev, gfxfont_t*font)
82 {
83     internal_t*i = (internal_t*)dev->internal;
84     i->filter->addfont(i->filter, font, i->out);
85 }
86 static void filter_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
87 {
88     internal_t*i = (internal_t*)dev->internal;
89     i->filter->drawchar(i->filter, font, glyphnr, color, matrix, i->out);
90 }
91 static void filter_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
92 {
93     internal_t*i = (internal_t*)dev->internal;
94     i->filter->drawlink(i->filter, line, action, i->out);
95 }
96 static void filter_endpage(gfxdevice_t*dev)
97 {
98     internal_t*i = (internal_t*)dev->internal;
99     i->filter->endpage(i->filter, i->out);
100 }
101 static gfxresult_t* filter_finish(gfxdevice_t*dev)
102 {
103     internal_t*i = (internal_t*)dev->internal;
104     gfxresult_t*r;
105     if(i->filter->finish) {
106         r = i->filter->finish(i->filter, i->out);
107     } else {
108         r = i->out->finish(i->out);
109     }
110     if(i->filter->internal) {
111         free(i->filter->internal);
112         i->filter->internal = 0;
113     }
114     free(i->filter);i->filter=0;
115     free(dev->internal);dev->internal=0;free(dev);
116     return r;
117 }
118
119
120 static int passthrough_setparameter(gfxdevice_t*dev, const char*key, const char*value)
121 {
122     internal_t*i = (internal_t*)dev->internal;
123     return i->out->setparameter(i->out, key, value);
124 }
125 static void passthrough_startpage(gfxdevice_t*dev, int width, int height)
126 {
127     internal_t*i = (internal_t*)dev->internal;
128     i->out->startpage(i->out, width, height);
129 }
130 static void passthrough_startclip(gfxdevice_t*dev, gfxline_t*line)
131 {
132     internal_t*i = (internal_t*)dev->internal;
133     i->out->startclip(i->out, line);
134 }
135 static void passthrough_endclip(gfxdevice_t*dev)
136 {
137     internal_t*i = (internal_t*)dev->internal;
138     i->out->endclip(i->out);
139 }
140 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)
141 {
142     internal_t*i = (internal_t*)dev->internal;
143     i->out->stroke(i->out, line, width, color, cap_style, joint_style, miterLimit);
144 }
145 static void passthrough_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
146 {
147     internal_t*i = (internal_t*)dev->internal;
148     i->out->fill(i->out, line, color);
149 }
150 static void passthrough_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
151 {
152     internal_t*i = (internal_t*)dev->internal;
153     i->out->fillbitmap(i->out, line, img, matrix, cxform);
154 }
155 static void passthrough_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
156 {
157     internal_t*i = (internal_t*)dev->internal;
158     i->out->fillgradient(i->out, line, gradient, type, matrix);
159 }
160 static void passthrough_addfont(gfxdevice_t*dev, gfxfont_t*font)
161 {
162     internal_t*i = (internal_t*)dev->internal;
163     i->out->addfont(i->out, font);
164 }
165 static void passthrough_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
166 {
167     internal_t*i = (internal_t*)dev->internal;
168     i->out->drawchar(i->out, font, glyphnr, color, matrix);
169 }
170 static void passthrough_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
171 {
172     internal_t*i = (internal_t*)dev->internal;
173     i->out->drawlink(i->out, line, action);
174 }
175 static void passthrough_endpage(gfxdevice_t*dev)
176 {
177     internal_t*i = (internal_t*)dev->internal;
178     i->out->endpage(i->out);
179 }
180
181 int discard_setparameter(gfxdevice_t*dev, const char*key, const char*value)
182 {
183     return 0;
184 }
185 static void discard_startpage(gfxdevice_t*dev, int width, int height)
186 {
187 }
188 static void discard_startclip(gfxdevice_t*dev, gfxline_t*line)
189 {
190 }
191 static void discard_endclip(gfxdevice_t*dev)
192 {
193 }
194 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)
195 {
196 }
197 static void discard_fill(gfxdevice_t*dev, gfxline_t*line, gfxcolor_t*color)
198 {
199 }
200 static void discard_fillbitmap(gfxdevice_t*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
201 {
202 }
203 static void discard_fillgradient(gfxdevice_t*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
204 {
205 }
206 static void discard_addfont(gfxdevice_t*dev, gfxfont_t*font)
207 {
208 }
209 static void discard_drawchar(gfxdevice_t*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
210 {
211 }
212 static void discard_drawlink(gfxdevice_t*dev, gfxline_t*line, const char*action)
213 {
214 }
215 static void discard_endpage(gfxdevice_t*dev)
216 {
217 }
218 static gfxresult_t* discard_finish(gfxdevice_t*dev)
219 {
220     return 0;
221 }
222
223 gfxdevice_t*gfxfilter_apply(gfxfilter_t*_filter, gfxdevice_t*out)
224 {
225     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
226     gfxdevice_t*dev = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
227     gfxfilter_t*filter = (gfxfilter_t*)rfx_alloc(sizeof(gfxfilter_t));
228     memcpy(filter, _filter, sizeof(gfxfilter_t));
229     
230     i->out = out;
231     i->filter = filter;
232     i->pass = 1;
233
234     dev->internal = i;
235     dev->name = filter->name?filter->name:"filter";
236     dev->setparameter = filter->setparameter?filter_setparameter:passthrough_setparameter;
237     dev->startpage = filter->startpage?filter_startpage:passthrough_startpage;
238     dev->startclip = filter->startclip?filter_startclip:passthrough_startclip;
239     dev->endclip = filter->endclip?filter_endclip:passthrough_endclip;
240     dev->stroke = filter->stroke?filter_stroke:passthrough_stroke;
241     dev->fill = filter->fill?filter_fill:passthrough_fill;
242     dev->fillbitmap = filter->fillbitmap?filter_fillbitmap:passthrough_fillbitmap;
243     dev->fillgradient = filter->fillgradient?filter_fillgradient:passthrough_fillgradient;
244     dev->addfont = filter->addfont?filter_addfont:passthrough_addfont;
245     dev->drawchar = filter->drawchar?filter_drawchar:passthrough_drawchar;
246     dev->drawlink = filter->drawlink?filter_drawlink:passthrough_drawlink;
247     dev->endpage = filter->endpage?filter_endpage:passthrough_endpage;
248     dev->finish = filter_finish;
249     return dev;
250 }
251
252 static void setup_twopass(gfxdevice_t*dev, gfxfilter_t*filter)
253 {
254     dev->name = filter->name?filter->name:"filter";
255     dev->setparameter = filter->setparameter?filter_setparameter:passthrough_setparameter;
256     dev->startpage = filter->startpage?filter_startpage:passthrough_startpage;
257     dev->startclip = filter->startclip?filter_startclip:passthrough_startclip;
258     dev->endclip = filter->endclip?filter_endclip:passthrough_endclip;
259     dev->stroke = filter->stroke?filter_stroke:passthrough_stroke;
260     dev->fill = filter->fill?filter_fill:passthrough_fill;
261     dev->fillbitmap = filter->fillbitmap?filter_fillbitmap:passthrough_fillbitmap;
262     dev->fillgradient = filter->fillgradient?filter_fillgradient:passthrough_fillgradient;
263     dev->addfont = filter->addfont?filter_addfont:passthrough_addfont;
264     dev->drawchar = filter->drawchar?filter_drawchar:passthrough_drawchar;
265     dev->drawlink = filter->drawlink?filter_drawlink:passthrough_drawlink;
266     dev->endpage = filter->endpage?filter_endpage:passthrough_endpage;
267 }
268
269 static gfxresult_t* twopass_finish(gfxdevice_t*dev)
270 {
271     internal_t*i = (internal_t*)dev->internal;
272
273     gfxresult_t*r;
274     if(i->filter->finish) {
275         r = i->filter->finish(i->filter, i->out);
276     } else {
277         r = i->out->finish(i->out);
278     }
279
280     if(i->pass == i->num_passes) {
281         free(i->twopass);
282         i->twopass = 0;
283         i->filter = 0;
284         free(i);
285         dev->internal=0;
286         free(dev);
287         return r;
288     }
289
290     /* switch to next pass filter */
291     i->filter = &i->twopass->pass2;
292     setup_twopass(dev, i->filter);
293     dev->finish = twopass_finish;
294
295     if(i->pass == i->num_passes-1) {
296         /* we don't record in the final pass- we just stream out to the 
297            next output device */
298         i->out = i->final_out;
299     } else {
300         // switch to a new tempfile- this only happens for 3 passes or more
301         assert(i->num_passes>2);
302         gfxdevice_record_init(&i->record, /*use tempfile*/1);
303         i->out = &i->record;
304     }
305
306     i->pass++;
307     gfxresult_record_replay(r, i->out);
308     r->destroy(r);
309
310     return twopass_finish(dev);
311 }
312
313 gfxdevice_t*gfxtwopassfilter_apply(gfxtwopassfilter_t*_twopass, gfxdevice_t*out)
314 {
315     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
316     gfxdevice_t*dev = (gfxdevice_t*)rfx_calloc(sizeof(gfxdevice_t));
317
318     gfxtwopassfilter_t*twopass = (gfxtwopassfilter_t*)rfx_alloc(sizeof(gfxtwopassfilter_t));
319     memcpy(twopass, _twopass, sizeof(gfxtwopassfilter_t));
320    
321     gfxdevice_record_init(&i->record, /*use tempfile*/1);
322
323     i->out = &i->record;
324     i->final_out = out;
325     i->twopass = twopass;
326     i->pass = 1;
327     i->num_passes = 2;
328     
329     dev->internal = i;
330    
331     i->filter = &twopass->pass1;
332     setup_twopass(dev, i->filter);
333     dev->finish = twopass_finish;
334
335     return dev;
336 }
337