1 // MovieClip_as.cpp: ActionScript "MovieClip" class, for Gnash.
3 // Copyright (C) 2009, 2010, 2011 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"
35 #include "NativeFunction.h"
38 #include "FillStyle.h"
39 #include "namedStrings.h"
41 #include "RunResources.h"
42 #include "ASConversions.h"
46 // Forward declarations
49 void attachMovieClipAS2Interface(as_object
& o
);
51 as_value
movieclip_as2_ctor(const fn_call
& fn
);
52 as_value
movieclip_transform(const fn_call
& fn
);
53 as_value
movieclip_scale9Grid(const fn_call
& fn
);
54 as_value
movieclip_attachVideo(const fn_call
& fn
);
55 as_value
movieclip_attachAudio(const fn_call
& fn
);
56 as_value
movieclip_attachMovie(const fn_call
& fn
);
57 as_value
movieclip_unloadMovie(const fn_call
& fn
);
58 as_value
movieclip_loadMovie(const fn_call
& fn
);
59 as_value
movieclip_getURL(const fn_call
& fn
);
60 as_value
movieclip_attachBitmap(const fn_call
& fn
);
61 as_value
movieclip_beginBitmapFill(const fn_call
& fn
);
62 as_value
movieclip_createEmptyMovieClip(const fn_call
& fn
);
63 as_value
movieclip_removeMovieClip(const fn_call
& fn
);
64 as_value
movieclip_curveTo(const fn_call
& fn
);
65 as_value
movieclip_beginFill(const fn_call
& fn
);
66 as_value
movieclip_prevFrame(const fn_call
& fn
);
67 as_value
movieclip_nextFrame(const fn_call
& fn
);
68 as_value
movieclip_endFill(const fn_call
& fn
);
69 as_value
movieclip_clear(const fn_call
& fn
);
70 as_value
movieclip_lineStyle(const fn_call
& fn
);
71 as_value
movieclip_lineTo(const fn_call
& fn
);
72 as_value
movieclip_moveTo(const fn_call
& fn
);
73 as_value
movieclip_beginGradientFill(const fn_call
& fn
);
74 as_value
movieclip_stopDrag(const fn_call
& fn
);
75 as_value
movieclip_startDrag(const fn_call
& fn
);
76 as_value
movieclip_gotoAndStop(const fn_call
& fn
);
77 as_value
movieclip_duplicateMovieClip(const fn_call
& fn
);
78 as_value
movieclip_gotoAndPlay(const fn_call
& fn
);
79 as_value
movieclip_stop(const fn_call
& fn
);
80 as_value
movieclip_play(const fn_call
& fn
);
81 as_value
movieclip_setMask(const fn_call
& fn
);
82 as_value
movieclip_getDepth(const fn_call
& fn
);
83 as_value
movieclip_getBytesTotal(const fn_call
& fn
);
84 as_value
movieclip_getBytesLoaded(const fn_call
& fn
);
85 as_value
movieclip_getBounds(const fn_call
& fn
);
86 as_value
movieclip_hitTest(const fn_call
& fn
);
87 as_value
movieclip_globalToLocal(const fn_call
& fn
);
88 as_value
movieclip_localToGlobal(const fn_call
& fn
);
89 as_value
movieclip_lockroot(const fn_call
& fn
);
90 as_value
movieclip_swapDepths(const fn_call
& fn
);
91 as_value
movieclip_scrollRect(const fn_call
& fn
);
92 as_value
movieclip_getInstanceAtDepth(const fn_call
& fn
);
93 as_value
movieclip_getNextHighestDepth(const fn_call
& fn
);
94 as_value
movieclip_getTextSnapshot(const fn_call
& fn
);
95 as_value
movieclip_tabIndex(const fn_call
& fn
);
96 as_value
movieclip_opaqueBackground(const fn_call
& fn
);
97 as_value
movieclip_filters(const fn_call
& fn
);
98 as_value
movieclip_forceSmoothing(const fn_call
& fn
);
99 as_value
movieclip_cacheAsBitmap(const fn_call
& fn
);
100 as_value
movieclip_lineGradientStyle(const fn_call
& fn
);
101 as_value
movieclip_beginMeshFill(const fn_call
& fn
);
102 as_value
movieclip_getRect(const fn_call
& fn
);
103 as_value
movieclip_meth(const fn_call
& fn
);
104 as_value
movieclip_getSWFVersion(const fn_call
& fn
);
105 as_value
movieclip_loadVariables(const fn_call
& fn
);
109 // extern (used by Global.cpp)
110 // proto = getDisplayObjectContainerInterface();
111 // proto = getDisplayObjectContainerInterface();
113 movieclip_class_init(as_object
& where
, const ObjectURI
& uri
)
115 Global_as
& gl
= getGlobal(where
);
116 as_object
* proto
= createObject(gl
);
118 as_object
* cl
= gl
.createClass(&movieclip_as2_ctor
, proto
);
119 attachMovieClipAS2Interface(*proto
);
121 where
.init_member(uri
, cl
, as_object::DefaultFlags
);
125 registerMovieClipNative(as_object
& where
)
127 VM
& vm
= getVM(where
);
129 vm
.registerNative(movieclip_attachMovie
, 900, 0);
130 vm
.registerNative(movieclip_swapDepths
, 900, 1);
131 vm
.registerNative(movieclip_localToGlobal
, 900, 2);
132 vm
.registerNative(movieclip_globalToLocal
, 900, 3);
133 vm
.registerNative(movieclip_hitTest
, 900, 4);
134 vm
.registerNative(movieclip_getBounds
, 900, 5);
135 vm
.registerNative(movieclip_getBytesTotal
, 900, 6);
136 vm
.registerNative(movieclip_getBytesLoaded
, 900, 7);
137 vm
.registerNative(movieclip_attachAudio
, 900, 8);
138 vm
.registerNative(movieclip_attachVideo
, 900, 9);
139 vm
.registerNative(movieclip_getDepth
, 900, 10);
140 vm
.registerNative(movieclip_setMask
, 900, 11);
141 vm
.registerNative(movieclip_play
, 900, 12);
142 vm
.registerNative(movieclip_stop
, 900, 13);
143 vm
.registerNative(movieclip_nextFrame
, 900, 14);
144 vm
.registerNative(movieclip_prevFrame
, 900, 15);
145 vm
.registerNative(movieclip_gotoAndPlay
, 900, 16);
146 vm
.registerNative(movieclip_gotoAndStop
, 900, 17);
147 vm
.registerNative(movieclip_duplicateMovieClip
, 900, 18);
148 vm
.registerNative(movieclip_removeMovieClip
, 900, 19);
149 vm
.registerNative(movieclip_startDrag
, 900, 20);
150 vm
.registerNative(movieclip_stopDrag
, 900, 21);
151 vm
.registerNative(movieclip_getNextHighestDepth
, 900, 22);
152 vm
.registerNative(movieclip_getInstanceAtDepth
, 900, 23);
153 vm
.registerNative(movieclip_getSWFVersion
, 900, 24);
154 vm
.registerNative(movieclip_attachBitmap
, 900, 25);
155 vm
.registerNative(movieclip_getRect
, 900, 26);
157 vm
.registerNative(movieclip_tabIndex
, 900, 200);
159 vm
.registerNative(movieclip_lockroot
, 900, 300);
161 vm
.registerNative(movieclip_cacheAsBitmap
, 900, 401);
162 vm
.registerNative(movieclip_opaqueBackground
, 900, 402);
163 vm
.registerNative(movieclip_scrollRect
, 900, 403);
165 vm
.registerNative(movieclip_filters
, 900, 417);
166 vm
.registerNative(movieclip_transform
, 900, 418);
167 vm
.registerNative(DisplayObject::blendMode
, 900, 500);
168 vm
.registerNative(movieclip_forceSmoothing
, 900, 502);
170 vm
.registerNative(movieclip_createEmptyMovieClip
, 901, 0);
171 vm
.registerNative(movieclip_beginFill
, 901, 1);
172 vm
.registerNative(movieclip_beginGradientFill
, 901, 2);
173 vm
.registerNative(movieclip_moveTo
, 901, 3);
174 vm
.registerNative(movieclip_lineTo
, 901, 4);
175 vm
.registerNative(movieclip_curveTo
, 901, 5);
176 vm
.registerNative(movieclip_lineStyle
, 901, 6);
177 vm
.registerNative(movieclip_endFill
, 901, 7);
178 vm
.registerNative(movieclip_clear
, 901, 8);
179 vm
.registerNative(movieclip_lineGradientStyle
, 901, 9);
180 vm
.registerNative(movieclip_beginMeshFill
, 901, 10);
181 vm
.registerNative(movieclip_beginBitmapFill
, 901, 11);
182 vm
.registerNative(movieclip_scale9Grid
, 901, 12);
189 // =======================
191 // =======================
193 /// Properties (and/or methods) *inherited* by MovieClip instances
195 attachMovieClipAS2Interface(as_object
& o
)
197 Global_as
& gl
= getGlobal(o
);
200 const int swf6Flags
= as_object::DefaultFlags
| PropFlags::onlySWF6Up
;
201 const int swf7Flags
= as_object::DefaultFlags
| PropFlags::onlySWF7Up
;
202 const int swf8Flags
= as_object::DefaultFlags
| PropFlags::onlySWF8Up
;
204 o
.init_member("attachMovie", vm
.getNative(900, 0));
205 o
.init_member("swapDepths", vm
.getNative(900, 1));
206 o
.init_member("localToGlobal", vm
.getNative(900, 2));
207 o
.init_member("globalToLocal", vm
.getNative(900, 3));
208 o
.init_member("hitTest", vm
.getNative(900, 4));
209 o
.init_member("getBounds", vm
.getNative(900, 5));
210 o
.init_member("getBytesTotal", vm
.getNative(900, 6));
211 o
.init_member("getBytesLoaded", vm
.getNative(900, 7));
212 o
.init_member("attachAudio", vm
.getNative(900, 8), swf6Flags
);
213 o
.init_member("attachVideo", vm
.getNative(900, 9), swf6Flags
);
214 o
.init_member("getDepth", vm
.getNative(900, 10), swf6Flags
);
215 o
.init_member("setMask", vm
.getNative(900, 11), swf6Flags
);
216 o
.init_member("play", vm
.getNative(900, 12));
217 o
.init_member("stop", vm
.getNative(900, 13));
218 o
.init_member("nextFrame", vm
.getNative(900, 14));
219 o
.init_member("prevFrame", vm
.getNative(900, 15));
220 o
.init_member("gotoAndPlay", vm
.getNative(900, 16));
221 o
.init_member("gotoAndStop", vm
.getNative(900, 17));
222 o
.init_member("duplicateMovieClip", vm
.getNative(900, 18));
223 o
.init_member("removeMovieClip", vm
.getNative(900, 19));
224 o
.init_member("startDrag", vm
.getNative(900, 20));
225 o
.init_member("stopDrag", vm
.getNative(900, 21));
226 o
.init_member("getNextHighestDepth", vm
.getNative(900, 22), swf7Flags
);
227 o
.init_member("getInstanceAtDepth", vm
.getNative(900, 23), swf7Flags
);
228 o
.init_member("getSWFVersion", vm
.getNative(900, 24));
229 o
.init_member("attachBitmap", vm
.getNative(900, 25), swf8Flags
);
230 o
.init_member("getRect", vm
.getNative(900, 26), swf8Flags
);
232 o
.init_member("loadMovie", gl
.createFunction(movieclip_loadMovie
));
233 o
.init_member("loadVariables", gl
.createFunction(movieclip_loadVariables
));
234 o
.init_member("unloadMovie", gl
.createFunction( movieclip_unloadMovie
));
235 o
.init_member("getURL", gl
.createFunction(movieclip_getURL
));
236 o
.init_member("meth", gl
.createFunction(movieclip_meth
));
238 o
.init_member("enabled", true);
239 o
.init_member("useHandCursor", true);
241 o
.init_member("createEmptyMovieClip", vm
.getNative(901, 0), swf6Flags
);
242 o
.init_member("beginFill", vm
.getNative(901, 1), swf6Flags
);
243 o
.init_member("beginGradientFill", vm
.getNative(901, 2), swf6Flags
);
244 o
.init_member("moveTo", vm
.getNative(901, 3), swf6Flags
);
245 o
.init_member("lineTo", vm
.getNative(901, 4), swf6Flags
);
246 o
.init_member("curveTo", vm
.getNative(901, 5), swf6Flags
);
247 o
.init_member("lineStyle", vm
.getNative(901, 6), swf6Flags
);
248 o
.init_member("endFill", vm
.getNative(901, 7), swf6Flags
);
249 o
.init_member("clear", vm
.getNative(901, 8), swf6Flags
);
250 o
.init_member("lineGradientStyle", vm
.getNative(901, 9), swf8Flags
);
251 o
.init_member("beginMeshFill", vm
.getNative(901, 10), swf8Flags
);
252 o
.init_member("beginBitmapFill", vm
.getNative(901, 11), swf8Flags
);
256 NativeFunction
* getset
;
258 getset
= vm
.getNative(900, 200);
259 o
.init_property("tabIndex", *getset
, *getset
);
260 getset
= vm
.getNative(900, 300);
261 o
.init_property("_lockroot", *getset
, *getset
);
262 getset
= vm
.getNative(900, 401);
263 o
.init_property("cacheAsBitmap", *getset
, *getset
, swf8Flags
);
264 getset
= vm
.getNative(900, 402);
265 o
.init_property("opaqueBackground", *getset
, *getset
, swf8Flags
);
266 getset
= vm
.getNative(900, 403);
267 o
.init_property("scrollRect", *getset
, *getset
, swf8Flags
);
268 getset
= vm
.getNative(900, 417);
269 o
.init_property("filters", *getset
, *getset
, swf8Flags
);
270 getset
= vm
.getNative(900, 418);
271 o
.init_property("transform", *getset
, *getset
, swf8Flags
);
272 getset
= vm
.getNative(900, 500);
273 o
.init_property("blendMode", *getset
, *getset
, swf8Flags
);
274 getset
= vm
.getNative(900, 502);
275 o
.init_property("forceSmoothing", *getset
, *getset
, swf8Flags
);
276 getset
= vm
.getNative(901, 12);
277 o
.init_property("scale9Grid", *getset
, *getset
, swf8Flags
);
279 // External functions.
280 o
.init_member("createTextField", vm
.getNative(104, 200));
281 o
.init_member("getTextSnapshot",
282 gl
.createFunction(movieclip_getTextSnapshot
), swf6Flags
);
286 //createEmptyMovieClip(name:String, depth:Number) : MovieClip
288 movieclip_createEmptyMovieClip(const fn_call
& fn
)
290 MovieClip
* ptr
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
294 IF_VERBOSE_ASCODING_ERRORS(
295 log_aserror(_("createEmptyMovieClip needs "
296 "2 args, but %d given,"
297 " returning undefined"),
302 IF_VERBOSE_ASCODING_ERRORS(
303 log_aserror(_("createEmptyMovieClip takes "
304 "2 args, but %d given, discarding"
310 Movie
* m
= ptr
->get_root();
311 as_object
* o
= getObjectWithPrototype(getGlobal(fn
), NSV::CLASS_MOVIE_CLIP
);
312 MovieClip
* mc
= new MovieClip(o
, 0, m
, ptr
);
315 mc
->set_name(getURI(vm
, fn
.arg(0).to_string()));
318 // Unlike other MovieClip methods, the depth argument of an empty movie clip
319 // can be any number. All numbers are converted to an int32_t, and are valid
320 // depths even when outside the usual bounds.
321 ptr
->addDisplayListObject(mc
, toInt(fn
.arg(1), getVM(fn
)));
327 movieclip_play(const fn_call
& fn
)
329 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
331 movieclip
->setPlayState(MovieClip::PLAYSTATE_PLAY
);
336 movieclip_stop(const fn_call
& fn
)
338 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
340 movieclip
->setPlayState(MovieClip::PLAYSTATE_STOP
);
346 //removeMovieClip() : Void
348 movieclip_removeMovieClip(const fn_call
& fn
)
350 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
351 movieclip
->removeMovieClip();
357 movieclip_cacheAsBitmap(const fn_call
& fn
)
359 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
361 LOG_ONCE( log_unimpl(_("MovieClip.cacheAsBitmap()")) );
367 movieclip_filters(const fn_call
& fn
)
369 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
373 LOG_ONCE(log_unimpl(_("MovieClip.filters()")));
377 Global_as
& gl
= getGlobal(fn
);
378 as_object
* array
= gl
.createArray();
379 return as_value(array
);
388 movieclip_forceSmoothing(const fn_call
& fn
)
390 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
392 LOG_ONCE(log_unimpl(_("MovieClip.forceSmoothing()")));
398 movieclip_opaqueBackground(const fn_call
& fn
)
400 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
402 LOG_ONCE(log_unimpl(_("MovieClip.opaqueBackground()")));
408 movieclip_scale9Grid(const fn_call
& fn
)
410 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
412 LOG_ONCE(log_unimpl(_("MovieClip.scale9Grid()")));
418 movieclip_scrollRect(const fn_call
& fn
)
420 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
422 LOG_ONCE(log_unimpl(_("MovieClip.scrollRect()")));
428 movieclip_tabIndex(const fn_call
& fn
)
430 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
432 LOG_ONCE(log_unimpl(_("MovieClip.tabIndex()")));
437 // attachMovie(idName:String, newName:String,
438 // depth:Number [, initObject:Object]) : MovieClip
440 movieclip_attachMovie(const fn_call
& fn
)
442 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
444 if (fn
.nargs
< 3 || fn
.nargs
> 4)
446 IF_VERBOSE_ASCODING_ERRORS(
447 log_aserror(_("attachMovie called with wrong number of arguments"
448 " expected 3 to 4, got (%d) - returning undefined"), fn
.nargs
);
453 // Get exported resource
454 const std::string
& id_name
= fn
.arg(0).to_string();
456 SWF::DefinitionTag
* exported_movie
=
457 movieclip
->get_root()->exportedCharacter(id_name
);
461 IF_VERBOSE_ASCODING_ERRORS(
462 log_aserror(_("attachMovie: exported resource '%s' "
463 "is not a DisplayObject definition. Returning undefined"), id_name
);
468 const std::string
& newname
= fn
.arg(1).to_string();
470 // Movies should be attachable from -16384 to 2130690045, according to
471 // kirupa (http://www.kirupa.com/developer/actionscript/depths2.htm)
472 // Tests in misc-ming.all/DepthLimitsTest.c show that 2130690044 is the
473 // maximum valid depth.
474 const double depth
= toNumber(fn
.arg(2), getVM(fn
));
476 // This also checks for overflow, as both numbers are expressible as
478 if (depth
< DisplayObject::lowerAccessibleBound
||
479 depth
> DisplayObject::upperAccessibleBound
)
481 IF_VERBOSE_ASCODING_ERRORS(
482 log_aserror(_("MovieClip.attachMovie: invalid depth %d "
483 "passed; not attaching"), depth
);
488 boost::int32_t depthValue
= static_cast<boost::int32_t>(depth
);
490 Global_as
& gl
= getGlobal(fn
);
491 DisplayObject
* newch
= exported_movie
->createDisplayObject(gl
, movieclip
);
494 newch
->set_name(getURI(vm
, newname
));
497 as_object
* initObj(0);
500 initObj
= toObject(fn
.arg(3), getVM(fn
));
502 // This is actually a valid thing to do,
503 // the documented behaviour is to just NOT
504 // initialize the properties in this
506 IF_VERBOSE_ASCODING_ERRORS(
507 log_aserror(_("Fourth argument of attachMovie doesn't cast to "
508 "an object (%s), we'll act as if it wasn't given"),
514 // placeDisplayObject() will set depth on newch
515 movieclip
->attachCharacter(*newch
, depthValue
, initObj
);
517 return as_value(getObject(newch
));
521 // attachAudio(id:Object) : Void
523 movieclip_attachAudio(const fn_call
& fn
)
525 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
528 IF_VERBOSE_ASCODING_ERRORS(
529 log_aserror("MovieClip.attachAudio(): %s", _("missing arguments"));
535 if (!isNativeType(toObject(fn
.arg(0), getVM(fn
)), ns
)) {
536 std::stringstream ss
; fn
.dump_args(ss
);
537 // TODO: find out what to do here
538 log_error("MovieClip.attachAudio(%s): first arg doesn't cast to a "
539 "NetStream", ss
.str());
543 ns
->setAudioController(movieclip
);
549 // MovieClip.attachVideo
551 movieclip_attachVideo(const fn_call
& fn
)
553 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
556 LOG_ONCE( log_unimpl("MovieClip.attachVideo()") );
562 movieclip_getDepth(const fn_call
& fn
)
564 // Unlike TextField.getDepth this works for any DisplayObject
565 DisplayObject
* d
= ensure
<IsDisplayObject
<> >(fn
);
566 return as_value(d
->get_depth());
569 //swapDepths(target:Object|target:Number)
573 movieclip_swapDepths(const fn_call
& fn
)
575 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
577 const int this_depth
= movieclip
->get_depth();
580 IF_VERBOSE_ASCODING_ERRORS(
581 log_aserror(_("%s.swapDepths() needs one arg"), movieclip
->getTarget());
586 // Lower bound of source depth below which swapDepth has no effect
587 // (below Timeline/static zone)
588 if (this_depth
< DisplayObject::lowerAccessibleBound
) {
589 IF_VERBOSE_ASCODING_ERRORS(
590 std::stringstream ss
;
592 log_aserror(_("%s.swapDepths(%s): won't swap a clip below "
593 "depth %d (%d)"), movieclip
->getTarget(), ss
.str(),
594 DisplayObject::lowerAccessibleBound
,
600 MovieClip
* this_parent
= dynamic_cast<MovieClip
*>(movieclip
->parent());
602 //CharPtr target = NULL;
603 int target_depth
= 0;
605 // movieclip.swapDepth(movieclip)
606 if (MovieClip
* target_movieclip
= fn
.arg(0).toMovieClip()) {
608 if (movieclip
== target_movieclip
) {
609 IF_VERBOSE_ASCODING_ERRORS(
610 log_aserror(_("%s.swapDepths(%s): invalid call, "
611 "swapping to self?"), movieclip
->getTarget(),
612 target_movieclip
->getTarget());
617 MovieClip
* target_parent
=
618 dynamic_cast<MovieClip
*>(movieclip
->parent());
620 if (this_parent
!= target_parent
) {
621 IF_VERBOSE_ASCODING_ERRORS(
622 log_aserror(_("%s.swapDepths(%s): invalid call, the two "
623 "DisplayObjects don't have the same parent"),
624 movieclip
->getTarget(), target_movieclip
->getTarget());
629 target_depth
= target_movieclip
->get_depth();
631 // Check we're not swapping the our own depth so
632 // to avoid unecessary bounds invalidation and immunizing
633 // the instance from subsequent PlaceObject tags attempting
635 if (movieclip
->get_depth() == target_depth
)
637 IF_VERBOSE_ASCODING_ERRORS(
638 std::stringstream ss
; fn
.dump_args(ss
);
639 log_aserror(_("%s.swapDepths(%s): ignored, source and "
640 "target DisplayObjects have the same depth %d"),
641 movieclip
->getTarget(), ss
.str(), target_depth
);
647 // movieclip.swapDepth(depth)
650 const double td
= toNumber(fn
.arg(0), getVM(fn
));
652 IF_VERBOSE_ASCODING_ERRORS(
653 std::stringstream ss
; fn
.dump_args(ss
);
654 log_aserror(_("%s.swapDepths(%s): first argument invalid "
655 "(neither a movieclip nor a number)"),
656 movieclip
->getTarget(), ss
.str());
660 if (td
> DisplayObject::upperAccessibleBound
) {
661 IF_VERBOSE_ASCODING_ERRORS(
662 std::stringstream ss
; fn
.dump_args(ss
);
663 log_aserror(_("%s.swapDepths(%s): requested depth is above "
664 "the accessible range."),
665 movieclip
->getTarget(), ss
.str());
670 target_depth
= static_cast<int>(td
);
672 // Check we're not swapping the our own depth so
673 // to avoid unecessary bounds invalidation and immunizing
674 // the instance from subsequent PlaceObjec tags attempting
676 if (movieclip
->get_depth() == target_depth
) {
677 IF_VERBOSE_ASCODING_ERRORS(
678 std::stringstream ss
; fn
.dump_args(ss
);
679 log_aserror(_("%s.swapDepths(%s): ignored, DisplayObject already "
681 movieclip
->getTarget(), ss
.str(), target_depth
);
685 // TODO : check other kind of validities ?
689 this_parent
->swapDepths(movieclip
, target_depth
);
692 movie_root
& root
= getRoot(fn
);
693 root
.swapLevels(movieclip
, target_depth
);
701 // TODO: wrap the functionality in a MovieClip method
702 // and invoke it from here, this should only be a wrapper
704 //duplicateMovieClip(name:String, depth:Number, [initObject:Object]) : MovieClip
706 movieclip_duplicateMovieClip(const fn_call
& fn
)
708 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
712 IF_VERBOSE_ASCODING_ERRORS(
713 log_aserror(_("MovieClip.duplicateMovieClip() needs 2 or 3 args"));
718 const std::string
& newname
= fn
.arg(0).to_string();
720 // Depth as in attachMovie
721 const double depth
= toNumber(fn
.arg(1), getVM(fn
));
723 // This also checks for overflow, as both numbers are expressible as
725 if (depth
< DisplayObject::lowerAccessibleBound
||
726 depth
> DisplayObject::upperAccessibleBound
)
728 IF_VERBOSE_ASCODING_ERRORS(
729 log_aserror(_("MovieClip.duplicateMovieClip: "
730 "invalid depth %d passed; not duplicating"), depth
);
735 boost::int32_t depthValue
= static_cast<boost::int32_t>(depth
);
739 // Copy members from initObject
742 as_object
* initObject
= toObject(fn
.arg(2), getVM(fn
));
743 ch
= movieclip
->duplicateMovieClip(newname
, depthValue
, initObject
);
747 ch
= movieclip
->duplicateMovieClip(newname
, depthValue
);
750 return as_value(getObject(ch
));
754 movieclip_gotoAndPlay(const fn_call
& fn
)
756 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
760 IF_VERBOSE_ASCODING_ERRORS(
761 log_aserror(_("movieclip_goto_and_play needs one arg"));
767 if ( ! movieclip
->get_frame_number(fn
.arg(0), frame_number
) )
770 IF_VERBOSE_ASCODING_ERRORS(
771 log_aserror(_("movieclip_goto_and_play('%s') -- invalid frame"),
777 // Convert to 0-based
778 movieclip
->goto_frame(frame_number
);
779 movieclip
->setPlayState(MovieClip::PLAYSTATE_PLAY
);
783 as_value
movieclip_gotoAndStop(const fn_call
& fn
)
785 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
789 IF_VERBOSE_ASCODING_ERRORS(
790 log_aserror(_("movieclip_goto_and_stop needs one arg"));
796 if ( ! movieclip
->get_frame_number(fn
.arg(0), frame_number
) )
799 IF_VERBOSE_ASCODING_ERRORS(
800 log_aserror(_("movieclip_goto_and_stop('%s') -- invalid frame"),
806 // Convert to 0-based
807 movieclip
->goto_frame(frame_number
);
808 movieclip
->setPlayState(MovieClip::PLAYSTATE_STOP
);
812 as_value
movieclip_nextFrame(const fn_call
& fn
)
814 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
816 const size_t frame_count
= movieclip
->get_frame_count();
817 const size_t current_frame
= movieclip
->get_current_frame();
818 if (current_frame
< frame_count
)
820 movieclip
->goto_frame(current_frame
+ 1);
822 movieclip
->setPlayState(MovieClip::PLAYSTATE_STOP
);
827 movieclip_prevFrame(const fn_call
& fn
)
829 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
831 const size_t current_frame
= movieclip
->get_current_frame();
832 if (current_frame
> 0)
834 movieclip
->goto_frame(current_frame
- 1);
836 movieclip
->setPlayState(MovieClip::PLAYSTATE_STOP
);
841 movieclip_getBytesLoaded(const fn_call
& fn
)
843 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
845 return as_value(movieclip
->get_bytes_loaded());
849 movieclip_getBytesTotal(const fn_call
& fn
)
851 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
853 return as_value(movieclip
->get_bytes_total());
856 // MovieClip.loadMovie(url:String [,variables:String]).
858 // Returns 1 for "get", 2 for "post", and otherwise 0. Case-insensitive.
859 // This *always* calls MovieClip.meth.
861 movieclip_loadMovie(const fn_call
& fn
)
863 DisplayObject
* dobj
= ensure
<IsDisplayObject
<> >(fn
);
867 val
= callMethod(getObject(dobj
), NSV::PROP_METH
, fn
.arg(1));
869 else val
= callMethod(getObject(dobj
), NSV::PROP_METH
);
871 if (fn
.nargs
< 1) // url
873 IF_VERBOSE_ASCODING_ERRORS(
874 log_aserror(_("MovieClip.loadMovie() "
875 "expected 1 or 2 args, got %d - returning undefined"),
881 const std::string
& urlstr
= fn
.arg(0).to_string();
884 IF_VERBOSE_ASCODING_ERRORS(
885 std::stringstream ss
; fn
.dump_args(ss
);
886 log_aserror(_("First argument of MovieClip.loadMovie(%s) "
887 "evaluates to an empty string - "
888 "returning undefined"),
894 movie_root
& mr
= getRoot(fn
);
895 std::string target
= dobj
->getTarget();
897 // TODO: if GET/POST should send variables of *this* movie,
898 // no matter if the target will be replaced by another movie !!
899 const MovieClip::VariablesMethod method
=
900 static_cast<MovieClip::VariablesMethod
>(toInt(val
, getVM(fn
)));
904 // This is just an optimization if we aren't going
905 // to send the data anyway. It might be wrong, though.
906 if (method
!= MovieClip::METHOD_NONE
) {
907 data
= getURLEncodedVars(*getObject(dobj
));
910 mr
.loadMovie(urlstr
, target
, data
, method
);
915 // my_mc.loadVariables(url:String [, variables:String]) : Void
917 movieclip_loadVariables(const fn_call
& fn
)
919 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
921 as_object
* obj
= getObject(movieclip
);
924 // This always calls MovieClip.meth, even when there are no
929 val
= callMethod(obj
, NSV::PROP_METH
, fn
.arg(1));
931 else val
= callMethod(obj
, NSV::PROP_METH
);
933 if (fn
.nargs
< 1) // url
935 IF_VERBOSE_ASCODING_ERRORS(
936 log_aserror(_("MovieClip.loadVariables() "
937 "expected 1 or 2 args, got %d - returning undefined"),
943 const std::string
& urlstr
= fn
.arg(0).to_string();
946 IF_VERBOSE_ASCODING_ERRORS(
947 std::stringstream ss
; fn
.dump_args(ss
);
948 log_aserror(_("First argument passed to MovieClip.loadVariables(%s) "
949 "evaluates to an empty string - "
950 "returning undefined"),
956 const MovieClip::VariablesMethod method
=
957 static_cast<MovieClip::VariablesMethod
>(toInt(val
, getVM(fn
)));
959 movieclip
->loadVariables(urlstr
, method
);
960 log_debug("MovieClip.loadVariables(%s) - TESTING ", urlstr
);
965 // my_mc.unloadMovie() : Void
967 movieclip_unloadMovie(const fn_call
& fn
)
969 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
971 movieclip
->unloadMovie();
977 movieclip_hitTest(const fn_call
& fn
)
979 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
985 const as_value
& tgt_val
= fn
.arg(0);
986 DisplayObject
* target
= findTarget(fn
.env(), tgt_val
.to_string());
989 IF_VERBOSE_ASCODING_ERRORS(
990 log_aserror(_("Can't find hitTest target %s"),
996 SWFRect thisbounds
= movieclip
->getBounds();
997 const SWFMatrix thismat
= getWorldMatrix(*movieclip
);
998 thismat
.transform(thisbounds
);
1000 SWFRect tgtbounds
= target
->getBounds();
1001 SWFMatrix tgtmat
= getWorldMatrix(*target
);
1002 tgtmat
.transform(tgtbounds
);
1004 return thisbounds
.getRange().intersects(tgtbounds
.getRange());
1011 boost::int32_t x
= pixelsToTwips(toNumber(fn
.arg(0), getVM(fn
)));
1012 boost::int32_t y
= pixelsToTwips(toNumber(fn
.arg(1), getVM(fn
)));
1014 return movieclip
->pointInBounds(x
, y
);
1017 case 3: // x, y, shapeFlag
1019 const boost::int32_t x
= pixelsToTwips(toNumber(fn
.arg(0),
1021 const boost::int32_t y
= pixelsToTwips(toNumber(fn
.arg(1),
1023 const bool shapeFlag
= toBool(fn
.arg(2), getVM(fn
));
1025 if (!shapeFlag
) return movieclip
->pointInBounds(x
, y
);
1026 else return movieclip
->pointInHitableShape(x
, y
);
1031 IF_VERBOSE_ASCODING_ERRORS(
1032 log_aserror(_("hitTest() called with %u args"),
1043 //getNextHighestDepth() : Number
1045 movieclip_getNextHighestDepth(const fn_call
& fn
)
1047 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1049 int nextdepth
= movieclip
->getNextHighestDepth();
1050 return as_value(static_cast<double>(nextdepth
));
1053 //getInstanceAtDepth(depth:Number) : MovieClip
1055 movieclip_getInstanceAtDepth(const fn_call
& fn
)
1057 MovieClip
* mc
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1059 if (fn
.nargs
< 1 || fn
.arg(0).is_undefined()) {
1060 IF_VERBOSE_ASCODING_ERRORS(
1061 log_aserror("MovieClip.getInstanceAtDepth(): missing or "
1062 "undefined depth argument");
1067 const int depth
= toInt(fn
.arg(0), getVM(fn
));
1069 DisplayObject
* ch
= mc
->getDisplayObjectAtDepth(depth
);
1071 // we want 'undefined', not 'null'
1072 if (!ch
) return as_value();
1074 return as_value(getObject(ch
));
1077 /// MovieClip.getURL(url:String[, window:String[, method:String]])
1079 /// Tested manually to function as a method of any as_object. Hard to
1080 /// test automatically as it doesn't return anything and only has external
1084 movieclip_getURL(const fn_call
& fn
)
1086 as_object
* movieclip
= ensure
<ValidThis
>(fn
);
1094 val
= callMethod(movieclip
, NSV::PROP_METH
, fn
.arg(2));
1096 else val
= callMethod(movieclip
, NSV::PROP_METH
);
1102 IF_VERBOSE_ASCODING_ERRORS(
1103 log_aserror(_("No arguments passed to MovieClip.getURL()"));
1109 IF_VERBOSE_ASCODING_ERRORS(
1110 std::ostringstream os
;
1112 log_aserror(_("MovieClip.getURL(%s): extra arguments "
1113 "dropped"), os
.str());
1117 // This argument has already been handled.
1119 target
= fn
.arg(1).to_string();
1121 urlstr
= fn
.arg(0).to_string();
1126 MovieClip::VariablesMethod method
=
1127 static_cast<MovieClip::VariablesMethod
>(toInt(val
, getVM(fn
)));
1131 if (method
!= MovieClip::METHOD_NONE
) {
1132 // Get encoded vars.
1133 vars
= getURLEncodedVars(*movieclip
);
1136 movie_root
& m
= getRoot(fn
);
1138 m
.getURL(urlstr
, target
, vars
, method
);
1143 // getSWFVersion() : Number
1145 movieclip_getSWFVersion(const fn_call
& fn
)
1147 DisplayObject
* o
= get
<DisplayObject
>(fn
.this_ptr
);
1148 if (!o
) return as_value(-1);
1149 return as_value(o
->getDefinitionVersion());
1152 // MovieClip.meth(<string>) : Number
1154 // Parses case-insensitive "get" and "post" into 1 and 2, 0 anything else
1157 movieclip_meth(const fn_call
& fn
)
1160 if (!fn
.nargs
) return as_value(MovieClip::METHOD_NONE
);
1162 as_object
* o
= toObject(fn
.arg(0), getVM(fn
));
1164 return as_value(MovieClip::METHOD_NONE
);
1167 as_value lc
= callMethod(o
, NSV::PROP_TO_LOWER_CASE
);
1169 std::string s
= lc
.to_string();
1171 if (s
== "get") return as_value(MovieClip::METHOD_GET
);
1172 if (s
== "post") return as_value(MovieClip::METHOD_POST
);
1173 return as_value(MovieClip::METHOD_NONE
);
1177 // getTextSnapshot() : TextSnapshot
1179 movieclip_getTextSnapshot(const fn_call
& fn
)
1181 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1183 // If not found, construction fails.
1184 as_value
textSnapshot(findObject(fn
.env(), "TextSnapshot"));
1186 as_function
* tsCtor
= textSnapshot
.to_function();
1189 IF_VERBOSE_ASCODING_ERRORS(
1190 log_aserror("MovieClip.getTextSnapshot: failed to construct "
1191 "TextSnapshot (object probably overridden)");
1196 // Construct a flash.geom.Transform object with "this" as argument.
1198 args
+= getObject(movieclip
);
1200 as_object
* ts
= constructInstance(*tsCtor
, fn
.env(), args
);
1202 return as_value(ts
);
1206 // getBounds(targetCoordinateSpace:Object) : Object
1208 movieclip_getBounds(const fn_call
& fn
)
1210 DisplayObject
* movieclip
= ensure
<IsDisplayObject
<> >(fn
);
1212 SWFRect bounds
= movieclip
->getBounds();
1216 DisplayObject
* target
= fn
.arg(0).toDisplayObject();
1219 IF_VERBOSE_ASCODING_ERRORS(
1220 log_aserror(_("MovieClip.getBounds(%s): invalid call, first "
1221 "arg must be a DisplayObject"),
1227 const SWFMatrix tgtwmat
= getWorldMatrix(*target
).invert();
1228 const SWFMatrix srcwmat
= getWorldMatrix(*movieclip
);
1230 srcwmat
.transform(bounds
);
1231 tgtwmat
.transform(bounds
);
1234 double xMin
, yMin
, xMax
, yMax
;
1236 if (!bounds
.is_null()) {
1237 // Round to the twip
1238 xMin
= twipsToPixels(bounds
.get_x_min());
1239 yMin
= twipsToPixels(bounds
.get_y_min());
1240 xMax
= twipsToPixels(bounds
.get_x_max());
1241 yMax
= twipsToPixels(bounds
.get_y_max());
1244 const double magicMin
= 6710886.35;
1245 xMin
= yMin
= xMax
= yMax
= magicMin
;
1248 // This is a bare object.
1249 as_object
* bounds_obj
= new as_object(getGlobal(fn
));
1250 bounds_obj
->init_member("xMin", xMin
);
1251 bounds_obj
->init_member("yMin", yMin
);
1252 bounds_obj
->init_member("xMax", xMax
);
1253 bounds_obj
->init_member("yMax", yMax
);
1255 return as_value(bounds_obj
);
1259 movieclip_globalToLocal(const fn_call
& fn
)
1261 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1267 IF_VERBOSE_ASCODING_ERRORS(
1268 log_aserror(_("MovieClip.globalToLocal() takes one arg"));
1273 as_object
* obj
= toObject(fn
.arg(0), getVM(fn
));
1275 IF_VERBOSE_ASCODING_ERRORS(
1276 log_aserror(_("MovieClip.globalToLocal(%s): "
1277 "first argument doesn't cast to an object"),
1284 boost::int32_t x
= 0;
1285 boost::int32_t y
= 0;
1287 if ( ! obj
->get_member(NSV::PROP_X
, &tmp
) )
1289 IF_VERBOSE_ASCODING_ERRORS(
1290 log_aserror(_("MovieClip.globalToLocal(%s): "
1291 "object parameter doesn't have an 'x' member"),
1296 x
= pixelsToTwips(toNumber(tmp
, getVM(fn
)));
1298 if ( ! obj
->get_member(NSV::PROP_Y
, &tmp
) )
1300 IF_VERBOSE_ASCODING_ERRORS(
1301 log_aserror(_("MovieClip.globalToLocal(%s): "
1302 "object parameter doesn't have an 'y' member"),
1307 y
= pixelsToTwips(toNumber(tmp
, getVM(fn
)));
1310 const SWFMatrix world_mat
= getWorldMatrix(*movieclip
).invert();
1311 world_mat
.transform(pt
);
1313 obj
->set_member(NSV::PROP_X
, twipsToPixels(pt
.x
));
1314 obj
->set_member(NSV::PROP_Y
, twipsToPixels(pt
.y
));
1320 movieclip_localToGlobal(const fn_call
& fn
)
1322 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1328 IF_VERBOSE_ASCODING_ERRORS(
1329 log_aserror(_("MovieClip.localToGlobal() takes one arg"));
1334 as_object
* obj
= toObject(fn
.arg(0), getVM(fn
));
1336 IF_VERBOSE_ASCODING_ERRORS(
1337 log_aserror(_("MovieClip.localToGlobal(%s): "
1338 "first argument doesn't cast to an object"),
1345 boost::int32_t x
= 0;
1346 boost::int32_t y
= 0;
1348 if (!obj
->get_member(NSV::PROP_X
, &tmp
)) {
1349 IF_VERBOSE_ASCODING_ERRORS(
1350 log_aserror(_("MovieClip.localToGlobal(%s): "
1351 "object parameter doesn't have an 'x' member"),
1356 x
= pixelsToTwips(toNumber(tmp
, getVM(fn
)));
1358 if ( ! obj
->get_member(NSV::PROP_Y
, &tmp
) )
1360 IF_VERBOSE_ASCODING_ERRORS(
1361 log_aserror(_("MovieClip.localToGlobal(%s): "
1362 "object parameter doesn't have an 'y' member"),
1367 y
= pixelsToTwips(toNumber(tmp
, getVM(fn
)));
1370 const SWFMatrix world_mat
= getWorldMatrix(*movieclip
);
1371 world_mat
.transform(pt
);
1373 obj
->set_member(NSV::PROP_X
, twipsToPixels(pt
.x
));
1374 obj
->set_member(NSV::PROP_Y
, twipsToPixels(pt
.y
));
1380 movieclip_setMask(const fn_call
& fn
)
1382 // swfdec/test/image/mask-textfield-6.swf shows that setMask should also
1383 // work against TextFields, we have no tests for other DisplayObject
1384 // types so we generalize it for any DisplayObject.
1385 DisplayObject
* maskee
= ensure
<IsDisplayObject
<> >(fn
);
1389 IF_VERBOSE_ASCODING_ERRORS(
1390 log_aserror(_("%s.setMask() : needs an argument"), maskee
->getTarget());
1395 const as_value
& arg
= fn
.arg(0);
1396 if ( arg
.is_null() || arg
.is_undefined() )
1404 as_object
* obj
= toObject(arg
, getVM(fn
));
1405 DisplayObject
* mask
= get
<DisplayObject
>(obj
);
1408 IF_VERBOSE_ASCODING_ERRORS(
1409 log_aserror(_("%s.setMask(%s) : first argument is not a DisplayObject"),
1410 maskee
->getTarget(), arg
);
1415 // ch is possibly NULL, which is intended
1416 maskee
->setMask(mask
);
1419 //log_debug("MovieClip.setMask() TESTING");
1421 return as_value(true);
1425 movieclip_endFill(const fn_call
& fn
)
1427 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1428 movieclip
->graphics().endFill();
1433 movieclip_lineTo(const fn_call
& fn
)
1435 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1438 IF_VERBOSE_ASCODING_ERRORS(
1439 log_aserror(_("MovieClip.lineTo() needs at least two arguments"));
1444 double x
= toNumber(fn
.arg(0), getVM(fn
));
1445 double y
= toNumber(fn
.arg(1), getVM(fn
));
1447 if (!isFinite(x
)) x
= 0;
1448 if (!isFinite(y
)) y
= 0;
1450 #ifdef DEBUG_DRAWING_API
1451 log_debug("%s.lineTo(%g,%g);", movieclip
->getTarget(), x
, y
);
1453 movieclip
->graphics().lineTo(pixelsToTwips(x
), pixelsToTwips(y
),
1454 movieclip
->getDefinitionVersion());
1459 movieclip_moveTo(const fn_call
& fn
)
1461 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1464 IF_VERBOSE_ASCODING_ERRORS(
1465 log_aserror(_("MovieClip.moveTo() takes two args"));
1470 double x
= toNumber(fn
.arg(0), getVM(fn
));
1471 double y
= toNumber(fn
.arg(1), getVM(fn
));
1473 if (!isFinite(x
)) x
= 0;
1474 if (!isFinite(y
)) y
= 0;
1476 movieclip
->graphics().moveTo(pixelsToTwips(x
), pixelsToTwips(y
));
1480 // SWF6,7: lineStyle(thickness:Number, rgb:Number, alpha:Number) : Void
1482 // SWF8+: lineStyle(thickness:Number, rgb:Number, alpha:Number,
1483 // pixelHinting:Boolean, noScale:String,
1484 // capsStyle:String, jointStyle:String,
1485 // miterLimit:Number) : Void
1487 movieclip_lineStyle(const fn_call
& fn
)
1489 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1492 movieclip
->graphics().resetLineStyle();
1496 boost::uint8_t r
= 0;
1497 boost::uint8_t g
= 0;
1498 boost::uint8_t b
= 0;
1499 boost::uint8_t a
= 255;
1500 boost::uint16_t thickness
= 0;
1501 bool scaleThicknessVertically
= true;
1502 bool scaleThicknessHorizontally
= true;
1503 bool pixelHinting
= false;
1504 bool noClose
= false;
1505 CapStyle capStyle
= CAP_ROUND
;
1506 JoinStyle joinStyle
= JOIN_ROUND
;
1507 float miterLimitFactor
= 1.0f
;
1509 int arguments
= fn
.nargs
;
1511 const int swfVersion
= getSWFVersion(fn
);
1512 if (swfVersion
< 8 && fn
.nargs
> 3) {
1513 IF_VERBOSE_ASCODING_ERRORS(
1514 std::ostringstream ss
;
1516 log_aserror(_("MovieClip.lineStyle(%s): args after the "
1517 "first three will be discarded"), ss
.str());
1522 switch (arguments
) {
1525 miterLimitFactor
= clamp
<int>(toInt(fn
.arg(7), getVM(fn
)), 1, 255);
1528 std::string joinStyleStr
= fn
.arg(6).to_string();
1529 if (joinStyleStr
== "miter") joinStyle
= JOIN_MITER
;
1530 else if (joinStyleStr
== "round") joinStyle
= JOIN_ROUND
;
1531 else if (joinStyleStr
== "bevel") joinStyle
= JOIN_BEVEL
;
1533 IF_VERBOSE_ASCODING_ERRORS(
1534 std::ostringstream ss
;
1536 log_aserror(_("MovieClip.lineStyle(%s): invalid joinStyle"
1537 "value '%s' (valid values: %s|%s|%s)"),
1538 ss
.str(), joinStyleStr
, "miter", "round", "bevel");
1544 const std::string capStyleStr
= fn
.arg(5).to_string();
1545 if (capStyleStr
== "none") capStyle
= CAP_NONE
;
1546 else if (capStyleStr
== "round") capStyle
= CAP_ROUND
;
1547 else if (capStyleStr
== "square") capStyle
= CAP_SQUARE
;
1549 IF_VERBOSE_ASCODING_ERRORS(
1550 std::ostringstream ss
;
1552 log_aserror(_("MovieClip.lineStyle(%s): invalid capStyle "
1553 "value '%s' (valid values: none|round|square)"),
1554 ss
.str(), capStyleStr
);
1560 // Both values to be set here are true, so just set the
1561 // appropriate values to false.
1562 const std::string noScaleString
= fn
.arg(4).to_string();
1563 if (noScaleString
== "none") {
1564 scaleThicknessVertically
= false;
1565 scaleThicknessHorizontally
= false;
1567 else if (noScaleString
== "vertical") {
1568 scaleThicknessVertically
= false;
1570 else if (noScaleString
== "horizontal") {
1571 scaleThicknessHorizontally
= false;
1573 else if (noScaleString
!= "normal") {
1574 IF_VERBOSE_ASCODING_ERRORS(
1575 std::ostringstream ss
;
1577 log_aserror(_("MovieClip.lineStyle(%s): invalid "
1578 "noScale value '%s' (valid values: "
1580 ss
.str(), noScaleString
, "none",
1581 "vertical", "horizontal", "normal");
1586 pixelHinting
= toBool(fn
.arg(3), getVM(fn
));
1589 const float alphaval
= clamp
<float>(toNumber(fn
.arg(2), getVM(fn
)),
1591 a
= boost::uint8_t(255 * (alphaval
/ 100));
1595 // See pollock.swf for eventual regressions.
1596 // It sets color to a random number from
1597 // 0 to 160000000 (about 10 times more then the max).
1598 boost::uint32_t rgbval
= toInt(fn
.arg(1), getVM(fn
));
1599 r
= boost::uint8_t((rgbval
& 0xFF0000) >> 16);
1600 g
= boost::uint8_t((rgbval
& 0x00FF00) >> 8);
1601 b
= boost::uint8_t((rgbval
& 0x0000FF) );
1604 thickness
= boost::uint16_t(pixelsToTwips(clamp
<float>(
1605 toNumber(fn
.arg(0), getVM(fn
)), 0, 255)));
1609 rgba
color(r
, g
, b
, a
);
1611 movieclip
->graphics().lineStyle(thickness
, color
,
1612 scaleThicknessVertically
, scaleThicknessHorizontally
,
1613 pixelHinting
, noClose
, capStyle
, capStyle
, joinStyle
, miterLimitFactor
);
1619 movieclip_curveTo(const fn_call
& fn
)
1621 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1624 IF_VERBOSE_ASCODING_ERRORS(
1625 log_aserror(_("MovieClip.curveTo() takes four args"));
1630 double cx
= toNumber(fn
.arg(0), getVM(fn
));
1631 double cy
= toNumber(fn
.arg(1), getVM(fn
));
1632 double ax
= toNumber(fn
.arg(2), getVM(fn
));
1633 double ay
= toNumber(fn
.arg(3), getVM(fn
));
1635 if (!isFinite(cx
)) cx
= 0;
1636 if (!isFinite(cy
)) cy
= 0;
1637 if (!isFinite(ax
)) ax
= 0;
1638 if (!isFinite(ay
)) ay
= 0;
1640 #ifdef DEBUG_DRAWING_API
1641 log_debug(_("%s.curveTo(%g,%g,%g,%g);"), movieclip
->getTarget(),
1644 movieclip
->graphics().curveTo(pixelsToTwips(cx
), pixelsToTwips(cy
),
1645 pixelsToTwips(ax
), pixelsToTwips(ay
),
1646 movieclip
->getDefinitionVersion());
1652 movieclip_clear(const fn_call
& fn
)
1654 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1655 movieclip
->graphics().clear();
1660 movieclip_beginFill(const fn_call
& fn
)
1662 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1665 IF_VERBOSE_ASCODING_ERRORS(
1666 log_aserror("beginFill() with no args is a no-op");
1671 // 2^24 is the max here
1672 const boost::uint32_t rgbval
=
1673 clamp
<float>(toNumber(fn
.arg(0), getVM(fn
)), 0, 16777216);
1675 const boost::uint8_t r
= (rgbval
& 0xFF0000) >> 16;
1676 const boost::uint8_t g
= (rgbval
& 0x00FF00) >> 8;
1677 const boost::uint8_t b
= rgbval
& 0x0000FF;
1678 boost::uint8_t a
= 255;
1681 a
= 255 * clamp
<int>(toInt(fn
.arg(1), getVM(fn
)), 0, 100) / 100;
1684 rgba
color(r
, g
, b
, a
);
1686 const FillStyle f
= SolidFill(color
);
1687 movieclip
->graphics().beginFill(f
);
1693 /// Create a dynamic gradient fill.
1695 /// fillType, colors, alphas, ratios, matrix, [spreadMethod,
1696 /// [interpolationMethod, [focalPointRatio]]]
1698 /// Colors, alphas and ratios must be (possibly fake) arrays, and must have
1699 /// the same size. This applies even when there are more gradients than
1702 movieclip_beginGradientFill(const fn_call
& fn
)
1704 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1706 // The arguments up to and including matrix must be present.
1708 IF_VERBOSE_ASCODING_ERRORS(
1709 std::stringstream ss
; fn
.dump_args(ss
);
1710 log_aserror(_("%s.beginGradientFill(%s): invalid call: 5 arguments "
1711 "needed"), movieclip
->getTarget(), ss
.str());
1716 const size_t maxargs
= getSWFVersion(fn
) >= 8 ? 8 : 5;
1718 if (fn
.nargs
> maxargs
) {
1719 IF_VERBOSE_ASCODING_ERRORS(
1720 std::stringstream ss
; fn
.dump_args(ss
);
1721 log_aserror(_("%s.beginGradientFill(%s): extra arguments "
1722 "invalidate call!"));
1727 GradientFill::Type t
;
1729 std::string typeStr
= fn
.arg(0).to_string();
1731 // An unexpected fill type results in no fill in all versions.
1732 if (typeStr
== "radial") {
1733 t
= GradientFill::RADIAL
;
1735 else if (typeStr
== "linear") {
1736 t
= GradientFill::LINEAR
;
1739 IF_VERBOSE_ASCODING_ERRORS(
1740 std::stringstream ss
; fn
.dump_args(ss
);
1741 log_aserror(_("%s.beginGradientFill(%s): first arg must be "
1742 "'radial', 'focal', or 'linear'"),
1743 movieclip
->getTarget(), ss
.str());
1748 typedef as_object
* ObjPtr
;
1749 ObjPtr colors
= toObject(fn
.arg(1), getVM(fn
));
1750 ObjPtr alphas
= toObject(fn
.arg(2), getVM(fn
));
1751 ObjPtr ratios
= toObject(fn
.arg(3), getVM(fn
));
1752 ObjPtr matrix
= toObject(fn
.arg(4), getVM(fn
));
1754 if (!colors
|| !alphas
|| !ratios
|| !matrix
) {
1755 IF_VERBOSE_ASCODING_ERRORS(
1756 std::stringstream ss
; fn
.dump_args(ss
);
1757 log_aserror(_("%s.beginGradientFill(%s): one or more of the "
1758 " args from 2nd to 5th don't cast to objects"),
1759 movieclip
->getTarget(), ss
.str());
1764 size_t stops
= arrayLength(*colors
);
1766 // Check that the arrays are all the same size.
1767 if (stops
!= arrayLength(*alphas
) || stops
!= arrayLength(*ratios
)) {
1769 IF_VERBOSE_ASCODING_ERRORS(
1770 std::stringstream ss
; fn
.dump_args(ss
);
1771 log_aserror(_("%s.beginGradientFill(%s): colors, alphas and "
1772 "ratios args don't have same length"),
1773 movieclip
->getTarget(), ss
.str());
1778 // Then limit gradients. It's documented to be a maximum of 15, though
1779 // this isn't tested. The arrays must be the same size regardless of
1782 std::stringstream ss
; fn
.dump_args(ss
);
1783 log_debug(_("%s.beginGradientFill(%s) : too many array elements"
1784 " for colors and ratios (%d), will trim to 8"),
1785 movieclip
->getTarget(), ss
.str(), stops
);
1789 SWFMatrix mat
= toSWFMatrix(*matrix
);
1791 // ----------------------------
1792 // Create the gradients vector
1793 // ----------------------------
1797 std::vector
<GradientRecord
> gradients
;
1798 gradients
.reserve(stops
);
1799 for (size_t i
= 0; i
< stops
; ++i
) {
1801 const ObjectURI
& key
= getURI(vm
, boost::lexical_cast
<std::string
>(i
));
1803 as_value colVal
= getMember(*colors
, key
);
1804 boost::uint32_t col
= colVal
.is_number() ? toInt(colVal
, getVM(fn
)) : 0;
1806 /// Alpha is the range 0..100.
1807 as_value alpVal
= getMember(*alphas
, key
);
1808 const double a
= alpVal
.is_number() ?
1809 clamp
<double>(toNumber(alpVal
, getVM(fn
)), 0, 100) : 0;
1810 const boost::uint8_t alp
= 0xff * (a
/ 100);
1812 // Ratio is the range 0..255, but a ratio may never be smaller than
1813 // the previous value. The pp adjusts it to be greater than the
1814 // last value if it is smaller. But we must be careful not to exceed
1817 // From looking it looks like the minimum adjustment is 2. Even
1818 // steps of 1 appear to be adjusted.
1820 const as_value
& ratVal
= getMember(*ratios
, key
);
1821 const boost::uint32_t minRatio
=
1822 gradients
.empty() ? 0 :
1823 std::min
<boost::uint32_t>(gradients
[i
- 1].ratio
+ step
, 0xff);
1825 boost::uint8_t rat
= ratVal
.is_number() ?
1826 clamp
<boost::uint32_t>(toInt(ratVal
, getVM(fn
)), minRatio
, 0xff)
1829 // The renderer may expect successively larger ratios; failure to
1830 // do this can lead to memory errors.
1831 if (!gradients
.empty()) {
1832 assert((rat
!= 0xff && rat
> gradients
[i
- 1].ratio
) ||
1833 (rat
>= gradients
[i
- 1].ratio
));
1837 color
.parseRGB(col
);
1840 gradients
.push_back(GradientRecord(rat
, color
));
1843 // Make sure we don't try to construct a GradientFill with only 1 stop!
1845 const FillStyle f
= SolidFill(gradients
[0].color
);
1846 movieclip
->graphics().beginFill(f
);
1850 GradientFill
fd(t
, mat
.invert(), gradients
);
1852 // Set spread mode if present. Defaults to "pad", which is GradientFill's
1855 const std::string
& spread
= fn
.arg(5).to_string();
1856 if (spread
== "reflect") fd
.spreadMode
= GradientFill::REFLECT
;
1857 else if (spread
== "repeat") fd
.spreadMode
= GradientFill::REPEAT
;
1858 else assert(fd
.spreadMode
== GradientFill::PAD
);
1862 const std::string
& inter
= fn
.arg(6).to_string();
1863 if (inter
== "rgb") fd
.interpolation
= GradientFill::RGB
;
1864 else if (inter
== "linearRGB") fd
.interpolation
= GradientFill::LINEAR_RGB
;
1865 else assert(fd
.interpolation
== GradientFill::RGB
);
1868 /// Add a focus if present.
1870 fd
.setFocalPoint(toNumber(fn
.arg(7), getVM(fn
)));
1873 movieclip
->graphics().beginFill(fd
);
1878 // startDrag([lockCenter:Boolean], [left:Number], [top:Number],
1879 // [right:Number], [bottom:Number]) : Void`
1881 movieclip_startDrag(const fn_call
& fn
)
1883 MovieClip
* movieclip
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1885 DragState
st(movieclip
);
1887 // mark this DisplayObject is transformed.
1888 movieclip
->transformedByScript();
1891 st
.setLockCentered(toBool(fn
.arg(0), getVM(fn
)));
1894 double x0
= toNumber(fn
.arg(1), getVM(fn
));
1895 double y0
= toNumber(fn
.arg(2), getVM(fn
));
1896 double x1
= toNumber(fn
.arg(3), getVM(fn
));
1897 double y1
= toNumber(fn
.arg(4), getVM(fn
));
1899 // check for infinite values
1900 bool gotinf
= false;
1901 if (!isFinite(x0
)) { x0
=0; gotinf
=true; }
1902 if (!isFinite(y0
)) { y0
=0; gotinf
=true; }
1903 if (!isFinite(x1
)) { x1
=0; gotinf
=true; }
1904 if (!isFinite(y1
)) { y1
=0; gotinf
=true; }
1906 // check for swapped values
1907 bool swapped
= false;
1918 IF_VERBOSE_ASCODING_ERRORS(
1919 if (gotinf
|| swapped
) {
1920 std::stringstream ss
; fn
.dump_args(ss
);
1922 log_aserror(_("min/max bbox values in "
1923 "MovieClip.startDrag(%s) swapped, fixing"),
1927 log_aserror(_("non-finite bbox values in "
1928 "MovieClip.startDrag(%s), took as zero"),
1934 SWFRect
bounds(pixelsToTwips(x0
), pixelsToTwips(y0
),
1935 pixelsToTwips(x1
), pixelsToTwips(y1
));
1936 st
.setBounds(bounds
);
1940 getRoot(fn
).setDragState(st
);
1945 // stopDrag() : Void
1947 movieclip_stopDrag(const fn_call
& fn
)
1949 // Should this be a MovieClip only function? It isn't
1951 getRoot(fn
).stop_drag();
1957 movieclip_beginBitmapFill(const fn_call
& fn
)
1959 MovieClip
* ptr
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
1964 as_object
* obj
= toObject(fn
.arg(0), getVM(fn
));
1967 if (!isNativeType(obj
, bd
) || bd
->disposed()) {
1968 IF_VERBOSE_ASCODING_ERRORS(
1969 log_debug("MovieClip.attachBitmap: first argument should be a "
1970 "valid BitmapData", fn
.arg(1));
1978 as_object
* matrix
= toObject(fn
.arg(1), getVM(fn
));
1980 mat
= toSWFMatrix(*matrix
);
1984 BitmapFill::Type t
= BitmapFill::TILED
;
1986 const bool repeat
= toBool(fn
.arg(2), getVM(fn
));
1987 if (!repeat
) t
= BitmapFill::CLIPPED
;
1990 BitmapFill::SmoothingPolicy p
= BitmapFill::SMOOTHING_OFF
;
1991 if (fn
.nargs
> 3 && toBool(fn
.arg(3), getVM(fn
))) {
1992 p
= BitmapFill::SMOOTHING_ON
;
1995 // This is needed to get the bitmap to the right size and have it in the
1996 // correct place. Maybe it would be better handled somewhere else, as it's
1997 // not exactly intuitive.
1999 mat
.concatenate_scale(1 / 20., 1 / 20.);
2000 mat
.set_x_translation(mat
.tx() / 20);
2001 mat
.set_y_translation(mat
.ty() / 20);
2003 ptr
->graphics().beginFill(BitmapFill(t
, bd
->bitmapInfo(), mat
, p
));
2011 movieclip_getRect(const fn_call
& fn
)
2013 MovieClip
* ptr
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
2015 LOG_ONCE( log_unimpl (__FUNCTION__
) );
2021 movieclip_lineGradientStyle(const fn_call
& fn
)
2023 MovieClip
* ptr
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
2025 LOG_ONCE( log_unimpl (__FUNCTION__
) );
2031 movieclip_attachBitmap(const fn_call
& fn
)
2034 MovieClip
* ptr
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
2037 IF_VERBOSE_ASCODING_ERRORS(
2038 log_debug("MovieClip.attachBitmap: expected 2 args, got %d",
2044 as_object
* obj
= toObject(fn
.arg(0), getVM(fn
));
2047 if (!isNativeType(obj
, bd
) || bd
->disposed()) {
2048 IF_VERBOSE_ASCODING_ERRORS(
2049 log_debug("MovieClip.attachBitmap: first argument should be a "
2050 "valid BitmapData", fn
.arg(1));
2055 int depth
= toInt(fn
.arg(1), getVM(fn
));
2057 DisplayObject
* bm
= new Bitmap(getRoot(fn
), 0, bd
, ptr
);
2058 ptr
->attachCharacter(*bm
, depth
, 0);
2065 movieclip_as2_ctor(const fn_call
& /*fn*/)
2072 movieclip_transform(const fn_call
& fn
)
2074 MovieClip
* ptr
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
2076 // If not found, construction fails.
2077 as_value
transform(findObject(fn
.env(), "flash.geom.Transform"));
2079 as_function
* transCtor
= transform
.to_function();
2082 IF_VERBOSE_ASCODING_ERRORS(
2083 log_aserror("Failed to construct flash.geom.Transform!");
2088 // Construct a flash.geom.Transform object with "this" as argument.
2090 args
+= getObject(ptr
);
2092 as_object
* newTrans
= constructInstance(*transCtor
, fn
.env(), args
);
2094 return as_value(newTrans
);
2098 movieclip_beginMeshFill(const fn_call
& /*fn*/)
2101 LOG_ONCE(log_unimpl("MovieClip.beginMeshFill"));
2107 movieclip_lockroot(const fn_call
& fn
)
2109 MovieClip
* ptr
= ensure
<IsDisplayObject
<MovieClip
> >(fn
);
2112 return as_value(ptr
->getLockRoot());
2115 ptr
->setLockRoot(toBool(fn
.arg(0), getVM(fn
)));
2119 } // anonymous namespace
2120 } // gnash namespace
2124 // indent-tabs-mode: t