1 /*
2 Ming, an SWF output library
3 Copyright (C) 2002 Opaque Industries - http://www.opaque.net/
4
5 This library is free software; you can redistribute it and/or
6 modify it under the terms of the GNU Lesser General Public
7 License as published by the Free Software Foundation; either
8 version 2.1 of the License, or (at your option) any later version.
9
10 This library is distributed in the hope that it will be useful,
11 but WITHOUT ANY WARRANTY; without even the implied warranty of
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 Lesser General Public License for more details.
14
15 You should have received a copy of the GNU Lesser General Public
16 License along with this library; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 */
19
20 /* $Id$ */
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <stdarg.h>
25
26 #include "shape.h"
27 #include "character.h"
28 #include "matrix.h"
29 #include "fillstyle.h"
30 #include "linestyle.h"
31 #include "font.h"
32 #include "libming.h"
33
34 struct stateChangeRecord
35 {
36 int flags;
37 int moveToX;
38 int moveToY;
39 int leftFill;
40 int rightFill;
41 int line;
42 /* newstyle not used.. */
43 };
44 typedef struct stateChangeRecord *StateChangeRecord;
45
46
47 struct lineToRecord
48 {
49 int dx;
50 int dy;
51 };
52 typedef struct lineToRecord *LineToRecord;
53
54
55 struct curveToRecord
56 {
57 int controlx;
58 int controly;
59 int anchorx;
60 int anchory;
61 };
62 typedef struct curveToRecord *CurveToRecord;
63
64 typedef enum
65 {
66 SHAPERECORD_STATECHANGE,
67 SHAPERECORD_LINETO,
68 SHAPERECORD_CURVETO
69 } shapeRecordType;
70
71
72 struct shapeRecord
73 {
74 shapeRecordType type;
75
76 union
77 {
78 StateChangeRecord stateChange;
79 LineToRecord lineTo;
80 CurveToRecord curveTo;
81 } record;
82 };
83 typedef struct shapeRecord ShapeRecord;
84
85
86 struct SWFShape_s
87 {
88 struct SWFCharacter_s character;
89
90 ShapeRecord *records;
91 int nRecords;
92 SWFOutput out;
93 int xpos; /* cursor for using abs. coords in lineTo, curveTo */
94 int ypos;
95 SWFLineStyle *lines;
96 SWFFillStyle *fills;
97 byte nLines;
98 byte nFills;
99 short lineWidth;
100 BOOL isMorph;
101 BOOL isEnded;
102 int useVersion;
103 // SWF_DEFINESHAPE4 extensions
104 unsigned char flags;
105 SWFRect edgeBounds;
106 #if TRACK_ALLOCS
107 /* memory node for garbage collection */
108 mem_node *gcnode;
109 #endif
110 };
111
112
113 static void
114 SWFShape_writeShapeRecord(SWFShape shape, ShapeRecord record, SWFOutput out);
115
116
117 static void
writeSWFShapeBlockToMethod(SWFBlock block,SWFByteOutputMethod method,void * data)118 writeSWFShapeBlockToMethod(SWFBlock block,
119 SWFByteOutputMethod method, void* data)
120 {
121 SWFOutput out = ((SWFShape)block)->out;
122 SWFOutput_writeToMethod(out, method, data);
123 }
124
125
126 static int
completeSWFShapeBlock(SWFBlock block)127 completeSWFShapeBlock(SWFBlock block)
128 {
129 SWFShape shape = (SWFShape)block;
130
131 SWFShape_end(shape);
132
133 return SWFOutput_getLength(shape->out);
134 }
135
136
137 void
destroySWFShape(SWFShape shape)138 destroySWFShape(SWFShape shape)
139 {
140 int i;
141 if(shape->fills != NULL)
142 {
143 // Fills have to be destroyed by users.
144 /*
145 for ( i=0; i<shape->nFills; ++i )
146 destroySWFFillStyle(shape->fills[i]);
147 */
148 free(shape->fills);
149 }
150 if(shape->records != NULL)
151 {
152 for(i = 0; i < shape->nRecords; i++)
153 {
154 free(shape->records[i].record.stateChange);
155 }
156 free(shape->records);
157 }
158
159 if(shape->edgeBounds != NULL)
160 free(shape->edgeBounds);
161
162 for ( i=0; i<shape->nLines; ++i )
163 free(shape->lines[i]);
164
165 if ( shape->lines != NULL )
166 free(shape->lines);
167
168 destroySWFOutput(shape->out);
169
170 #if TRACK_ALLOCS
171 ming_gc_remove_node(shape->gcnode);
172 #endif
173
174 destroySWFCharacter((SWFCharacter) shape);
175 }
176
177 SWFShape
newSWFGlyphShape()178 newSWFGlyphShape()
179 {
180 SWFShape shape = (SWFShape)malloc(sizeof(struct SWFShape_s));
181
182 /* If malloc failed, return NULL to signify this */
183 if (NULL == shape)
184 return NULL;
185
186 SWFCharacterInit((SWFCharacter)shape);
187
188 BLOCK(shape)->writeBlock = NULL;
189 BLOCK(shape)->complete = NULL;
190 BLOCK(shape)->dtor = NULL;
191 BLOCK(shape)->type = SWF_UNUSEDBLOCK;
192
193 shape->out = newSWFOutput();
194 CHARACTER(shape)->bounds = newSWFRect(0,0,0,0);
195 shape->edgeBounds = newSWFRect(0,0,0,0);
196
197 shape->records = NULL;
198 shape->lines = NULL;
199 shape->fills = NULL;
200
201 shape->nRecords = 0;
202 shape->xpos = 0;
203 shape->ypos = 0;
204 shape->nLines = 0;
205 shape->nFills = 0;
206 shape->lineWidth = 0;
207 shape->isMorph = FALSE;
208 shape->isEnded = FALSE;
209 shape->flags = 0;
210 shape->useVersion = 0;
211
212 SWFOutput_writeUInt8(shape->out, 0); /* space for nFillBits, nLineBits */
213
214 #if TRACK_ALLOCS
215 shape->gcnode = ming_gc_add_node(shape, (dtorfunctype) destroySWFShape);
216 #endif
217
218 return shape;
219 }
220
221 SWFShape
newSWFShape()222 newSWFShape()
223 {
224 SWFShape shape = (SWFShape)malloc(sizeof(struct SWFShape_s));
225
226 /* If malloc failed, return NULL to signify this */
227 if (NULL == shape)
228 return NULL;
229
230 SWFCharacterInit((SWFCharacter)shape);
231
232 BLOCK(shape)->writeBlock = writeSWFShapeBlockToMethod;
233 BLOCK(shape)->complete = completeSWFShapeBlock;
234 BLOCK(shape)->dtor = (destroySWFBlockMethod) destroySWFShape;
235 BLOCK(shape)->type = SWF_DEFINESHAPE3;
236
237 CHARACTERID(shape) = ++SWF_gNumCharacters;
238
239 shape->out = newSWFOutput();
240 CHARACTER(shape)->bounds = newSWFRect(0,0,0,0);
241 shape->edgeBounds = newSWFRect(0,0,0,0);
242
243 shape->records = NULL;
244 shape->lines = NULL;
245 shape->fills = NULL;
246
247 shape->nRecords = 0;
248 shape->xpos = 0;
249 shape->ypos = 0;
250 shape->nLines = 0;
251 shape->nFills = 0;
252 shape->lineWidth = 0;
253 shape->isMorph = FALSE;
254 shape->isEnded = FALSE;
255 shape->flags = 0;
256 shape->useVersion = SWF_SHAPE3;
257
258 SWFOutput_writeUInt8(shape->out, 0); /* space for nFillBits, nLineBits */
259
260 #if TRACK_ALLOCS
261 shape->gcnode = ming_gc_add_node(shape, (dtorfunctype) destroySWFShape);
262 #endif
263
264 return shape;
265 }
266
267
268 /*
269 * Creates a shape filled with bitmap
270 */
271 SWFShape
newSWFShapeFromBitmap(SWFBitmap bitmap,int flag)272 newSWFShapeFromBitmap(SWFBitmap bitmap, int flag)
273 {
274 SWFShape shape = newSWFShape();
275 SWFFillStyle fill;
276 int width, height;
277
278 if ( flag != SWFFILL_TILED_BITMAP &&
279 flag != SWFFILL_CLIPPED_BITMAP &&
280 flag != SWFFILL_NONSMOOTHED_TILED_BITMAP &&
281 flag != SWFFILL_NONSMOOTHED_CLIPPED_BITMAP)
282 {
283 SWF_error("Invalid bitmap fill flag");
284 }
285
286 fill = SWFShape_addBitmapFillStyle(shape, bitmap, flag);
287
288 width = SWFBitmap_getWidth(bitmap);
289 height = SWFBitmap_getHeight(bitmap);
290
291 SWFShape_setRightFillStyle(shape, fill);
292
293 // XXX - scale shouldn't be hardcoded! (here, or in newSWFBitmapFillStyle)
294 SWFShape_drawScaledLine(shape, width * 20, 0);
295 SWFShape_drawScaledLine(shape, 0, height * 20);
296 SWFShape_drawScaledLine(shape, -width * 20, 0);
297 SWFShape_drawScaledLine(shape, 0, -height * 20);
298
299 return shape;
300 }
301
302 void
SWFOutput_writeGlyphShape(SWFOutput out,SWFShape shape)303 SWFOutput_writeGlyphShape(SWFOutput out, SWFShape shape)
304 {
305 unsigned char c;
306 int styleDone = 0;
307 int i;
308
309 c = 1<<4;
310 SWFOutput_writeUInt8(out, c);
311 shape->nFills = 1;
312 shape->nLines = 0;
313 for ( i=0; i<shape->nRecords; ++i )
314 {
315 if(!styleDone && shape->records[i].type == SHAPERECORD_STATECHANGE)
316 {
317 shape->records[i].record.stateChange->flags |= SWF_SHAPE_FILLSTYLE0FLAG;
318 shape->records[i].record.stateChange->leftFill = 1;
319 styleDone = 1;
320 }
321
322 if ( i < shape->nRecords-1 ||
323 shape->records[i].type != SHAPERECORD_STATECHANGE )
324 {
325 SWFShape_writeShapeRecord(shape, shape->records[i], out);
326 }
327 }
328
329 SWFOutput_writeBits(out, 0, 6); /* end tag */
330 SWFOutput_byteAlign(out);
331 }
332
333 void
SWFShape_end(SWFShape shape)334 SWFShape_end(SWFShape shape)
335 {
336 int i;
337 byte* buffer;
338
339 if ( shape->isEnded )
340 return;
341
342 shape->isEnded = TRUE;
343
344 buffer = SWFOutput_getBuffer(shape->out);
345 buffer[0] =
346 (SWFOutput_numBits(shape->nFills) << 4) + SWFOutput_numBits(shape->nLines);
347
348 for ( i=0; i<shape->nRecords; ++i )
349 {
350 if ( i < shape->nRecords-1 ||
351 shape->records[i].type != SHAPERECORD_STATECHANGE )
352 {
353 SWFShape_writeShapeRecord(shape, shape->records[i], shape->out);
354 }
355
356 free(shape->records[i].record.stateChange); /* all in union are pointers */
357 }
358
359 SWFOutput_writeBits(shape->out, 0, 6); /* end tag */
360 SWFOutput_byteAlign(shape->out);
361
362 /* addStyleHeader creates a new output and adds the existing one after
363 itself- so even though it's called afterwards it's written before,
364 as it should be */
365 if ( BLOCK(shape)->type > 0 )
366 {
367 switch (shape->useVersion)
368 {
369 case SWF_SHAPE1:
370 BLOCK(shape)->type = SWF_DEFINESHAPE;
371 break;
372 case SWF_SHAPE2:
373 BLOCK(shape)->type = SWF_DEFINESHAPE2;
374 break;
375 case SWF_SHAPE4:
376 BLOCK(shape)->type = SWF_DEFINESHAPE4;
377 break;
378 }
379 SWFShape_addStyleHeader(shape);
380 }
381 free(shape->records);
382 shape->records = NULL;
383 shape->nRecords = 0;
384 }
385
386
387 SWFOutput
SWFShape_getOutput(SWFShape shape)388 SWFShape_getOutput(SWFShape shape)
389 {
390 return shape->out;
391 }
392
393
394 void
SWFShape_getFills(SWFShape shape,SWFFillStyle ** fills,int * nFills)395 SWFShape_getFills(SWFShape shape, SWFFillStyle** fills, int* nFills)
396 {
397 *fills = shape->fills;
398 *nFills = shape->nFills;
399 }
400
401
402 void
SWFShape_getLines(SWFShape shape,SWFLineStyle ** lines,int * nLines)403 SWFShape_getLines(SWFShape shape, SWFLineStyle** lines, int* nLines)
404 {
405 *lines = shape->lines;
406 *nLines = shape->nLines;
407 }
408
409
410 void
SWFShape_setMorphFlag(SWFShape shape)411 SWFShape_setMorphFlag(SWFShape shape)
412 {
413 shape->isMorph = TRUE;
414 }
415
416
417 void
SWFShape_addStyleHeader(SWFShape shape)418 SWFShape_addStyleHeader(SWFShape shape)
419 {
420 SWFOutput out = newSWFOutput();
421 SWFOutput_writeUInt16(out, CHARACTERID(shape));
422 SWFOutput_writeRect(out, SWFCharacter_getBounds(CHARACTER(shape)));
423 if(shape->useVersion == SWF_SHAPE4)
424 {
425 SWFOutput_writeRect(out, shape->edgeBounds);
426 SWFOutput_writeUInt8(out, shape->flags);
427 }
428
429 SWFOutput_writeFillStyles(out, shape->fills, shape->nFills,
430 BLOCK(shape)->type, shape->edgeBounds);
431 SWFOutput_writeLineStyles(out, shape->lines, shape->nLines,
432 BLOCK(shape)->type, shape->edgeBounds);
433
434 /* prepend shape->out w/ shape header */
435 SWFOutput_setNext(out, shape->out);
436 shape->out = out;
437 }
438
439
440 /*
441 ShapeRecords are an intermediate storage so that we don't have to specify
442 fill/line types in advance.
443 */
444
445 #define SHAPERECORD_INCREMENT 32
446
447 /* copy shaperecord from other shape */
addShapeRecord(SWFShape shape,ShapeRecord record,int * vx,int * vy,float scale)448 static ShapeRecord addShapeRecord(SWFShape shape, ShapeRecord record,
449 int *vx, int *vy, float scale)
450 {
451 if ( shape->nRecords % SHAPERECORD_INCREMENT == 0 )
452 {
453 shape->records = (ShapeRecord*) realloc(shape->records,
454 sizeof(ShapeRecord) *
455 (shape->nRecords + SHAPERECORD_INCREMENT));
456 }
457
458 switch ( record.type )
459 {
460 case SHAPERECORD_STATECHANGE:
461 {
462 StateChangeRecord change = (StateChangeRecord)
463 calloc(1,sizeof(struct stateChangeRecord));
464 *change = *record.record.stateChange;
465 shape->records[shape->nRecords].record.stateChange = change;
466 change->moveToX += shape->xpos;
467 change->moveToY += shape->ypos;
468 change->moveToX *= scale;
469 change->moveToY *= scale;
470
471 *vx = change->moveToX;
472 *vy = change->moveToY;
473 break;
474 }
475 case SHAPERECORD_LINETO:
476 {
477 LineToRecord lineTo = (LineToRecord)
478 calloc(1,sizeof(struct lineToRecord));
479 *lineTo = *record.record.lineTo;
480 lineTo->dx *= scale;
481 lineTo->dy *= scale;
482 shape->records[shape->nRecords].record.lineTo = lineTo;
483
484 *vx += lineTo->dx;
485 *vy += lineTo->dy;
486 SWFRect_includePoint(SWFCharacter_getBounds(CHARACTER(shape)),
487 *vx, *vy, shape->lineWidth);
488 SWFRect_includePoint(shape->edgeBounds, *vx, *vy, 0);
489 break;
490 }
491 case SHAPERECORD_CURVETO:
492 {
493 CurveToRecord curveTo = (CurveToRecord)
494 calloc(1,sizeof(struct curveToRecord));
495 *curveTo = *record.record.curveTo;
496 curveTo->controlx *= scale;
497 curveTo->controly *= scale;
498 curveTo->anchorx *= scale;
499 curveTo->anchory *= scale;
500 shape->records[shape->nRecords].record.curveTo = curveTo;
501
502 *vx += curveTo->controlx;
503 *vy += curveTo->controly;
504 SWFRect_includePoint(SWFCharacter_getBounds(CHARACTER(shape)),
505 *vx, *vy, shape->lineWidth);
506 SWFRect_includePoint(shape->edgeBounds, *vx, *vy, 0);
507 *vx += curveTo->anchorx;
508 *vy += curveTo->anchory;
509 SWFRect_includePoint(SWFCharacter_getBounds(CHARACTER(shape)),
510 *vx, *vy, shape->lineWidth);
511 SWFRect_includePoint(shape->edgeBounds, *vx, *vy, 0);
512 break;
513 }
514 }
515 shape->records[shape->nRecords].type = record.type;
516 shape->nRecords++;
517 return shape->records[shape->nRecords-1];
518
519 }
520
521 static ShapeRecord
newShapeRecord(SWFShape shape,shapeRecordType type)522 newShapeRecord(SWFShape shape, shapeRecordType type)
523 {
524 if ( shape->nRecords % SHAPERECORD_INCREMENT == 0 )
525 {
526 shape->records = (ShapeRecord*) realloc(shape->records,
527 sizeof(ShapeRecord) *
528 (shape->nRecords + SHAPERECORD_INCREMENT));
529 }
530
531 switch ( type )
532 {
533 case SHAPERECORD_STATECHANGE:
534 {
535 StateChangeRecord change = (StateChangeRecord)calloc(1,sizeof(struct stateChangeRecord));
536 shape->records[shape->nRecords].record.stateChange = change;
537 break;
538 }
539 case SHAPERECORD_LINETO:
540 {
541 LineToRecord lineTo = (LineToRecord) calloc(1,sizeof(struct lineToRecord));
542 shape->records[shape->nRecords].record.lineTo = lineTo;
543 break;
544 }
545 case SHAPERECORD_CURVETO:
546 {
547 CurveToRecord curveTo = (CurveToRecord) calloc(1,sizeof(struct curveToRecord));
548 shape->records[shape->nRecords].record.curveTo = curveTo;
549 break;
550 }
551 }
552
553 shape->records[shape->nRecords].type = type;
554
555 // this is intentional - at least one popular compiler cannot handle [shape->nRecords++]
556 shape->nRecords++;
557 return shape->records[shape->nRecords-1];
558 }
559
560
561 void
SWFShape_writeShapeRecord(SWFShape shape,ShapeRecord record,SWFOutput out)562 SWFShape_writeShapeRecord(SWFShape shape, ShapeRecord record, SWFOutput out)
563 {
564 switch(record.type)
565 {
566 case SHAPERECORD_STATECHANGE:
567 {
568 int flags = record.record.stateChange->flags;
569
570 if(flags == 0)
571 return;
572
573 SWFOutput_writeBits(out, flags, 6);
574
575 if(flags & SWF_SHAPE_MOVETOFLAG)
576 {
577 int x = record.record.stateChange->moveToX;
578 int y = record.record.stateChange->moveToY;
579 int nBits = max(SWFOutput_numSBits(x), SWFOutput_numSBits(y));
580
581 SWF_assert(nBits<32);
582 SWFOutput_writeBits(out, nBits, 5);
583 SWFOutput_writeSBits(out, x, nBits);
584 SWFOutput_writeSBits(out, y, nBits);
585 }
586
587 if(flags & SWF_SHAPE_FILLSTYLE0FLAG)
588 {
589 SWFOutput_writeBits(out, record.record.stateChange->leftFill,
590 SWFOutput_numBits(shape->nFills));
591 }
592
593 if(flags & SWF_SHAPE_FILLSTYLE1FLAG)
594 {
595 SWFOutput_writeBits(out, record.record.stateChange->rightFill,
596 SWFOutput_numBits(shape->nFills));
597 }
598
599 if(flags & SWF_SHAPE_LINESTYLEFLAG)
600 {
601 SWFOutput_writeBits(out, record.record.stateChange->line,
602 SWFOutput_numBits(shape->nLines));
603 }
604
605 /* newstyle's never used. But this is what it looks like:
606
607 if ( flags & SWF_SHAPE_NEWSTYLEFLAG )
608 {
609 SWFOutput_writeFillStyles(shape->out, shape->fills, shape->nFills,
610 BLOCK(shape)->type);
611
612 SWFOutput_writeLineStyles(shape->out, shape->lines, shape->nLines,
613 BLOCK(shape)->type);
614
615 SWFOutput_writeBits(shape->out, SWFOutput_numBits(shape->nFills), 4);
616 SWFOutput_writeBits(shape->out, SWFOutput_numBits(shape->nLines), 4);
617 }
618
619 */
620
621 break;
622 }
623
624 case SHAPERECORD_LINETO:
625 {
626 int nBits;
627 int dx = record.record.lineTo->dx;
628 int dy = record.record.lineTo->dy;
629
630 SWFOutput_writeBits(out, 3, 2); /* straight edge */
631
632 if(dx==0)
633 {
634 nBits = SWFOutput_numSBits(dy);
635 SWF_assert(nBits<18);
636 SWFOutput_writeBits(out, nBits-2, 4);
637 SWFOutput_writeBits(out, 1, 2); /* vertical line */
638 SWFOutput_writeSBits(out, dy, nBits);
639 }
640 else if(dy==0)
641 {
642 nBits = SWFOutput_numSBits(dx);
643 SWF_assert(nBits<18);
644 SWFOutput_writeBits(out, nBits-2, 4);
645 SWFOutput_writeBits(out, 0, 2); /* horizontal line */
646 SWFOutput_writeSBits(out, dx, nBits);
647 }
648 else
649 {
650 nBits = max(SWFOutput_numSBits(dx), SWFOutput_numSBits(dy));
651 SWF_assert(nBits<18);
652 SWFOutput_writeBits(out, nBits-2, 4);
653 SWFOutput_writeBits(out, 1, 1); /* general line */
654 SWFOutput_writeSBits(out, dx, nBits);
655 SWFOutput_writeSBits(out, dy, nBits);
656 }
657
658 break;
659 }
660
661 case SHAPERECORD_CURVETO:
662 {
663 int controlx = record.record.curveTo->controlx;
664 int controly = record.record.curveTo->controly;
665 int anchorx = record.record.curveTo->anchorx;
666 int anchory = record.record.curveTo->anchory;
667
668 int nBits = max(max(SWFOutput_numSBits(controlx),
669 SWFOutput_numSBits(controly)),
670 max(SWFOutput_numSBits(anchorx),
671 SWFOutput_numSBits(anchory)));
672
673 if ( nBits < 2 )
674 nBits = 2;
675
676 SWF_assert(nBits < 18);
677
678 SWFOutput_writeBits(out, 2, 2); /* curved edge */
679 SWFOutput_writeBits(out, nBits-2, 4);
680 SWFOutput_writeSBits(out, controlx, nBits);
681 SWFOutput_writeSBits(out, controly, nBits);
682 SWFOutput_writeSBits(out, anchorx, nBits);
683 SWFOutput_writeSBits(out, anchory, nBits);
684
685 break;
686 }
687
688 default:
689 SWF_error("Unknown shapeRecordType");
690 }
691 }
692
693
694 /* x,y relative to shape origin */
695 void
SWFShape_drawScaledLineTo(SWFShape shape,int x,int y)696 SWFShape_drawScaledLineTo(SWFShape shape, int x, int y)
697 {
698 SWFShape_drawScaledLine(shape, x-shape->xpos, y-shape->ypos);
699 }
700
701
702 void
SWFShape_drawScaledLine(SWFShape shape,int dx,int dy)703 SWFShape_drawScaledLine(SWFShape shape, int dx, int dy)
704 {
705 ShapeRecord record;
706
707 if ( shape->isEnded )
708 return;
709
710 if ( dx == 0 && dy == 0 )
711 return;
712
713 record = newShapeRecord(shape, SHAPERECORD_LINETO);
714
715 SWF_assert(SWFOutput_numSBits(dx) < 18);
716 SWF_assert(SWFOutput_numSBits(dy) < 18);
717
718 record.record.lineTo->dx = dx;
719 record.record.lineTo->dy = dy;
720
721 shape->xpos += dx;
722 shape->ypos += dy;
723
724 SWFRect_includePoint(SWFCharacter_getBounds(CHARACTER(shape)),
725 shape->xpos, shape->ypos, shape->lineWidth);
726 SWFRect_includePoint(shape->edgeBounds, shape->xpos, shape->ypos, 0);
727 }
728
729
730 void
SWFShape_drawScaledCurveTo(SWFShape shape,int controlx,int controly,int anchorx,int anchory)731 SWFShape_drawScaledCurveTo(SWFShape shape,
732 int controlx, int controly,
733 int anchorx, int anchory)
734 {
735 SWFShape_drawScaledCurve(shape, controlx-shape->xpos, controly-shape->ypos,
736 anchorx-controlx, anchory-controly);
737 }
738
739
740 void
SWFShape_drawScaledCurve(SWFShape shape,int controldx,int controldy,int anchordx,int anchordy)741 SWFShape_drawScaledCurve(SWFShape shape,
742 int controldx, int controldy,
743 int anchordx, int anchordy)
744 {
745 ShapeRecord record;
746
747 if ( shape->isEnded )
748 return;
749
750 if ( controldx == 0 && controldy == 0 && anchordx == 0 && anchordy == 0 )
751 return;
752
753 // printf("curve %i,%i, %i, %i\n", controldx, controldy, anchordx, anchordy);
754
755 record = newShapeRecord(shape, SHAPERECORD_CURVETO);
756
757 record.record.curveTo->controlx = controldx;
758 record.record.curveTo->controly = controldy;
759 record.record.curveTo->anchorx = anchordx;
760 record.record.curveTo->anchory = anchordy;
761
762 if ( SWFOutput_numSBits(controldx) >= 18 ||
763 SWFOutput_numSBits(controldy) >= 18 ||
764 SWFOutput_numSBits(anchordx) >= 18 ||
765 SWFOutput_numSBits(anchordy) >= 18 )
766 SWF_error("Curve parameters too large");
767
768 /* including the control point is sloppy, but safe.. */
769
770 shape->xpos += controldx;
771 shape->ypos += controldy;
772
773 SWFRect_includePoint(SWFCharacter_getBounds(CHARACTER(shape)),
774 shape->xpos, shape->ypos, shape->lineWidth);
775 SWFRect_includePoint(shape->edgeBounds, shape->xpos, shape->ypos, 0);
776 shape->xpos += anchordx;
777 shape->ypos += anchordy;
778
779 SWFRect_includePoint(SWFCharacter_getBounds(CHARACTER(shape)),
780 shape->xpos, shape->ypos, shape->lineWidth);
781 SWFRect_includePoint(shape->edgeBounds, shape->xpos, shape->ypos, 0);
782 }
783
784
785 #define STYLE_INCREMENT 4
786
growLineArray(SWFShape shape)787 static inline void growLineArray(SWFShape shape)
788 {
789 int size;
790
791 if ( shape->nLines % STYLE_INCREMENT != 0 )
792 return;
793
794 size = (shape->nLines+STYLE_INCREMENT) * sizeof(SWFLineStyle);
795 shape->lines = (SWFLineStyle*)realloc(shape->lines, size);
796 }
797
798 static int
SWFShape_addLineStyle2filled(SWFShape shape,unsigned short width,SWFFillStyle fill,int flags,float miterLimit)799 SWFShape_addLineStyle2filled(SWFShape shape, unsigned short width,
800 SWFFillStyle fill,
801 int flags, float miterLimit)
802 {
803 growLineArray(shape);
804 SWFShape_useVersion(shape, SWF_SHAPE4);
805 SWFFillStyle_addDependency(fill, (SWFCharacter)shape);
806 shape->lines[shape->nLines] = newSWFLineStyle2_filled(width, fill, flags, miterLimit);
807 return ++shape->nLines;
808 }
809
810 static int
SWFShape_addLineStyle2(SWFShape shape,unsigned short width,byte r,byte g,byte b,byte a,int flags,float miterLimit)811 SWFShape_addLineStyle2(SWFShape shape, unsigned short width,
812 byte r, byte g, byte b, byte a,
813 int flags, float miterLimit)
814 {
815 growLineArray(shape);
816 SWFShape_useVersion(shape, SWF_SHAPE4);
817 shape->lines[shape->nLines] = newSWFLineStyle2(width, r, g, b, a, flags, miterLimit);
818 return ++shape->nLines;
819 }
820
821 static int
SWFShape_addLineStyle(SWFShape shape,unsigned short width,byte r,byte g,byte b,byte a)822 SWFShape_addLineStyle(SWFShape shape, unsigned short width,
823 byte r, byte g, byte b, byte a)
824 {
825 growLineArray(shape);
826 shape->lines[shape->nLines] = newSWFLineStyle(width, r, g, b, a);
827 return ++shape->nLines;
828 }
829
830
831 /* if the current shape record isn't a style change record, add one */
832 static ShapeRecord
addStyleRecord(SWFShape shape)833 addStyleRecord(SWFShape shape)
834 {
835 if ( shape->nRecords > 0 &&
836 shape->records[shape->nRecords-1].type == SHAPERECORD_STATECHANGE )
837 {
838 return shape->records[shape->nRecords-1];
839 }
840 else
841 return newShapeRecord(shape, SHAPERECORD_STATECHANGE);
842 }
843
844
845 void
SWFShape_hideLine(SWFShape shape)846 SWFShape_hideLine(SWFShape shape)
847 {
848 ShapeRecord record;
849
850 if ( shape->isEnded )
851 return;
852
853 if ( shape->isMorph )
854 return;
855
856 record = addStyleRecord(shape);
857
858 record.record.stateChange->line = 0;
859 record.record.stateChange->flags |= SWF_SHAPE_LINESTYLEFLAG;
860 }
861
finishSetLine(SWFShape shape,int line,unsigned short width)862 static void finishSetLine(SWFShape shape, int line, unsigned short width)
863 {
864 ShapeRecord record;
865
866 if ( width == 0 )
867 shape->lineWidth = 0;
868 else
869 shape->lineWidth = (SWFLineStyle_getWidth(shape->lines[line-1]) + 1) / 2;
870
871 if ( shape->isMorph )
872 return;
873
874 record = addStyleRecord(shape);
875
876 record.record.stateChange->line = line;
877 record.record.stateChange->flags |= SWF_SHAPE_LINESTYLEFLAG;
878 }
879
880 /*
881 * set filled Linestyle2 introduce with SWF 8.
882 *
883 * set line width in TWIPS
884 *
885 * WARNING: this is an internal interface
886 * external use is deprecated! use setLine2 instead
887 *
888 * Instead of providing a fill color, a FillStyle can be applied
889 * to a line.
890 *
891 * Linestyle2 also extends Linestyle1 with some extra flags:
892 *
893 * Line cap style: select one of the following flags (default is round cap style)
894 * SWF_LINESTYLE_CAP_ROUND
895 * SWF_LINESTYLE_CAP_NONE
896 * SWF_LINESTYLE_CAP_SQUARE
897 *
898 * Line join style: select one of the following flags (default is round join style)
899 * SWF_LINESTYLE_JOIN_ROUND
900 * SWF_LINESTYLE_JOIN_BEVEL
901 * SWF_LINESTYLE_JOIN_MITER
902 *
903 * Scaling flags: disable horizontal / vertical scaling
904 * SWF_LINESTYLE_FLAG_NOHSCALE
905 * SWF_LINESTYLE_FLAG_NOVSCALE
906 *
907 * Enable pixel hinting to correct blurry vertical / horizontal lines
908 * -> all anchors will be aligned to full pixels
909 * SWF_LINESTYLE_FLAG_HINTING
910 *
911 * Disable stroke closure: if no-close flag is set caps will be applied
912 * instead of joins
913 * SWF_LINESTYLE_FLAG_NOCLOSE
914 *
915 * End-cap style: default round
916 * SWF_LINESTYLE_FLAG_ENDCAP_ROUND
917 * SWF_LINESTYLE_FLAG_ENDCAP_NONE
918 * SWF_LINESTYLE_FLAG_ENDCAP_SQUARE
919 *
920 * If join style is SWF_LINESTYLE_JOIN_MITER a miter limit factor
921 * must be set. Miter max length is then calculated as:
922 * max miter len = miter limit * width.
923 * If join style is not miter, this value will be ignored.
924 */
925 void
SWFShape_setLineStyle2filled_internal(SWFShape shape,unsigned short width,SWFFillStyle fill,int flags,float miterLimit)926 SWFShape_setLineStyle2filled_internal(SWFShape shape, unsigned short width,
927 SWFFillStyle fill,
928 int flags, float miterLimit)
929 {
930 int line;
931
932 if ( shape->isEnded )
933 return;
934
935 for ( line=0; line<shape->nLines; ++line )
936 {
937 if ( SWFLineStyle_equals2filled(shape->lines[line], width, fill, flags) )
938 break;
939 }
940
941 if ( line == shape->nLines )
942 line = SWFShape_addLineStyle2filled(shape, width, fill, flags, miterLimit);
943 else
944 ++line;
945
946 finishSetLine(shape, line, width);
947 }
948
949
950 /*
951 * set Linestyle2 introduce with SWF 8.
952 *
953 * set line width in TWIPS
954 * WARNING: this is an internal interface
955 * external use is deprecated! use setLine2 instead !
956 * set color {r, g, b, a}
957 *
958 * Linestyle2 extends Linestyle1 with some extra flags:
959 *
960 * Line cap style: select one of the following flags (default is round cap style)
961 * SWF_LINESTYLE_CAP_ROUND
962 * SWF_LINESTYLE_CAP_NONE
963 * SWF_LINESTYLE_CAP_SQUARE
964 *
965 * Line join style: select one of the following flags (default is round join style)
966 * SWF_LINESTYLE_JOIN_ROUND
967 * SWF_LINESTYLE_JOIN_BEVEL
968 * SWF_LINESTYLE_JOIN_MITER
969 *
970 * Scaling flags: disable horizontal / vertical scaling
971 * SWF_LINESTYLE_FLAG_NOHSCALE
972 * SWF_LINESTYLE_FLAG_NOVSCALE
973 *
974 * Enable pixel hinting to correct blurry vertical / horizontal lines
975 * -> all anchors will be aligned to full pixels
976 * SWF_LINESTYLE_FLAG_HINTING
977 *
978 * Disable stroke closure: if no-close flag is set caps will be applied
979 * instead of joins
980 * SWF_LINESTYLE_FLAG_NOCLOSE
981 *
982 * End-cap style: default round
983 * SWF_LINESTYLE_FLAG_ENDCAP_ROUND
984 * SWF_LINESTYLE_FLAG_ENDCAP_NONE
985 * SWF_LINESTYLE_FLAG_ENDCAP_SQUARE
986 *
987 * If join style is SWF_LINESTYLE_JOIN_MITER a miter limit factor
988 * must be set. Miter max length is then calculated as:
989 * max miter len = miter limit * width.
990 * If join style is not miter, this value will be ignored.
991 */
992 void
SWFShape_setLineStyle2_internal(SWFShape shape,unsigned short width,byte r,byte g,byte b,byte a,int flags,float miterLimit)993 SWFShape_setLineStyle2_internal(SWFShape shape, unsigned short width,
994 byte r, byte g, byte b, byte a,
995 int flags, float miterLimit)
996 {
997 int line;
998
999 if ( shape->isEnded )
1000 return;
1001
1002 for ( line=0; line<shape->nLines; ++line )
1003 {
1004 if ( SWFLineStyle_equals(shape->lines[line], width, r, g, b, a, flags) )
1005 break;
1006 }
1007
1008 if ( line == shape->nLines )
1009 line = SWFShape_addLineStyle2(shape, width, r, g, b, a, flags, miterLimit);
1010 else
1011 ++line;
1012
1013 finishSetLine(shape, line, width);
1014 }
1015
1016
1017 /*
1018 * set line width and line color
1019 *
1020 * set line width in TWIPS
1021 * set line color as {r, g, b, a}
1022 *
1023 * WARNING: this is an internal interface.
1024 * external use is deprecated! use setLine instead !
1025 */
1026 void
SWFShape_setLineStyle_internal(SWFShape shape,unsigned short width,byte r,byte g,byte b,byte a)1027 SWFShape_setLineStyle_internal(SWFShape shape, unsigned short width,
1028 byte r, byte g, byte b, byte a)
1029 {
1030 int line;
1031
1032 if ( shape->isEnded )
1033 return;
1034
1035 for ( line=0; line<shape->nLines; ++line )
1036 {
1037 if ( SWFLineStyle_equals(shape->lines[line], width, r, g, b, a, 0) )
1038 break;
1039 }
1040
1041 if ( line == shape->nLines )
1042 line = SWFShape_addLineStyle(shape, width, r, g, b, a);
1043 else
1044 ++line;
1045
1046 finishSetLine(shape, line, width);
1047 }
1048
1049
1050 /* fill 0 is no fill, so set idx to one more than the shape's fill index */
getFillIdx(SWFShape shape,SWFFillStyle fill)1051 static int getFillIdx(SWFShape shape, SWFFillStyle fill)
1052 {
1053 int i;
1054
1055 for ( i=0; i<shape->nFills; ++i )
1056 {
1057 if ( SWFFillStyle_equals(fill, shape->fills[i]) )
1058 return (i+1);
1059 }
1060 return 0; // no fill
1061 }
1062
addFillStyle(SWFShape shape,SWFFillStyle fill)1063 static int addFillStyle(SWFShape shape, SWFFillStyle fill)
1064 {
1065 int i;
1066
1067 for ( i=0; i<shape->nFills; ++i )
1068 {
1069 if ( SWFFillStyle_equals(fill, shape->fills[i]) )
1070 return i;
1071 }
1072
1073 if ( shape->isEnded )
1074 return -1;
1075
1076 if ( shape->nFills%STYLE_INCREMENT == 0 )
1077 {
1078 int size = (shape->nFills+STYLE_INCREMENT) * sizeof(SWFFillStyle);
1079 shape->fills = (SWFFillStyle*)realloc(shape->fills, size);
1080 }
1081
1082 shape->fills[shape->nFills] = fill;
1083 ++shape->nFills;
1084 return shape->nFills;
1085 }
1086
1087
1088 SWFFillStyle
SWFShape_addSolidFillStyle(SWFShape shape,byte r,byte g,byte b,byte a)1089 SWFShape_addSolidFillStyle(SWFShape shape, byte r, byte g, byte b, byte a)
1090 {
1091 int ret;
1092
1093 SWFFillStyle fill = newSWFSolidFillStyle(r, g, b, a);
1094
1095 ret = addFillStyle(shape, fill);
1096 if(ret < 0) /* error */
1097 {
1098 destroySWFFillStyle(fill);
1099 return NULL;
1100 }
1101 else if(ret == shape->nFills) /* new fill */
1102 {
1103 return fill;
1104 }
1105 else /* fill is known */
1106 {
1107 destroySWFFillStyle(fill);
1108 return shape->fills[ret];
1109 }
1110 }
1111
1112
1113 SWFFillStyle
SWFShape_addGradientFillStyle(SWFShape shape,SWFGradient gradient,byte flags)1114 SWFShape_addGradientFillStyle(SWFShape shape, SWFGradient gradient, byte flags)
1115 {
1116 SWFFillStyle fill = newSWFGradientFillStyle(gradient, flags);
1117 if(addFillStyle(shape, fill) < 0)
1118 {
1119 destroySWFFillStyle(fill);
1120 return NULL;
1121 }
1122 return fill;
1123 }
1124
1125
1126 SWFFillStyle
SWFShape_addBitmapFillStyle(SWFShape shape,SWFBitmap bitmap,byte flags)1127 SWFShape_addBitmapFillStyle(SWFShape shape, SWFBitmap bitmap, byte flags)
1128 {
1129 SWFFillStyle fill;
1130
1131 if ( bitmap )
1132 {
1133 SWFCharacter_addDependency((SWFCharacter)shape,
1134 (SWFCharacter)bitmap);
1135 }
1136
1137 fill = newSWFBitmapFillStyle(bitmap, flags);
1138 if(addFillStyle(shape, fill) < 0)
1139 {
1140 destroySWFFillStyle(fill);
1141 return NULL;
1142 }
1143 return fill;
1144 }
1145
1146
1147 void
SWFShape_setLeftFillStyle(SWFShape shape,SWFFillStyle fill)1148 SWFShape_setLeftFillStyle(SWFShape shape, SWFFillStyle fill)
1149 {
1150 ShapeRecord record;
1151 int idx;
1152
1153 if ( shape->isEnded || shape->isMorph )
1154 return;
1155
1156 if(fill == NOFILL)
1157 {
1158 record = addStyleRecord(shape);
1159 record.record.stateChange->leftFill = 0;
1160 record.record.stateChange->flags |= SWF_SHAPE_FILLSTYLE0FLAG;
1161 return;
1162 }
1163
1164 idx = getFillIdx(shape, fill);
1165 if(idx == 0) // fill not present in array
1166 {
1167 SWFFillStyle_addDependency(fill, (SWFCharacter)shape);
1168 if(addFillStyle(shape, fill) < 0)
1169 return;
1170 idx = getFillIdx(shape, fill);
1171 }
1172
1173 record = addStyleRecord(shape);
1174 record.record.stateChange->leftFill = idx;
1175 record.record.stateChange->flags |= SWF_SHAPE_FILLSTYLE0FLAG;
1176 }
1177
1178
1179 void
SWFShape_setRightFillStyle(SWFShape shape,SWFFillStyle fill)1180 SWFShape_setRightFillStyle(SWFShape shape, SWFFillStyle fill)
1181 {
1182 ShapeRecord record;
1183 int idx;
1184
1185 if ( shape->isEnded || shape->isMorph )
1186 return;
1187
1188 if(fill == NOFILL)
1189 {
1190 record = addStyleRecord(shape);
1191 record.record.stateChange->rightFill = 0;
1192 record.record.stateChange->flags |= SWF_SHAPE_FILLSTYLE1FLAG;
1193 return;
1194 }
1195
1196 idx = getFillIdx(shape, fill);
1197 if(idx == 0) // fill not present in array
1198 {
1199 SWFFillStyle_addDependency(fill, (SWFCharacter)shape);
1200 if(addFillStyle(shape, fill) < 0)
1201 return;
1202 idx = getFillIdx(shape, fill);
1203 }
1204 else if (idx >= 255 && shape->useVersion == SWF_SHAPE1)
1205 {
1206 SWF_error("Too many fills for SWFShape V1.\n"
1207 "Use a higher SWFShape version\n");
1208 }
1209
1210 record = addStyleRecord(shape);
1211 record.record.stateChange->rightFill = idx;
1212 record.record.stateChange->flags |= SWF_SHAPE_FILLSTYLE1FLAG;
1213 }
1214
1215 /* move pen relative to shape origin */
1216 void
SWFShape_moveScaledPenTo(SWFShape shape,int x,int y)1217 SWFShape_moveScaledPenTo(SWFShape shape, int x, int y)
1218 {
1219 ShapeRecord record;
1220 if ( shape->isEnded )
1221 return;
1222
1223 record = addStyleRecord(shape);
1224
1225 record.record.stateChange->moveToX = shape->xpos = x;
1226 record.record.stateChange->moveToY = shape->ypos = y;
1227
1228 record.record.stateChange->flags |= SWF_SHAPE_MOVETOFLAG;
1229
1230 if ( shape->nRecords == 0 ||
1231 (shape->nRecords == 1 &&
1232 shape->records[0].type == SHAPERECORD_STATECHANGE) )
1233 {
1234 SWFRect_setBounds(SWFCharacter_getBounds(CHARACTER(shape)), x, x, y, y);
1235 SWFRect_setBounds(shape->edgeBounds, x, x, y, y);
1236 }
1237 }
1238
1239
1240 void
SWFShape_moveScaledPen(SWFShape shape,int x,int y)1241 SWFShape_moveScaledPen(SWFShape shape, int x, int y)
1242 {
1243 SWFShape_moveScaledPenTo(shape, shape->xpos+x, shape->ypos+y);
1244 }
1245
1246
1247 int
SWFShape_getScaledPenX(SWFShape shape)1248 SWFShape_getScaledPenX(SWFShape shape)
1249 {
1250 return shape->xpos;
1251 }
1252
1253
1254 int
SWFShape_getScaledPenY(SWFShape shape)1255 SWFShape_getScaledPenY(SWFShape shape)
1256 {
1257 return shape->ypos;
1258 }
1259
1260 void
SWFShape_drawScaledGlyph(SWFShape shape,SWFFont font,unsigned short c,int size)1261 SWFShape_drawScaledGlyph(SWFShape shape,
1262 SWFFont font, unsigned short c, int size)
1263 {
1264 SWFShape glyph;
1265 int i, vx, vy;
1266 if(font == NULL)
1267 return;
1268
1269 glyph = SWFFont_getGlyph(font, c);
1270 if(glyph == NULL)
1271 {
1272 SWF_warn("SWFShape_drawScaledGlyph: no glyph for code %i found \n", c);
1273 return;
1274 }
1275
1276 vx = shape->xpos;
1277 vy = shape->ypos;
1278 for(i = 0; i < glyph->nRecords; i++)
1279 addShapeRecord(shape, glyph->records[i], &vx, &vy, size/1024.0);
1280 }
1281
1282 /*
1283 * set shape version manualy
1284 * This function allows to set the shapes version information. The
1285 * version is only a hint. if necessary the version is upgraded.
1286 * valid values: SWF_SHAPE3 and SWF_SHAPE4
1287 * 3 is default
1288 * 4 if linestyle2 is used
1289 */
SWFShape_useVersion(SWFShape shape,int version)1290 void SWFShape_useVersion(SWFShape shape, int version)
1291 {
1292 if(shape->useVersion >= version)
1293 return;
1294 if(version > SWF_SHAPE4)
1295 return;
1296 shape->useVersion = version;
1297 }
1298
1299 /*
1300 * get shape version
1301 * possible values SWF_SHAPE3 and SWF_SHAPE4
1302 */
SWFShape_getVersion(SWFShape shape)1303 int SWFShape_getVersion(SWFShape shape)
1304 {
1305 return shape->useVersion;
1306 }
1307
1308 /*
1309 * set render hinting flags
1310 * possible values:
1311 * SWF_SHAPE_USESCALINGSTROKES SWF_SHAPE_USENONSCALINGSTROKES
1312 */
SWFShape_setRenderHintingFlags(SWFShape shape,int flags)1313 void SWFShape_setRenderHintingFlags(SWFShape shape, int flags)
1314 {
1315 flags &= (SWF_SHAPE_USESCALINGSTROKES | SWF_SHAPE_USENONSCALINGSTROKES);
1316 shape->flags = flags;
1317 SWFShape_useVersion(shape, SWF_SHAPE4);
1318 }
1319
SWFShape_getEdgeBounds(SWFShape shape)1320 SWFRect SWFShape_getEdgeBounds(SWFShape shape)
1321 {
1322 if(shape->useVersion == SWF_SHAPE4)
1323 return shape->edgeBounds;
1324 else
1325 return NULL;
1326 }
1327
SWFShape_getFlags(SWFShape shape)1328 int SWFShape_getFlags(SWFShape shape)
1329 {
1330 if(shape->useVersion == SWF_SHAPE4)
1331 return shape->flags;
1332 else
1333 return 0;
1334 }
1335
1336 struct out
1337 { char *buf, *ptr;
1338 int len;
1339 };
1340
oprintf(struct out * op,const char * fmt,...)1341 static void oprintf(struct out *op, const char *fmt, ...)
1342 {
1343 va_list ap;
1344 char buf[256];
1345 int d, l;
1346
1347 va_start(ap, fmt);
1348 l = vsprintf(buf, fmt, ap);
1349 while((d = op->ptr - op->buf) + l >= op->len-1)
1350 {
1351 op->buf = (char *) realloc(op->buf, op->len += 100);
1352 op->ptr = op->buf + d;
1353 }
1354 for(d = 0 ; d < l ; d++)
1355 *op->ptr++ = buf[d];
1356 }
1357
SWFShape_dumpOutline(SWFShape s)1358 char * SWFShape_dumpOutline(SWFShape s)
1359 {
1360 struct out o;
1361 int i;
1362 int x = 0, y = 0;
1363
1364 o.len = 0;
1365 o.ptr = o.buf = (char *)malloc(1);
1366 *o.ptr = 0;
1367
1368 for (i = 0; i < s->nRecords; i++)
1369 {
1370 ShapeRecord *record = s->records + i;
1371 switch(record->type)
1372 {
1373 case SHAPERECORD_STATECHANGE:
1374 {
1375 if(!record->record.stateChange->flags & SWF_SHAPE_MOVETOFLAG)
1376 continue;
1377 x = record->record.stateChange->moveToX;
1378 y = record->record.stateChange->moveToY;
1379 oprintf(&o, "moveto %d,%d\n", x, y);
1380 break;
1381 }
1382 case SHAPERECORD_LINETO:
1383 {
1384 x += record->record.lineTo->dx;
1385 y += record->record.lineTo->dy;
1386 oprintf(&o, "lineto %d,%d\n", x, y);
1387 break;
1388 }
1389 case SHAPERECORD_CURVETO:
1390 {
1391 int controlX = record->record.curveTo->controlx;
1392 int controlY = record->record.curveTo->controly;
1393 int anchorX = record->record.curveTo->anchorx;
1394 int anchorY = record->record.curveTo->anchory;
1395
1396 oprintf(&o, "curveto %d,%d %d,%d\n",
1397 x+controlX, y+controlY,
1398 x+controlX+anchorX, y+controlY+anchorY);
1399
1400 x += controlX + anchorX;
1401 y += controlY + anchorY;
1402 break;
1403 }
1404 default: break;
1405 }
1406 }
1407
1408 *o.ptr = 0;
1409 return o.buf;
1410 }
1411
1412
1413 /*
1414 * Local variables:
1415 * tab-width: 2
1416 * c-basic-offset: 2
1417 * End:
1418 */
1419