treat glyphs with alpha=0 differently than normal glyphs (remove outlines)
[swftools.git] / lib / filters / remove_font_transforms.c
1 /* remove_font_transform.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 <math.h>
23 #include <memory.h>
24 #include "../gfxfilter.h"
25 #include "../gfxtools.h"
26 #include "../gfxfont.h"
27 #include "../types.h"
28 #include "../mem.h"
29 #include "../q.h"
30 #include "../log.h"
31
32 typedef struct _mymatrix {
33     float m00,m01,m10,m11;
34     char*id;
35     unsigned char alpha;
36 } mymatrix_t;
37
38 static void* mymatrix_clone(const void*_m) {
39     if(_m==0) 
40         return 0;
41     const mymatrix_t*m1=_m;
42     mymatrix_t*m2 = malloc(sizeof(mymatrix_t));
43     *m2 = *m1;
44     m2->id = strdup(m1->id);
45     return m2;
46 }
47 static unsigned int mymatrix_hash(const void*_m) {
48     if(!_m)
49         return 0;
50     const mymatrix_t*m = (mymatrix_t*)_m;
51     unsigned int h=0;
52     h = crc32_add_bytes(h, (char*)&m->m00, sizeof(m->m00));
53     h = crc32_add_bytes(h, (char*)&m->m01, sizeof(m->m01));
54     h = crc32_add_bytes(h, (char*)&m->m10, sizeof(m->m10));
55     h = crc32_add_bytes(h, (char*)&m->m11, sizeof(m->m11));
56     h = crc32_add_bytes(h, (char*)&m->alpha, 1);
57     h = crc32_add_string(h, m->id);
58     return h;
59 }
60 static void mymatrix_destroy(void*_m) {
61     mymatrix_t*m = (mymatrix_t*)_m;
62     free(m->id);m->id=0;
63     free(m);
64 }
65 static char mymatrix_equals(const void*_m1, const void*_m2) {
66     const mymatrix_t*m1=_m1;
67     const mymatrix_t*m2=_m2;
68     if(!m1 || !m2) 
69         return m1==m2;
70     
71     /* we do a binary comparison of the float32
72        bits here instead of a numerical comparison
73        to prevent the compiler from e.g. removing the
74        (float) cast during optimization, which would break
75        the equivalence between equals() and hash() (as
76        the hash is derived from the float32 values) */
77     return *(U32*)&m1->m00 == *(U32*)&m2->m00 &&
78            *(U32*)&m1->m01 == *(U32*)&m2->m01 &&
79            *(U32*)&m1->m10 == *(U32*)&m2->m10 &&
80            *(U32*)&m1->m11 == *(U32*)&m2->m11 &&
81            m1->alpha == m2->alpha &&
82            !strcmp(m1->id, m2->id);
83 }
84 type_t mymatrix_type = {
85     dup: mymatrix_clone,
86     hash: mymatrix_hash,
87     free: mymatrix_destroy,
88     equals: mymatrix_equals
89 };
90
91 typedef struct _internal {
92     dict_t*matrices;
93     char first;
94 } internal_t;
95
96
97 #ifdef __GNUC__
98 void __attribute__((noinline)) 
99      matrix_convert(gfxmatrix_t*in, const char*id, mymatrix_t*out, gfxmatrix_t*scalematrix, unsigned char alpha)
100 #else
101 void matrix_convert(gfxmatrix_t*in, const char*id, mymatrix_t*out, gfxmatrix_t*scalematrix, unsigned char alpha)
102 #endif
103 {
104     double l1 = sqrt(in->m00 * in->m00 + in->m01 * in->m01);
105     double l2 = sqrt(in->m10 * in->m10 + in->m11 * in->m11);
106     double l = (l1+l2)/2.0;
107     if(l < 1e-10) {
108         memset(out, 0, sizeof(*out));
109         return;
110     }
111     out->m00 = in->m00 / l;
112     out->m10 = in->m10 / l;
113     out->m01 = -in->m01 / l;
114     out->m11 = -in->m11 / l;
115     out->id = (char*)id;
116     out->alpha = alpha?1:0;
117
118     if(scalematrix) {
119         scalematrix->m00 = l;
120         scalematrix->m01 = 0;
121         scalematrix->m10 = 0;
122         scalematrix->m11 = -l;
123         scalematrix->tx = in->tx;
124         scalematrix->ty = in->ty;
125     }
126 }
127
128 typedef struct _matrixdata {
129     gfxfontlist_t*fonts;
130 } matrixdata_t;
131
132 typedef struct _transformedfont {
133     gfxfont_t*orig;
134     gfxfont_t*font;
135     mymatrix_t matrix;
136     int*used;
137     double dx;
138 } transformedfont_t;
139
140 static transformedfont_t* transformedfont_new(gfxfont_t*orig, mymatrix_t*m)
141 {
142     transformedfont_t*f = rfx_calloc(sizeof(transformedfont_t));
143     f->orig = orig;
144     f->matrix = *m;
145     f->used = rfx_calloc(sizeof(f->used[0])*orig->num_glyphs);
146     int t;
147     for(t=0;t<orig->num_glyphs;t++) {
148         if(orig->glyphs[t].unicode==32)
149             f->used[t]=1; //always preserve the space char in fonts
150     }
151     return f;
152 }
153
154 static void pass1_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix, gfxdevice_t*out)
155 {
156     internal_t*i = (internal_t*)f->internal;
157     mymatrix_t m;
158     if(!font->id) 
159         msg("<error> Font has no ID");
160     matrix_convert(matrix, font->id?font->id:"unknown", &m, 0, color->a);
161     transformedfont_t*fd = dict_lookup(i->matrices, &m);
162     if(!fd) {
163         fd = transformedfont_new(font, &m);
164         dict_put(i->matrices, &m, fd);
165     }
166     fd->used[glyphnr]=1;
167     out->drawchar(out, font, glyphnr, color, matrix);
168 }
169
170 static void glyph_transform(gfxglyph_t*g, mymatrix_t*mm)
171 {
172     gfxmatrix_t m;
173     m.m00 = mm->m00;
174     m.m01 = mm->m01;
175     m.m10 = mm->m10;
176     m.m11 = mm->m11;
177     m.tx = 0;
178     m.ty = 0;
179     if(m.m00>0)
180         g->advance *= m.m00;
181     if(!mm->alpha) {
182         /* for OCR: remove the outlines of characters that are only
183            ever displayed with alpha=0 */
184         g->line = (gfxline_t*)rfx_calloc(sizeof(gfxline_t));
185         g->line->type = gfx_moveTo;
186         g->line->x = g->advance;
187     } else {
188         g->line = gfxline_clone(g->line);
189         gfxline_transform(g->line, &m);
190     }
191 }
192
193 static gfxresult_t* pass1_finish(gfxfilter_t*f, gfxdevice_t*out)
194 {
195     internal_t*i = (internal_t*)f->internal;
196     DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
197         gfxfont_t*font = fd->font = rfx_calloc(sizeof(gfxfont_t));
198         char id[80];
199         static int fontcount=0;
200         sprintf(id, "font%d", fontcount++);
201         font->id = strdup(id);
202         int t;
203         int count=0;
204         for(t=0;t<fd->orig->num_glyphs;t++) {
205             if(fd->used[t]) 
206                 count++;
207         }
208         font->num_glyphs = count;
209         font->glyphs = rfx_calloc(sizeof(gfxglyph_t)*font->num_glyphs);
210         count = 0;
211         for(t=0;t<fd->orig->num_glyphs;t++) {
212             if(fd->used[t]) {
213                 font->glyphs[count] = fd->orig->glyphs[t];
214                 glyph_transform(&font->glyphs[count], &fd->matrix);
215                 fd->used[t] = count;
216                 count++;
217             }
218         }
219
220         /* adjust the origin so that every character is to the
221            right of the origin */
222         gfxbbox_t total = {0,0,0,0};
223         double average_xmax = 0;
224         for(t=0;t<count;t++) {
225             gfxline_t*line = font->glyphs[t].line;
226             gfxbbox_t b = gfxline_getbbox(line);
227             total = gfxbbox_expand_to_bbox(total, b);
228         }
229         if(count) 
230             average_xmax /= count;
231
232         fd->dx = -total.xmin;
233
234         font->ascent = total.ymax;
235         font->descent = -total.ymin;
236
237         for(t=0;t<count;t++) {
238             gfxline_t*line = font->glyphs[t].line;
239             while(line) {
240                 line->x += fd->dx;
241                 line->sx += fd->dx;
242                 line = line->next;
243             }
244         }
245         gfxfont_fix_unicode(font);
246     }
247     return out->finish(out);
248 }
249
250 static void pass2_addfont(gfxfilter_t*f, gfxfont_t*font, gfxdevice_t*out)
251 {
252     /* we throw away original fonts, and do the addfont() for the transformed
253        fonts in the first drawchar() */
254 }
255
256 static void pass2_drawchar(gfxfilter_t*f, gfxfont_t*font, int glyphnr, gfxcolor_t*_color, gfxmatrix_t*matrix, gfxdevice_t*out)
257 {
258     internal_t*i = (internal_t*)f->internal;
259     gfxcolor_t color = *_color;
260
261     if(i->first) {
262         i->first = 0;
263         DICT_ITERATE_DATA(i->matrices, transformedfont_t*, fd) {
264             out->addfont(out, fd->font);
265         }
266     }
267
268     mymatrix_t m;
269     gfxmatrix_t scalematrix;
270     matrix_convert(matrix, font->id?font->id:"unknown", &m, &scalematrix, color.a);
271     transformedfont_t*d = dict_lookup(i->matrices, &m);
272     scalematrix.tx -= d->dx*scalematrix.m00;
273
274     /* if this character is invisible (alpha=0), then we will have removed the
275        outline, so we make set the alpha color channel to "fully visible" again to allow
276        output devices to be more performant (transparency is expensive) */
277     if(!m.alpha) 
278         color.a = 255;
279
280     out->drawchar(out, d->font, d->used[glyphnr], &color, &scalematrix);
281 }
282
283 void gfxtwopassfilter_remove_font_transforms_init(gfxtwopassfilter_t*f)
284 {
285     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
286
287     memset(f, 0, sizeof(gfxtwopassfilter_t));
288     f->type = gfxfilter_twopass;
289
290     f->pass1.name = "remove font transforms pass 1";
291     f->pass1.drawchar = pass1_drawchar;
292     f->pass1.finish = pass1_finish;
293     f->pass1.internal = i;
294
295     f->pass2.name = "remove font transforms pass 2";
296     f->pass2.addfont = pass2_addfont;
297     f->pass2.drawchar = pass2_drawchar;
298     f->pass2.internal = i;
299
300     i->matrices = dict_new2(&mymatrix_type);
301     i->first = 1;
302 }
303