fixed bug in swf_Relocate()
[swftools.git] / lib / devices / record.c
1 /* gfxdevice_record.cc
2
3    Part of the swftools package.
4
5    Copyright (c) 2005 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 <math.h>
24 #include <unistd.h>
25 #include <memory.h>
26 #include "../gfxdevice.h"
27 #include "../gfxtools.h"
28 #include "../types.h"
29 #include "../bitio.h"
30
31 typedef struct _internal {
32     gfxfontlist_t* fontlist;
33     writer_t w;
34 } internal_t;
35
36 typedef struct _internal_result {
37     void*data;
38     int length;
39 } internal_result_t;
40
41 #define OP_END 0x00
42 #define OP_SETPARAM 0x01
43 #define OP_STROKE 0x02
44 #define OP_STARTCLIP 0x03
45 #define OP_ENDCLIP 0x04
46 #define OP_FILL 0x05
47 #define OP_FILLBITMAP 0x06
48 #define OP_FILLGRADIENT 0x07
49 #define OP_ADDFONT 0x08
50 #define OP_DRAWCHAR 0x09
51 #define OP_DRAWLINK 0x0a
52 #define OP_STARTPAGE 0x0b
53 #define OP_ENDPAGE 0x0c
54 #define OP_FINISH 0x0d
55
56 #define OP_MOVETO 0x0e
57 #define OP_LINETO 0x0f
58 #define OP_SPLINETO 0x10
59
60 static int record_setparameter(struct _gfxdevice*dev, const char*key, const char*value)
61 {
62     internal_t*i = (internal_t*)dev->internal;
63     writer_writeU8(&i->w, OP_SETPARAM);
64     writer_writeString(&i->w, key);
65     writer_writeString(&i->w, value);
66     return 1;
67 }
68
69 static void dumpLine(writer_t*w, gfxline_t*line)
70 {
71     while(line) {
72         if(line->type == gfx_moveTo) {
73             writer_writeU8(w, OP_MOVETO);
74             writer_writeDouble(w, line->x);
75             writer_writeDouble(w, line->y);
76         } else if(line->type == gfx_lineTo) {
77             writer_writeU8(w, OP_LINETO);
78             writer_writeDouble(w, line->x);
79             writer_writeDouble(w, line->y);
80         } else if(line->type == gfx_splineTo) {
81             writer_writeU8(w, OP_SPLINETO);
82             writer_writeDouble(w, line->x);
83             writer_writeDouble(w, line->y);
84             writer_writeDouble(w, line->sx);
85             writer_writeDouble(w, line->sy);
86         }
87         line = line->next;
88     }
89     writer_writeU8(w, OP_END);
90 }
91 static gfxline_t* readLine(reader_t*r)
92 {
93     gfxline_t*start = 0, *pos = 0;
94     while(1) {
95         unsigned char op = reader_readU8(r);
96         if(op == OP_END)
97             break;
98         gfxline_t*line = rfx_calloc(sizeof(gfxline_t));
99         if(!start) {
100             start = pos = line;
101         } else {
102             pos->next = line;
103             pos = line;
104         }
105         if(op == OP_MOVETO) {
106             line->type = gfx_moveTo;
107             line->x = reader_readDouble(r);
108             line->y = reader_readDouble(r);
109         } else if(op == OP_LINETO) {
110             line->type = gfx_lineTo;
111             line->x = reader_readDouble(r);
112             line->y = reader_readDouble(r);
113         } else if(op == OP_SPLINETO) {
114             line->type = gfx_splineTo;
115             line->x = reader_readDouble(r);
116             line->y = reader_readDouble(r);
117             line->sx = reader_readDouble(r);
118             line->sy = reader_readDouble(r);
119         }
120     }
121     return start;
122 }
123 static gfximage_t readImage(reader_t*r)
124 {
125     gfximage_t img;
126     img.width = reader_readU16(r);
127     img.height = reader_readU16(r);
128     img.data = rfx_alloc(img.width*img.height*4);
129     r->read(r, img.data, img.width*img.height*4);
130     return img;
131 }
132 static gfxmatrix_t readMatrix(reader_t*r)
133 {
134     gfxmatrix_t matrix;
135     matrix.m00 = reader_readDouble(r);
136     matrix.m01 = reader_readDouble(r);
137     matrix.m10 = reader_readDouble(r);
138     matrix.m11 = reader_readDouble(r);
139     matrix.tx = reader_readDouble(r);
140     matrix.ty = reader_readDouble(r);
141     return matrix;
142 }
143 static gfxcolor_t readColor(reader_t*r)
144 {
145     gfxcolor_t col;
146     col.r = reader_readU8(r);
147     col.g = reader_readU8(r);
148     col.b = reader_readU8(r);
149     col.a = reader_readU8(r);
150     return col;
151 }
152 static gfxgradient_t* readGradient(reader_t*r)
153 {
154     gfxgradient_t*start = 0, *pos = 0;
155     while(1) {
156         U8 op = reader_readU8(r);
157         if(!op)
158             break;
159         gfxgradient_t*g = rfx_calloc(sizeof(gfxgradient_t));
160         if(!start) {
161             start = pos = g;
162         } else {
163             pos->next = g;
164             pos = g;
165         }
166         g->color = readColor(r);
167         g->pos = reader_readFloat(r);
168     }
169     return start;
170 }
171 static gfxcxform_t* readCXForm(reader_t*r)
172 {
173     U8 type = reader_readU8(r);
174     if(!type)
175         return 0;
176     gfxcxform_t* c = rfx_calloc(sizeof(gfxcxform_t));
177     c->rr = reader_readFloat(r); c->rg = reader_readFloat(r); c->rb = reader_readFloat(r); c->ra = reader_readFloat(r);
178     c->gr = reader_readFloat(r); c->gg = reader_readFloat(r); c->gb = reader_readFloat(r); c->ga = reader_readFloat(r);
179     c->br = reader_readFloat(r); c->bg = reader_readFloat(r); c->bb = reader_readFloat(r); c->ba = reader_readFloat(r);
180     c->ar = reader_readFloat(r); c->ag = reader_readFloat(r); c->ab = reader_readFloat(r); c->aa = reader_readFloat(r);
181     return c;
182 }
183
184 static void dumpColor(writer_t*w, gfxcolor_t*color)
185 {
186     writer_writeU8(w, color->r);
187     writer_writeU8(w, color->g);
188     writer_writeU8(w, color->b);
189     writer_writeU8(w, color->a);
190 }
191 static void dumpMatrix(writer_t*w, gfxmatrix_t*matrix)
192 {
193     writer_writeDouble(w, matrix->m00);
194     writer_writeDouble(w, matrix->m01);
195     writer_writeDouble(w, matrix->m10);
196     writer_writeDouble(w, matrix->m11);
197     writer_writeDouble(w, matrix->tx);
198     writer_writeDouble(w, matrix->ty);
199 }
200 static void dumpGradient(writer_t*w, gfxgradient_t*gradient)
201 {
202     while(gradient) {
203         writer_writeU8(w, 1);
204         dumpColor(w, &gradient->color);
205         writer_writeFloat(w, gradient->pos);
206         gradient = gradient->next;
207     }
208     writer_writeU8(w, 0);
209 }
210 static void dumpImage(writer_t*w, gfximage_t*image)
211 {
212     writer_writeU16(w, image->width);
213     writer_writeU16(w, image->height);
214     w->write(w, image->data, image->width*image->height*4);
215 }
216 static void dumpCXForm(writer_t*w, gfxcxform_t*c)
217 {
218     if(!c) {
219         writer_writeU8(w, 0);
220     } else {
221         writer_writeU8(w, 1);
222         writer_writeFloat(w, c->rr); writer_writeFloat(w, c->rg); writer_writeFloat(w, c->rb); writer_writeFloat(w, c->ra);
223         writer_writeFloat(w, c->gr); writer_writeFloat(w, c->gg); writer_writeFloat(w, c->gb); writer_writeFloat(w, c->ga);
224         writer_writeFloat(w, c->br); writer_writeFloat(w, c->bg); writer_writeFloat(w, c->bb); writer_writeFloat(w, c->ba);
225         writer_writeFloat(w, c->ar); writer_writeFloat(w, c->ag); writer_writeFloat(w, c->ab); writer_writeFloat(w, c->aa);
226     }
227 }
228 static void dumpFont(writer_t*w, gfxfont_t*font)
229 {
230     writer_writeString(w, font->id);
231     writer_writeU32(w, font->num_glyphs);
232     writer_writeU32(w, font->max_unicode);
233     int t;
234     for(t=0;t<font->num_glyphs;t++) {
235         dumpLine(w, font->glyphs[t].line);
236         writer_writeDouble(w, font->glyphs[t].advance);
237         writer_writeU32(w, font->glyphs[t].unicode);
238         if(font->glyphs[t].name) {
239             writer_writeString(w,font->glyphs[t].name);
240         } else {
241             writer_writeU8(w,0);
242         }
243     }
244     for(t=0;t<font->max_unicode;t++) {
245         writer_writeU32(w, font->unicode2glyph[t]);
246     }
247 }
248 static gfxfont_t*readFont(reader_t*r)
249 {
250     gfxfont_t* font = rfx_calloc(sizeof(gfxfont_t));
251     font->id = reader_readString(r);
252     font->num_glyphs = reader_readU32(r);
253     font->max_unicode = reader_readU32(r);
254     font->glyphs = rfx_calloc(sizeof(gfxglyph_t)*font->num_glyphs);
255     font->unicode2glyph = rfx_calloc(sizeof(font->unicode2glyph[0])*font->max_unicode);
256     int t;
257     for(t=0;t<font->num_glyphs;t++) {
258         font->glyphs[t].line = readLine(r);
259         font->glyphs[t].advance = reader_readDouble(r);
260         font->glyphs[t].unicode = reader_readU32(r);
261         font->glyphs[t].name = reader_readString(r);
262         if(!font->glyphs[t].name[0]) {
263             free(font->glyphs[t].name);
264             font->glyphs[t].name = 0;
265         }
266     }
267     for(t=0;t<font->max_unicode;t++) {
268         font->unicode2glyph[t] = reader_readU32(r);
269     }
270     return font;
271 }
272
273 static void record_stroke(struct _gfxdevice*dev, gfxline_t*line, gfxcoord_t width, gfxcolor_t*color, gfx_capType cap_style, gfx_joinType joint_style, gfxcoord_t miterLimit)
274 {
275     internal_t*i = (internal_t*)dev->internal;
276     writer_writeU8(&i->w, OP_STROKE);
277     writer_writeDouble(&i->w, width);
278     writer_writeDouble(&i->w, miterLimit);
279     dumpColor(&i->w, color);
280     writer_writeU8(&i->w, cap_style);
281     writer_writeU8(&i->w, joint_style);
282     dumpLine(&i->w, line);
283 }
284
285 static void record_startclip(struct _gfxdevice*dev, gfxline_t*line)
286 {
287     internal_t*i = (internal_t*)dev->internal;
288     writer_writeU8(&i->w, OP_STARTCLIP);
289     dumpLine(&i->w, line);
290 }
291
292 static void record_endclip(struct _gfxdevice*dev)
293 {
294     internal_t*i = (internal_t*)dev->internal;
295     writer_writeU8(&i->w, OP_ENDCLIP);
296 }
297
298 static void record_fill(struct _gfxdevice*dev, gfxline_t*line, gfxcolor_t*color)
299 {
300     internal_t*i = (internal_t*)dev->internal;
301     writer_writeU8(&i->w, OP_FILL);
302     dumpColor(&i->w, color);
303     dumpLine(&i->w, line);
304 }
305
306 static void record_fillbitmap(struct _gfxdevice*dev, gfxline_t*line, gfximage_t*img, gfxmatrix_t*matrix, gfxcxform_t*cxform)
307 {
308     internal_t*i = (internal_t*)dev->internal;
309     writer_writeU8(&i->w, OP_FILLBITMAP);
310     dumpImage(&i->w, img);
311     dumpMatrix(&i->w, matrix);
312     dumpLine(&i->w, line);
313     dumpCXForm(&i->w, cxform);
314 }
315
316 static void record_fillgradient(struct _gfxdevice*dev, gfxline_t*line, gfxgradient_t*gradient, gfxgradienttype_t type, gfxmatrix_t*matrix)
317 {
318     internal_t*i = (internal_t*)dev->internal;
319     writer_writeU8(&i->w, OP_FILLGRADIENT);
320     writer_writeU8(&i->w, type);
321     dumpGradient(&i->w, gradient);
322     dumpMatrix(&i->w, matrix);
323     dumpLine(&i->w, line);
324 }
325
326 static void record_addfont(struct _gfxdevice*dev, gfxfont_t*font)
327 {
328     internal_t*i = (internal_t*)dev->internal;
329     writer_writeU8(&i->w, OP_ADDFONT);
330     dumpFont(&i->w, font);
331     i->fontlist = gfxfontlist_addfont(i->fontlist, font);
332 }
333
334 static void record_drawchar(struct _gfxdevice*dev, gfxfont_t*font, int glyphnr, gfxcolor_t*color, gfxmatrix_t*matrix)
335 {
336     internal_t*i = (internal_t*)dev->internal;
337     if(font && !gfxfontlist_hasfont(i->fontlist, font))
338         record_addfont(dev, font);
339
340     writer_writeU8(&i->w, OP_DRAWCHAR);
341     if(font) 
342         writer_writeString(&i->w, font->id);
343     else
344         writer_writeString(&i->w, "*NULL*");
345     writer_writeU32(&i->w, glyphnr);
346     dumpColor(&i->w, color);
347     dumpMatrix(&i->w, matrix);
348 }
349
350 static void record_startpage(struct _gfxdevice*dev, int width, int height)
351 {
352     internal_t*i = (internal_t*)dev->internal;
353     writer_writeU8(&i->w, OP_STARTPAGE);
354     writer_writeU16(&i->w, width);
355     writer_writeU16(&i->w, height);
356 }
357
358 static void record_endpage(struct _gfxdevice*dev)
359 {
360     internal_t*i = (internal_t*)dev->internal;
361     writer_writeU8(&i->w, OP_ENDPAGE);
362 }
363
364 static void record_drawlink(struct _gfxdevice*dev, gfxline_t*line, char*action)
365 {
366     internal_t*i = (internal_t*)dev->internal;
367     writer_writeU8(&i->w, OP_DRAWLINK);
368     dumpLine(&i->w, line);
369     writer_writeString(&i->w, action);
370 }
371
372 void gfxresult_record_replay(gfxresult_t*result, gfxdevice_t*device)
373 {
374     internal_result_t*i = (internal_result_t*)result->internal;
375     reader_t r2;
376     reader_t*r = &r2;
377     reader_init_memreader(r, i->data, i->length);
378     gfxfontlist_t* fontlist = gfxfontlist_create();
379
380     while(1) {
381         unsigned char op = reader_readU8(r);
382         switch(op) {
383             case OP_END:
384                 return;
385             case OP_SETPARAM: {
386                 char*key;
387                 char*value;
388                 key = reader_readString(r);
389                 value = reader_readString(r);
390                 device->setparameter(device, key, value);
391                 free(key);
392                 free(value);
393                 break;
394             }
395             case OP_STARTPAGE: {
396                 U16 width = reader_readU16(r);
397                 U16 height = reader_readU16(r);
398                 device->startpage(device, width, height);
399                 break;
400             }
401             case OP_ENDPAGE: {
402                 break;
403             }
404             case OP_FINISH: {
405                 break;
406             }
407             case OP_STROKE: {
408                 double width = reader_readDouble(r);
409                 double miterlimit = reader_readDouble(r);
410                 gfxcolor_t color = readColor(r);
411                 gfx_capType captype = reader_readU8(r);
412                 gfx_joinType jointtype = reader_readU8(r);
413                 gfxline_t* line = readLine(r);
414                 device->stroke(device, line, width, &color, captype, jointtype,miterlimit);
415                 gfxline_free(line);
416                 break;
417             }
418             case OP_STARTCLIP: {
419                 gfxline_t* line = readLine(r);
420                 device->startclip(device, line);
421                 gfxline_free(line);
422                 break;
423             }
424             case OP_ENDCLIP: {
425                 device->endclip(device);
426                 break;
427             }
428             case OP_FILL: {
429                 gfxcolor_t color = readColor(r);
430                 gfxline_t* line = readLine(r);
431                 device->fill(device, line, &color);
432                 gfxline_free(line);
433                 break;
434             }
435             case OP_FILLBITMAP: {
436                 gfximage_t img = readImage(r);
437                 gfxmatrix_t matrix = readMatrix(r);
438                 gfxline_t* line = readLine(r);
439                 gfxcxform_t* cxform = readCXForm(r);
440                 device->fillbitmap(device, line, &img, &matrix, cxform);
441                 if(cxform)
442                     free(cxform);
443                 break;
444             }
445             case OP_FILLGRADIENT: {
446                 gfxgradienttype_t type = reader_readU8(r);
447                 gfxgradient_t*gradient = readGradient(r);
448                 gfxmatrix_t matrix = readMatrix(r);
449                 gfxline_t* line = readLine(r);
450                 device->fillgradient(device, line, gradient, type, &matrix);
451                 break;
452             }
453             case OP_DRAWLINK: {
454                 gfxline_t* line = readLine(r);
455                 char* s = reader_readString(r);
456                 device->drawlink(device,line,s);
457                 gfxline_free(line);
458                 free(s);
459                 break;
460             }
461             case OP_ADDFONT: {
462                 gfxfont_t*font = readFont(r);
463                 fontlist = gfxfontlist_addfont(fontlist, font);
464                 device->addfont(device, font);
465                 break;
466             }
467             case OP_DRAWCHAR: {
468                 char* id = reader_readString(r);
469                 gfxfont_t*font = id?gfxfontlist_findfont(fontlist, id):0;
470                 U32 glyph = reader_readU32(r);
471                 gfxcolor_t color = readColor(r);
472                 gfxmatrix_t matrix = readMatrix(r);
473                 device->drawchar(device, font, glyph, &color, &matrix);
474                 free(id);
475                 break;
476             }
477         }
478     }
479 }
480
481 static void record_result_write(gfxresult_t*r, int filedesc)
482 {
483     internal_result_t*i = (internal_result_t*)r->internal;
484     write(filedesc, i->data, i->length);
485 }
486 static int record_result_save(gfxresult_t*r, char*filename)
487 {
488     internal_result_t*i = (internal_result_t*)r->internal;
489     FILE*fi = fopen(filename, "wb");
490     if(!fi) {
491         fprintf(stderr, "Couldn't open file %s for writing\n", filename);
492         return -1;
493     }
494     fwrite(i->data, i->length, 1, fi);
495     fclose(fi);
496     return 0;
497 }
498 static void*record_result_get(gfxresult_t*r, char*name)
499 {
500     internal_result_t*i = (internal_result_t*)r->internal;
501     if(!strcmp(name, "data")) {
502         return i->data;
503     } else if(!strcmp(name, "length")) {
504         return &i->length;
505     }
506     return 0;
507 }
508 static void record_result_destroy(gfxresult_t*r)
509 {
510     internal_result_t*i = (internal_result_t*)r->internal;
511     free(i->data);i->data = 0;
512     free(r->internal);r->internal = 0;
513     free(r);
514 }
515
516
517 static gfxresult_t* record_finish(struct _gfxdevice*dev)
518 {
519     internal_t*i = (internal_t*)dev->internal;
520     
521     writer_writeU8(&i->w, OP_END);
522    
523     internal_result_t*ir = (internal_result_t*)rfx_calloc(sizeof(gfxresult_t));
524     ir->data = writer_growmemwrite_getmem(&i->w);
525     ir->length = i->w.pos;
526
527     gfxresult_t*result= (gfxresult_t*)rfx_calloc(sizeof(gfxresult_t));
528     result->save = record_result_save;
529     result->get = record_result_get;
530     result->destroy = record_result_destroy;
531     result->internal = ir;
532
533     free(dev->internal);memset(dev, 0, sizeof(gfxdevice_t));
534     
535     return result;
536 }
537 void gfxdevice_record_init(gfxdevice_t*dev)
538 {
539     internal_t*i = (internal_t*)rfx_calloc(sizeof(internal_t));
540     memset(dev, 0, sizeof(gfxdevice_t));
541     
542     dev->name = "record";
543
544     dev->internal = i;
545     
546     writer_init_growingmemwriter(&i->w, 1048576);
547     i->fontlist = gfxfontlist_create();
548
549     dev->setparameter = record_setparameter;
550     dev->startpage = record_startpage;
551     dev->startclip = record_startclip;
552     dev->endclip = record_endclip;
553     dev->stroke = record_stroke;
554     dev->fill = record_fill;
555     dev->fillbitmap = record_fillbitmap;
556     dev->fillgradient = record_fillgradient;
557     dev->addfont = record_addfont;
558     dev->drawchar = record_drawchar;
559     dev->drawlink = record_drawlink;
560     dev->endpage = record_endpage;
561     dev->finish = record_finish;
562 }
563