update copyright date
[gnash.git] / libcore / asobj / MovieClip_as.cpp
blob5aa6244b6085052652bf5fb0cf97438cbbe3aada
1 // MovieClip_as.cpp: ActionScript "MovieClip" class, for Gnash.
2 //
3 // Copyright (C) 2009, 2010, 2011 Free Software Foundation, Inc.
4 //
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.
9 //
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"
25 #include "Movie.h"
26 #include "display/BitmapData_as.h"
27 #include "NetStream_as.h"
28 #include "movie_root.h"
29 #include "GnashNumeric.h"
30 #include "as_value.h"
31 #include "log.h"
32 #include "fn_call.h"
33 #include "Global_as.h"
34 #include "smart_ptr.h"
35 #include "NativeFunction.h"
36 #include "Bitmap.h"
37 #include "Array_as.h"
38 #include "FillStyle.h"
39 #include "namedStrings.h"
40 #include "Renderer.h"
41 #include "RunResources.h"
42 #include "ASConversions.h"
44 namespace gnash {
46 // Forward declarations
47 namespace {
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();
112 void
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);
124 void
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);
187 namespace {
189 // =======================
190 // AS2 interface
191 // =======================
193 /// Properties (and/or methods) *inherited* by MovieClip instances
194 void
195 attachMovieClipAS2Interface(as_object& o)
197 Global_as& gl = getGlobal(o);
198 VM& vm = getVM(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);
254 // Accessors
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
287 as_value
288 movieclip_createEmptyMovieClip(const fn_call& fn)
290 MovieClip* ptr = ensure<IsDisplayObject<MovieClip> >(fn);
292 if (fn.nargs != 2) {
293 if (fn.nargs < 2) {
294 IF_VERBOSE_ASCODING_ERRORS(
295 log_aserror(_("createEmptyMovieClip needs "
296 "2 args, but %d given,"
297 " returning undefined"),
298 fn.nargs);
300 return as_value();
302 IF_VERBOSE_ASCODING_ERRORS(
303 log_aserror(_("createEmptyMovieClip takes "
304 "2 args, but %d given, discarding"
305 " the excess"),
306 fn.nargs);
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);
314 VM& vm = getVM(fn);
315 mc->set_name(getURI(vm, fn.arg(0).to_string()));
316 mc->setDynamic();
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)));
322 return as_value(o);
326 as_value
327 movieclip_play(const fn_call& fn)
329 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
331 movieclip->setPlayState(MovieClip::PLAYSTATE_PLAY);
332 return as_value();
335 as_value
336 movieclip_stop(const fn_call& fn)
338 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
340 movieclip->setPlayState(MovieClip::PLAYSTATE_STOP);
342 return as_value();
346 //removeMovieClip() : Void
347 as_value
348 movieclip_removeMovieClip(const fn_call& fn)
350 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
351 movieclip->removeMovieClip();
352 return as_value();
356 as_value
357 movieclip_cacheAsBitmap(const fn_call& fn)
359 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
360 UNUSED(movieclip);
361 LOG_ONCE( log_unimpl(_("MovieClip.cacheAsBitmap()")) );
362 return as_value();
366 as_value
367 movieclip_filters(const fn_call& fn)
369 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
371 UNUSED(movieclip);
373 LOG_ONCE(log_unimpl(_("MovieClip.filters()")));
375 if (!fn.nargs) {
376 // Getter
377 Global_as& gl = getGlobal(fn);
378 as_object* array = gl.createArray();
379 return as_value(array);
382 // Setter
383 return as_value();
387 as_value
388 movieclip_forceSmoothing(const fn_call& fn)
390 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
391 UNUSED(movieclip);
392 LOG_ONCE(log_unimpl(_("MovieClip.forceSmoothing()")));
393 return as_value();
397 as_value
398 movieclip_opaqueBackground(const fn_call& fn)
400 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
401 UNUSED(movieclip);
402 LOG_ONCE(log_unimpl(_("MovieClip.opaqueBackground()")));
403 return as_value();
407 as_value
408 movieclip_scale9Grid(const fn_call& fn)
410 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
411 UNUSED(movieclip);
412 LOG_ONCE(log_unimpl(_("MovieClip.scale9Grid()")));
413 return as_value();
417 as_value
418 movieclip_scrollRect(const fn_call& fn)
420 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
421 UNUSED(movieclip);
422 LOG_ONCE(log_unimpl(_("MovieClip.scrollRect()")));
423 return as_value();
427 as_value
428 movieclip_tabIndex(const fn_call& fn)
430 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
431 UNUSED(movieclip);
432 LOG_ONCE(log_unimpl(_("MovieClip.tabIndex()")));
433 return as_value();
437 // attachMovie(idName:String, newName:String,
438 // depth:Number [, initObject:Object]) : MovieClip
439 as_value
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);
450 return as_value();
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);
459 if (!exported_movie)
461 IF_VERBOSE_ASCODING_ERRORS(
462 log_aserror(_("attachMovie: exported resource '%s' "
463 "is not a DisplayObject definition. Returning undefined"), id_name);
465 return as_value();
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
477 // boost::int32_t.
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);
485 return as_value();
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);
493 VM& vm = getVM(fn);
494 newch->set_name(getURI(vm, newname));
495 newch->setDynamic();
497 as_object* initObj(0);
499 if (fn.nargs > 3 ) {
500 initObj = toObject(fn.arg(3), getVM(fn));
501 if (!initObj) {
502 // This is actually a valid thing to do,
503 // the documented behaviour is to just NOT
504 // initialize the properties in this
505 // case.
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"),
509 fn.arg(3));
514 // placeDisplayObject() will set depth on newch
515 movieclip->attachCharacter(*newch, depthValue, initObj);
517 return as_value(getObject(newch));
521 // attachAudio(id:Object) : Void
522 as_value
523 movieclip_attachAudio(const fn_call& fn)
525 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
527 if (!fn.nargs) {
528 IF_VERBOSE_ASCODING_ERRORS(
529 log_aserror("MovieClip.attachAudio(): %s", _("missing arguments"));
531 return as_value();
534 NetStream_as* ns;
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());
540 return as_value();
543 ns->setAudioController(movieclip);
545 return as_value();
549 // MovieClip.attachVideo
550 as_value
551 movieclip_attachVideo(const fn_call& fn)
553 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
554 UNUSED(movieclip);
556 LOG_ONCE( log_unimpl("MovieClip.attachVideo()") );
557 return as_value();
561 as_value
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)
571 // Returns void.
572 as_value
573 movieclip_swapDepths(const fn_call& fn)
575 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
577 const int this_depth = movieclip->get_depth();
579 if (fn.nargs < 1) {
580 IF_VERBOSE_ASCODING_ERRORS(
581 log_aserror(_("%s.swapDepths() needs one arg"), movieclip->getTarget());
583 return as_value();
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;
591 fn.dump_args(ss);
592 log_aserror(_("%s.swapDepths(%s): won't swap a clip below "
593 "depth %d (%d)"), movieclip->getTarget(), ss.str(),
594 DisplayObject::lowerAccessibleBound,
595 this_depth);
597 return as_value();
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());
614 return as_value();
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());
626 return as_value();
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
634 // to transform it.
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);
643 return as_value();
647 // movieclip.swapDepth(depth)
648 else {
650 const double td = toNumber(fn.arg(0), getVM(fn));
651 if (isNaN(td)) {
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());
658 return as_value();
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());
667 return as_value();
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
675 // to transform it.
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 "
680 "at depth %d"),
681 movieclip->getTarget(), ss.str(), target_depth);
683 return as_value();
685 // TODO : check other kind of validities ?
688 if (this_parent) {
689 this_parent->swapDepths(movieclip, target_depth);
691 else {
692 movie_root& root = getRoot(fn);
693 root.swapLevels(movieclip, target_depth);
694 return as_value();
697 return as_value();
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
705 as_value
706 movieclip_duplicateMovieClip(const fn_call& fn)
708 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
710 if (fn.nargs < 2)
712 IF_VERBOSE_ASCODING_ERRORS(
713 log_aserror(_("MovieClip.duplicateMovieClip() needs 2 or 3 args"));
715 return as_value();
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
724 // boost::int32_t.
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);
732 return as_value();
735 boost::int32_t depthValue = static_cast<boost::int32_t>(depth);
737 MovieClip* ch;
739 // Copy members from initObject
740 if (fn.nargs == 3)
742 as_object* initObject = toObject(fn.arg(2), getVM(fn));
743 ch = movieclip->duplicateMovieClip(newname, depthValue, initObject);
745 else
747 ch = movieclip->duplicateMovieClip(newname, depthValue);
750 return as_value(getObject(ch));
753 as_value
754 movieclip_gotoAndPlay(const fn_call& fn)
756 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
758 if (fn.nargs < 1)
760 IF_VERBOSE_ASCODING_ERRORS(
761 log_aserror(_("movieclip_goto_and_play needs one arg"));
763 return as_value();
766 size_t frame_number;
767 if ( ! movieclip->get_frame_number(fn.arg(0), frame_number) )
769 // No dice.
770 IF_VERBOSE_ASCODING_ERRORS(
771 log_aserror(_("movieclip_goto_and_play('%s') -- invalid frame"),
772 fn.arg(0));
774 return as_value();
777 // Convert to 0-based
778 movieclip->goto_frame(frame_number);
779 movieclip->setPlayState(MovieClip::PLAYSTATE_PLAY);
780 return as_value();
783 as_value movieclip_gotoAndStop(const fn_call& fn)
785 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
787 if (fn.nargs < 1)
789 IF_VERBOSE_ASCODING_ERRORS(
790 log_aserror(_("movieclip_goto_and_stop needs one arg"));
792 return as_value();
795 size_t frame_number;
796 if ( ! movieclip->get_frame_number(fn.arg(0), frame_number) )
798 // No dice.
799 IF_VERBOSE_ASCODING_ERRORS(
800 log_aserror(_("movieclip_goto_and_stop('%s') -- invalid frame"),
801 fn.arg(0));
803 return as_value();
806 // Convert to 0-based
807 movieclip->goto_frame(frame_number);
808 movieclip->setPlayState(MovieClip::PLAYSTATE_STOP);
809 return as_value();
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);
823 return as_value();
826 as_value
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);
837 return as_value();
840 as_value
841 movieclip_getBytesLoaded(const fn_call& fn)
843 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
845 return as_value(movieclip->get_bytes_loaded());
848 as_value
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.
860 as_value
861 movieclip_loadMovie(const fn_call& fn)
863 DisplayObject* dobj = ensure<IsDisplayObject<> >(fn);
865 as_value val;
866 if (fn.nargs > 1) {
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"),
876 fn.nargs);
878 return as_value();
881 const std::string& urlstr = fn.arg(0).to_string();
882 if (urlstr.empty())
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"),
889 ss.str());
891 return as_value();
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)));
902 std::string data;
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);
912 return as_value();
915 // my_mc.loadVariables(url:String [, variables:String]) : Void
916 as_value
917 movieclip_loadVariables(const fn_call& fn)
919 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
921 as_object* obj = getObject(movieclip);
922 assert(obj);
924 // This always calls MovieClip.meth, even when there are no
925 // arguments.
926 as_value val;
927 if (fn.nargs > 1)
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"),
938 fn.nargs);
940 return as_value();
943 const std::string& urlstr = fn.arg(0).to_string();
944 if (urlstr.empty())
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"),
951 ss.str());
953 return as_value();
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);
962 return as_value();
965 // my_mc.unloadMovie() : Void
966 as_value
967 movieclip_unloadMovie(const fn_call& fn)
969 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
971 movieclip->unloadMovie();
973 return as_value();
976 as_value
977 movieclip_hitTest(const fn_call& fn)
979 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
981 switch (fn.nargs)
983 case 1: // target
985 const as_value& tgt_val = fn.arg(0);
986 DisplayObject* target = findTarget(fn.env(), tgt_val.to_string());
987 if ( ! target )
989 IF_VERBOSE_ASCODING_ERRORS(
990 log_aserror(_("Can't find hitTest target %s"),
991 tgt_val);
993 return as_value();
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());
1006 break;
1009 case 2: // x, y
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),
1020 getVM(fn)));
1021 const boost::int32_t y = pixelsToTwips(toNumber(fn.arg(1),
1022 getVM(fn)));
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);
1029 default:
1031 IF_VERBOSE_ASCODING_ERRORS(
1032 log_aserror(_("hitTest() called with %u args"),
1033 fn.nargs);
1035 break;
1039 return as_value();
1043 //getNextHighestDepth() : Number
1044 as_value
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
1054 as_value
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");
1064 return as_value();
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
1081 /// side-effects.
1082 /// Returns void.
1083 as_value
1084 movieclip_getURL(const fn_call& fn)
1086 as_object* movieclip = ensure<ValidThis>(fn);
1088 std::string urlstr;
1089 std::string target;
1091 as_value val;
1092 if (fn.nargs > 2)
1094 val = callMethod(movieclip, NSV::PROP_METH, fn.arg(2));
1096 else val = callMethod(movieclip, NSV::PROP_METH);
1098 switch (fn.nargs)
1100 case 0:
1102 IF_VERBOSE_ASCODING_ERRORS(
1103 log_aserror(_("No arguments passed to MovieClip.getURL()"));
1105 return as_value();
1107 default:
1109 IF_VERBOSE_ASCODING_ERRORS(
1110 std::ostringstream os;
1111 fn.dump_args(os);
1112 log_aserror(_("MovieClip.getURL(%s): extra arguments "
1113 "dropped"), os.str());
1116 case 3:
1117 // This argument has already been handled.
1118 case 2:
1119 target = fn.arg(1).to_string();
1120 case 1:
1121 urlstr = fn.arg(0).to_string();
1122 break;
1126 MovieClip::VariablesMethod method =
1127 static_cast<MovieClip::VariablesMethod>(toInt(val, getVM(fn)));
1129 std::string vars;
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);
1140 return as_value();
1143 // getSWFVersion() : Number
1144 as_value
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
1156 as_value
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));
1163 if (!o) {
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
1178 as_value
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();
1188 if (!tsCtor) {
1189 IF_VERBOSE_ASCODING_ERRORS(
1190 log_aserror("MovieClip.getTextSnapshot: failed to construct "
1191 "TextSnapshot (object probably overridden)");
1193 return as_value();
1196 // Construct a flash.geom.Transform object with "this" as argument.
1197 fn_call::Args args;
1198 args += getObject(movieclip);
1200 as_object* ts = constructInstance(*tsCtor, fn.env(), args);
1202 return as_value(ts);
1206 // getBounds(targetCoordinateSpace:Object) : Object
1207 as_value
1208 movieclip_getBounds(const fn_call& fn)
1210 DisplayObject* movieclip = ensure<IsDisplayObject<> >(fn);
1212 SWFRect bounds = movieclip->getBounds();
1214 if ( fn.nargs > 0 )
1216 DisplayObject* target = fn.arg(0).toDisplayObject();
1217 if ( ! target )
1219 IF_VERBOSE_ASCODING_ERRORS(
1220 log_aserror(_("MovieClip.getBounds(%s): invalid call, first "
1221 "arg must be a DisplayObject"),
1222 fn.arg(0));
1224 return as_value();
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());
1243 else {
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);
1258 as_value
1259 movieclip_globalToLocal(const fn_call& fn)
1261 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
1263 as_value ret;
1265 if ( fn.nargs < 1 )
1267 IF_VERBOSE_ASCODING_ERRORS(
1268 log_aserror(_("MovieClip.globalToLocal() takes one arg"));
1270 return ret;
1273 as_object* obj = toObject(fn.arg(0), getVM(fn));
1274 if (!obj) {
1275 IF_VERBOSE_ASCODING_ERRORS(
1276 log_aserror(_("MovieClip.globalToLocal(%s): "
1277 "first argument doesn't cast to an object"),
1278 fn.arg(0));
1280 return ret;
1283 as_value tmp;
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"),
1292 fn.arg(0));
1294 return ret;
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"),
1303 fn.arg(0));
1305 return ret;
1307 y = pixelsToTwips(toNumber(tmp, getVM(fn)));
1309 point pt(x, y);
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));
1316 return ret;
1319 as_value
1320 movieclip_localToGlobal(const fn_call& fn)
1322 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
1324 as_value ret;
1326 if ( fn.nargs < 1 )
1328 IF_VERBOSE_ASCODING_ERRORS(
1329 log_aserror(_("MovieClip.localToGlobal() takes one arg"));
1331 return ret;
1334 as_object* obj = toObject(fn.arg(0), getVM(fn));
1335 if (!obj) {
1336 IF_VERBOSE_ASCODING_ERRORS(
1337 log_aserror(_("MovieClip.localToGlobal(%s): "
1338 "first argument doesn't cast to an object"),
1339 fn.arg(0));
1341 return ret;
1344 as_value tmp;
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"),
1352 fn.arg(0));
1354 return ret;
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"),
1363 fn.arg(0));
1365 return ret;
1367 y = pixelsToTwips(toNumber(tmp, getVM(fn)));
1369 point pt(x, y);
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));
1375 return ret;
1379 as_value
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);
1387 if ( ! fn.nargs )
1389 IF_VERBOSE_ASCODING_ERRORS(
1390 log_aserror(_("%s.setMask() : needs an argument"), maskee->getTarget());
1392 return as_value();
1395 const as_value& arg = fn.arg(0);
1396 if ( arg.is_null() || arg.is_undefined() )
1398 // disable mask
1399 maskee->setMask(0);
1401 else
1404 as_object* obj = toObject(arg, getVM(fn));
1405 DisplayObject* mask = get<DisplayObject>(obj);
1406 if (!mask)
1408 IF_VERBOSE_ASCODING_ERRORS(
1409 log_aserror(_("%s.setMask(%s) : first argument is not a DisplayObject"),
1410 maskee->getTarget(), arg);
1412 return as_value();
1415 // ch is possibly NULL, which is intended
1416 maskee->setMask(mask);
1419 //log_debug("MovieClip.setMask() TESTING");
1421 return as_value(true);
1424 as_value
1425 movieclip_endFill(const fn_call& fn)
1427 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
1428 movieclip->graphics().endFill();
1429 return as_value();
1432 as_value
1433 movieclip_lineTo(const fn_call& fn)
1435 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
1437 if (fn.nargs < 2) {
1438 IF_VERBOSE_ASCODING_ERRORS(
1439 log_aserror(_("MovieClip.lineTo() needs at least two arguments"));
1441 return as_value();
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);
1452 #endif
1453 movieclip->graphics().lineTo(pixelsToTwips(x), pixelsToTwips(y),
1454 movieclip->getDefinitionVersion());
1455 return as_value();
1458 as_value
1459 movieclip_moveTo(const fn_call& fn)
1461 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
1463 if (fn.nargs < 2) {
1464 IF_VERBOSE_ASCODING_ERRORS(
1465 log_aserror(_("MovieClip.moveTo() takes two args"));
1467 return as_value();
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));
1477 return as_value();
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
1486 as_value
1487 movieclip_lineStyle(const fn_call& fn)
1489 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
1491 if (!fn.nargs) {
1492 movieclip->graphics().resetLineStyle();
1493 return as_value();
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;
1515 fn.dump_args(ss);
1516 log_aserror(_("MovieClip.lineStyle(%s): args after the "
1517 "first three will be discarded"), ss.str());
1519 arguments = 3;
1522 switch (arguments) {
1523 default:
1524 case 8:
1525 miterLimitFactor = clamp<int>(toInt(fn.arg(7), getVM(fn)), 1, 255);
1526 case 7:
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;
1532 else {
1533 IF_VERBOSE_ASCODING_ERRORS(
1534 std::ostringstream ss;
1535 fn.dump_args(ss);
1536 log_aserror(_("MovieClip.lineStyle(%s): invalid joinStyle"
1537 "value '%s' (valid values: %s|%s|%s)"),
1538 ss.str(), joinStyleStr, "miter", "round", "bevel");
1542 case 6:
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;
1548 else {
1549 IF_VERBOSE_ASCODING_ERRORS(
1550 std::ostringstream ss;
1551 fn.dump_args(ss);
1552 log_aserror(_("MovieClip.lineStyle(%s): invalid capStyle "
1553 "value '%s' (valid values: none|round|square)"),
1554 ss.str(), capStyleStr);
1558 case 5:
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;
1576 fn.dump_args(ss);
1577 log_aserror(_("MovieClip.lineStyle(%s): invalid "
1578 "noScale value '%s' (valid values: "
1579 "%s|%s|%s|%s)"),
1580 ss.str(), noScaleString, "none",
1581 "vertical", "horizontal", "normal");
1585 case 4:
1586 pixelHinting = toBool(fn.arg(3), getVM(fn));
1587 case 3:
1589 const float alphaval = clamp<float>(toNumber(fn.arg(2), getVM(fn)),
1590 0, 100);
1591 a = boost::uint8_t(255 * (alphaval / 100));
1593 case 2:
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) );
1603 case 1:
1604 thickness = boost::uint16_t(pixelsToTwips(clamp<float>(
1605 toNumber(fn.arg(0), getVM(fn)), 0, 255)));
1606 break;
1609 rgba color(r, g, b, a);
1611 movieclip->graphics().lineStyle(thickness, color,
1612 scaleThicknessVertically, scaleThicknessHorizontally,
1613 pixelHinting, noClose, capStyle, capStyle, joinStyle, miterLimitFactor);
1615 return as_value();
1618 as_value
1619 movieclip_curveTo(const fn_call& fn)
1621 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
1623 if (fn.nargs < 4) {
1624 IF_VERBOSE_ASCODING_ERRORS(
1625 log_aserror(_("MovieClip.curveTo() takes four args"));
1627 return as_value();
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(),
1642 cx, cy, ax, ay);
1643 #endif
1644 movieclip->graphics().curveTo(pixelsToTwips(cx), pixelsToTwips(cy),
1645 pixelsToTwips(ax), pixelsToTwips(ay),
1646 movieclip->getDefinitionVersion());
1648 return as_value();
1651 as_value
1652 movieclip_clear(const fn_call& fn)
1654 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
1655 movieclip->graphics().clear();
1656 return as_value();
1659 as_value
1660 movieclip_beginFill(const fn_call& fn)
1662 MovieClip* movieclip = ensure<IsDisplayObject<MovieClip> >(fn);
1664 if (fn.nargs < 1) {
1665 IF_VERBOSE_ASCODING_ERRORS(
1666 log_aserror("beginFill() with no args is a no-op");
1668 return as_value();
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;
1680 if (fn.nargs > 1) {
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);
1689 return as_value();
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
1700 /// allowed.
1701 as_value
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.
1707 if (fn.nargs < 5) {
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());
1713 return as_value();
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!"));
1724 return as_value();
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;
1738 else {
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());
1745 return as_value();
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());
1761 return as_value();
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());
1775 return as_value();
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
1780 // this limit.
1781 if (stops > 15) {
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);
1786 stops = 15;
1789 SWFMatrix mat = toSWFMatrix(*matrix);
1791 // ----------------------------
1792 // Create the gradients vector
1793 // ----------------------------
1795 VM& vm = getVM(fn);
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
1815 // 0xff.
1817 // From looking it looks like the minimum adjustment is 2. Even
1818 // steps of 1 appear to be adjusted.
1819 const int step = 2;
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)
1827 : minRatio;
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));
1836 rgba color;
1837 color.parseRGB(col);
1838 color.m_a = alp;
1840 gradients.push_back(GradientRecord(rat, color));
1843 // Make sure we don't try to construct a GradientFill with only 1 stop!
1844 if (stops < 2) {
1845 const FillStyle f = SolidFill(gradients[0].color);
1846 movieclip->graphics().beginFill(f);
1847 return as_value();
1850 GradientFill fd(t, mat.invert(), gradients);
1852 // Set spread mode if present. Defaults to "pad", which is GradientFill's
1853 // default.
1854 if (fn.nargs > 5) {
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);
1861 if (fn.nargs > 6) {
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.
1869 if (fn.nargs > 7) {
1870 fd.setFocalPoint(toNumber(fn.arg(7), getVM(fn)));
1873 movieclip->graphics().beginFill(fd);
1875 return as_value();
1878 // startDrag([lockCenter:Boolean], [left:Number], [top:Number],
1879 // [right:Number], [bottom:Number]) : Void`
1880 as_value
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();
1890 if (fn.nargs) {
1891 st.setLockCentered(toBool(fn.arg(0), getVM(fn)));
1893 if (fn.nargs > 4) {
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;
1908 if (y1 < y0) {
1909 std::swap(y1, y0);
1910 swapped = true;
1913 if (x1 < x0) {
1914 std::swap(x1, x0);
1915 swapped = true;
1918 IF_VERBOSE_ASCODING_ERRORS(
1919 if (gotinf || swapped) {
1920 std::stringstream ss; fn.dump_args(ss);
1921 if (swapped) {
1922 log_aserror(_("min/max bbox values in "
1923 "MovieClip.startDrag(%s) swapped, fixing"),
1924 ss.str());
1926 if (gotinf) {
1927 log_aserror(_("non-finite bbox values in "
1928 "MovieClip.startDrag(%s), took as zero"),
1929 ss.str());
1934 SWFRect bounds(pixelsToTwips(x0), pixelsToTwips(y0),
1935 pixelsToTwips(x1), pixelsToTwips(y1));
1936 st.setBounds(bounds);
1940 getRoot(fn).setDragState(st);
1942 return as_value();
1945 // stopDrag() : Void
1946 as_value
1947 movieclip_stopDrag(const fn_call& fn)
1949 // Should this be a MovieClip only function? It isn't
1950 // necessary.
1951 getRoot(fn).stop_drag();
1952 return as_value();
1956 as_value
1957 movieclip_beginBitmapFill(const fn_call& fn)
1959 MovieClip* ptr = ensure<IsDisplayObject<MovieClip> >(fn);
1960 if (fn.nargs < 1) {
1961 return as_value();
1964 as_object* obj = toObject(fn.arg(0), getVM(fn));
1965 BitmapData_as* bd;
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));
1972 return as_value();
1975 SWFMatrix mat;
1977 if (fn.nargs > 1) {
1978 as_object* matrix = toObject(fn.arg(1), getVM(fn));
1979 if (matrix) {
1980 mat = toSWFMatrix(*matrix);
1984 BitmapFill::Type t = BitmapFill::TILED;
1985 if (fn.nargs > 2) {
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.
1998 mat.invert();
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));
2004 bd->attach(ptr);
2006 return as_value();
2010 as_value
2011 movieclip_getRect(const fn_call& fn)
2013 MovieClip* ptr = ensure<IsDisplayObject<MovieClip> >(fn);
2014 UNUSED(ptr);
2015 LOG_ONCE( log_unimpl (__FUNCTION__) );
2016 return as_value();
2020 as_value
2021 movieclip_lineGradientStyle(const fn_call& fn)
2023 MovieClip* ptr = ensure<IsDisplayObject<MovieClip> >(fn);
2024 UNUSED(ptr);
2025 LOG_ONCE( log_unimpl (__FUNCTION__) );
2026 return as_value();
2030 as_value
2031 movieclip_attachBitmap(const fn_call& fn)
2034 MovieClip* ptr = ensure<IsDisplayObject<MovieClip> >(fn);
2036 if (fn.nargs < 2) {
2037 IF_VERBOSE_ASCODING_ERRORS(
2038 log_debug("MovieClip.attachBitmap: expected 2 args, got %d",
2039 fn.nargs);
2041 return as_value();
2044 as_object* obj = toObject(fn.arg(0), getVM(fn));
2045 BitmapData_as* bd;
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));
2052 return as_value();
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);
2060 return as_value();
2064 as_value
2065 movieclip_as2_ctor(const fn_call& /*fn*/)
2067 return as_value();
2071 as_value
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();
2081 if (!transCtor) {
2082 IF_VERBOSE_ASCODING_ERRORS(
2083 log_aserror("Failed to construct flash.geom.Transform!");
2085 return as_value();
2088 // Construct a flash.geom.Transform object with "this" as argument.
2089 fn_call::Args args;
2090 args += getObject(ptr);
2092 as_object* newTrans = constructInstance(*transCtor, fn.env(), args);
2094 return as_value(newTrans);
2097 as_value
2098 movieclip_beginMeshFill(const fn_call& /*fn*/)
2101 LOG_ONCE(log_unimpl("MovieClip.beginMeshFill"));
2102 return as_value();
2106 as_value
2107 movieclip_lockroot(const fn_call& fn)
2109 MovieClip* ptr = ensure<IsDisplayObject<MovieClip> >(fn);
2111 if (!fn.nargs) {
2112 return as_value(ptr->getLockRoot());
2115 ptr->setLockRoot(toBool(fn.arg(0), getVM(fn)));
2116 return as_value();
2119 } // anonymous namespace
2120 } // gnash namespace
2122 // local Variables:
2123 // mode: C++
2124 // indent-tabs-mode: t
2125 // End: