1 /* libsswf_tag_shape.c++ -- written by Alexis WILKE for Made to Order Software Corp. (c) 2002-2008 */
2 
3 /*
4 
5 Copyright (c) 2002-2008 Made to Order Software Corp.
6 
7 Permission is hereby granted, free of charge, to any
8 person obtaining a copy of this software and
9 associated documentation files (the "Software"), to
10 deal in the Software without restriction, including
11 without limitation the rights to use, copy, modify,
12 merge, publish, distribute, sublicense, and/or sell
13 copies of the Software, and to permit persons to whom
14 the Software is furnished to do so, subject to the
15 following conditions:
16 
17 The above copyright notice and this permission notice
18 shall be included in all copies or substantial
19 portions of the Software.
20 
21 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF
22 ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
23 LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
24 FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO
25 EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
26 LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
27 WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
28 ARISING FROM, OUT OF OR IN CONNECTION WITH THE
29 SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
30 SOFTWARE.
31 
32 */
33 
34 /** \file
35  *
36  * \brief The implementation of the sswf::TagShape class
37  *
38  * This file declares the body of the functions which are not
39  * inline. It is part of the SSWF library.
40  */
41 
42 #include	"sswf/libsswf.h"
43 
44 using namespace sswf;
45 
46 
47 
48 
49 /////////////////////////////////////////// TagShape
50 
51 
52 
53 /** \class sswf::TagShape
54  *
55  * \brief Defines an SWF shape.
56  *
57  * This object defines a shape or character.
58  *
59  * A shape is a set of lines and curves defining a picture.
60  * It is composed of fill and line styles and edges. The
61  * edges define the (x, y) coordinates of the images to
62  * render. The fill styles define the colors, images and
63  * gradients used to fill inside the polygones defined
64  * by the edges. And the line styles define how to render
65  * the segments defined by the edges.
66  *
67  * It is possible to not have a fill or line.
68  *
69  * Note that a glyph in a font is a special type of shape.
70  * (a shape without all the bells and wissels)
71  *
72  * All the possibilities define a shape of many different
73  * SWF versions.
74  *
75  * \sa sswf::Style
76  * \sa sswf::TagFont
77  * \sa <a href="../SWFalexref.html#tag_defineshape">SWF Alexis' Reference&mdash;Define Shape</a>
78  * \sa <a href="../SWFalexref.html#tag_defineshape2">SWF Alexis' Reference&mdash;Define Shape2</a>
79  * \sa <a href="../SWFalexref.html#tag_defineshape3">SWF Alexis' Reference&mdash;Define Shape3</a>
80  * \sa <a href="../SWFalexref.html#tag_defineshape4">SWF Alexis' Reference&mdash;Define Shape4</a>
81  * \sa <a href="../SWFalexref.html#tag_definemorphshape">SWF Alexis' Reference&mdash;Define Morph Shape</a>
82  * \sa <a href="../SWFalexref.html#tag_definemorphshape2">SWF Alexis' Reference&mdash;Define Morph Shape2</a>
83  * \sa <a href="../SWFalexref.html#swf_shape">SWF Alexis' Reference&mdash;swf_shape</a>
84  * \sa <a href="../SWFalexref.html#swf_shape_record">SWF Alexis' Reference&mdash;swf_shape_record</a>
85  * \sa <a href="../SWFalexref.html#swf_shape_with_style">SWF Alexis' Reference&mdash;swf_shape_with_style</a>
86  * \sa <a href="../SWFalexref.html#swf_tag">SWF Alexis' Reference&mdash;swf_tag</a>
87  */
88 
89 
90 /** \enum sswf::TagShape::morph_t
91  *
92  * \brief The mode used to add a style or edge.
93  *
94  * This enumeration helps you create only the first, only the
95  * last or both shapes at once.
96  */
97 
98 /** \var sswf::TagShape::MORPH_MODE_SHAPE0
99  *
100  * \brief Added the information to the first shape.
101  *
102  * This is used to add information to the first shape of a morph.
103  *
104  * Use this if you are not creating a morph shape.
105  *
106  * \sa sswf::TagShape::MORPH_MODE_SHAPE1
107  * \sa sswf::TagShape::MORPH_MODE_BOTH_SHAPES
108  */
109 
110 /** \var sswf::TagShape::MORPH_MODE_SHAPE1
111  *
112  * \brief Added the information to the last shape.
113  *
114  * This is used to add information to the last shape of a morph.
115  *
116  * Use this only if you intend to create a morph shape.
117  *
118  * Remember that a morph shape must have the exact same number of
119  * entries in both shapes.
120  *
121  * \sa sswf::TagShape::MORPH_MODE_SHAPE0
122  * \sa sswf::TagShape::MORPH_MODE_BOTH_SHAPES
123  */
124 
125 /** \var sswf::TagShape::MORPH_MODE_BOTH_SHAPES
126  *
127  * \brief Added the information to both shapes.
128  *
129  * This is used to add the same information the first and the
130  * last shapes at the same time.
131  *
132  * It is equivalent to calling the corresponding function
133  * once with TagShape::MORPH_MODE_SHAPE0 and once with
134  * TagShape::MORPH_MODE_SHAPE1.
135  *
136  * \sa sswf::TagShape::MORPH_MODE_SHAPE0
137  * \sa sswf::TagShape::MORPH_MODE_SHAPE1
138  */
139 
140 
141 
142 /** \enum sswf::TagShape::fill_t
143  *
144  * \brief Which fill is to be defined.
145  *
146  * This enumeration is used to determine the fill to
147  * define. Whenever rendering a filled shape, two
148  * fills are available: Fill0 and Fill1 (or even and
149  * odd fills.)
150  */
151 
152 /** \var sswf::TagShape::SHAPE_FILL_EVEN
153  *
154  * \brief Use Fill0
155  *
156  * This enumeration defines Fill0 (or the even fill.)
157  */
158 
159 /** \var sswf::TagShape::SHAPE_FILL_ODD
160  *
161  * \brief Use Fill1
162  *
163  * This enumeration defines Fill1 (or the odd fill.)
164  */
165 
166 
167 
168 
169 /** \brief Initializes the shape object.
170  *
171  * This function initializes the shape.
172  *
173  * By default the shape is empty.
174  *
175  * \param[in] parent The TagHeader in which this TagShape is inserted
176  */
TagShape(TagBase * parent)177 TagShape::TagShape(TagBase *parent)
178 	: TagBaseID("shape", parent)
179 {
180 	f_version = 1;
181 	f_morph = false;
182 	f_is_glyph = false;
183 	f_show_bounds = false;
184 	f_show_origin = false;
185 	f_has_non_scaling_strokes = true;	// smart default?
186 	f_has_scaling_strokes = true;		// smart default?
187 	// f_align_zone -- auto-init
188 	// f_bounds[0/1] -- auto-init
189 	// f_strokes_bounds[0/1] -- auto-init
190 	// f_shapes -- auto-init
191 	f_edges = 0;
192 	f_morph_edges = 0;
193 	f_setup = 0;
194 	// f_fill_styles -- auto-init
195 	// f_line_styles -- auto-init
196 	// f_record -- auto-init
197 	// f_morph_record -- auto-init
198 }
199 
200 
201 /** \brief Returns the type of a shape object.
202  *
203  * The shape object is a definition, it references other objects
204  * and it has an identifier.
205  *
206  * \return SWF_TYPE_DEFINE, SWF_TYPE_REFERENCE and SWF_TYPE_HAS_ID
207  */
TypeFlags(void) const208 TagBase::swf_type_t TagShape::TypeFlags(void) const
209 {
210 	return SWF_TYPE_DEFINE | SWF_TYPE_REFERENCE | SWF_TYPE_HAS_ID;
211 }
212 
213 
214 /** \brief Record the edges.
215  *
216  * This function is used to record the current edges in order to
217  * start saving new style specifications.
218  *
219  * This function can safely be called multiple times.
220  */
RecordEdges(void)221 void TagShape::RecordEdges(void)
222 {
223 	if(f_edges != 0) {
224 		f_record.Set(-1, f_edges);
225 		f_edges = 0;
226 	}
227 }
228 
229 
230 
231 /** \brief Record the setup.
232  *
233  * This function records the current setup. This allows one to
234  * continue by adding new edges.
235  *
236  * This function can safely be called multiple times.
237  */
RecordSetup(void)238 void TagShape::RecordSetup(void)
239 {
240 	if(f_setup != 0) {
241 		f_record.Set(-1, f_setup);
242 		f_setup = 0;
243 	}
244 }
245 
246 
247 
248 /** \brief Allocate a new edges structure.
249  *
250  * This function ensures that the current setup is recorded
251  * and creates a new edges structure.
252  */
NewEdges(void)253 void TagShape::NewEdges(void)
254 {
255 	RecordSetup();
256 
257 	if(f_edges == 0) {
258 		f_edges = new shape_edges_t(SHAPE_EDGES);
259 		MemAttach(f_edges, sizeof(shape_edges_t), "TagShape::NewEdges() -- shape edges array");
260 		//f_edges->f_edges -- initialized by the new operator
261 	}
262 }
263 
264 
265 /** \brief Allocate a new setup structure.
266  *
267  * This function ensures that the current edges are recorded
268  * and creates a new setup structure.
269  */
NewSetup(void)270 void TagShape::NewSetup(void)
271 {
272 	RecordEdges();
273 
274 	if(f_setup == 0) {
275 		f_setup = new shape_setup_t(SHAPE_SETUP);
276 		MemAttach(f_setup, sizeof(shape_setup_t), "TagShape::NewSetup() -- shape setup info");
277 	}
278 }
279 
280 
281 
282 /** \brief Add an edge.
283  *
284  * Shapes are drawn using edges. An edge defines a curve or a line segment.
285  *
286  * To draw a morph shape, you can drawn in both shapes at the same time
287  * or choose to draw only in one or the other.
288  *
289  * \param[in] morph_mode	One of the morph mode available
290  * \param[in] edge		The edge to add
291  */
AddEdge(morph_t morph_mode,const Edges::edge_t & edge)292 ErrorManager::error_code_t TagShape::AddEdge(morph_t morph_mode, const Edges::edge_t& edge)
293 {
294 	switch(morph_mode) {
295 	case MORPH_MODE_SHAPE0:
296 	case MORPH_MODE_SHAPE1:
297 	case MORPH_MODE_BOTH_SHAPES:
298 		break;
299 
300 	default:
301 		return OnError(ErrorManager::ERROR_CODE_INVALID_MORPH_INDEX, "the morph_mode parameter must be one of the MORPH_MODE_... enumeration item");
302 
303 	}
304 
305 	if(morph_mode == MORPH_MODE_SHAPE1 || morph_mode == MORPH_MODE_BOTH_SHAPES) {
306 		SetMorph();
307 
308 		if(f_morph_edges == 0) {
309 			f_morph_edges = new shape_edges_t(SHAPE_EDGES);
310 			MemAttach(f_morph_edges, sizeof(shape_edges_t), "TagShape::AddEdge() -- shape morph edges array");
311 			//f_morph_edges->f_edges -- initialized by the new operator
312 		}
313 
314 		f_morph_edges->f_edges.Set(edge);
315 	}
316 
317 	if(morph_mode == MORPH_MODE_SHAPE0 || morph_mode == MORPH_MODE_BOTH_SHAPES) {
318 		// we need at least one style
319 		if(f_fill_styles.Count() == 0 && f_line_styles.Count() == 0) {
320 			return OnError(ErrorManager::ERROR_CODE_INVALID_SHAPE, "cannot insert an edge without any style");
321 		}
322 
323 		NewEdges();
324 		f_edges->f_edges.Set(edge);
325 	}
326 
327 	return ErrorManager::ERROR_CODE_NONE;
328 }
329 
330 
331 /** \fn sswf::TagShape::AddEdge(morph_t morph_mode, long x, long y, long ctrl_x, long ctrl_y)
332  *
333  * \brief Save a curve segment.
334  *
335  * Add the specified curve. This is a helper function which saves the parameters
336  * in an edge_t structure then calls
337  * sswf::TagShape::AddEdge(morph_t morph_mode, const Edges::edge_t& edge)
338  *
339  * \sa sswf::TagShape::AddEdge(morph_t morph_mode, const Edges::edge_t& edge)
340  */
341 
342 /** \fn sswf::TagShape::AddEdge(morph_t morph_mode, long x, long y)
343  *
344  * \brief Save a line segment.
345  *
346  * Add the specified line segment. This is a helper function which saves the parameters
347  * in an edge_t structure then calls
348  * sswf::TagShape::AddEdge(morph_t morph_mode, const Edges::edge_t& edge)
349  *
350  * \sa sswf::TagShape::AddEdge(morph_t morph_mode, const Edges::edge_t& edge)
351  */
352 
353 
354 
355 
356 /** \brief Add a style to the shape.
357  *
358  * Since version 3 of SWF, it is possible to add new styles after adding
359  * edges. Otherwise, all the styles should be defined before the edges.
360  *
361  * However, the SSWF library works in a slightly different way. It expects
362  * the user to call AddStyle() with a full and a line style. Then to call
363  * AddEdges(). For the next set of edges (next polygon), one is expected
364  * to call AddStyle() again and AddEdges().
365  *
366  * The library will take care of compressing the result as much as possible
367  * eventually making use of the new feature of defining extraneous styles
368  * within the shape.
369  *
370  * \param[in] style	The style to add
371  * \param[in] fill	The odd or even fill (i.e. SHAPE_FILL_EVEN or SHAPE_FILL_ODD)
372  *
373  * \return An Error code or ErrorManager::ERROR_CODE_NONE
374  */
AddStyle(const Style & style,fill_t fill)375 ErrorManager::error_code_t TagShape::AddStyle(const Style& style, fill_t fill)
376 {
377 	// when a new style is given, we first search in the existing styles to see if
378 	// it is the first time it is inserted; if not, then we use the previous one
379 	// (we avoid duplicating the same style(s) many times!)
380 	int		idx;
381 	const Style	*s;
382 	Style		*keep;
383 
384 	if(fill != SHAPE_FILL_EVEN && fill != SHAPE_FILL_ODD) {
385 		return OnError(ErrorManager::ERROR_CODE_INVALID_FILL_PARAMETER, "invalid fill parameter in TagShape::AddStyle()");
386 	}
387 
388 	NewSetup();
389 
390 	// This should certainly be done in the PreSave() function instead!
391 	if(style.HasAlpha() && f_version < 3) {
392 		f_version = 3;
393 	}
394 	if(style.HasHardEdges() && f_version < 7) {
395 		f_version = 7;
396 	}
397 	if(style.Gradients() > 8 && f_version < 8) {
398 		f_version = 8;
399 	}
400 	switch(style.Type()) {
401 	case Style::STYLE_TYPE_GRADIENT_FOCAL:
402 	case Style::STYLE_TYPE_ENHANCED_LINE:
403 		if(f_version < 8) {
404 			f_version = 8;
405 		}
406 		break;
407 
408 	default:	// ignore others
409 		break;
410 
411 	}
412 	if(style.HasMorph()) {
413 		SetMorph();
414 	}
415 
416 	idx = 0;
417 	switch(style.Type()) {
418 	case Style::STYLE_TYPE_NO_LINE:
419 		f_setup->f_line_ref = 0;
420 		break;
421 
422 	case Style::STYLE_TYPE_LINE:
423 	case Style::STYLE_TYPE_ENHANCED_LINE:
424 		idx = f_line_styles.Count();
425 		while(idx > 0) {
426 			idx--;
427 			s = dynamic_cast<const Style *>(f_line_styles.Get(idx));
428 			if(style == *s) {
429 				// use that already saved style
430 				goto add_line;
431 			}
432 		}
433 		// it doesn't exist, we need to insert it now
434 		idx = f_line_styles.Count();
435 		// TODO: if the Count() is 32768 then we need to reset the counters...
436 		//	 don't forget to copy the current fill styles
437 		//	 See the NewStyles() function
438 		// NOTE: if the maximum version is V1.0, then the limit should be
439 		//	 set to 255 instead
440 		if(idx >= 32766) {
441 			return OnError(ErrorManager::ERROR_CODE_TOO_MANY_STYLES, "too many line styles");
442 		}
443 		keep = new Style(style);		// copy the style
444 		MemAttach(keep, sizeof(Style), "TagShape::AddStyle() -- create a style (line)");
445 		f_line_styles.Set(-1, keep);
446 add_line:
447 		f_setup->f_line_ref = idx + 1;
448 		break;
449 
450 	case Style::STYLE_TYPE_NO_FILL:
451 		f_setup->f_fill_ref[fill] = 0;
452 		break;
453 
454 	default:	// most other cases...
455 		idx = f_fill_styles.Count();
456 		while(idx > 0) {
457 			idx--;
458 			s = dynamic_cast<const Style *>(f_fill_styles.Get(idx));
459 			if(style == *s) {
460 				// use that already saved style
461 				goto add_fill;
462 			}
463 		}
464 		// it doesn't exist, we need to insert it now
465 		idx = f_fill_styles.Count();
466 		// TODO: if the Count() is 32768 then we need to reset the counters...
467 		//	 don't forget to copy the other current fill & line styles
468 		//	 See the NewStyles() function
469 		// NOTE: if the maximum version is V1.0, then the limit should be
470 		//	 set to 255 instead
471 		if(idx >= 32766) {
472 			return OnError(ErrorManager::ERROR_CODE_TOO_MANY_STYLES, "too many fill styles");
473 		}
474 		keep = new Style(style);
475 		MemAttach(keep, sizeof(Style), "TagShape::AddStyle() -- create a style (fill)");
476 		//*keep = style;			// copy the style
477 		f_fill_styles.Set(-1, keep);
478 add_fill:
479 		f_setup->f_fill_ref[fill] = idx + 1;
480 		break;
481 
482 	}
483 
484 	// overflow for V1.0 of Flash
485 	if(idx > 255 && f_version < 2) {
486 		f_version = 2;
487 	}
488 
489 	return ErrorManager::ERROR_CODE_NONE;
490 }
491 
492 
493 /** \brief Add a move to a specific location.
494  *
495  * This function is used to move the drawing pen to a specified location.
496  * No drawing happens when moving the pen in this way.
497  *
498  * The function can be called multiple times in a raw, though only the
499  * last position is kept.
500  *
501  * \param[in] morph_mode	One of the available morph modes
502  * \param[in] x			The new horizontal location
503  * \param[in] y			The new vertical location
504  *
505  * \return An error code or ErrorManager::ERROR_CODE_NONE
506  */
AddMove(morph_t morph_mode,long x,long y)507 ErrorManager::error_code_t TagShape::AddMove(morph_t morph_mode, long x, long y)
508 {
509 	switch(morph_mode) {
510 	case MORPH_MODE_SHAPE0:
511 	case MORPH_MODE_SHAPE1:
512 	case MORPH_MODE_BOTH_SHAPES:
513 		break;
514 
515 	default:
516 		return OnError(ErrorManager::ERROR_CODE_INVALID_MORPH_INDEX, "the morph_mode parameter must be one of the MORPH_MODE_... enumeration item");
517 
518 	}
519 
520 	if(morph_mode == MORPH_MODE_SHAPE1 || morph_mode == MORPH_MODE_BOTH_SHAPES) {
521 		SetMorph();
522 
523 		if(f_morph_edges != 0) {
524 			f_morph_record.Set(-1, f_morph_edges);
525 			f_morph_edges = 0;
526 		}
527 
528 		shape_setup_t *setup = new shape_setup_t(SHAPE_SETUP);
529 		MemAttach(setup, sizeof(shape_setup_t), "TagShape::AddMove() -- shape morph setup info (i.e. move only)");
530 		setup->f_x = x;
531 		setup->f_y = y;
532 		f_morph_record.Set(-1, setup);
533 	}
534 
535 	if(morph_mode == MORPH_MODE_SHAPE0 || morph_mode == MORPH_MODE_BOTH_SHAPES) {
536 		NewSetup();
537 
538 		f_setup->f_x = x;
539 		f_setup->f_y = y;
540 	}
541 
542 	return ErrorManager::ERROR_CODE_NONE;
543 }
544 
545 
546 /** \brief Mark the shape as a morphing shape
547  *
548  * This function marks the shape as a morphing shape.
549  *
550  * This means all the styles and edges must be defined
551  * twice. Once for the "normal" shape and once for the
552  * "morphed" shape.
553  */
SetMorph(void)554 void TagShape::SetMorph(void)
555 {
556 	f_morph = true;
557 	if(f_version < 3) {
558 		f_version = 3;
559 	}
560 }
561 
562 
563 /** \brief Set the alignment zone rectangle of a glyph
564  *
565  * This function is used to define a alignment zone to properly place glyphs
566  * on a pixel boundary.
567  *
568  * Calling this function implies calling the Glyph() function to mark
569  * the shape as being a glyph.
570  *
571  * You must set an align zone on all the glyphs of a font for them to be
572  * taken in account.
573  *
574  * \param[in] zone		The align zone rectangle
575  */
SetAlignZone(const SRectangle & zone)576 void TagShape::SetAlignZone(const SRectangle& zone)
577 {
578 	f_align_zone = zone;
579 	Glyph();
580 }
581 
582 
583 
584 
585 /** \brief Check whether a zone align rectangle is defined.
586  *
587  * This function returns true if an align zone is defined.
588  * This means that the rectangle used to define the alignment zone
589  * is not empty.
590  *
591  * \return True if the alignment zone is defined in this glyph.
592  */
HasAlignZone(void) const593 bool TagShape::HasAlignZone(void) const
594 {
595 	return !f_align_zone.IsEmpty();
596 }
597 
598 
599 
600 /** \brief Save alignment zone in a Data buffer
601  *
602  * This function saves the alignment zone in a Data buffer.
603  *
604  * This represents one entry in an swf_zone_array structure.
605  *
606  * \bug
607  * At this time I assume that the rectangle has to be defined
608  * in TWIPS. This means each coordinate is divided by 20 before
609  * being saved. That seems to correspond to what the documentation
610  * says, but the exact type of coordinate is not really defined.
611  *
612  * \param[in] data	The Data buffer where the zone is saved
613  */
SaveAlignZone(Data & data) const614 void TagShape::SaveAlignZone(Data& data) const
615 {
616 	data.PutShortFloat(f_align_zone.XMin() / 20.0f);
617 	data.PutShortFloat((f_align_zone.XMax() - f_align_zone.XMin()) / 20.0f);
618 	data.PutShortFloat(f_align_zone.YMin() / 20.0f);
619 	data.PutShortFloat((f_align_zone.YMax() - f_align_zone.YMin()) / 20.0f);
620 }
621 
622 
623 
624 
625 
626 /** \brief Set the rectangle within which the shape is enclosed
627  *
628  * This function is used to mark the edges of the shape. This is the limit
629  * where the points on a shape go. Watchout, whenever you draw a curve, the
630  * points drawn can go outside of the limits defined by the control points.
631  *
632  * \bug
633  * The show bounds feature does not work for a shape used as the hit test
634  * of a button or in a glyph.
635  *
636  * \param[in] index		The morph image index (0 or 1)
637  * \param[in] rect		The rectangle defining the bounds
638  * \param[in] show_bounds	Whether the Save() function should generate lines
639  *				to show the bounds in the resulting animation
640  *
641  * \return An error code or ErrorManager::ERROR_CODE_NONE
642  */
SetBounds(int index,const SRectangle & rect,bool show_bounds)643 ErrorManager::error_code_t TagShape::SetBounds(int index, const SRectangle& rect, bool show_bounds)
644 {
645 	if(index != 0 && index != 1) {
646 		return OnError(ErrorManager::ERROR_CODE_INVALID_MORPH_INDEX, "invalid index for TagShape::SetBounds()");
647 	}
648 
649 	f_show_bounds = show_bounds;
650 	f_bounds[index] = rect;
651 	if(index == 1) {
652 		SetMorph();
653 	}
654 
655 	return ErrorManager::ERROR_CODE_NONE;
656 }
657 
658 
659 /** \fn sswf::TagShape::Bounds(int index) const
660  *
661  * \brief Retrieve a copy of the bounds.
662  *
663  * This function retrieves a copy of the bounds rectangle
664  * currently defined in the shape.
665  *
666  * The function returns a direct reference to the bounds
667  * defined inside the shape. It will change whenever the
668  * TagShape::SetBounds() function is called.
669  *
670  * \param[in] index		The morph image index (0 or 1)
671  *
672  * \return true if the bounds rectangle was defined
673  *
674  * \sa sswf::TagShape::SetBounds(int index, const SRectangle& rect, bool show_bounds)
675  * \sa sswf::TagShape::HasBounds(void) const
676  * \sa sswf::TagShape::HasShowBounds(void) const
677  */
678 
679 /** \fn sswf::TagShape::HasBounds(void) const
680  *
681  * \brief Check whether bounds were properly defined.
682  *
683  * This function defines whether the bounds are defined or
684  * not.
685  *
686  * \return true if the bounds rectangle was defined
687  *
688  * \sa sswf::TagShape::SetBounds(int index, const SRectangle& rect, bool show_bounds)
689  */
690 
691 /** \fn sswf::TagShape::HasShowBounds(void) const
692  *
693  * \brief Check whether bounds should be shown.
694  *
695  * This function defines whether the Save() function will
696  * add lines around the shape to show the location of the
697  * bounds.
698  *
699  * \return true if the bounds rectangle was defined
700  *
701  * \sa sswf::TagShape::SetBounds(int index, const SRectangle& rect, bool show_bounds)
702  */
703 
704 /** \fn sswf::TagShape::ShowBounds(bool show = true)
705  *
706  * \brief Set whether the shape should show the bounds.
707  *
708  * This function defines the flag used to tell the shape
709  * whether to show the bounds with lines.
710  *
711  * \bug
712  * The show bounds feature does not work for a shape used as the hit test
713  * of a button or in a glyph.
714  *
715  * \param[in] show Set to true if the bounds rectangle should be draw
716  *
717  * \sa sswf::TagShape::SetBounds(int index, const SRectangle& rect, bool show_bounds)
718  */
719 
720 /** \fn sswf::TagShape::ShowOrigin(bool show = true)
721  *
722  * \brief Set whether the shape should draw origin lines.
723  *
724  * This function defines the flag used to tell the shape
725  * whether to draw lines to show the origin of the shape.
726  *
727  * This is particularly useful if the shape needs to be rotated.
728  *
729  * \bug
730  * The show origin feature does not work for a shape used as the hit test
731  * of a button or in a glyph.
732  *
733  * \param[in] show Set to true if the origin lines should be draw
734  *
735  * \sa sswf::TagShape::SetBounds(int index, const SRectangle& rect, bool show_bounds)
736  */
737 
738 
739 
740 
741 /** \brief Set the rectangle where the shape including "leaking" strokes is to be rendered
742  *
743  * This function is used to mark the edges of the shape including the strokes.
744  * This is the limit where the points of a shape go plus the width of the strokes
745  * used by the lines on the edges. Watchout, whenever you draw a curve, the
746  * points drawn can go outside of the limits defined by the control points.
747  *
748  * \param[in] index		The morph image index (0 or 1)
749  * \param[in] rect		The rectangle defining the bounds
750  *
751  * \return An error code or ErrorManager::ERROR_CODE_NONE
752  */
SetStrokesBounds(int index,const SRectangle & rect)753 ErrorManager::error_code_t TagShape::SetStrokesBounds(int index, const SRectangle& rect)
754 {
755 	if(index != 0 && index != 1) {
756 		return OnError(ErrorManager::ERROR_CODE_INVALID_MORPH_INDEX, "invalid index for TagShape::SetStrokesBounds()");
757 	}
758 
759 	f_strokes_bounds[index] = rect;
760 	if(index == 1) {
761 		SetMorph();
762 	}
763 
764 	return ErrorManager::ERROR_CODE_NONE;
765 }
766 
767 
768 /** \fn sswf::TagShape::SetNonScalingStrokes(bool has_non_scaling_strokes)
769  *
770  * \brief Set whether the shape has any non-scaling strokes.
771  *
772  * This function can be used to mark whether some of the LINE2 are
773  * marked as non-scaling.
774  *
775  * \bug
776  * This needs to be done dynamically whenever we save the shapes.
777  *
778  * \param[in] has_non_scaling_strokes Set whether that flag should be saved as true or false
779  */
780 
781 /** \fn sswf::TagShape::SetScalingStrokes(bool has_scaling_strokes)
782  *
783  * \brief Set whether the shape has any scaling strokes.
784  *
785  * This function can be used to mark whether some of the LINE2 are
786  * marked as scaling.
787  *
788  * \bug
789  * This needs to be done dynamically whenever we save the shapes.
790  *
791  * \param[in] has_scaling_strokes Set whether that flag should be saved as true or false
792  */
793 
794 
795 
796 
797 /** \brief Force a new set of styles.
798  *
799  * This function can be called to request the TagShape object to create
800  * a new set of styles.
801  *
802  * \note
803  * This will enable one to have smaller fill & line style
804  * references (less bits) -- however that is most probably
805  * useless (my point of view.)
806  */
NewStyles(void)807 void TagShape::NewStyles(void)
808 {
809 	shape_record_t	*sr;
810 
811 	// valid only in V2.0+
812 	if(f_version < 2) {
813 		f_version = 2;
814 	}
815 
816 	sr = new shape_record_t;
817 	MemAttach(sr, sizeof(shape_record_t), "TagShape::NewStyles() -- shape record used to have new styles");
818 
819 	sr->f_fill_styles = new Vectors(f_fill_styles);
820 	MemAttach(sr->f_fill_styles, sizeof(Vectors), "TagShape::NewStyles() -- fill styles record copy");
821 	f_fill_styles.Empty();
822 
823 	sr->f_line_styles = new Vectors(f_line_styles);
824 	MemAttach(sr->f_line_styles, sizeof(Vectors), "TagShape::NewStyles() -- line styles record copy");
825 	f_line_styles.Empty();
826 
827 	sr->f_record = new Vectors(f_record);
828 	MemAttach(sr->f_record, sizeof(Vectors), "TagShape::NewStyles() -- shape record copy");
829 	f_record.Empty();
830 
831 	f_shapes.Set(-1, sr);
832 }
833 
834 
835 
836 /** \brief Mark the shape as being a glyph
837  *
838  * This function marks the shape as being a glyph. It has
839  * two effects: make sure the shape can be used in a font
840  * as a glyph and it removes the identification used for
841  * this shape.
842  */
Glyph(void)843 void TagShape::Glyph(void)
844 {
845 	f_is_glyph = true;
846 	NoIdentification();
847 }
848 
849 
850 /** \brief Check whehter this shape is a glyph.
851  *
852  * This function checks whether the Glyph() function was called.
853  *
854  * \return true if the shape is a glyph
855  */
IsGlyph(void) const856 bool TagShape::IsGlyph(void) const
857 {
858 	return f_is_glyph;
859 }
860 
861 
862 /** \brief Check whether there is anything in this shape.
863  *
864  * This function checks to see whether the shape is still
865  * empty.
866  *
867  * \return true if the shape was not yet defined.
868  */
IsEmpty(void) const869 bool TagShape::IsEmpty(void) const
870 {
871 	return f_record.Count() == 0 && f_edges == 0;
872 }
873 
874 
875 /** \brief Ensure the minimum version is acceptable.
876  *
877  * This function ensures that the minimum version necessary to
878  * save that TagShape is acceptable.
879  *
880  * \return An error code or ErrorManager::ERROR_CODE_NONE.
881  */
PreSave(void)882 ErrorManager::error_code_t TagShape::PreSave(void)
883 {
884 	MinimumVersion(f_version);
885 
886 	return ErrorManager::ERROR_CODE_NONE;
887 }
888 
889 
890 /** \brief This function saves the TagShape in a Data buffer
891  *
892  * This function determines the type of a shape this shape is
893  * (DefineShape, DefineShape2, DefineShape3, DefineShape4,
894  * DefineMorphShape or DefineMorphShape2) and then saves it
895  * in the specified Data buffer.
896  *
897  * The PreSave() function must have been called once before.
898  *
899  * \note
900  * Glyphs aren't saved via this function. Instead the
901  * Font saves the edges as required.
902  *
903  * \param[in] data	The Data buffer where the edges are to be saved
904  *
905  * \return An error code or ErrorManager::ERROR_CODE_NONE.
906  */
Save(Data & data)907 ErrorManager::error_code_t TagShape::Save(Data& data)
908 {
909 	// In order to save a shape properly we need to (1) know its
910 	// version (f_version) and whether it is a morphing shape
911 	// (f_morph).
912 	save_info_t			info, sub_info, *info_ptr;
913 	swf_tag_t			tag;
914 	int				idx, max;
915 	ErrorManager::error_code_t	ec;
916 	shape_setup_t			last_setup(SHAPE_SETUP, true);
917 	SRectangle			rect;
918 
919 	RecordEdges();
920 	// RecordSetup(); -- don't record the last setup!!! that's totally useless!!!
921 
922 	if(f_is_glyph) {
923 		// save nothing for a glyph since these are saved within a font instead
924 		return ErrorManager::ERROR_CODE_NONE;
925 	}
926 
927 	// default for V1.0
928 	tag = SWF_TAG_DEFINE_SHAPE;
929 	info.f_save_alpha = false;
930 	info.f_ext_size = false;
931 	info.f_shape4 = false;
932 
933 	switch(f_version) {
934 	case 2:
935 		//info.f_save_alpha = false;
936 		info.f_ext_size = true;
937 		tag = SWF_TAG_DEFINE_SHAPE2;
938 		break;
939 
940 	case 3:
941 	case 7:
942 		info.f_save_alpha = true;
943 		info.f_ext_size = true;
944 		if(f_morph) {
945 			tag = SWF_TAG_DEFINE_MORPH_SHAPE;
946 		}
947 		else {
948 			tag = SWF_TAG_DEFINE_SHAPE3;
949 		}
950 		break;
951 
952 	case 8:
953 		info.f_save_alpha = true;
954 		info.f_ext_size = true;
955 		info.f_shape4 = true;
956 		if(f_morph) {
957 			tag = SWF_TAG_DEFINE_MORPH_SHAPE2;
958 		}
959 		else {
960 			tag = SWF_TAG_DEFINE_SHAPE4;
961 		}
962 		break;
963 
964 #if DEBUG
965 	case 1:
966 		break;
967 
968 	default:
969 		assert(0, "invalid f_version in a TagShape object to be saved");
970 		return OnError(ErrorManager::ERROR_CODE_INTERNAL_ERROR, "invalid f_version in a TagShape object to be saved");
971 #endif
972 
973 	}
974 	info.f_first = true;
975 	info.f_save_styles = true;
976 
977 	if(f_morph && f_bounds[1].IsEmpty()) {
978 		f_bounds[1] = f_bounds[0];
979 	}
980 
981 	SaveID(info.f_data);
982 	max = f_morph ? 2 : 1;
983 	for(idx = 0; idx < max; idx++) {
984 		info.f_data.Align();
985 		if(f_show_bounds) {
986 			// first we have to make sure that it is properly ordered
987 			rect.SetReorder(f_bounds[idx].XMin(), f_bounds[idx].XMax(), f_bounds[idx].YMin(), f_bounds[idx].YMax());
988 			// then we can enlarge it as required to include the bounding line
989 			rect.Set(rect.XMin() - 2, rect.XMax() + 2, rect.YMin() - 2, rect.YMax() + 2);
990 			rect.Save(info.f_data);
991 		}
992 		else {
993 			f_bounds[idx].Save(info.f_data);
994 		}
995 	}
996 
997 	if(info.f_shape4) {
998 		for(idx = 0; idx < max; idx++) {
999 			info.f_data.Align();
1000 			if(f_strokes_bounds[idx].IsEmpty()) {
1001 				f_strokes_bounds[idx] = f_bounds[idx];
1002 			}
1003 			f_strokes_bounds[idx].Save(info.f_data);
1004 		}
1005 
1006 		info.f_data.WriteBits(0, 6);
1007 		info.f_data.WriteBits(f_has_non_scaling_strokes, 1);
1008 		info.f_data.WriteBits(f_has_scaling_strokes, 1);
1009 	}
1010 
1011 	if(f_morph) {
1012 		// in case of a morph we need to save all the styles
1013 		// and the first shape to know the size to save in
1014 		// the offset;
1015 		sub_info.f_save_alpha = info.f_save_alpha;
1016 		sub_info.f_ext_size = info.f_ext_size;
1017 		sub_info.f_shape4 = info.f_shape4;
1018 		sub_info.f_first = true;
1019 		sub_info.f_save_styles = true;
1020 
1021 		info_ptr = &sub_info;
1022 	}
1023 	else {
1024 		info_ptr = &info;
1025 	}
1026 
1027 	// save the extras
1028 	max = f_shapes.Count();
1029 	for(idx = 0; idx < max; idx++) {
1030 		info_ptr->f_sr = *dynamic_cast<shape_record_t *>(f_shapes.Get(idx));
1031 		ec = SaveShape(*info_ptr, last_setup);
1032 		if(ec != ErrorManager::ERROR_CODE_NONE) {
1033 			return ec;
1034 		}
1035 	}
1036 
1037 	// now save the current
1038 	info_ptr->f_sr.f_fill_styles = &f_fill_styles;
1039 	info_ptr->f_sr.f_line_styles = &f_line_styles;
1040 	info_ptr->f_sr.f_record = &f_record;
1041 	ec = SaveShape(*info_ptr, last_setup);
1042 	if(ec != ErrorManager::ERROR_CODE_NONE) {
1043 		return ec;
1044 	}
1045 
1046 
1047 	if(f_morph) {
1048 		// the last setup for the morphed version has
1049 		// to start from scratch
1050 		shape_setup_t	last_setup(SHAPE_SETUP, true);
1051 
1052 		// save an end of record now (between both shapes)
1053 		sub_info.f_data.WriteBits(0, 6);
1054 
1055 		// save the offset to the 2nd set of edges
1056 		info.f_data.PutLong(sub_info.f_data.ByteSize());
1057 
1058 		// now we can add the style & 1st set of edges to the main buffer
1059 		info.f_data.Append(sub_info.f_data);
1060 
1061 		// we need to save a count for the styles; since they are
1062 		// not referenced at all, we always use 0x11
1063 		info.f_data.PutByte(0x11);
1064 
1065 		// we need to record the last edges now
1066 		if(f_morph_edges != 0) {
1067 			f_morph_record.Set(-1, f_morph_edges);
1068 			f_morph_edges = 0;
1069 		}
1070 
1071 		// now save the morphed edges
1072 		info.f_sr.f_fill_styles = &f_fill_styles;
1073 		info.f_sr.f_line_styles = &f_line_styles;
1074 		info.f_sr.f_record = &f_morph_record;
1075 		info.f_save_styles = false;
1076 		ec = SaveShape(info, last_setup);
1077 		if(ec != ErrorManager::ERROR_CODE_NONE) {
1078 			return ec;
1079 		}
1080 	}
1081 	else {
1082 		// TODO: the bounds and/or origin do not work properly in
1083 		// different types of shapes (i.e. hit area for a button)
1084 		// we should somehow test that... (it could be that a hit
1085 		// area needs to be a fill only?)
1086 		if(f_show_bounds || f_show_origin) {
1087 			Vectors		fill_styles;		// empty!
1088 			Vectors		line_styles;		// one style: a red line
1089 			Vectors		record;			// setup + rect.
1090 			Style		red_line(*Header());
1091 			Color		red;
1092 			shape_setup_t	bounds_setup(SHAPE_SETUP);
1093 			shape_setup_t	horizontal_setup(SHAPE_SETUP);
1094 			shape_setup_t	vertical_setup(SHAPE_SETUP);
1095 			shape_edges_t	bounds_edges(SHAPE_EDGES);
1096 			shape_edges_t	horizontal_edges(SHAPE_EDGES);
1097 			shape_edges_t	vertical_edges(SHAPE_EDGES);
1098 
1099 			// create red line style
1100 			red.Set(255, 0, 0);
1101 			red_line.SetLine(0, 2, red);
1102 			line_styles.Set(-1, &red_line);
1103 
1104 			// insert line style
1105 			if(f_show_bounds) {
1106 				bounds_setup.f_fill_ref[0] = 0;
1107 				bounds_setup.f_fill_ref[1] = 0;
1108 				bounds_setup.f_line_ref = 1;
1109 				bounds_setup.f_x = f_bounds[0].XMin();
1110 				bounds_setup.f_y = f_bounds[0].YMin();
1111 				record.Set(-1, &bounds_setup);
1112 
1113 				// draw rectangle
1114 				bounds_edges.f_edges.Set(f_bounds[0].XMax() - f_bounds[0].XMin(), 0);
1115 				bounds_edges.f_edges.Set(0, f_bounds[0].YMax() - f_bounds[0].YMin());
1116 				bounds_edges.f_edges.Set(f_bounds[0].XMin() - f_bounds[0].XMax(), 0);
1117 				bounds_edges.f_edges.Set(0, f_bounds[0].YMin() - f_bounds[0].YMax());
1118 
1119 				// insert rect.
1120 				record.Set(-1, &bounds_edges);
1121 			}
1122 
1123 			if(f_show_origin) {
1124 				// insert X axis
1125 				horizontal_setup.f_fill_ref[0] = 0;
1126 				horizontal_setup.f_fill_ref[1] = 0;
1127 				horizontal_setup.f_line_ref = 1;
1128 				horizontal_setup.f_x = f_bounds[0].XMin();
1129 				horizontal_setup.f_y = 0;
1130 				record.Set(-1, &horizontal_setup);
1131 				horizontal_edges.f_edges.Set(f_bounds[0].XMax() - f_bounds[0].XMin(), 0);
1132 				record.Set(-1, &horizontal_edges);
1133 
1134 				// insert Y axis
1135 				vertical_setup.f_fill_ref[0] = 0;
1136 				vertical_setup.f_fill_ref[1] = 0;
1137 				vertical_setup.f_line_ref = 1;
1138 				vertical_setup.f_x = 0;
1139 				vertical_setup.f_y = f_bounds[0].YMin();
1140 				record.Set(-1, &vertical_setup);
1141 				vertical_edges.f_edges.Set(0, f_bounds[0].YMax() - f_bounds[0].YMin());
1142 				record.Set(-1, &vertical_edges);
1143 			}
1144 
1145 			// insert all of that in the shape now
1146 			info.f_sr.f_fill_styles = &fill_styles;
1147 			info.f_sr.f_line_styles = &line_styles;
1148 			info.f_sr.f_record = &record;
1149 			ec = SaveShape(info, last_setup);
1150 			if(ec != ErrorManager::ERROR_CODE_NONE) {
1151 				return ec;
1152 			}
1153 		}
1154 	}
1155 
1156 	// save an end of record now
1157 	info.f_data.WriteBits(0, 6);
1158 
1159 	SaveTag(data, tag, info.f_data.ByteSize());
1160 	data.Append(info.f_data);
1161 
1162 	return ErrorManager::ERROR_CODE_NONE;
1163 }
1164 
1165 
1166 
1167 /** \brief Save the edges only.
1168  *
1169  * This function is used by the sswf::TagFont object to save the
1170  * edges without any style.
1171  *
1172  * Note that this function is expected to be used to save glyphs
1173  * only and thus shapes that are not morphing.
1174  *
1175  * \param[in] data	The Data buffer where the edges are to be saved
1176  *
1177  * \return An error code or ErrorManager::ERROR_CODE_NONE.
1178  */
SaveWithoutStyles(Data & data)1179 ErrorManager::error_code_t TagShape::SaveWithoutStyles(Data& data)
1180 {
1181 	save_info_t			info;
1182 	int				idx, max;
1183 	shape_setup_t			last_setup(SHAPE_SETUP, true);
1184 	ErrorManager::error_code_t	ec;
1185 
1186 	RecordEdges();
1187 	// don't record the last setup!!! that's totally useless!!!
1188 
1189 	if(f_morph) {
1190 		return OnError(ErrorManager::ERROR_CODE_INCOMPATIBLE_MORPH, "cannot save a morphing glyph");
1191 	}
1192 
1193 	// assume V1.0 for fonts (that's what this is for!)
1194 	info.f_save_alpha = false;
1195 	info.f_ext_size = false;
1196 	info.f_first = false;		// no style is to be saved... (only style changes?)
1197 	info.f_save_styles = false;
1198 
1199 	// this is not automatically defined in this case
1200 	info.f_fill_bits_count = 1;
1201 	info.f_line_bits_count = 1;
1202 
1203 	max = f_shapes.Count();
1204 	for(idx = 0; idx < max; idx++) {
1205 		info.f_sr = *dynamic_cast<shape_record_t *>(f_shapes.Get(idx));
1206 		ec = SaveShape(info, last_setup);
1207 		if(ec != ErrorManager::ERROR_CODE_NONE) {
1208 			return ec;
1209 		}
1210 	}
1211 
1212 	info.f_sr.f_fill_styles = &f_fill_styles;
1213 	info.f_sr.f_line_styles = &f_line_styles;
1214 	info.f_sr.f_record = &f_record;
1215 	ec = SaveShape(info, last_setup);
1216 	if(ec != ErrorManager::ERROR_CODE_NONE) {
1217 		return ec;
1218 	}
1219 
1220 	// save an end of record now
1221 	info.f_data.WriteBits(0, 6);
1222 
1223 	data.PutByte(0x11);		// TODO: should that be 0x11 or dynamically determined?
1224 	data.Append(info.f_data);
1225 
1226 	return ErrorManager::ERROR_CODE_NONE;
1227 }
1228 
1229 
1230 /** \brief Save a shape.
1231  *
1232  * This function saves a shape depending on the info definitions.
1233  *
1234  * \param[in] info		Definition of what needs to be saved.
1235  * \param[in] last_setup	Current shape setup (move, fill & line styles)
1236  *
1237  * \return An error code or ErrorManager::ERROR_CODE_NONE.
1238  */
SaveShape(save_info_t & info,shape_setup_t & last_setup)1239 ErrorManager::error_code_t TagShape::SaveShape(save_info_t& info, shape_setup_t& last_setup)
1240 {
1241 	ErrorManager::error_code_t	ec;
1242 	int				idx, max;
1243 	shape_what_t			*rec;
1244 	shape_edges_t			*edges;
1245 	shape_setup_t			*setup;
1246 
1247 	idx = 0;
1248 
1249 	if(info.f_save_styles) {
1250 		if(!info.f_first) {
1251 			// we need a "special" setup entry with the new fill & line styles
1252 			// NOTE: I'm not sure whether it could be merged with the following
1253 			//	 setup record, it is possible to use idx = 1 for that purpose;
1254 			//	 however it would mean that the number of bits used to
1255 			//	 define the new fill & line styles isn't known before you
1256 			//	 read the new styles which happen AFTER the rest!?!? strange
1257 			//	 (stupid?) idea from Macromedia I think
1258 			info.f_data.WriteBits(0x10, 6);		// setup with new styles byte indicator
1259 		}
1260 		info.f_first = false;
1261 		ec = SaveStyles(info);
1262 		if(ec != ErrorManager::ERROR_CODE_NONE) {
1263 			return ec;
1264 		}
1265 	}
1266 
1267 	max = info.f_sr.f_record->Count();
1268 //fprintf(stderr, "Count = %d\n", max);
1269 	while(idx < max) {
1270 		rec = dynamic_cast<shape_what_t *>(info.f_sr.f_record->Get(idx));
1271 		if(rec->f_what == SHAPE_SETUP) {
1272 			// change setup here
1273 			setup = dynamic_cast<shape_setup_t *>(rec);
1274 
1275 //fprintf(stderr, "  setup = %p\n", setup);
1276 
1277 			ec = SaveSetup(info, *setup, last_setup);
1278 			if(ec != ErrorManager::ERROR_CODE_NONE) {
1279 				return ec;
1280 			}
1281 		}
1282 		else {
1283 			// we've got a set of edges
1284 			edges = dynamic_cast<shape_edges_t *>(rec);
1285 			edges->f_edges.Save(info.f_data, last_setup.f_x, last_setup.f_y);
1286 		}
1287 		idx++;
1288 	}
1289 
1290 	return ErrorManager::ERROR_CODE_NONE;
1291 }
1292 
1293 
1294 /** \brief Save the specified shape setup.
1295  *
1296  * This function saves a setup (move, fill and lines).
1297  *
1298  * \param[in] info	Definition of what needs to be saved
1299  * \param[in] setup	Setup to save
1300  * \param[in] last	Previous setup saved (to avoid repeats)
1301  *
1302  * \return An error code or ErrorManager::ERROR_CODE_NONE.
1303  */
SaveSetup(save_info_t & info,const shape_setup_t & setup,shape_setup_t & last)1304 ErrorManager::error_code_t TagShape::SaveSetup(save_info_t& info, const shape_setup_t& setup, shape_setup_t& last)
1305 {
1306 	bool		has_line_style, has_fill_style[2], has_move;
1307 	int		sz, bits;
1308 	long		x, y;
1309 
1310 	info.f_data.WriteBits(0, 1);		// a setup entry
1311 
1312 	// V2.0+ have a NewStyles flag
1313 #if 0
1314 	if(f_version != 1) {
1315 		info.f_data.WriteBits(0, 1);		// has no new styles
1316 	}
1317 #endif
1318 	info.f_data.WriteBits(0, 1);		// has no new styles
1319 
1320 	// is the line style setup changing?
1321 	has_line_style = /*!info.f_save_styles ||*/ setup.f_line_ref != -1 && setup.f_line_ref != last.f_line_ref;
1322 	info.f_data.WriteBits(has_line_style, 1);
1323 
1324 	// is one of the fill style changing?
1325 	has_fill_style[0] = setup.f_fill_ref[0] != -1 && setup.f_fill_ref[0] != last.f_fill_ref[0];
1326 	has_fill_style[1] = setup.f_fill_ref[1] != -1 && setup.f_fill_ref[1] != last.f_fill_ref[1];
1327 	info.f_data.WriteBits(has_fill_style[1] * 2 + has_fill_style[0], 2);
1328 
1329 	// is it moving?
1330 	x = setup.f_x;
1331 	y = setup.f_y;
1332 	if(x == LONG_MIN) {
1333 		x = last.f_x;
1334 	}
1335 	if(y == LONG_MIN) {
1336 		y = last.f_y;
1337 	}
1338 	has_move = x != last.f_x || y != last.f_y;
1339 	info.f_data.WriteBits(has_move, 1);
1340 
1341 #if 0
1342 printf("has line: %d has fill: %d/%d, has move: %d (%ld, %ld) vs (%ld, %ld)\n",
1343 		has_line_style, has_fill_style[0], has_fill_style[1], has_move,
1344 		x, y, last.f_x, last.f_y);
1345 #endif
1346 
1347 	// save the move if there is one
1348 	if(has_move) {
1349 		sz = SIBitSize(x);
1350 		bits = SIBitSize(y);
1351 		if(bits > sz) {
1352 			sz = bits;
1353 		}
1354 		info.f_data.WriteBits(sz, 5);
1355 		info.f_data.WriteBits(x, sz);
1356 		info.f_data.WriteBits(y, sz);
1357 		last.f_x = x;
1358 		last.f_y = y;
1359 	}
1360 
1361 	// save fills if they changed; the # of bits was defined while
1362 	// saving the styles last
1363 	// TODO: if we have to save new fill/line styles we will need
1364 	//	 the new fill/line bits counts BEFORE to save the
1365 	//	 following... (is that logical?!?)
1366 	if(has_fill_style[0]) {
1367 		info.f_data.WriteBits(setup.f_fill_ref[0], info.f_fill_bits_count);
1368 		last.f_fill_ref[0] = setup.f_fill_ref[0];
1369 	}
1370 	if(has_fill_style[1]) {
1371 		info.f_data.WriteBits(setup.f_fill_ref[1], info.f_fill_bits_count);
1372 		last.f_fill_ref[1] = setup.f_fill_ref[1];
1373 	}
1374 
1375 	// save the line style if it changed
1376 	if(has_line_style) {
1377 		info.f_data.WriteBits(setup.f_line_ref, info.f_line_bits_count);
1378 		last.f_line_ref = setup.f_line_ref;
1379 
1380 		/* tried that -- doesn't seem to be necessary!
1381 		if(info.f_save_styles) {
1382 		}
1383 		else {
1384 			info.f_data.WriteBits(0, info.f_line_bits_count);
1385 printf("Save line style of 0!\n");
1386 		}
1387 		*/
1388 	}
1389 
1390 	// we never have new fill & styles here
1391 
1392 	return ErrorManager::ERROR_CODE_NONE;
1393 }
1394 
1395 
1396 /** \brief Save the specified shape setup.
1397  *
1398  * This function saves a setup (move, fill and lines).
1399  *
1400  * \param[in] info	Definition of what needs to be saved
1401  * \param[in] count	Save this count depending on the version
1402  *
1403  * \return An error code or ErrorManager::ERROR_CODE_NONE.
1404  */
SaveStylesCount(save_info_t & info,long count)1405 ErrorManager::error_code_t TagShape::SaveStylesCount(save_info_t& info, long count)
1406 {
1407 	if(f_version == 1) {
1408 		if(count >= 256) {
1409 			return OnError(ErrorManager::ERROR_CODE_TOO_MANY_STYLES, "invalid style count of %ld for a V1.0 flash", count);
1410 		}
1411 		info.f_data.PutByte((char) count);
1412 	}
1413 	else if(count > 254) {
1414 		info.f_data.PutByte((char) 255);
1415 		info.f_data.PutShort((short) count);
1416 	}
1417 	else {
1418 		info.f_data.PutByte((char) count);
1419 	}
1420 
1421 	return ErrorManager::ERROR_CODE_NONE;
1422 }
1423 
1424 
1425 /** \brief Save the specified shape styles.
1426  *
1427  * This function saves a set of styles.
1428  *
1429  * \param[in] info	Definition of what needs to be saved
1430  *
1431  * \return An error code or ErrorManager::ERROR_CODE_NONE.
1432  */
SaveStyles(save_info_t & info)1433 ErrorManager::error_code_t TagShape::SaveStyles(save_info_t& info)
1434 {
1435 	ErrorManager::error_code_t	ec;
1436 	int				idx, count;
1437 	Style				*style;
1438 
1439 // save the FILL STYLES
1440 	count = info.f_sr.f_fill_styles->Count();
1441 	ec = SaveStylesCount(info, count);
1442 	if(ec != ErrorManager::ERROR_CODE_NONE) {
1443 		return ec;
1444 	}
1445 
1446 	for(idx = 0; idx < count; idx++) {
1447 		style = dynamic_cast<Style *>(info.f_sr.f_fill_styles->Get(idx));
1448 		ec = style->Save(info.f_data, info.f_save_alpha, f_morph);
1449 		if(ec != ErrorManager::ERROR_CODE_NONE) {
1450 			return ec;
1451 		}
1452 	}
1453 
1454 	info.f_fill_bits_count = UIBitSize(count);
1455 
1456 // save the LINE STYLES
1457 	count = info.f_sr.f_line_styles->Count();
1458 	ec = SaveStylesCount(info, count);
1459 	if(ec != ErrorManager::ERROR_CODE_NONE) {
1460 		return ec;
1461 	}
1462 
1463 	for(idx = 0; idx < count; idx++) {
1464 		style = dynamic_cast<Style *>(info.f_sr.f_line_styles->Get(idx));
1465 		if(info.f_shape4) {
1466 			// make sure we have enhanced lines with a shape4
1467 			style->SetType(Style::STYLE_TYPE_ENHANCED_LINE);
1468 		}
1469 		else {
1470 			// make sure we do not have enhanced lines with shape1,2,3
1471 			style->SetType(Style::STYLE_TYPE_LINE);
1472 		}
1473 		ec = style->Save(info.f_data, info.f_save_alpha, f_morph);
1474 		if(ec != ErrorManager::ERROR_CODE_NONE) {
1475 			return ec;
1476 		}
1477 	}
1478 
1479 	info.f_line_bits_count = UIBitSize(count);
1480 
1481 // save the number of bits we will use to save fill & line references from now on
1482 	info.f_data.PutByte((info.f_fill_bits_count << 4) + info.f_line_bits_count);
1483 
1484 	return ErrorManager::ERROR_CODE_NONE;
1485 }
1486 
1487 
1488 
1489 
1490 
1491 /* The following options fold the documentation; use 'zi' to turn on and off
1492  *
1493  * vim: foldexpr=getline(v\:lnum)!~'^/\\*\\*'&&getline(v\:lnum)!~'^\ \\*'?0\:1 foldcolumn=2 foldmethod=expr
1494  */
1495