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&mdash;Place Object</a>
59  * \sa <a href="../SWFalexref.html#tag_placeobject2">SWF Alexis' Reference&mdash;Place Object 2 &amp; 3</a>
60  * \sa <a href="../SWFalexref.html#swf_tag">SWF Alexis' Reference&mdash;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