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