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