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