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—Define Shape</a>
78 * \sa <a href="../SWFalexref.html#tag_defineshape2">SWF Alexis' Reference—Define Shape2</a>
79 * \sa <a href="../SWFalexref.html#tag_defineshape3">SWF Alexis' Reference—Define Shape3</a>
80 * \sa <a href="../SWFalexref.html#tag_defineshape4">SWF Alexis' Reference—Define Shape4</a>
81 * \sa <a href="../SWFalexref.html#tag_definemorphshape">SWF Alexis' Reference—Define Morph Shape</a>
82 * \sa <a href="../SWFalexref.html#tag_definemorphshape2">SWF Alexis' Reference—Define Morph Shape2</a>
83 * \sa <a href="../SWFalexref.html#swf_shape">SWF Alexis' Reference—swf_shape</a>
84 * \sa <a href="../SWFalexref.html#swf_shape_record">SWF Alexis' Reference—swf_shape_record</a>
85 * \sa <a href="../SWFalexref.html#swf_shape_with_style">SWF Alexis' Reference—swf_shape_with_style</a>
86 * \sa <a href="../SWFalexref.html#swf_tag">SWF Alexis' Reference—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