1 /* libsswf_tag_place.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::TagPlace 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 /////////////////////////////////////////// TagPlace
48
49
50 /** \class sswf::TagPlace
51 *
52 * \brief Tag used to place (or remove) an object in the display list
53 *
54 * This tag can be used to place or remove an object in the display list.
55 *
56 * The default behavior is to replace the object at the specified depth.
57 *
58 * \sa <a href="../SWFalexref.html#tag_placeobject">SWF Alexis' Reference—Place Object</a>
59 * \sa <a href="../SWFalexref.html#tag_placeobject2">SWF Alexis' Reference—Place Object 2 & 3</a>
60 * \sa <a href="../SWFalexref.html#swf_tag">SWF Alexis' Reference—swf_tag</a>
61 */
62
63
64
65
66
67 /** \brief Initialize a TagPlace object
68 *
69 * The constructor initializes the TagPlace to defaults. The following
70 * gives you the default value and the function to use to change the default
71 * value.
72 *
73 * \li No reference defined -- sswf::TagPlace::SetObjectID(sswf_id_t id)
74 * \li Replace any existing object at that depth -- sswf::TagPlace::Replace(void)
75 * and sswf::TagPlace::Place(void)
76 * \li Undefined depth (-1) -- sswf::TagPlace::SetDepth(int depth)
77 * \li No clipping -- sswf::TagPlace::SetClip(int depth)
78 * \li Unnamed -- sswf::TagPlace::SetName(const char *name)
79 * \li No morphing position -- sswf::TagPlace::SetMorphPosition(int position)
80 * \li No blending -- sswf::TagPlace::SetBlendMode(blend_mode_t blend_mode)
81 * \li No bitmap caching -- sswf::TagPlace::SetBitmapCaching(int bitmap_caching)
82 * and SetBlendModeName(const char *blend_mode_name)
83 * \li No transformation matrix -- sswf::TagPlace::SetMatrix(const Matrix& matrix)
84 * \li No color matrix -- sswf::TagPlace::SetColorTransform(const ColorTransform& color_transform)
85 * \li No events -- sswf::TagPlace::AddEvent(Event *event)
86 */
TagPlace(TagBase * parent)87 TagPlace::TagPlace(TagBase *parent)
88 : TagBase("place", parent)
89 {
90 assert(parent != 0, "a Place tag must have a parent");
91
92 f_id_defined = false;
93 f_id = 0; // not defined yet
94 f_replace = 1;
95 f_depth = -1;
96 f_clip_depth = 0;
97 f_name = 0;
98 f_events_all_flags = 0; // not necessary, it is reset in PreSave()
99 f_position = -1; // not defined yet
100 //f_blend_mode = ...
101 f_bitmap_caching = -1;
102 f_has_matrix = false;
103 //f_matrix = ...
104 //f_color_transform = ...
105 //f_events = ...
106 }
107
108
109 /** \brief This function returns the flags representing the type of the TagPlace tag
110 *
111 * This function returns the flag CONTROL, this tag is used to control
112 * the display list.
113 *
114 * This function returns the flag SCRIPT, it can include a script executed
115 * when responding to events.
116 *
117 * This function returns the flag REFERENCE, it references one object which
118 * it places in the display list.
119 *
120 * \return The CONTROL, SCRIPT and REFERENCE bits
121 */
TypeFlags(void) const122 TagBase::swf_type_t TagPlace::TypeFlags(void) const
123 {
124 return SWF_TYPE_CONTROL | SWF_TYPE_SCRIPT | SWF_TYPE_REFERENCE;
125 }
126
127
128 /** \brief Change the identifier of the object to place
129 *
130 * This function defines the object that this TagPlace inserts in the
131 * display list.
132 *
133 * Note that in newer SWF versions it is possible to not define any
134 * object identifier to use the TagPlace to remove an object at the
135 * specified depth. Thus calling this function is not mandatory.
136 * Also, if you do call this function, you lose that other functionality.
137 *
138 * \param id The identifier of the object to place
139 */
SetObjectID(sswf_id_t id)140 void TagPlace::SetObjectID(sswf_id_t id)
141 {
142 f_id = id;
143 f_id_defined = true;
144 }
145
146
147 /** \brief The depth at which the object is placed
148 *
149 * The sswf::TagPlace object is used to place an object in the
150 * display list. The display list is organized as an array of
151 * objects sorted by depth. Each object should have a distinct
152 * depth (though older versions of the Flash player would accept
153 * multiple objects at the same depth, it may not last forever!)
154 *
155 * The depth 0 should not be used for your objects.
156 *
157 * Bit 15 (0x8000) of the depth is used in a special way (thought
158 * I cannot find the docs about that right now...). So you really
159 * only can use depth 1 to 32767 which is likely to be enough
160 * anyway!
161 *
162 * \param depth The new object depth
163 */
SetDepth(int depth)164 void TagPlace::SetDepth(int depth)
165 {
166 f_depth = depth;
167 }
168
169
170 /** \brief Insert a mask in the display list, not a shape
171 *
172 * When defining a clipping, this TagPlace must reference
173 * either a shape or a text. Other objects will not work
174 * (at least up to version 6 of SWF). The referenced object
175 * defines a mask to use as a clipping mask.
176 *
177 * The sswf::TagPlace::SetDepth(int depth) and sswf::TagPlace::SetClip(int depth)
178 * functions must both be called to define the range of depths affected by the
179 * clipping. All the objects placed in that range will be affected by this
180 * clipping mask. The depths defined in the Depth and Clip are inclusive.
181 *
182 * \bug The depth and clipping depth are hard coded and thus this feature
183 * is not that practical to use.
184 *
185 * \param depth The top of the clipping range
186 */
SetClip(int depth)187 void TagPlace::SetClip(int depth)
188 {
189 f_clip_depth = depth;
190 }
191
192
193 /** \brief Set the geometric transformation matrix
194 *
195 * This function defines the transformation matrix used to move, scale,
196 * rotate and skew the referenced object on the screen.
197 *
198 * \note
199 * A flag is used to know whether the matrix was changed. Unfortunately
200 * this means setting an identity matrix will result in incorporating
201 * the matrix in the result even thought it is useless. But it seemed
202 * also that without incorporating the matrix in the TagPlace, you could
203 * not tweak it with an ActionScript. This may have been fixed since I
204 * checked (or maybe my test was bogus...). So it could be useful to
205 * have a matrix even if it is the identity.
206 *
207 * \param matrix The matrix to apply to the object
208 */
SetMatrix(const Matrix & matrix)209 void TagPlace::SetMatrix(const Matrix& matrix)
210 {
211 f_matrix = matrix;
212 f_has_matrix = true;
213 }
214
215
216 /** \brief Set the color transformation matrix
217 *
218 * This function sets the transformation matrix to apply on the color.
219 *
220 * This matrix can affect the alpha channel. It can be used to
221 * transform the colors by scaling the components and adding a bias
222 * to each component. It is not a full matrix. To tweak the full
223 * matrix, you need to use the color matrix filter instead (which
224 * requires version 8 of SWF also).
225 *
226 * \param color_transform The color matrix to save in this PlaceObject
227 */
SetColorTransform(const ColorTransform & color_transform)228 void TagPlace::SetColorTransform(const ColorTransform& color_transform)
229 {
230 f_color_transform = color_transform;
231 }
232
233
234 /** \brief Defines the name of a TagPlace object
235 *
236 * This function is used to define the name of the TagPlace object.
237 *
238 * The name can later be used to reference the object from an ActionScript.
239 *
240 * By default, an object being placed has no name.
241 *
242 * You can set the name to NULL or "" to remove the name.
243 *
244 * \param name The name of the object
245 */
SetName(const char * name)246 void TagPlace::SetName(const char *name)
247 {
248 MemFree(f_name);
249 f_name = StrDup(name);
250 }
251
252
253 /** \brief Set the morphing position
254 *
255 * This function is used to change the position of the morph.
256 *
257 * This is a static setup in a TagPlace object.
258 *
259 * The position of 0 represents the first object and a position of
260 * 65535 represents the last object.
261 *
262 * By default the position is set to -1 meaning that you are not
263 * referencing a morph object.
264 *
265 * Placing a morph object without a position is similar to placing
266 * that object with a position of 0, thought, for forward compatibility,
267 * it is a good idea to always use a corresponding place.
268 *
269 * \param position The morph position
270 */
SetMorphPosition(int position)271 void TagPlace::SetMorphPosition(int position)
272 {
273 f_position = position;
274 }
275
276
277 /** \brief Defines a blending filter
278 *
279 * This function defines the filter to use with this TagPlace object.
280 * It determines how the object is rendered on the screen.
281 *
282 * By specifying a blend mode, TagPlace generates one of the
283 * SWF_TAG_PLACE_OBJECT2 or SWF_TAG_PLACE_OBJECT3.
284 *
285 * \sa sswf::TagPlace::SetBlendModeName(const char *blend_mode_name)
286 */
SetBlendMode(const BlendMode & blend_mode)287 void TagPlace::SetBlendMode(const BlendMode& blend_mode)
288 {
289 f_blend_mode = blend_mode;
290 }
291
292
293
294
295 /** \brief Determine whether bitmap caching can be done
296 *
297 * This function sets the flag \e bitmap \e caching to true or false.
298 *
299 * Newer versions of SWF can save the bitmaps in buffers and not re-render
300 * everything on each frame. This is particularly useful if you create a
301 * complex but inanimated sprite which can therefore be drawn once in a
302 * buffer and any number of time from that buffer to the final display
303 * screen.
304 *
305 * \param bitmap_caching \em needs testing, use 0 or 1 to not set or set the bitmap caching
306 */
SetBitmapCaching(int bitmap_caching)307 void TagPlace::SetBitmapCaching(int bitmap_caching)
308 {
309 f_bitmap_caching = bitmap_caching;
310 }
311
312
313 /** \brief Mark the TagPlace as a replacement
314 *
315 * The tag is used to replace another object at the specified depth.
316 *
317 * If no object reference is defined, then this becomes a "remove" instead
318 * (in other words, it always removes, and may insert)
319 *
320 * \sa sswf::TagPlace::Place(void)
321 * \sa sswf::TagPlace::SetObjectID(sswf_id_t id)
322 * \sa sswf::TagRemove
323 */
Replace(void)324 void TagPlace::Replace(void)
325 {
326 f_replace = 0;
327 }
328
329
330 /** \brief Mark the TagPlace as an additional placement
331 *
332 * This function marks this TagPlace as an 'add' function. The object
333 * referenced will be added to the display list whether there is already
334 * an object at the same depth.
335 *
336 * \note
337 * When you add new objects at the same depth, the first ones stay on
338 * top and the last go below the other objects. This was true in older
339 * versions and is likely working the same way.
340 *
341 * \sa sswf::TagPlace::Replace(void)
342 * \sa sswf::TagPlace::SetObjectID(sswf_id_t id)
343 */
Place(void)344 void TagPlace::Place(void)
345 {
346 f_replace = 1;
347 }
348
349
350 /** \brief Add the specified event to the place tag
351 *
352 * This function adds the specified event to the TagPlace object.
353 *
354 * An event is a set of flags which determine when the event is
355 * triggered and a set of actions to execute then.
356 *
357 * As many events as you need can be added. However, the first
358 * event matching has its ActionScript executed and then the
359 * process stops (to be confirmed!)
360 *
361 * \param event The event to add to this TagPlace object
362 *
363 * \return This function returns true if the event is accepted
364 * as is. If the event has flags which are not compatible with
365 * the TagPlace tag, then the function returns false and the
366 * event is not added.
367 */
AddEvent(Event * event)368 bool TagPlace::AddEvent(Event *event)
369 {
370 if((event->Events() & ~(SSWF_EVENT_V5 | SSWF_EVENT_V6 | SSWF_EVENT_V7)) != 0) {
371 return false;
372 }
373
374 f_events.Set(-1, event);
375
376 return true;
377 }
378
379
380 /** \brief Prepare the object to be saved
381 *
382 * This function checks all the parameters to determine the minimum
383 * version required to save this TagPlace object.
384 *
385 * By default, the minimum is considered to be 1.
386 *
387 * The use of events forces the version to be 5, 6 or 7 depending on
388 * the actions and flags used.
389 *
390 * The use of a name, no identifier, a color transform with an alpha
391 * channel, a clip depth forces the version to a minimum of 3.
392 *
393 * \return Zero when no error occurs, non-zero otherwise
394 */
PreSave(void)395 ErrorManager::error_code_t TagPlace::PreSave(void)
396 {
397 int idx;
398 long v, version;
399 Event *event;
400
401 f_events_all_flags = 0;
402
403 if(f_blend_mode.HasBlendMode()
404 || f_bitmap_caching != -1) {
405 version = 8;
406 }
407 else {
408 version = 3;
409 }
410
411 if(version >= 8
412 || f_replace == 0
413 || !f_id_defined
414 || f_position != -1
415 || f_name != 0
416 || !f_color_transform.IsSolidCompatible()
417 || f_clip_depth != 0) {
418 /* in this case the order in which we check the events doesn't matter */
419 idx = f_events.Count();
420 while(idx > 0) {
421 idx--;
422 event = dynamic_cast<Event *>(f_events.Get(idx));
423 f_events_all_flags |= event->Events();
424 v = Action::MinimumListVersion(event->Actions());
425 if(v > version) {
426 version = v;
427 }
428 }
429 if(f_events_all_flags != 0) {
430 // generate an error on flags which should not be set
431 if((f_events_all_flags & ~(SSWF_EVENT_V5 | SSWF_EVENT_V6 | SSWF_EVENT_V7)) != 0) {
432 return ErrorManager::ERROR_CODE_UNEXPECTED_EVENT_FLAG;
433 }
434 if((f_events_all_flags & SSWF_EVENT_V7) != 0) {
435 v = 7;
436 }
437 else if((f_events_all_flags & SSWF_EVENT_V6) != 0) {
438 v = 6;
439 }
440 else {
441 v = 5;
442 }
443 }
444 else {
445 v = 3;
446 }
447 if(v > version) {
448 version = v;
449 }
450 MinimumVersion((unsigned char) version);
451 }
452
453 return ErrorManager::ERROR_CODE_NONE;
454 }
455
456
457 /** \brief Save the TagPlace in the specified Data buffer
458 *
459 * This function saves the TagPlace in the specified Data buffer.
460 *
461 * The tag is saved only if the depth is correct.
462 *
463 * \param data The Data buffer where the tag is saved
464 *
465 * \return Zero when no error occurs, non-zero otherwise
466 */
Save(Data & data)467 ErrorManager::error_code_t TagPlace::Save(Data& data)
468 {
469 Data sub_data, action_data;
470 swf_tag_t tag;
471 int idx, max;
472 unsigned long flags;
473 Action *action;
474 Event *event;
475 ErrorManager::error_code_t ec;
476
477 if(f_depth < 0 || f_depth > 65535) {
478 // bad depth
479 return OnError(ErrorManager::ERROR_CODE_INVALID_DEPTH, "depth out of bounds (0 <= %d <= 65535 not satisfied)", f_depth);
480 }
481
482 if(f_blend_mode.HasBlendMode()
483 || f_bitmap_caching != -1
484 || f_replace == 0
485 || !f_id_defined
486 || f_position != -1
487 || f_name != 0
488 || !f_color_transform.IsSolidCompatible()
489 || f_clip_depth != 0
490 || f_events_all_flags != 0) {
491 if(f_blend_mode.HasBlendMode()
492 || f_bitmap_caching != -1) {
493 tag = SWF_TAG_PLACE_OBJECT3;
494 sub_data.WriteBits(0, 5);
495 sub_data.WriteBits(f_bitmap_caching != -1 ? 1 : 0, 1);
496 sub_data.WriteBits(f_blend_mode.HasBlendMode() ? 1 : 0, 1);
497 sub_data.WriteBits(0, 1);
498 }
499 else {
500 tag = SWF_TAG_PLACE_OBJECT2;
501 }
502 sub_data.WriteBits(f_events_all_flags != 0, 1); // TODO: events + actions (v5+)
503 sub_data.WriteBits(f_clip_depth != 0, 1);
504 sub_data.WriteBits(f_name != 0, 1);
505 sub_data.WriteBits(f_position != -1, 1);
506 sub_data.WriteBits(!f_color_transform.IsNull(true), 1);
507 sub_data.WriteBits(f_has_matrix, 1);
508 sub_data.WriteBits(f_id_defined, 1);
509 /* note that if both f_replace and f_id_defined are zero, then the Macromedia plugin crashes! */
510 sub_data.WriteBits(f_replace == 0 && !f_id_defined ? 1 : f_replace, 1);
511 sub_data.PutShort(f_depth);
512 if(f_id_defined) {
513 sub_data.PutShort(f_id);
514 }
515 if(f_has_matrix) {
516 // we have to save null matrices in a PlaceObject2 because we
517 // often need the translate of (0, 0)!!!
518 f_matrix.Save(sub_data);
519 }
520 if(!f_color_transform.IsNull(true)) {
521 f_color_transform.Save(sub_data, true);
522 }
523 if(f_position != -1) {
524 sub_data.PutShort(f_position);
525 }
526 if(f_name != 0) {
527 ec = SaveString(sub_data, f_name);
528 if(ec != ErrorManager::ERROR_CODE_NONE) {
529 return ec;
530 }
531 }
532 if(f_clip_depth != 0) {
533 sub_data.PutShort((unsigned short) f_clip_depth);
534 }
535 if(f_blend_mode.HasBlendMode()) {
536 f_blend_mode.Save(sub_data);
537 }
538 if(f_bitmap_caching != -1) {
539 sub_data.PutByte(f_bitmap_caching);
540 }
541 if(f_events_all_flags != 0) {
542 max = f_events.Count();
543 if(Version() == 5) {
544 /* version 5 uses U16 for event masks (to be confirmed) */
545 sub_data.PutShort(0);
546 sub_data.PutShort((unsigned short) f_events_all_flags);
547 for(idx = 0; idx < max; idx++) {
548 event = dynamic_cast<Event *>(f_events.Get(idx));
549 sub_data.PutShort((unsigned short) event->Events());
550 action_data.Empty();
551 if(event->Actions().Count() > 0) {
552 action = dynamic_cast<Action *>(event->Actions().Get(0));
553 action->SaveList(&event->Actions(), action_data);
554 }
555 sub_data.PutLong(action_data.ByteSize());
556 sub_data.Append(action_data);
557 }
558 sub_data.PutShort(0);
559 }
560 else {
561 /* version 6+ uses U32 for event masks */
562 sub_data.PutShort(0);
563 sub_data.PutLong(f_events_all_flags);
564 for(idx = 0; idx < max; idx++) {
565 event = dynamic_cast<Event *>(f_events.Get(idx));
566 flags = event->Events();
567 sub_data.PutLong(flags);
568 action_data.Empty();
569 if((flags & Event::EVENT_KEY_PRESS) != 0) {
570 action_data.PutByte(event->Key());
571 }
572 if(event->Actions().Count() > 0) {
573 action = dynamic_cast<Action *>(event->Actions().Get(0));
574 action->SaveList(&event->Actions(), action_data);
575 }
576 sub_data.PutLong(action_data.ByteSize());
577 sub_data.Append(action_data);
578 }
579 sub_data.PutLong(0);
580 }
581 }
582 }
583 else {
584 // V1.0 is sufficent for this one
585 tag = SWF_TAG_PLACE_OBJECT;
586 sub_data.PutShort(f_id);
587 sub_data.PutShort(f_depth);
588 f_matrix.Save(sub_data);
589 if(!f_color_transform.IsNull(false)) { // save the Color Transformation only if required
590 f_color_transform.Save(sub_data, false);
591 }
592 }
593
594 SaveTag(data, tag, sub_data.ByteSize());
595 data.Append(sub_data);
596
597 return ErrorManager::ERROR_CODE_NONE;
598 }
599
600
601
602
603
604
605 /* The following options fold the documentation; use 'zi' to turn on and off
606 *
607 * vim: foldexpr=getline(v\:lnum)!~'^/\\*\\*'&&getline(v\:lnum)!~'^\ \\*'?0\:1 foldcolumn=2 foldmethod=expr
608 */
609