1 // MovieClip_as.cpp: ActionScript "MovieClip" class, for Gnash.
3 // Copyright (C) 2009, 2010 Free Software Foundation, Inc.
5 // This program is free software; you can redistribute it and/or modify
6 // it under the terms of the GNU General Public License as published by
7 // the Free Software Foundation; either version 3 of the License, or
8 // (at your option) any later version.
10 // This program is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 // GNU General Public License for more details.
15 // You should have received a copy of the GNU General Public License
16 // along with this program; if not, write to the Free Software
17 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "MovieClip_as.h"
22 #include <boost/lexical_cast.hpp>
24 #include "MovieClip.h"
26 #include "display/BitmapData_as.h"
27 #include "NetStream_as.h"
28 #include "movie_root.h"
29 #include "GnashNumeric.h"
33 #include "Global_as.h"
34 #include "smart_ptr.h" // for boost intrusive_ptr
35 #include "builtin_function.h" // need builtin_function
36 #include "NativeFunction.h"
39 #include "FillStyle.h"
40 #include "namedStrings.h"
42 #include "RunResources.h"
43 #include "ASConversions.h"
47 // Forward declarations
50 void attachMovieClipAS2Interface(as_object
& o
);
52 as_value
movieclip_as2_ctor(const fn_call
& fn
);
53 as_value
movieclip_transform(const fn_call
& fn
);
54 as_value
movieclip_scale9Grid(const fn_call
& fn
);
55 as_value
movieclip_attachVideo(const fn_call
& fn
);
56 as_value
movieclip_attachAudio(const fn_call
& fn
);
57 as_value
movieclip_attachMovie(const fn_call
& fn
);
58 as_value
movieclip_unloadMovie(const fn_call
& fn
);
59 as_value
movieclip_loadMovie(const fn_call
& fn
);
60 as_value
movieclip_getURL(const fn_call
& fn
);
61 as_value
movieclip_attachBitmap(const fn_call
& fn
);
62 as_value
movieclip_beginBitmapFill(const fn_call
& fn
);
63 as_value
movieclip_createEmptyMovieClip(const fn_call
& fn
);
64 as_value
movieclip_removeMovieClip(const fn_call
& fn
);
65 as_value
movieclip_curveTo(const fn_call
& fn
);
66 as_value
movieclip_beginFill(const fn_call
& fn
);
67 as_value
movieclip_prevFrame(const fn_call
& fn
);
68 as_value
movieclip_nextFrame(const fn_call
& fn
);
69 as_value
movieclip_endFill(const fn_call
& fn
);
70 as_value
movieclip_clear(const fn_call
& fn
);
71 as_value
movieclip_lineStyle(const fn_call
& fn
);
72 as_value
movieclip_lineTo(const fn_call
& fn
);
73 as_value
movieclip_moveTo(const fn_call
& fn
);
74 as_value
movieclip_beginGradientFill(const fn_call
& fn
);
75 as_value
movieclip_stopDrag(const fn_call
& fn
);
76 as_value
movieclip_startDrag(const fn_call
& fn
);
77 as_value
movieclip_gotoAndStop(const fn_call
& fn
);
78 as_value
movieclip_duplicateMovieClip(const fn_call
& fn
);
79 as_value
movieclip_gotoAndPlay(const fn_call
& fn
);
80 as_value
movieclip_stop(const fn_call
& fn
);
81 as_value
movieclip_play(const fn_call
& fn
);
82 as_value
movieclip_setMask(const fn_call
& fn
);
83 as_value
movieclip_getDepth(const fn_call
& fn
);
84 as_value
movieclip_getBytesTotal(const fn_call
& fn
);
85 as_value
movieclip_getBytesLoaded(const fn_call
& fn
);
86 as_value
movieclip_getBounds(const fn_call
& fn
);
87 as_value
movieclip_hitTest(const fn_call
& fn
);
88 as_value
movieclip_globalToLocal(const fn_call
& fn
);
89 as_value
movieclip_localToGlobal(const fn_call
& fn
);
90 as_value
movieclip_lockroot(const fn_call
& fn
);
91 as_value
movieclip_swapDepths(const fn_call
& fn
);
92 as_value
movieclip_scrollRect(const fn_call
& fn
);
93 as_value
movieclip_getInstanceAtDepth(const fn_call
& fn
);
94 as_value
movieclip_getNextHighestDepth(const fn_call
& fn
);
95 as_value
movieclip_getTextSnapshot(const fn_call
& fn
);
96 as_value
movieclip_tabIndex(const fn_call
& fn
);
97 as_value
movieclip_opaqueBackground(const fn_call
& fn
);
98 as_value
movieclip_filters(const fn_call
& fn
);
99 as_value
movieclip_forceSmoothing(const fn_call
& fn
);
100 as_value
movieclip_cacheAsBitmap(const fn_call
& fn
);
101 as_value
movieclip_lineGradientStyle(const fn_call
& fn
);
102 as_value
movieclip_beginMeshFill(const fn_call
& fn
);
103 as_value
movieclip_getRect(const fn_call
& fn
);
104 as_value
movieclip_meth(const fn_call
& fn
);
105 as_value
movieclip_getSWFVersion(const fn_call
& fn
);
106 as_value
movieclip_loadVariables(const fn_call
& fn
);
110 // extern (used by Global.cpp)
111 // proto = getDisplayObjectContainerInterface();
112 // proto = getDisplayObjectContainerInterface();
114 movieclip_class_init(as_object
& where
, const ObjectURI
& uri
)
116 Global_as
& gl
= getGlobal(where
);
117 as_object
* proto
= createObject(gl
);
119 as_object
* cl
= gl
.createClass(&movieclip_as2_ctor
, proto
);
120 attachMovieClipAS2Interface(*proto
);
122 where
.init_member(uri
, cl
, as_object::DefaultFlags
);
126 registerMovieClipNative(as_object
& where
)
128 VM
& vm
= getVM(where
);
130 vm
.registerNative(movieclip_attachMovie
, 900, 0);
131 vm
.registerNative(movieclip_swapDepths
, 900, 1);
132 vm
.registerNative(movieclip_localToGlobal
, 900, 2);
133 vm
.registerNative(movieclip_globalToLocal
, 900, 3);
134 vm
.registerNative(movieclip_hitTest
, 900, 4);
135 vm
.registerNative(movieclip_getBounds
, 900, 5);
136 vm
.registerNative(movieclip_getBytesTotal
, 900, 6);
137 vm
.registerNative(movieclip_getBytesLoaded
, 900, 7);
138 vm
.registerNative(movieclip_attachAudio
, 900, 8);
139 vm
.registerNative(movieclip_attachVideo
, 900, 9);
140 vm
.registerNative(movieclip_getDepth
, 900, 10);
141 vm
.registerNative(movieclip_setMask
, 900, 11);
142 vm
.registerNative(movieclip_play
, 900, 12);
143 vm
.registerNative(movieclip_stop
, 900, 13);
144 vm
.registerNative(movieclip_nextFrame
, 900, 14);
145 vm
.registerNative(movieclip_prevFrame
, 900, 15);
146 vm
.registerNative(movieclip_gotoAndPlay
, 900, 16);
147 vm
.registerNative(movieclip_gotoAndStop
, 900, 17);
148 vm
.registerNative(movieclip_duplicateMovieClip
, 900, 18);
149 vm
.registerNative(movieclip_removeMovieClip
, 900, 19);
150 vm
.registerNative(movieclip_startDrag
, 900, 20);
151 vm
.registerNative(movieclip_stopDrag
, 900, 21);
152 vm
.registerNative(movieclip_getNextHighestDepth
, 900, 22);
153 vm
.registerNative(movieclip_getInstanceAtDepth
, 900, 23);
154 vm
.registerNative(movieclip_getSWFVersion
, 900, 24);
155 vm
.registerNative(movieclip_attachBitmap
, 900, 25);
156 vm
.registerNative(movieclip_getRect
, 900, 26);
158 vm
.registerNative(movieclip_tabIndex
, 900, 200);
160 vm
.registerNative(movieclip_lockroot
, 900, 300);
162 vm
.registerNative(movieclip_cacheAsBitmap
, 900, 401);
163 vm
.registerNative(movieclip_opaqueBackground
, 900, 402);
164 vm
.registerNative(movieclip_scrollRect
, 900, 403);
166 vm
.registerNative(movieclip_filters
, 900, 417);
167 vm
.registerNative(movieclip_transform
, 900, 418);
168 vm
.registerNative(DisplayObject::blendMode
, 900, 500);
169 vm
.registerNative(movieclip_forceSmoothing
, 900, 502);
171 vm
.registerNative(movieclip_createEmptyMovieClip
, 901, 0);
172 vm
.registerNative(movieclip_beginFill
, 901, 1);
173 vm
.registerNative(movieclip_beginGradientFill
, 901, 2);
174 vm
.registerNative(movieclip_moveTo
, 901, 3);
175 vm
.registerNative(movieclip_lineTo
, 901, 4);
176 vm
.registerNative(movieclip_curveTo
, 901, 5);
177 vm
.registerNative(movieclip_lineStyle
, 901, 6);
178 vm
.registerNative(movieclip_endFill
, 901, 7);
179 vm
.registerNative(movieclip_clear
, 901, 8);
180 vm
.registerNative(movieclip_lineGradientStyle
, 901, 9);
181 vm
.registerNative(movieclip_beginMeshFill
, 901, 10);
182 vm
.registerNative(movieclip_beginBitmapFill
, 901, 11);
183 vm
.registerNative(movieclip_scale9Grid
, 901, 12);
190 // =======================
192 // =======================
194 /// Properties (and/or methods) *inherited* by MovieClip instances
196 attachMovieClipAS2Interface(as_object
& o
)
198 Global_as
& gl
= getGlobal(o
);
201 const int swf6Flags
= as_object::DefaultFlags
| PropFlags::onlySWF6Up
;
202 const int swf7Flags
= as_object::DefaultFlags
| PropFlags::onlySWF7Up
;
203 const int swf8Flags
= as_object::DefaultFlags
| PropFlags::onlySWF8Up
;
205 o
.init_member("attachMovie", vm
.getNative(900, 0));
206 o
.init_member("swapDepths", vm
.getNative(900, 1));
207 o
.init_member("localToGlobal", vm
.getNative(900, 2));
208 o
.init_member("globalToLocal", vm
.getNative(900, 3));
209 o
.init_member("hitTest", vm
.getNative(900, 4));
210 o
.init_member("getBounds", vm
.getNative(900, 5));
211 o
.init_member("getBytesTotal", vm
.getNative(900, 6));
212 o
.init_member("getBytesLoaded", vm
.getNative(900, 7));
213 o
.init_member("attachAudio", vm
.getNative(900, 8), swf6Flags
);
214 o
.init_member("attachVideo", vm
.getNative(900, 9), swf6Flags
);
215 o
.init_member("getDepth", vm
.getNative(900, 10), swf6Flags
);
216 o
.init_member("setMask", vm
.getNative(900, 11), swf6Flags
);
217 o
.init_member("play", vm
.getNative(900, 12));
218 o
.init_member("stop", vm
.getNative(900, 13));
219 o
.init_member("nextFrame", vm
.getNative(900, 14));
220 o
.init_member("prevFrame", vm
.getNative(900, 15));
221 o
.init_member("gotoAndPlay", vm
.getNative(900, 16));
222 o
.init_member("gotoAndStop", vm
.getNative(900, 17));
223 o
.init_member("duplicateMovieClip", vm
.getNative(900, 18));
224 o
.init_member("removeMovieClip", vm
.getNative(900, 19));
225 o
.init_member("startDrag", vm
.getNative(900, 20));
226 o
.init_member("stopDrag", vm
.getNative(900, 21));
227 o
.init_member("getNextHighestDepth", vm
.getNative(900, 22), swf7Flags
);
228 o
.init_member("getInstanceAtDepth", vm
.getNative(900, 23), swf7Flags
);
229 o
.init_member("getSWFVersion", vm
.getNative(900, 24));
230 o
.init_member("attachBitmap", vm
.getNative(900, 25), swf8Flags
);
231 o
.init_member("getRect", vm
.getNative(900, 26), swf8Flags
);
233 o
.init_member("loadMovie", gl
.createFunction(movieclip_loadMovie
));
234 o
.init_member("loadVariables", gl
.createFunction(movieclip_loadVariables
));
235 o
.init_member("unloadMovie", gl
.createFunction( movieclip_unloadMovie
));
236 o
.init_member("getURL", gl
.createFunction(movieclip_getURL
));
237 o
.init_member("meth", gl
.createFunction(movieclip_meth
));
239 o
.init_member("enabled", true);
240 o
.init_member("useHandCursor", true);
242 o
.init_member("createEmptyMovieClip", vm
.getNative(901, 0), swf6Flags
);
243 o
.init_member("beginFill", vm
.getNative(901, 1), swf6Flags
);
244 o
.init_member("beginGradientFill", vm
.getNative(901, 2), swf6Flags
);
245 o
.init_member("moveTo", vm
.getNative(901, 3), swf6Flags
);
246 o
.init_member("lineTo", vm
.getNative(901, 4), swf6Flags
);
247 o
.init_member("curveTo", vm
.getNative(901, 5), swf6Flags
);
248 o
.init_member("lineStyle", vm
.getNative(901, 6), swf6Flags
);
249 o
.init_member("endFill", vm
.getNative(901, 7), swf6Flags
);
250 o
.init_member("clear", vm
.getNative(901, 8), swf6Flags
);
251 o
.init_member("lineGradientStyle", vm
.getNative(901, 9), swf8Flags
);
252 o
.init_member("beginMeshFill", vm
.getNative(901, 10), swf8Flags
);
253 o
.init_member("beginBitmapFill", vm
.getNative(901, 11), swf8Flags
);
257 NativeFunction
* getset
;
259 getset
= vm
.getNative(900, 200);
260 o
.init_property("tabIndex", *getset
, *getset
);
261 getset
= vm
.getNative(900, 300);
262 o
.init_property("_lockroot", *getset
, *getset
);
263 getset
= vm
.getNative(900, 401);
264 o
.init_property("cacheAsBitmap", *getset
, *getset
, swf8Flags
);
265 getset
= vm
.getNative(900, 402);
266 o
.init_property("opaqueBackground", *getset
, *getset
, swf8Flags
);
267 getset
= vm
.getNative(900, 403);
268 o
.init_property("scrollRect", *getset
, *getset
, swf8Flags
);
269 getset
= vm
.getNative(900, 417);
270 o
.init_property("filters", *getset
, *getset
, swf8Flags
);
271 getset
= vm
.getNative(900, 418);
272 o
.init_property("transform", *getset
, *getset
, swf8Flags
);
273 getset
= vm
.getNative(900, 500);
274 o
.init_property("blendMode", *getset
, *getset
, swf8Flags
);
275 getset
= vm
.getNative(900, 502);
276 o
.init_property("forceSmoothing", *getset
, *getset
, swf8Flags
);
277 getset
= vm
.getNative(901, 12);
278 o
.init_property("scale9Grid", *getset
, *getset
, swf8Flags
);
280 // External functions.
281 o
.init_member("createTextField", vm
.getNative(104, 200));
282 o
.init_member("getTextSnapshot",
283 gl
.createFunction(movieclip_getTextSnapshot
), swf6Flags
);
287 //createEmptyMovieClip(name:String, depth:Number) : MovieClip
289 movieclip_createEmptyMovieClip(const fn_call
& fn
)
291 MovieClip
* ptr
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
295 IF_VERBOSE_ASCODING_ERRORS(
296 log_aserror(_("createEmptyMovieClip needs "
297 "2 args, but %d given,"
298 " returning undefined"),
303 IF_VERBOSE_ASCODING_ERRORS(
304 log_aserror(_("createEmptyMovieClip takes "
305 "2 args, but %d given, discarding"
311 Movie
* m
= ptr
->get_root();
312 as_object
* o
= getObjectWithPrototype(getGlobal(fn
), NSV::CLASS_MOVIE_CLIP
);
313 MovieClip
* mc
= new MovieClip(o
, 0, m
, ptr
);
316 mc
->set_name(getURI(vm
, fn
.arg(0).to_string()));
319 // Unlike other MovieClip methods, the depth argument of an empty movie clip
320 // can be any number. All numbers are converted to an int32_t, and are valid
321 // depths even when outside the usual bounds.
322 ptr
->addDisplayListObject(mc
, toInt(fn
.arg(1), getVM(fn
)));
328 movieclip_play(const fn_call
& fn
)
330 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
332 movieclip
->setPlayState(MovieClip::PLAYSTATE_PLAY
);
337 movieclip_stop(const fn_call
& fn
)
339 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
341 movieclip
->setPlayState(MovieClip::PLAYSTATE_STOP
);
347 //removeMovieClip() : Void
349 movieclip_removeMovieClip(const fn_call
& fn
)
351 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
352 movieclip
->removeMovieClip();
358 movieclip_cacheAsBitmap(const fn_call
& fn
)
360 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
362 LOG_ONCE( log_unimpl(_("MovieClip.cacheAsBitmap()")) );
368 movieclip_filters(const fn_call
& fn
)
370 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
374 LOG_ONCE(log_unimpl(_("MovieClip.filters()")));
378 Global_as
& gl
= getGlobal(fn
);
379 as_object
* array
= gl
.createArray();
380 return as_value(array
);
389 movieclip_forceSmoothing(const fn_call
& fn
)
391 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
393 LOG_ONCE(log_unimpl(_("MovieClip.forceSmoothing()")));
399 movieclip_opaqueBackground(const fn_call
& fn
)
401 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
403 LOG_ONCE(log_unimpl(_("MovieClip.opaqueBackground()")));
409 movieclip_scale9Grid(const fn_call
& fn
)
411 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
413 LOG_ONCE(log_unimpl(_("MovieClip.scale9Grid()")));
419 movieclip_scrollRect(const fn_call
& fn
)
421 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
423 LOG_ONCE(log_unimpl(_("MovieClip.scrollRect()")));
429 movieclip_tabIndex(const fn_call
& fn
)
431 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
433 LOG_ONCE(log_unimpl(_("MovieClip.tabIndex()")));
438 // attachMovie(idName:String, newName:String,
439 // depth:Number [, initObject:Object]) : MovieClip
441 movieclip_attachMovie(const fn_call
& fn
)
443 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
445 if (fn
.nargs
< 3 || fn
.nargs
> 4)
447 IF_VERBOSE_ASCODING_ERRORS(
448 log_aserror(_("attachMovie called with wrong number of arguments"
449 " expected 3 to 4, got (%d) - returning undefined"), fn
.nargs
);
454 // Get exported resource
455 const std::string
& id_name
= fn
.arg(0).to_string();
457 SWF::DefinitionTag
* exported_movie
=
458 movieclip
->get_root()->exportedCharacter(id_name
);
462 IF_VERBOSE_ASCODING_ERRORS(
463 log_aserror(_("attachMovie: exported resource '%s' "
464 "is not a DisplayObject definition. Returning undefined"), id_name
);
469 const std::string
& newname
= fn
.arg(1).to_string();
471 // Movies should be attachable from -16384 to 2130690045, according to
472 // kirupa (http://www.kirupa.com/developer/actionscript/depths2.htm)
473 // Tests in misc-ming.all/DepthLimitsTest.c show that 2130690044 is the
474 // maximum valid depth.
475 const double depth
= toNumber(fn
.arg(2), getVM(fn
));
477 // This also checks for overflow, as both numbers are expressible as
479 if (depth
< DisplayObject::lowerAccessibleBound
||
480 depth
> DisplayObject::upperAccessibleBound
)
482 IF_VERBOSE_ASCODING_ERRORS(
483 log_aserror(_("MovieClip.attachMovie: invalid depth %d "
484 "passed; not attaching"), depth
);
489 boost::int32_t depthValue
= static_cast<boost::int32_t>(depth
);
491 Global_as
& gl
= getGlobal(fn
);
492 DisplayObject
* newch
= exported_movie
->createDisplayObject(gl
, movieclip
);
495 newch
->set_name(getURI(vm
, newname
));
498 boost::intrusive_ptr
<as_object
> initObj
;
501 initObj
= toObject(fn
.arg(3), getVM(fn
));
503 // This is actually a valid thing to do,
504 // the documented behaviour is to just NOT
505 // initialize the properties in this
507 IF_VERBOSE_ASCODING_ERRORS(
508 log_aserror(_("Fourth argument of attachMovie doesn't cast to "
509 "an object (%s), we'll act as if it wasn't given"),
515 // placeDisplayObject() will set depth on newch
516 if (!movieclip
->attachCharacter(*newch
, depthValue
, initObj
.get()))
518 log_error(_("Could not attach DisplayObject at depth %d"), depthValue
);
522 return as_value(getObject(newch
));
526 // attachAudio(id:Object) : Void
528 movieclip_attachAudio(const fn_call
& fn
)
530 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
534 IF_VERBOSE_ASCODING_ERRORS(
535 log_aserror("MovieClip.attachAudio(): %s", _("missing arguments"));
541 if (!isNativeType(toObject(fn
.arg(0), getVM(fn
)), ns
))
543 std::stringstream ss
; fn
.dump_args(ss
);
544 // TODO: find out what to do here
545 log_error("MovieClip.attachAudio(%s): first arg doesn't cast to a "
546 "NetStream", ss
.str());
550 ns
->setAudioController(movieclip
);
552 LOG_ONCE( log_unimpl("MovieClip.attachAudio() - TESTING") );
557 // MovieClip.attachVideo
559 movieclip_attachVideo(const fn_call
& fn
)
561 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
564 LOG_ONCE( log_unimpl("MovieClip.attachVideo()") );
570 movieclip_getDepth(const fn_call
& fn
)
572 // Unlike TextField.getDepth this works for any DisplayObject
573 DisplayObject
* d
= ensure
<IsDisplayObject
<> >(fn
);
575 const int n
= d
->get_depth();
580 //swapDepths(target:Object|target:Number)
584 movieclip_swapDepths(const fn_call
& fn
)
587 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
589 const int this_depth
= movieclip
->get_depth();
593 IF_VERBOSE_ASCODING_ERRORS(
594 log_aserror(_("%s.swapDepths() needs one arg"), movieclip
->getTarget());
599 // Lower bound of source depth below which swapDepth has no effect
600 // (below Timeline/static zone)
601 if (this_depth
< DisplayObject::lowerAccessibleBound
)
603 IF_VERBOSE_ASCODING_ERRORS(
604 std::stringstream ss
;
606 log_aserror(_("%s.swapDepths(%s): won't swap a clip below "
607 "depth %d (%d)"), movieclip
->getTarget(), ss
.str(),
608 DisplayObject::lowerAccessibleBound
,
614 MovieClip
* this_parent
= dynamic_cast<MovieClip
*>(movieclip
->parent());
616 //CharPtr target = NULL;
617 int target_depth
= 0;
619 // movieclip.swapDepth(movieclip)
620 if (MovieClip
* target_movieclip
= fn
.arg(0).toMovieClip()) {
622 if (movieclip
== target_movieclip
) {
623 IF_VERBOSE_ASCODING_ERRORS(
624 log_aserror(_("%s.swapDepths(%s): invalid call, "
625 "swapping to self?"), movieclip
->getTarget(),
626 target_movieclip
->getTarget());
631 MovieClip
* target_parent
=
632 dynamic_cast<MovieClip
*>(movieclip
->parent());
634 if (this_parent
!= target_parent
) {
635 IF_VERBOSE_ASCODING_ERRORS(
636 log_aserror(_("%s.swapDepths(%s): invalid call, the two "
637 "DisplayObjects don't have the same parent"),
638 movieclip
->getTarget(), target_movieclip
->getTarget());
643 target_depth
= target_movieclip
->get_depth();
645 // Check we're not swapping the our own depth so
646 // to avoid unecessary bounds invalidation and immunizing
647 // the instance from subsequent PlaceObject tags attempting
649 if (movieclip
->get_depth() == target_depth
)
651 IF_VERBOSE_ASCODING_ERRORS(
652 std::stringstream ss
; fn
.dump_args(ss
);
653 log_aserror(_("%s.swapDepths(%s): ignored, source and "
654 "target DisplayObjects have the same depth %d"),
655 movieclip
->getTarget(), ss
.str(), target_depth
);
661 // movieclip.swapDepth(depth)
664 const double td
= toNumber(fn
.arg(0), getVM(fn
));
666 IF_VERBOSE_ASCODING_ERRORS(
667 std::stringstream ss
; fn
.dump_args(ss
);
668 log_aserror(_("%s.swapDepths(%s): first argument invalid "
669 "(neither a movieclip nor a number)"),
670 movieclip
->getTarget(), ss
.str());
674 if (td
> DisplayObject::upperAccessibleBound
) {
675 IF_VERBOSE_ASCODING_ERRORS(
676 std::stringstream ss
; fn
.dump_args(ss
);
677 log_aserror(_("%s.swapDepths(%s): requested depth is above "
678 "the accessible range."),
679 movieclip
->getTarget(), ss
.str());
684 target_depth
= static_cast<int>(td
);
686 // Check we're not swapping the our own depth so
687 // to avoid unecessary bounds invalidation and immunizing
688 // the instance from subsequent PlaceObjec tags attempting
690 if (movieclip
->get_depth() == target_depth
) {
691 IF_VERBOSE_ASCODING_ERRORS(
692 std::stringstream ss
; fn
.dump_args(ss
);
693 log_aserror(_("%s.swapDepths(%s): ignored, DisplayObject already "
695 movieclip
->getTarget(), ss
.str(), target_depth
);
699 // TODO : check other kind of validities ?
703 this_parent
->swapDepths(movieclip
, target_depth
);
706 movie_root
& root
= getRoot(fn
);
707 root
.swapLevels(movieclip
, target_depth
);
715 // TODO: wrap the functionality in a MovieClip method
716 // and invoke it from here, this should only be a wrapper
718 //duplicateMovieClip(name:String, depth:Number, [initObject:Object]) : MovieClip
720 movieclip_duplicateMovieClip(const fn_call
& fn
)
722 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
726 IF_VERBOSE_ASCODING_ERRORS(
727 log_aserror(_("MovieClip.duplicateMovieClip() needs 2 or 3 args"));
732 const std::string
& newname
= fn
.arg(0).to_string();
734 // Depth as in attachMovie
735 const double depth
= toNumber(fn
.arg(1), getVM(fn
));
737 // This also checks for overflow, as both numbers are expressible as
739 if (depth
< DisplayObject::lowerAccessibleBound
||
740 depth
> DisplayObject::upperAccessibleBound
)
742 IF_VERBOSE_ASCODING_ERRORS(
743 log_aserror(_("MovieClip.duplicateMovieClip: "
744 "invalid depth %d passed; not duplicating"), depth
);
749 boost::int32_t depthValue
= static_cast<boost::int32_t>(depth
);
753 // Copy members from initObject
756 as_object
* initObject
= toObject(fn
.arg(2), getVM(fn
));
757 ch
= movieclip
->duplicateMovieClip(newname
, depthValue
, initObject
);
761 ch
= movieclip
->duplicateMovieClip(newname
, depthValue
);
764 return as_value(getObject(ch
));
768 movieclip_gotoAndPlay(const fn_call
& fn
)
770 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
774 IF_VERBOSE_ASCODING_ERRORS(
775 log_aserror(_("movieclip_goto_and_play needs one arg"));
781 if ( ! movieclip
->get_frame_number(fn
.arg(0), frame_number
) )
784 IF_VERBOSE_ASCODING_ERRORS(
785 log_aserror(_("movieclip_goto_and_play('%s') -- invalid frame"),
791 // Convert to 0-based
792 movieclip
->goto_frame(frame_number
);
793 movieclip
->setPlayState(MovieClip::PLAYSTATE_PLAY
);
797 as_value
movieclip_gotoAndStop(const fn_call
& fn
)
799 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
803 IF_VERBOSE_ASCODING_ERRORS(
804 log_aserror(_("movieclip_goto_and_stop needs one arg"));
810 if ( ! movieclip
->get_frame_number(fn
.arg(0), frame_number
) )
813 IF_VERBOSE_ASCODING_ERRORS(
814 log_aserror(_("movieclip_goto_and_stop('%s') -- invalid frame"),
820 // Convert to 0-based
821 movieclip
->goto_frame(frame_number
);
822 movieclip
->setPlayState(MovieClip::PLAYSTATE_STOP
);
826 as_value
movieclip_nextFrame(const fn_call
& fn
)
828 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
830 const size_t frame_count
= movieclip
->get_frame_count();
831 const size_t current_frame
= movieclip
->get_current_frame();
832 if (current_frame
< frame_count
)
834 movieclip
->goto_frame(current_frame
+ 1);
836 movieclip
->setPlayState(MovieClip::PLAYSTATE_STOP
);
841 movieclip_prevFrame(const fn_call
& fn
)
843 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
845 const size_t current_frame
= movieclip
->get_current_frame();
846 if (current_frame
> 0)
848 movieclip
->goto_frame(current_frame
- 1);
850 movieclip
->setPlayState(MovieClip::PLAYSTATE_STOP
);
855 movieclip_getBytesLoaded(const fn_call
& fn
)
857 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
859 return as_value(movieclip
->get_bytes_loaded());
863 movieclip_getBytesTotal(const fn_call
& fn
)
865 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
867 return as_value(movieclip
->get_bytes_total());
870 // MovieClip.loadMovie(url:String [,variables:String]).
872 // Returns 1 for "get", 2 for "post", and otherwise 0. Case-insensitive.
873 // This *always* calls MovieClip.meth.
875 movieclip_loadMovie(const fn_call
& fn
)
877 DisplayObject
* dobj
= ensure
<IsDisplayObject
<> >(fn
);
881 val
= callMethod(getObject(dobj
), NSV::PROP_METH
, fn
.arg(1));
883 else val
= callMethod(getObject(dobj
), NSV::PROP_METH
);
885 if (fn
.nargs
< 1) // url
887 IF_VERBOSE_ASCODING_ERRORS(
888 log_aserror(_("MovieClip.loadMovie() "
889 "expected 1 or 2 args, got %d - returning undefined"),
895 const std::string
& urlstr
= fn
.arg(0).to_string();
898 IF_VERBOSE_ASCODING_ERRORS(
899 std::stringstream ss
; fn
.dump_args(ss
);
900 log_aserror(_("First argument of MovieClip.loadMovie(%s) "
901 "evaluates to an empty string - "
902 "returning undefined"),
908 movie_root
& mr
= getRoot(fn
);
909 std::string target
= dobj
->getTarget();
911 // TODO: if GET/POST should send variables of *this* movie,
912 // no matter if the target will be replaced by another movie !!
913 const MovieClip::VariablesMethod method
=
914 static_cast<MovieClip::VariablesMethod
>(toInt(val
, getVM(fn
)));
918 // This is just an optimization if we aren't going
919 // to send the data anyway. It might be wrong, though.
920 if (method
!= MovieClip::METHOD_NONE
) {
921 data
= getURLEncodedVars(*getObject(dobj
));
924 mr
.loadMovie(urlstr
, target
, data
, method
);
929 // my_mc.loadVariables(url:String [, variables:String]) : Void
931 movieclip_loadVariables(const fn_call
& fn
)
933 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
935 as_object
* obj
= getObject(movieclip
);
938 // This always calls MovieClip.meth, even when there are no
943 val
= callMethod(obj
, NSV::PROP_METH
, fn
.arg(1));
945 else val
= callMethod(obj
, NSV::PROP_METH
);
947 if (fn
.nargs
< 1) // url
949 IF_VERBOSE_ASCODING_ERRORS(
950 log_aserror(_("MovieClip.loadVariables() "
951 "expected 1 or 2 args, got %d - returning undefined"),
957 const std::string
& urlstr
= fn
.arg(0).to_string();
960 IF_VERBOSE_ASCODING_ERRORS(
961 std::stringstream ss
; fn
.dump_args(ss
);
962 log_aserror(_("First argument passed to MovieClip.loadVariables(%s) "
963 "evaluates to an empty string - "
964 "returning undefined"),
970 const MovieClip::VariablesMethod method
=
971 static_cast<MovieClip::VariablesMethod
>(toInt(val
, getVM(fn
)));
973 movieclip
->loadVariables(urlstr
, method
);
974 log_debug("MovieClip.loadVariables(%s) - TESTING ", urlstr
);
979 // my_mc.unloadMovie() : Void
981 movieclip_unloadMovie(const fn_call
& fn
)
983 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
985 movieclip
->unloadMovie();
991 movieclip_hitTest(const fn_call
& fn
)
993 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
999 const as_value
& tgt_val
= fn
.arg(0);
1000 DisplayObject
* target
= findTarget(fn
.env(), tgt_val
.to_string());
1003 IF_VERBOSE_ASCODING_ERRORS(
1004 log_aserror(_("Can't find hitTest target %s"),
1010 SWFRect thisbounds
= movieclip
->getBounds();
1011 const SWFMatrix thismat
= getWorldMatrix(*movieclip
);
1012 thismat
.transform(thisbounds
);
1014 SWFRect tgtbounds
= target
->getBounds();
1015 SWFMatrix tgtmat
= getWorldMatrix(*target
);
1016 tgtmat
.transform(tgtbounds
);
1018 return thisbounds
.getRange().intersects(tgtbounds
.getRange());
1025 boost::int32_t x
= pixelsToTwips(toNumber(fn
.arg(0), getVM(fn
)));
1026 boost::int32_t y
= pixelsToTwips(toNumber(fn
.arg(1), getVM(fn
)));
1028 return movieclip
->pointInBounds(x
, y
);
1031 case 3: // x, y, shapeFlag
1033 const boost::int32_t x
= pixelsToTwips(toNumber(fn
.arg(0),
1035 const boost::int32_t y
= pixelsToTwips(toNumber(fn
.arg(1),
1037 const bool shapeFlag
= toBool(fn
.arg(2), getVM(fn
));
1039 if (!shapeFlag
) return movieclip
->pointInBounds(x
, y
);
1040 else return movieclip
->pointInHitableShape(x
, y
);
1045 IF_VERBOSE_ASCODING_ERRORS(
1046 log_aserror(_("hitTest() called with %u args"),
1057 //getNextHighestDepth() : Number
1059 movieclip_getNextHighestDepth(const fn_call
& fn
)
1061 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1063 int nextdepth
= movieclip
->getNextHighestDepth();
1064 return as_value(static_cast<double>(nextdepth
));
1067 //getInstanceAtDepth(depth:Number) : MovieClip
1069 movieclip_getInstanceAtDepth(const fn_call
& fn
)
1071 MovieClip
* mc
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1073 if (fn
.nargs
< 1 || fn
.arg(0).is_undefined()) {
1074 IF_VERBOSE_ASCODING_ERRORS(
1075 log_aserror("MovieClip.getInstanceAtDepth(): missing or "
1076 "undefined depth argument");
1081 const int depth
= toInt(fn
.arg(0), getVM(fn
));
1083 DisplayObject
* ch
= mc
->getDisplayObjectAtDepth(depth
);
1085 // we want 'undefined', not 'null'
1086 if (!ch
) return as_value();
1088 return as_value(getObject(ch
));
1091 /// MovieClip.getURL(url:String[, window:String[, method:String]])
1093 /// Tested manually to function as a method of any as_object. Hard to
1094 /// test automatically as it doesn't return anything and only has external
1098 movieclip_getURL(const fn_call
& fn
)
1100 as_object
* movieclip
= ensure
<ValidThis
>(fn
);
1108 val
= callMethod(movieclip
, NSV::PROP_METH
, fn
.arg(2));
1110 else val
= callMethod(movieclip
, NSV::PROP_METH
);
1116 IF_VERBOSE_ASCODING_ERRORS(
1117 log_aserror(_("No arguments passed to MovieClip.getURL()"));
1123 IF_VERBOSE_ASCODING_ERRORS(
1124 std::ostringstream os
;
1126 log_aserror(_("MovieClip.getURL(%s): extra arguments "
1127 "dropped"), os
.str());
1131 // This argument has already been handled.
1133 target
= fn
.arg(1).to_string();
1135 urlstr
= fn
.arg(0).to_string();
1140 MovieClip::VariablesMethod method
=
1141 static_cast<MovieClip::VariablesMethod
>(toInt(val
, getVM(fn
)));
1145 if (method
!= MovieClip::METHOD_NONE
) {
1146 // Get encoded vars.
1147 vars
= getURLEncodedVars(*movieclip
);
1150 movie_root
& m
= getRoot(fn
);
1152 m
.getURL(urlstr
, target
, vars
, method
);
1157 // getSWFVersion() : Number
1159 movieclip_getSWFVersion(const fn_call
& fn
)
1161 DisplayObject
* o
= get
<DisplayObject
>(fn
.this_ptr
);
1162 if (!o
) return as_value(-1);
1163 return as_value(o
->getDefinitionVersion());
1166 // MovieClip.meth(<string>) : Number
1168 // Parses case-insensitive "get" and "post" into 1 and 2, 0 anything else
1171 movieclip_meth(const fn_call
& fn
)
1174 if (!fn
.nargs
) return as_value(MovieClip::METHOD_NONE
);
1176 as_object
* o
= toObject(fn
.arg(0), getVM(fn
));
1178 return as_value(MovieClip::METHOD_NONE
);
1181 as_value lc
= callMethod(o
, NSV::PROP_TO_LOWER_CASE
);
1183 std::string s
= lc
.to_string();
1185 if (s
== "get") return as_value(MovieClip::METHOD_GET
);
1186 if (s
== "post") return as_value(MovieClip::METHOD_POST
);
1187 return as_value(MovieClip::METHOD_NONE
);
1191 // getTextSnapshot() : TextSnapshot
1193 movieclip_getTextSnapshot(const fn_call
& fn
)
1195 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1197 // If not found, construction fails.
1198 as_value
textSnapshot(findObject(fn
.env(), "TextSnapshot"));
1200 boost::intrusive_ptr
<as_function
> tsCtor
= textSnapshot
.to_function();
1203 IF_VERBOSE_ASCODING_ERRORS(
1204 log_aserror("MovieClip.getTextSnapshot: failed to construct "
1205 "TextSnapshot (object probably overridden)");
1210 // Construct a flash.geom.Transform object with "this" as argument.
1212 args
+= getObject(movieclip
);
1214 boost::intrusive_ptr
<as_object
> ts
=
1215 constructInstance(*tsCtor
, fn
.env(), args
);
1217 return as_value(ts
.get());
1221 // getBounds(targetCoordinateSpace:Object) : Object
1223 movieclip_getBounds(const fn_call
& fn
)
1225 DisplayObject
* movieclip
= ensure
<IsDisplayObject
<> >(fn
);
1227 SWFRect bounds
= movieclip
->getBounds();
1231 DisplayObject
* target
= fn
.arg(0).toDisplayObject();
1234 IF_VERBOSE_ASCODING_ERRORS(
1235 log_aserror(_("MovieClip.getBounds(%s): invalid call, first "
1236 "arg must be a DisplayObject"),
1242 const SWFMatrix tgtwmat
= getWorldMatrix(*target
).invert();
1243 const SWFMatrix srcwmat
= getWorldMatrix(*movieclip
);
1245 srcwmat
.transform(bounds
);
1246 tgtwmat
.transform(bounds
);
1249 double xMin
, yMin
, xMax
, yMax
;
1251 if (!bounds
.is_null()) {
1252 // Round to the twip
1253 xMin
= twipsToPixels(bounds
.get_x_min());
1254 yMin
= twipsToPixels(bounds
.get_y_min());
1255 xMax
= twipsToPixels(bounds
.get_x_max());
1256 yMax
= twipsToPixels(bounds
.get_y_max());
1259 const double magicMin
= 6710886.35;
1260 xMin
= yMin
= xMax
= yMax
= magicMin
;
1263 // This is a bare object.
1264 as_object
* bounds_obj
= new as_object(getGlobal(fn
));
1265 bounds_obj
->init_member("xMin", xMin
);
1266 bounds_obj
->init_member("yMin", yMin
);
1267 bounds_obj
->init_member("xMax", xMax
);
1268 bounds_obj
->init_member("yMax", yMax
);
1270 return as_value(bounds_obj
);
1274 movieclip_globalToLocal(const fn_call
& fn
)
1276 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1282 IF_VERBOSE_ASCODING_ERRORS(
1283 log_aserror(_("MovieClip.globalToLocal() takes one arg"));
1288 boost::intrusive_ptr
<as_object
> obj
= toObject(fn
.arg(0), getVM(fn
));
1291 IF_VERBOSE_ASCODING_ERRORS(
1292 log_aserror(_("MovieClip.globalToLocal(%s): "
1293 "first argument doesn't cast to an object"),
1300 boost::int32_t x
= 0;
1301 boost::int32_t y
= 0;
1303 if ( ! obj
->get_member(NSV::PROP_X
, &tmp
) )
1305 IF_VERBOSE_ASCODING_ERRORS(
1306 log_aserror(_("MovieClip.globalToLocal(%s): "
1307 "object parameter doesn't have an 'x' member"),
1312 x
= pixelsToTwips(toNumber(tmp
, getVM(fn
)));
1314 if ( ! obj
->get_member(NSV::PROP_Y
, &tmp
) )
1316 IF_VERBOSE_ASCODING_ERRORS(
1317 log_aserror(_("MovieClip.globalToLocal(%s): "
1318 "object parameter doesn't have an 'y' member"),
1323 y
= pixelsToTwips(toNumber(tmp
, getVM(fn
)));
1326 const SWFMatrix world_mat
= getWorldMatrix(*movieclip
).invert();
1327 world_mat
.transform(pt
);
1329 obj
->set_member(NSV::PROP_X
, twipsToPixels(pt
.x
));
1330 obj
->set_member(NSV::PROP_Y
, twipsToPixels(pt
.y
));
1336 movieclip_localToGlobal(const fn_call
& fn
)
1338 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1344 IF_VERBOSE_ASCODING_ERRORS(
1345 log_aserror(_("MovieClip.localToGlobal() takes one arg"));
1350 boost::intrusive_ptr
<as_object
> obj
= toObject(fn
.arg(0), getVM(fn
));
1353 IF_VERBOSE_ASCODING_ERRORS(
1354 log_aserror(_("MovieClip.localToGlobal(%s): "
1355 "first argument doesn't cast to an object"),
1362 boost::int32_t x
= 0;
1363 boost::int32_t y
= 0;
1365 if ( ! obj
->get_member(NSV::PROP_X
, &tmp
) )
1367 IF_VERBOSE_ASCODING_ERRORS(
1368 log_aserror(_("MovieClip.localToGlobal(%s): "
1369 "object parameter doesn't have an 'x' member"),
1374 x
= pixelsToTwips(toNumber(tmp
, getVM(fn
)));
1376 if ( ! obj
->get_member(NSV::PROP_Y
, &tmp
) )
1378 IF_VERBOSE_ASCODING_ERRORS(
1379 log_aserror(_("MovieClip.localToGlobal(%s): "
1380 "object parameter doesn't have an 'y' member"),
1385 y
= pixelsToTwips(toNumber(tmp
, getVM(fn
)));
1388 const SWFMatrix world_mat
= getWorldMatrix(*movieclip
);
1389 world_mat
.transform(pt
);
1391 obj
->set_member(NSV::PROP_X
, twipsToPixels(pt
.x
));
1392 obj
->set_member(NSV::PROP_Y
, twipsToPixels(pt
.y
));
1398 movieclip_setMask(const fn_call
& fn
)
1400 // swfdec/test/image/mask-textfield-6.swf shows that setMask should also
1401 // work against TextFields, we have no tests for other DisplayObject
1402 // types so we generalize it for any DisplayObject.
1403 DisplayObject
* maskee
= ensure
<IsDisplayObject
<> >(fn
);
1407 IF_VERBOSE_ASCODING_ERRORS(
1408 log_aserror(_("%s.setMask() : needs an argument"), maskee
->getTarget());
1413 const as_value
& arg
= fn
.arg(0);
1414 if ( arg
.is_null() || arg
.is_undefined() )
1422 as_object
* obj
= toObject(arg
, getVM(fn
));
1423 DisplayObject
* mask
= get
<DisplayObject
>(obj
);
1426 IF_VERBOSE_ASCODING_ERRORS(
1427 log_aserror(_("%s.setMask(%s) : first argument is not a DisplayObject"),
1428 maskee
->getTarget(), arg
);
1433 // ch is possibly NULL, which is intended
1434 maskee
->setMask(mask
);
1437 //log_debug("MovieClip.setMask() TESTING");
1439 return as_value(true);
1443 movieclip_endFill(const fn_call
& fn
)
1445 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1446 movieclip
->graphics().endFill();
1451 movieclip_lineTo(const fn_call
& fn
)
1453 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1456 IF_VERBOSE_ASCODING_ERRORS(
1457 log_aserror(_("MovieClip.lineTo() needs at least two arguments"));
1462 double x
= toNumber(fn
.arg(0), getVM(fn
));
1463 double y
= toNumber(fn
.arg(1), getVM(fn
));
1465 if (!isFinite(x
)) x
= 0;
1466 if (!isFinite(y
)) y
= 0;
1468 #ifdef DEBUG_DRAWING_API
1469 log_debug("%s.lineTo(%g,%g);", movieclip
->getTarget(), x
, y
);
1471 movieclip
->graphics().lineTo(pixelsToTwips(x
), pixelsToTwips(y
),
1472 movieclip
->getDefinitionVersion());
1477 movieclip_moveTo(const fn_call
& fn
)
1479 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1482 IF_VERBOSE_ASCODING_ERRORS(
1483 log_aserror(_("MovieClip.moveTo() takes two args"));
1488 double x
= toNumber(fn
.arg(0), getVM(fn
));
1489 double y
= toNumber(fn
.arg(1), getVM(fn
));
1491 if (!isFinite(x
)) x
= 0;
1492 if (!isFinite(y
)) y
= 0;
1494 movieclip
->graphics().moveTo(pixelsToTwips(x
), pixelsToTwips(y
));
1498 // SWF6,7: lineStyle(thickness:Number, rgb:Number, alpha:Number) : Void
1500 // SWF8+: lineStyle(thickness:Number, rgb:Number, alpha:Number,
1501 // pixelHinting:Boolean, noScale:String,
1502 // capsStyle:String, jointStyle:String,
1503 // miterLimit:Number) : Void
1505 movieclip_lineStyle(const fn_call
& fn
)
1507 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1510 movieclip
->graphics().resetLineStyle();
1514 boost::uint8_t r
= 0;
1515 boost::uint8_t g
= 0;
1516 boost::uint8_t b
= 0;
1517 boost::uint8_t a
= 255;
1518 boost::uint16_t thickness
= 0;
1519 bool scaleThicknessVertically
= true;
1520 bool scaleThicknessHorizontally
= true;
1521 bool pixelHinting
= false;
1522 bool noClose
= false;
1523 CapStyle capStyle
= CAP_ROUND
;
1524 JoinStyle joinStyle
= JOIN_ROUND
;
1525 float miterLimitFactor
= 1.0f
;
1527 int arguments
= fn
.nargs
;
1529 const int swfVersion
= getSWFVersion(fn
);
1530 if (swfVersion
< 8 && fn
.nargs
> 3) {
1531 IF_VERBOSE_ASCODING_ERRORS(
1532 std::ostringstream ss
;
1534 log_aserror(_("MovieClip.lineStyle(%s): args after the "
1535 "first three will be discarded"), ss
.str());
1540 switch (arguments
) {
1543 miterLimitFactor
= clamp
<int>(toInt(fn
.arg(7), getVM(fn
)), 1, 255);
1546 std::string joinStyleStr
= fn
.arg(6).to_string();
1547 if (joinStyleStr
== "miter") joinStyle
= JOIN_MITER
;
1548 else if (joinStyleStr
== "round") joinStyle
= JOIN_ROUND
;
1549 else if (joinStyleStr
== "bevel") joinStyle
= JOIN_BEVEL
;
1551 IF_VERBOSE_ASCODING_ERRORS(
1552 std::ostringstream ss
;
1554 log_aserror(_("MovieClip.lineStyle(%s): invalid joinStyle"
1555 "value '%s' (valid values: %s|%s|%s)"),
1556 ss
.str(), joinStyleStr
, "miter", "round", "bevel");
1562 const std::string capStyleStr
= fn
.arg(5).to_string();
1563 if (capStyleStr
== "none") capStyle
= CAP_NONE
;
1564 else if (capStyleStr
== "round") capStyle
= CAP_ROUND
;
1565 else if (capStyleStr
== "square") capStyle
= CAP_SQUARE
;
1567 IF_VERBOSE_ASCODING_ERRORS(
1568 std::ostringstream ss
;
1570 log_aserror(_("MovieClip.lineStyle(%s): invalid capStyle "
1571 "value '%s' (valid values: none|round|square)"),
1572 ss
.str(), capStyleStr
);
1578 // Both values to be set here are true, so just set the
1579 // appropriate values to false.
1580 const std::string noScaleString
= fn
.arg(4).to_string();
1581 if (noScaleString
== "none") {
1582 scaleThicknessVertically
= false;
1583 scaleThicknessHorizontally
= false;
1585 else if (noScaleString
== "vertical") {
1586 scaleThicknessVertically
= false;
1588 else if (noScaleString
== "horizontal") {
1589 scaleThicknessHorizontally
= false;
1591 else if (noScaleString
!= "normal") {
1592 IF_VERBOSE_ASCODING_ERRORS(
1593 std::ostringstream ss
;
1595 log_aserror(_("MovieClip.lineStyle(%s): invalid "
1596 "noScale value '%s' (valid values: "
1598 ss
.str(), noScaleString
, "none",
1599 "vertical", "horizontal", "normal");
1604 pixelHinting
= toBool(fn
.arg(3), getVM(fn
));
1607 const float alphaval
= clamp
<float>(toNumber(fn
.arg(2), getVM(fn
)),
1609 a
= boost::uint8_t(255 * (alphaval
/ 100));
1613 // See pollock.swf for eventual regressions.
1614 // It sets color to a random number from
1615 // 0 to 160000000 (about 10 times more then the max).
1616 boost::uint32_t rgbval
= toInt(fn
.arg(1), getVM(fn
));
1617 r
= boost::uint8_t((rgbval
& 0xFF0000) >> 16);
1618 g
= boost::uint8_t((rgbval
& 0x00FF00) >> 8);
1619 b
= boost::uint8_t((rgbval
& 0x0000FF) );
1622 thickness
= boost::uint16_t(pixelsToTwips(clamp
<float>(
1623 toNumber(fn
.arg(0), getVM(fn
)), 0, 255)));
1627 rgba
color(r
, g
, b
, a
);
1629 movieclip
->graphics().lineStyle(thickness
, color
,
1630 scaleThicknessVertically
, scaleThicknessHorizontally
,
1631 pixelHinting
, noClose
, capStyle
, capStyle
, joinStyle
, miterLimitFactor
);
1637 movieclip_curveTo(const fn_call
& fn
)
1639 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1642 IF_VERBOSE_ASCODING_ERRORS(
1643 log_aserror(_("MovieClip.curveTo() takes four args"));
1648 double cx
= toNumber(fn
.arg(0), getVM(fn
));
1649 double cy
= toNumber(fn
.arg(1), getVM(fn
));
1650 double ax
= toNumber(fn
.arg(2), getVM(fn
));
1651 double ay
= toNumber(fn
.arg(3), getVM(fn
));
1653 if (!isFinite(cx
)) cx
= 0;
1654 if (!isFinite(cy
)) cy
= 0;
1655 if (!isFinite(ax
)) ax
= 0;
1656 if (!isFinite(ay
)) ay
= 0;
1658 #ifdef DEBUG_DRAWING_API
1659 log_debug(_("%s.curveTo(%g,%g,%g,%g);"), movieclip
->getTarget(),
1662 movieclip
->graphics().curveTo(pixelsToTwips(cx
), pixelsToTwips(cy
),
1663 pixelsToTwips(ax
), pixelsToTwips(ay
),
1664 movieclip
->getDefinitionVersion());
1670 movieclip_clear(const fn_call
& fn
)
1672 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1673 movieclip
->graphics().clear();
1678 movieclip_beginFill(const fn_call
& fn
)
1680 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1683 IF_VERBOSE_ASCODING_ERRORS(
1684 log_aserror("beginFill() with no args is a no-op");
1689 // 2^24 is the max here
1690 const boost::uint32_t rgbval
=
1691 clamp
<float>(toNumber(fn
.arg(0), getVM(fn
)), 0, 16777216);
1693 const boost::uint8_t r
= (rgbval
& 0xFF0000) >> 16;
1694 const boost::uint8_t g
= (rgbval
& 0x00FF00) >> 8;
1695 const boost::uint8_t b
= rgbval
& 0x0000FF;
1696 boost::uint8_t a
= 255;
1699 a
= 255 * clamp
<int>(toInt(fn
.arg(1), getVM(fn
)), 0, 100) / 100;
1702 rgba
color(r
, g
, b
, a
);
1704 const FillStyle f
= SolidFill(color
);
1705 movieclip
->graphics().beginFill(f
);
1711 /// Create a dynamic gradient fill.
1713 /// fillType, colors, alphas, ratios, matrix, [spreadMethod,
1714 /// [interpolationMethod, [focalPointRatio]]]
1716 /// Colors, alphas and ratios must be (possibly fake) arrays, and must have
1717 /// the same size. This applies even when there are more gradients than
1720 movieclip_beginGradientFill(const fn_call
& fn
)
1722 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1724 // The arguments up to and including matrix must be present.
1726 IF_VERBOSE_ASCODING_ERRORS(
1727 std::stringstream ss
; fn
.dump_args(ss
);
1728 log_aserror(_("%s.beginGradientFill(%s): invalid call: 5 arguments "
1729 "needed"), movieclip
->getTarget(), ss
.str());
1734 const size_t maxargs
= getSWFVersion(fn
) >= 8 ? 8 : 5;
1736 if (fn
.nargs
> maxargs
) {
1737 IF_VERBOSE_ASCODING_ERRORS(
1738 std::stringstream ss
; fn
.dump_args(ss
);
1739 log_aserror(_("%s.beginGradientFill(%s): extra arguments "
1740 "invalidate call!"));
1745 GradientFill::Type t
;
1747 std::string typeStr
= fn
.arg(0).to_string();
1749 // An unexpected fill type results in no fill in all versions.
1750 if (typeStr
== "radial") {
1751 t
= GradientFill::RADIAL
;
1753 else if (typeStr
== "linear") {
1754 t
= GradientFill::LINEAR
;
1757 IF_VERBOSE_ASCODING_ERRORS(
1758 std::stringstream ss
; fn
.dump_args(ss
);
1759 log_aserror(_("%s.beginGradientFill(%s): first arg must be "
1760 "'radial', 'focal', or 'linear'"),
1761 movieclip
->getTarget(), ss
.str());
1766 typedef boost::intrusive_ptr
<as_object
> ObjPtr
;
1767 ObjPtr colors
= toObject(fn
.arg(1), getVM(fn
));
1768 ObjPtr alphas
= toObject(fn
.arg(2), getVM(fn
));
1769 ObjPtr ratios
= toObject(fn
.arg(3), getVM(fn
));
1770 ObjPtr matrix
= toObject(fn
.arg(4), getVM(fn
));
1772 if (!colors
|| !alphas
|| !ratios
|| !matrix
) {
1773 IF_VERBOSE_ASCODING_ERRORS(
1774 std::stringstream ss
; fn
.dump_args(ss
);
1775 log_aserror(_("%s.beginGradientFill(%s): one or more of the "
1776 " args from 2nd to 5th don't cast to objects"),
1777 movieclip
->getTarget(), ss
.str());
1782 size_t stops
= arrayLength(*colors
);
1784 // Check that the arrays are all the same size.
1785 if (stops
!= arrayLength(*alphas
) || stops
!= arrayLength(*ratios
)) {
1787 IF_VERBOSE_ASCODING_ERRORS(
1788 std::stringstream ss
; fn
.dump_args(ss
);
1789 log_aserror(_("%s.beginGradientFill(%s): colors, alphas and "
1790 "ratios args don't have same length"),
1791 movieclip
->getTarget(), ss
.str());
1796 // Then limit gradients. It's documented to be a maximum of 15, though
1797 // this isn't tested. The arrays must be the same size regardless of
1800 std::stringstream ss
; fn
.dump_args(ss
);
1801 log_debug(_("%s.beginGradientFill(%s) : too many array elements"
1802 " for colors and ratios (%d), will trim to 8"),
1803 movieclip
->getTarget(), ss
.str(), stops
);
1807 SWFMatrix mat
= toSWFMatrix(*matrix
);
1809 // ----------------------------
1810 // Create the gradients vector
1811 // ----------------------------
1815 std::vector
<GradientRecord
> gradients
;
1816 gradients
.reserve(stops
);
1817 for (size_t i
= 0; i
< stops
; ++i
) {
1819 const ObjectURI
& key
= getURI(vm
, boost::lexical_cast
<std::string
>(i
));
1821 as_value colVal
= getMember(*colors
, key
);
1822 boost::uint32_t col
= colVal
.is_number() ? toInt(colVal
, getVM(fn
)) : 0;
1824 /// Alpha is the range 0..100.
1825 as_value alpVal
= getMember(*alphas
, key
);
1826 const double a
= alpVal
.is_number() ?
1827 clamp
<double>(toNumber(alpVal
, getVM(fn
)), 0, 100) : 0;
1828 const boost::uint8_t alp
= 0xff * (a
/ 100);
1830 // Ratio is the range 0..255, but a ratio may never be smaller than
1831 // the previous value. The pp adjusts it to be greater than the
1832 // last value if it is smaller. But we must be careful not to exceed
1835 // From looking it looks like the minimum adjustment is 2. Even
1836 // steps of 1 appear to be adjusted.
1838 const as_value
& ratVal
= getMember(*ratios
, key
);
1839 const boost::uint32_t minRatio
=
1840 gradients
.empty() ? 0 :
1841 std::min
<boost::uint32_t>(gradients
[i
- 1].ratio
+ step
, 0xff);
1843 boost::uint8_t rat
= ratVal
.is_number() ?
1844 clamp
<boost::uint32_t>(toInt(ratVal
, getVM(fn
)), minRatio
, 0xff)
1847 // The renderer may expect successively larger ratios; failure to
1848 // do this can lead to memory errors.
1849 if (!gradients
.empty()) {
1850 assert((rat
!= 0xff && rat
> gradients
[i
- 1].ratio
) ||
1851 (rat
>= gradients
[i
- 1].ratio
));
1855 color
.parseRGB(col
);
1858 gradients
.push_back(GradientRecord(rat
, color
));
1861 // Make sure we don't try to construct a GradientFill with only 1 stop!
1863 const FillStyle f
= SolidFill(gradients
[0].color
);
1864 movieclip
->graphics().beginFill(f
);
1868 GradientFill
fd(t
, mat
.invert(), gradients
);
1870 // Set spread mode if present. Defaults to "pad", which is GradientFill's
1873 const std::string
& spread
= fn
.arg(5).to_string();
1874 if (spread
== "reflect") fd
.spreadMode
= GradientFill::REFLECT
;
1875 else if (spread
== "repeat") fd
.spreadMode
= GradientFill::REPEAT
;
1876 else assert(fd
.spreadMode
== GradientFill::PAD
);
1879 /// TODO: set interpolation mode and spread mode.
1881 /// Add a focus if present.
1883 fd
.setFocalPoint(toNumber(fn
.arg(7), getVM(fn
)));
1886 movieclip
->graphics().beginFill(fd
);
1891 // startDrag([lockCenter:Boolean], [left:Number], [top:Number],
1892 // [right:Number], [bottom:Number]) : Void`
1894 movieclip_startDrag(const fn_call
& fn
)
1896 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1898 DragState
st(movieclip
);
1900 // mark this DisplayObject is transformed.
1901 movieclip
->transformedByScript();
1904 st
.setLockCentered(toBool(fn
.arg(0), getVM(fn
)));
1907 double x0
= toNumber(fn
.arg(1), getVM(fn
));
1908 double y0
= toNumber(fn
.arg(2), getVM(fn
));
1909 double x1
= toNumber(fn
.arg(3), getVM(fn
));
1910 double y1
= toNumber(fn
.arg(4), getVM(fn
));
1912 // check for infinite values
1913 bool gotinf
= false;
1914 if (!isFinite(x0
)) { x0
=0; gotinf
=true; }
1915 if (!isFinite(y0
)) { y0
=0; gotinf
=true; }
1916 if (!isFinite(x1
)) { x1
=0; gotinf
=true; }
1917 if (!isFinite(y1
)) { y1
=0; gotinf
=true; }
1919 // check for swapped values
1920 bool swapped
= false;
1931 IF_VERBOSE_ASCODING_ERRORS(
1932 if (gotinf
|| swapped
) {
1933 std::stringstream ss
; fn
.dump_args(ss
);
1935 log_aserror(_("min/max bbox values in "
1936 "MovieClip.startDrag(%s) swapped, fixing"),
1940 log_aserror(_("non-finite bbox values in "
1941 "MovieClip.startDrag(%s), took as zero"),
1947 SWFRect
bounds(pixelsToTwips(x0
), pixelsToTwips(y0
),
1948 pixelsToTwips(x1
), pixelsToTwips(y1
));
1949 st
.setBounds(bounds
);
1953 getRoot(fn
).setDragState(st
);
1958 // stopDrag() : Void
1960 movieclip_stopDrag(const fn_call
& fn
)
1962 // Should this be a MovieClip only function? It isn't
1964 getRoot(fn
).stop_drag();
1970 movieclip_beginBitmapFill(const fn_call
& fn
)
1972 MovieClip
* ptr
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1977 as_object
* obj
= toObject(fn
.arg(0), getVM(fn
));
1980 if (!isNativeType(obj
, bd
) || bd
->disposed()) {
1981 IF_VERBOSE_ASCODING_ERRORS(
1982 log_debug("MovieClip.attachBitmap: first argument should be a "
1983 "valid BitmapData", fn
.arg(1));
1991 as_object
* matrix
= toObject(fn
.arg(1), getVM(fn
));
1993 mat
= toSWFMatrix(*matrix
);
1997 BitmapFill::Type t
= BitmapFill::TILED
;
1999 const bool repeat
= toBool(fn
.arg(2), getVM(fn
));
2000 if (!repeat
) t
= BitmapFill::CLIPPED
;
2003 BitmapFill::SmoothingPolicy p
= BitmapFill::SMOOTHING_OFF
;
2004 if (fn
.nargs
> 3 && toBool(fn
.arg(3), getVM(fn
))) {
2005 p
= BitmapFill::SMOOTHING_ON
;
2008 // This is needed to get the bitmap to the right size and have it in the
2009 // correct place. Maybe it would be better handled somewhere else, as it's
2010 // not exactly intuitive.
2012 mat
.concatenate_scale(1 / 20., 1 / 20.);
2016 ptr
->graphics().beginFill(BitmapFill(t
, bd
->bitmapInfo(), mat
, p
));
2024 movieclip_getRect(const fn_call
& fn
)
2026 MovieClip
* ptr
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
2028 LOG_ONCE( log_unimpl (__FUNCTION__
) );
2034 movieclip_lineGradientStyle(const fn_call
& fn
)
2036 MovieClip
* ptr
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
2038 LOG_ONCE( log_unimpl (__FUNCTION__
) );
2044 movieclip_attachBitmap(const fn_call
& fn
)
2047 MovieClip
* ptr
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
2050 IF_VERBOSE_ASCODING_ERRORS(
2051 log_debug("MovieClip.attachBitmap: expected 2 args, got %d",
2057 as_object
* obj
= toObject(fn
.arg(0), getVM(fn
));
2060 if (!isNativeType(obj
, bd
) || bd
->disposed()) {
2061 IF_VERBOSE_ASCODING_ERRORS(
2062 log_debug("MovieClip.attachBitmap: first argument should be a "
2063 "valid BitmapData", fn
.arg(1));
2068 int depth
= toInt(fn
.arg(1), getVM(fn
));
2070 DisplayObject
* bm
= new Bitmap(getRoot(fn
), 0, bd
, ptr
);
2071 ptr
->attachCharacter(*bm
, depth
, 0);
2078 movieclip_as2_ctor(const fn_call
& /*fn*/)
2085 movieclip_transform(const fn_call
& fn
)
2087 MovieClip
* ptr
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
2089 // If not found, construction fails.
2090 as_value
transform(findObject(fn
.env(), "flash.geom.Transform"));
2092 boost::intrusive_ptr
<as_function
> transCtor
= transform
.to_function();
2095 log_error("Failed to construct flash.geom.Transform!");
2099 // Construct a flash.geom.Transform object with "this" as argument.
2101 args
+= getObject(ptr
);
2103 boost::intrusive_ptr
<as_object
> newTrans
=
2104 constructInstance(*transCtor
, fn
.env(), args
);
2106 return as_value(newTrans
.get());
2110 movieclip_beginMeshFill(const fn_call
& /*fn*/)
2113 LOG_ONCE(log_unimpl("MovieClip.beginMeshFill"));
2119 movieclip_lockroot(const fn_call
& fn
)
2121 MovieClip
* ptr
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
2124 return as_value(ptr
->getLockRoot());
2127 ptr
->setLockRoot(toBool(fn
.arg(0), getVM(fn
)));
2131 } // anonymous namespace
2132 } // gnash namespace
2136 // indent-tabs-mode: t