use the VERSION instead of master in the revno.h generated from a src tarball
[gnash.git] / libcore / DisplayList.cpp
blob6d2a14c449a60890b0cce952177ca428c15da505
1 // dlist.cpp: Display lists, for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
4 // Foundation, Inc
5 //
6 // This program is free software; you can redistribute it and/or modify
7 // it under the terms of the GNU General Public License as published by
8 // the Free Software Foundation; either version 3 of the License, or
9 // (at your option) any later version.
10 //
11 // This program is distributed in the hope that it will be useful,
12 // but WITHOUT ANY WARRANTY; without even the implied warranty of
13 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 // GNU General Public License for more details.
16 // You should have received a copy of the GNU General Public License
17 // along with this program; if not, write to the Free Software
18 // Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 #include "smart_ptr.h"
22 #include "DisplayList.h"
23 #include "log.h"
24 #include "Renderer.h"
25 #include "StringPredicates.h"
26 #include "MovieClip.h"
27 #include "ObjectURI.h"
29 #include <typeinfo>
30 #include <ostream>
31 #include <algorithm>
32 #include <stack>
33 #include <cassert>
34 #include <boost/bind.hpp>
36 namespace gnash {
38 // Forward declarations
39 namespace {
40 /// Return an iterator to the first element of the container NOT
41 /// in the "removed" depth zone
42 DisplayList::iterator beginNonRemoved(DisplayList::container_type& c);
44 /// Return an constant iterator to the first element of the
45 /// container NOT in the "removed" depth zone
46 DisplayList::const_iterator beginNonRemoved(
47 const DisplayList::container_type& c);
49 /// Return the first element in the DisplayList whose depth exceeds
50 /// 65535 (-16384).
51 DisplayList::iterator dlistTagsEffectiveZoneEnd(
52 DisplayList::container_type& c);
56 /// Anonymous namespace for generic algorithm functors.
57 namespace {
59 class DepthEquals
61 public:
63 DepthEquals(int depth) : _depth(depth) {}
65 bool operator() (const DisplayObject* item) const {
66 if (!item) return false;
67 return item->get_depth() == _depth;
70 private:
71 const int _depth;
74 struct DepthGreaterThan
76 bool operator()(const DisplayObject* a, const DisplayObject* b) const {
77 return a->get_depth() > b->get_depth();
81 struct DepthLessThan
83 bool operator()(const DisplayObject* a, const DisplayObject* b) const {
84 return a->get_depth() < b->get_depth();
88 class DepthGreaterOrEqual
90 public:
92 DepthGreaterOrEqual(int depth) : _depth(depth) {}
94 bool operator() (const DisplayObject* item) const {
95 if (!item) return false;
96 return item->get_depth() >= _depth;
98 private:
99 const int _depth;
103 class NameEquals
105 public:
106 NameEquals(string_table& st, const ObjectURI& uri, bool caseless)
108 _ce(st, caseless),
109 _name(uri)
112 bool operator() (const DisplayObject* item) {
113 assert (item);
115 // TODO: this is necessary because destroy() is called in
116 // movie_root, leaving destroyed items on the DisplayList. They
117 // shouldn't be found. A better fix would be to stop destroying
118 // objects there and add to the invariant that there are never
119 // destroyed DisplayObjects in the DisplayList.
120 if (item->isDestroyed()) return false;
122 return _ce(item->get_name(), _name);
125 private:
126 const ObjectURI::CaseEquals _ce;
127 const ObjectURI& _name;
130 } // anonymous namespace
133 DisplayList::getNextHighestDepth() const
135 testInvariant();
137 int nexthighestdepth=0;
138 for (const_iterator it = _charsByDepth.begin(),
139 itEnd = _charsByDepth.end(); it != itEnd; ++it) {
141 DisplayObject* ch = *it;
143 const int chdepth = ch->get_depth();
144 if (chdepth >= nexthighestdepth) {
145 nexthighestdepth = chdepth+1;
148 return nexthighestdepth;
151 DisplayObject*
152 DisplayList::getDisplayObjectAtDepth(int depth) const
154 testInvariant();
156 for (const_iterator it = _charsByDepth.begin(), itEnd = _charsByDepth.end();
157 it != itEnd; ++it) {
159 DisplayObject* ch = *it;
161 // Should not be there!
162 if (ch->isDestroyed()) continue;
164 // found
165 if (ch->get_depth() == depth) return ch;
167 // non-existent (chars are ordered by depth)
168 if (ch->get_depth() > depth) return 0;
171 return 0;
176 DisplayObject*
177 DisplayList::getDisplayObjectByName(string_table& st, const ObjectURI& uri,
178 bool caseless) const
180 testInvariant();
182 const container_type::const_iterator e = _charsByDepth.end();
184 container_type::const_iterator it =
185 std::find_if(_charsByDepth.begin(), e, NameEquals(st, uri, caseless));
187 if (it == e) return 0;
189 return *it;
193 void
194 DisplayList::placeDisplayObject(DisplayObject* ch, int depth)
196 assert(!ch->unloaded());
197 ch->set_invalidated();
198 ch->set_depth(depth);
200 container_type::iterator it =
201 std::find_if( _charsByDepth.begin(), _charsByDepth.end(),
202 DepthGreaterOrEqual(depth));
204 if (it == _charsByDepth.end() || (*it)->get_depth() != depth) {
205 // add the new char
206 _charsByDepth.insert(it, ch);
208 else {
209 // remember bounds of old char
210 InvalidatedRanges old_ranges;
211 (*it)->add_invalidated_bounds(old_ranges, true);
213 // make a copy (before replacing)
214 DisplayObject* oldCh = *it;
216 // replace existing char (before calling unload!)
217 *it = ch;
219 if (oldCh->unload()) {
220 // reinsert removed DisplayObject if needed
221 reinsertRemovedCharacter(oldCh);
223 else oldCh->destroy();
225 // extend invalidated bounds
226 ch->extend_invalidated_bounds(old_ranges);
229 testInvariant();
232 void
233 DisplayList::add(DisplayObject* ch, bool replace)
235 const int depth = ch->get_depth();
237 container_type::iterator it =
238 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
239 DepthGreaterOrEqual(depth));
241 if (it == _charsByDepth.end() || (*it)->get_depth() != depth) {
242 _charsByDepth.insert(it, ch);
244 else if (replace) *it = ch;
246 testInvariant();
249 void
250 DisplayList::replaceDisplayObject(DisplayObject* ch, int depth,
251 bool use_old_cxform, bool use_old_matrix)
253 testInvariant();
255 //GNASH_REPORT_FUNCTION;
256 assert(!ch->unloaded());
258 ch->set_invalidated();
259 ch->set_depth(depth);
261 container_type::iterator it =
262 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
263 DepthGreaterOrEqual(depth));
265 if (it == _charsByDepth.end() || (*it)->get_depth() != depth) {
266 _charsByDepth.insert(it, ch);
268 else {
269 // Make a copy (before replacing)
270 DisplayObject* oldch = *it;
272 InvalidatedRanges old_ranges;
274 if (use_old_cxform) {
275 // Use the SWFCxForm from the old DisplayObject.
276 ch->setCxForm(getCxForm(*oldch));
279 if (use_old_matrix) {
280 // Use the SWFMatrix from the old DisplayObject.
281 ch->setMatrix(getMatrix(*oldch), true);
284 // remember bounds of old char
285 oldch->add_invalidated_bounds(old_ranges, true);
287 // replace existing char (before calling unload)
288 *it = ch;
290 // Unload old char
291 if (oldch->unload()) {
292 // reinsert removed DisplayObject if needed
293 reinsertRemovedCharacter(oldch);
295 else oldch->destroy();
297 // extend invalidated bounds
298 // WARNING: when a new Button DisplayObject is added,
299 // the invalidated bounds computation will likely
300 // be bogus, as the actual DisplayObject shown is not
301 // instantiated until stagePlacementCallback for buttons
302 // (I'd say this is a bug in Button).
303 ch->extend_invalidated_bounds(old_ranges);
307 testInvariant();
311 // Updates the transform properties of the DisplayObject at
312 // the specified depth.
313 void
314 DisplayList::moveDisplayObject(int depth, const SWFCxForm* color_xform,
315 const SWFMatrix* mat, boost::uint16_t* ratio)
317 testInvariant();
319 DisplayObject* ch = getDisplayObjectAtDepth(depth);
320 if (! ch) {
321 // FIXME, should this be log_aserror?
322 IF_VERBOSE_MALFORMED_SWF(
323 log_swferror(_("moveDisplayObject() -- can't find object at depth %d"),
324 depth);
326 return;
329 if (ch->unloaded()) {
330 log_error("Request to move an unloaded DisplayObject");
331 assert(!ch->unloaded());
334 // TODO: is sign of depth related to accepting anim moves ?
335 if (!ch->get_accept_anim_moves()) {
336 // This DisplayObject is rejecting anim moves. This happens after it
337 // has been manipulated by ActionScript.
338 return;
341 if (color_xform) ch->setCxForm(*color_xform);
342 if (mat) ch->setMatrix(*mat, true);
343 if (ratio) ch->set_ratio(*ratio);
345 testInvariant();
349 // Removes the object at the specified depth.
350 void
351 DisplayList::removeDisplayObject(int depth)
353 testInvariant();
355 #ifndef NDEBUG
356 container_type::size_type size = _charsByDepth.size();
357 #endif
359 // TODO: would it be legal to call removeDisplayObject with a depth
360 // in the "removed" zone ?
361 // TODO: optimize to take by-depth order into account
362 container_type::iterator it =
363 std::find_if( _charsByDepth.begin(), _charsByDepth.end(),
364 DepthEquals(depth));
366 if (it != _charsByDepth.end()) {
367 // Make a copy (before erasing)
368 DisplayObject* oldCh = *it;
370 // Erase (before calling unload)
371 _charsByDepth.erase(it);
373 if (oldCh->unload()) {
374 // reinsert removed DisplayObject if needed
375 // NOTE: could be optimized if we knew exactly how
376 // to handle the case in which the target depth
377 // (after the shift) is occupied already
379 reinsertRemovedCharacter(oldCh);
381 else oldCh->destroy();
384 assert(size >= _charsByDepth.size());
386 testInvariant();
390 // TODO: take DisplayObject by ref ?
391 void
392 DisplayList::swapDepths(DisplayObject* ch1, int newdepth)
394 testInvariant();
396 if (newdepth < DisplayObject::staticDepthOffset) {
397 IF_VERBOSE_ASCODING_ERRORS(
398 log_aserror("%s.swapDepth(%d) : ignored call with target depth "
399 "less then %d", ch1->getTarget(), newdepth,
400 DisplayObject::staticDepthOffset);
402 return;
405 const int srcdepth = ch1->get_depth();
407 // what if source char is at a lower depth ?
408 assert(srcdepth >= DisplayObject::staticDepthOffset);
410 assert(srcdepth != newdepth);
412 // TODO: optimize this scan by taking ch1 depth into account ?
413 container_type::iterator it1 =
414 std::find(_charsByDepth.begin(), _charsByDepth.end(), ch1);
416 // upper bound ...
417 container_type::iterator it2 =
418 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
419 DepthGreaterOrEqual(newdepth));
421 if (it1 == _charsByDepth.end()) {
422 log_error("First argument to DisplayList::swapDepth() "
423 "is NOT a DisplayObject in the list. Call ignored.");
424 return;
427 // Found another DisplayObject at the given depth
428 if (it2 != _charsByDepth.end() && (*it2)->get_depth() == newdepth) {
429 DisplayObject* ch2 = *it2;
430 ch2->set_depth(srcdepth);
432 // TODO: we're not actually invalidated ourselves, rather
433 // our parent is...
434 ch2->set_invalidated();
436 // We won't accept static transforms after a depth swap.
437 // See displaylist_depths_test6.swf for more info.
438 ch2->transformedByScript();
440 std::iter_swap(it1, it2);
442 else {
443 // No DisplayObject found at the given depth
444 // Move the DisplayObject to the new position
445 // NOTE: insert *before* erasing, in case the list is
446 // the only referer of the ref-counted DisplayObject
447 _charsByDepth.insert(it2, ch1);
448 _charsByDepth.erase(it1);
451 // don't change depth before the iter_swap case above, as
452 // we'll need it to assign to the new DisplayObject
453 ch1->set_depth(newdepth);
455 // TODO: we're not actually invalidated ourselves, rather our parent is...
456 // UdoG ? Want to verify this ?
457 ch1->set_invalidated();
459 // We won't accept static transforms after a depth swap.
460 // See displaylist_depths_test6.swf for more info.
461 ch1->transformedByScript();
463 testInvariant();
466 DisplayObject*
467 DisplayList::removeDisplayObjectAt(int index)
469 container_type::iterator it =
470 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
471 DepthEquals(index));
473 if (it == _charsByDepth.end()) return 0;
475 DisplayObject* obj = *it;
476 _charsByDepth.erase(it);
477 return obj;
480 void
481 DisplayList::removeDisplayObject(DisplayObject* obj)
483 container_type::iterator it = std::find(_charsByDepth.begin(),
484 _charsByDepth.end(), obj);
486 if (it != _charsByDepth.end()) {
487 _charsByDepth.erase(it);
491 void
492 DisplayList::insertDisplayObject(DisplayObject* obj, int index)
494 testInvariant();
496 assert(!obj->unloaded());
498 obj->set_invalidated();
499 obj->set_depth(index);
501 // Find the first index greater than or equal to the required index
502 container_type::iterator it =
503 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
504 DepthGreaterOrEqual(index));
506 // Insert the DisplayObject before that position
507 _charsByDepth.insert(it, obj);
509 // Shift depths upwards until no depths are duplicated. No DisplayObjects
510 // are removed!
511 while (it != _charsByDepth.end() && (*it)->get_depth() == index) {
512 (*it)->set_depth(index + 1);
513 ++index, ++it;
516 testInvariant();
519 void
520 DisplayList::addDisplayObject(DisplayObject* obj)
522 testInvariant();
524 assert(!obj->unloaded());
526 obj->set_invalidated();
527 if (_charsByDepth.empty()) {
528 obj->set_depth(0);
530 else {
531 container_type::const_reverse_iterator it = _charsByDepth.rbegin();
532 obj->set_depth((*it)->get_depth() + 1);
535 // Insert the DisplayObject at the end
536 _charsByDepth.insert(_charsByDepth.end(), obj);
538 testInvariant();
542 bool
543 DisplayList::unload()
545 testInvariant();
547 bool unloadHandler = false;
549 // All children with an unload handler should be unloaded. As soon as
550 // the first unload handler is encountered, subsequent children should
551 // not be destroyed or removed from the display list. This affects
552 // children without an unload handler.
553 for (iterator it = beginNonRemoved(_charsByDepth),
554 itEnd = _charsByDepth.end(); it != itEnd; ) {
555 // make a copy
556 DisplayObject* di = *it;
558 // Destroyed objects should not be there!
559 assert(!di->isDestroyed());
561 // Destroy those with a handler anyway?
562 if (di->unload()) {
563 unloadHandler = true;
564 ++it;
565 continue;
568 if (!unloadHandler) {
569 di->destroy();
570 it = _charsByDepth.erase(it);
572 else ++it;
575 testInvariant();
577 return unloadHandler;
581 void
582 DisplayList::destroy()
584 testInvariant();
586 for (iterator it = _charsByDepth.begin(), itEnd = _charsByDepth.end();
587 it != itEnd; ) {
589 // make a copy
590 DisplayObject* di = *it;
592 // skip if already unloaded
593 if ( di->isDestroyed() ) {
594 ++it;
595 continue;
598 di->destroy();
599 it = _charsByDepth.erase(it);
601 testInvariant();
604 // Display the referenced DisplayObjects. Lower depths
605 // are obscured by higher depths.
606 void
607 DisplayList::display(Renderer& renderer, const Transform& base)
609 testInvariant();
611 std::stack<int> clipDepthStack;
613 // We only display DisplayObjects which are out of the "removed" zone
614 // (or should we check unloaded?)
615 iterator it = beginNonRemoved(_charsByDepth);
616 for (iterator endIt = _charsByDepth.end(); it != endIt; ++it) {
617 DisplayObject* ch = *it;
618 assert(!ch->isDestroyed());
620 // Don't display dynamic masks
621 if (ch->isDynamicMask()) continue;
623 assert(!ch->unloaded()); // we don't advance unloaded chars
625 // Check if this charater or any of its parents is a mask.
626 // Characters acting as masks should always be rendered to the
627 // mask buffer despite their visibility.
629 DisplayObject* p = ch->parent();
630 bool renderAsMask = ch->isMaskLayer();
632 while (!renderAsMask && p) {
633 renderAsMask = p->isMaskLayer();
634 p = p->parent();
637 // check for non-mask hiden DisplayObjects
638 if (!renderAsMask && (!ch->visible())) {
639 ch->omit_display();
640 // Don't display non-mask hidden DisplayObjects
641 continue;
644 const int depth = ch->get_depth();
645 // Discard useless masks
646 while (!clipDepthStack.empty() && (depth > clipDepthStack.top())) {
647 clipDepthStack.pop();
648 renderer.disable_mask();
651 // Push a new mask to the masks stack
652 if (ch->isMaskLayer()) {
653 const int clipDepth = ch->get_clip_depth();
654 clipDepthStack.push(clipDepth);
655 renderer.begin_submit_mask();
658 if (ch->boundsInClippingArea(renderer)) {
659 ch->display(renderer, base);
661 else ch->omit_display();
663 // Notify the renderer that mask drawing has finished.
664 if (ch->isMaskLayer()) renderer.end_submit_mask();
667 // Discard any remaining masks
668 while (!clipDepthStack.empty()) {
669 clipDepthStack.pop();
670 renderer.disable_mask();
674 void
675 DisplayList::omit_display()
677 iterator it = beginNonRemoved(_charsByDepth);
678 for (iterator endIt = _charsByDepth.end(); it != endIt; ++it) {
679 DisplayObject* ch = *it;
680 ch->omit_display();
684 void
685 DisplayList::dump() const
687 if (_charsByDepth.empty()) return;
689 string_table& st = getStringTable(*getObject(_charsByDepth.front()));
690 ObjectURI::Logger l(st);
692 int num=0;
693 for (const_iterator it = _charsByDepth.begin(),
694 endIt = _charsByDepth.end(); it != endIt; ++it) {
696 const DisplayObject* dobj = *it;
697 log_debug(_("Item %d(%s) at depth %d (char name %s, type %s)"
698 "Destroyed: %s, unloaded: %s"),
699 num, dobj, dobj->get_depth(), l.debug(dobj->get_name()), typeName(*dobj),
700 dobj->isDestroyed(), dobj->unloaded());
701 num++;
706 void
707 DisplayList::add_invalidated_bounds(InvalidatedRanges& ranges, bool force)
709 testInvariant();
711 // This function generally has nothing else to do than calling the
712 // add_invalidated_bounds() function of all items in the display list.
713 // However, special optimization is included for masks, which makes it
714 // look a bit more complicated. We want to avoid that a masked DisplayObject
715 // invalidates an area where the DisplayObject is invisible because of it's
716 // mask (which is quite common). For example, think of a large bitmap that
717 // covers the entire stage and is masked by a very small circle in the
718 // middle of the stage (ie. it's only visible there). Changes in the
719 // bitmap sprite would invalidate the whole stage even if only the small
720 // masked portion in the middle really needs to be drawn again.
722 // So, like display(), we keep a stack of masks. Instead of drawing the
723 // mask we keep a separate list of InvalidatedRanges for the masks which
724 // later are intersected with the masked DisplayObjects' InvalidatedRanges.
726 // The code is much based on the display() function, so some references
727 // in comments have been added for convenience.
729 // For a simpler implementation (that doesn't care about masks, but
730 // still produces correct results) see CVS revision 1.96
732 // TODO: review this function to take "dynamic" mask and maskees
733 // into account
735 std::stack<int> clipDepthStack; // same method used in display()
736 std::stack<InvalidatedRanges> rangesStack;
737 bool drawing_mask = false;
739 iterator it = beginNonRemoved(_charsByDepth);
740 for (iterator endIt = _charsByDepth.end(); it != endIt; ++it) {
741 DisplayObject* dobj = *it;
743 const int depth = dobj->get_depth();
745 // Discard useless masks
746 while (!clipDepthStack.empty() && (depth > clipDepthStack.top())) {
747 clipDepthStack.pop();
748 rangesStack.pop(); // disable_mask() equivalent
752 // Push a new mask to the masks stack
753 if (dobj->isMaskLayer()) {
755 const int clipDepth = dobj->get_clip_depth();
756 clipDepthStack.push(clipDepth);
758 drawing_mask = true; // begin_submit_mask equivalent
760 if (rangesStack.empty()) {
761 InvalidatedRanges item;
762 rangesStack.push(item);
764 else {
765 // copy the top mask
766 rangesStack.push(rangesStack.top());
770 if (drawing_mask) {
772 // --> The child is part of a mask, so add ranges to our
773 // mask ranges stack
775 assert(!rangesStack.empty());
776 dobj->add_invalidated_bounds(rangesStack.top(), true);
778 // need to call add_invalidated_bounds again because the previous
779 // call needs force==true. Changes to the mask itself may also
780 // require re-rendering of the mask area, so we have to
781 // add the mask itself to the global ranges, but this time
782 // with normal "force" value...
783 // As long the mask has not been invalidated and force==false this
784 // call won't modify the "ranges" list.
785 dobj->add_invalidated_bounds(ranges, force);
787 else {
789 if (rangesStack.empty()) {
790 // --> normal case for unmasked DisplayObjects
791 dobj->add_invalidated_bounds(ranges, force);
793 else {
794 // --> DisplayObject is masked, so intersect with "mask"
796 // first get the ranges of the child in a separate list
797 InvalidatedRanges childRanges;
798 childRanges.inheritConfig(ranges);
800 dobj->add_invalidated_bounds(childRanges, force);
802 // then intersect ranges with topmost "mask"
803 childRanges.intersect(rangesStack.top());
805 // add result to the global ranges
806 ranges.add(childRanges);
808 } // not drawing mask
810 // <== end of display() equivalent
811 // Mask "drawing" has finished
812 if (dobj->isMaskLayer()) {
813 // end_submit_mask equivalent
814 drawing_mask = false;
819 void
820 DisplayList::mergeDisplayList(DisplayList& newList)
822 testInvariant();
824 iterator itOld = beginNonRemoved(_charsByDepth);
825 iterator itNew = beginNonRemoved(newList._charsByDepth);
827 iterator itOldEnd = dlistTagsEffectiveZoneEnd(_charsByDepth);
829 // There used to be an assertion here that no character in the new list
830 // is at depth 65535 or higher. There's no reason why the tags executed
831 // on the new list shouldn't do this though. Bug #29282 does this.
832 // TODO: check whether we should be ignoring that character.
833 iterator itNewEnd = dlistTagsEffectiveZoneEnd(newList._charsByDepth);
835 // step1.
836 // starting scanning both lists.
837 while (itOld != itOldEnd) {
839 iterator itOldBackup = itOld;
841 DisplayObject* chOld = *itOldBackup;
842 int depthOld = chOld->get_depth();
844 while (itNew != itNewEnd) {
845 iterator itNewBackup = itNew;
847 DisplayObject* chNew = *itNewBackup;
848 int depthNew = chNew->get_depth();
850 // depth in old list is occupied, and empty in new list.
851 if (depthOld < depthNew) {
853 itOld++;
854 // unload the DisplayObject if it's in static zone(-16384,0)
855 if (depthOld < 0) {
856 _charsByDepth.erase(itOldBackup);
858 if (chOld->unload()) reinsertRemovedCharacter(chOld);
859 else chOld->destroy();
862 break;
864 // depth is occupied in both lists
866 if (depthOld == depthNew) {
867 ++itOld;
868 ++itNew;
870 bool is_ratio_compatible =
871 (chOld->get_ratio() == chNew->get_ratio());
873 if (!is_ratio_compatible || chOld->isDynamic() ||
874 !isReferenceable(*chOld)) {
875 // replace the DisplayObject in old list with
876 // corresponding DisplayObject in new list
877 _charsByDepth.insert(itOldBackup, *itNewBackup);
878 _charsByDepth.erase(itOldBackup);
880 // unload the old DisplayObject
881 if (chOld->unload()) reinsertRemovedCharacter(chOld);
882 else chOld->destroy();
884 else {
885 newList._charsByDepth.erase(itNewBackup);
887 // replace the transformation SWFMatrix if the old
888 // DisplayObject accepts static transformation.
889 if (chOld->get_accept_anim_moves()) {
890 chOld->setMatrix(getMatrix(*chNew), true);
891 chOld->setCxForm(getCxForm(*chNew));
893 chNew->unload();
894 chNew->destroy();
897 break;
900 // depth in old list is empty, but occupied in new list.
901 ++ itNew;
902 // add the new DisplayObject to the old list.
903 _charsByDepth.insert(itOldBackup, *itNewBackup );
906 // break if finish scanning the new list
907 if (itNew == itNewEnd) break;
910 // step2(only required if scanning of new list finished earlier in step1).
911 // continue to scan the static zone of the old list.
912 // unload remaining DisplayObjects directly.
913 while((itOld != itOldEnd) && ((*itOld)->get_depth() < 0)) {
915 DisplayObject* chOld = *itOld;
916 itOld = _charsByDepth.erase(itOld);
918 if (chOld->unload()) reinsertRemovedCharacter(chOld);
919 else chOld->destroy();
922 // step3(only required if scanning of old list finished earlier in step1).
923 // continue to scan the new list.
924 // add remaining DisplayObjects directly.
925 if (itNew != itNewEnd) _charsByDepth.insert(itOld, itNew, itNewEnd);
927 // step4.
928 // Copy all unloaded DisplayObjects from the new display list to the
929 // old display list, and clear the new display list
930 for (itNew = newList._charsByDepth.begin(); itNew != itNewEnd; ++itNew) {
932 DisplayObject* chNew = *itNew;
933 int depthNew = chNew->get_depth();
935 if (chNew->unloaded()) {
936 iterator it =
937 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
938 DepthGreaterOrEqual(depthNew));
940 _charsByDepth.insert(it, *itNew);
944 // clear the new display list after merge
945 // ASSERT:
946 // - Any element in newList._charsByDepth is either marked as unloaded
947 // or found in this list
948 #if GNASH_PARANOIA_LEVEL > 1
949 for (iterator i = newList._charsByDepth.begin(),
950 e = newList._charsByDepth.end(); i != e; ++i) {
952 DisplayObject* ch = *i;
953 if (!ch->unloaded()) {
955 iterator found =
956 std::find(_charsByDepth.begin(), _charsByDepth.end(), ch);
958 if (found == _charsByDepth.end())
960 log_error("mergeDisplayList: DisplayObject %s (%s at depth "
961 "%d [%d]) about to be discarded in given display list"
962 " is not marked as unloaded and not found in the"
963 " merged current displaylist",
964 ch->getTarget(), typeName(*ch), ch->get_depth(),
965 ch->get_depth()-DisplayObject::staticDepthOffset);
966 std::abort();
970 #endif
971 newList._charsByDepth.clear();
973 testInvariant();
977 void
978 DisplayList::reinsertRemovedCharacter(DisplayObject* ch)
980 assert(ch->unloaded());
981 assert(!ch->isDestroyed());
982 testInvariant();
984 // TODO: have this done by DisplayObject::unload() instead ?
985 int oldDepth = ch->get_depth();
986 int newDepth = DisplayObject::removedDepthOffset - oldDepth;
987 ch->set_depth(newDepth);
989 // TODO: optimize this by searching from the end(lowest depth).
990 container_type::iterator it =
991 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
992 DepthGreaterOrEqual(newDepth));
994 _charsByDepth.insert(it, ch);
996 testInvariant();
999 void
1000 DisplayList::removeUnloaded()
1002 testInvariant();
1004 _charsByDepth.remove_if(boost::mem_fn(&DisplayObject::unloaded));
1006 testInvariant();
1010 #if GNASH_PARANOIA_LEVEL > 1 && !defined(NDEBUG)
1011 DisplayList::const_iterator
1012 DisplayList::nonRemoved() const
1014 return beginNonRemoved(_charsByDepth);
1016 #endif
1018 namespace {
1020 DisplayList::iterator
1021 beginNonRemoved(DisplayList::container_type& c)
1023 const int depth = DisplayObject::removedDepthOffset -
1024 DisplayObject::staticDepthOffset;
1026 return std::find_if(c.begin(), c.end(), DepthGreaterOrEqual(depth));
1029 DisplayList::const_iterator
1030 beginNonRemoved(const DisplayList::container_type& c)
1032 const int depth = DisplayObject::removedDepthOffset -
1033 DisplayObject::staticDepthOffset;
1035 return std::find_if(c.begin(), c.end(), DepthGreaterOrEqual(depth));
1038 DisplayList::iterator
1039 dlistTagsEffectiveZoneEnd(DisplayList::container_type& c)
1041 return std::find_if(c.begin(), c.end(),
1042 DepthGreaterOrEqual(0xffff + DisplayObject::staticDepthOffset));
1045 } // anonymous namespace
1048 std::ostream&
1049 operator<<(std::ostream& os, const DisplayList& dl)
1051 os << "By depth: ";
1053 if (dl._charsByDepth.empty()) return os;
1055 string_table& st = getStringTable(*getObject(dl._charsByDepth.front()));
1056 ObjectURI::Logger l(st);
1058 for (DisplayList::const_iterator it = dl._charsByDepth.begin(),
1059 itEnd = dl._charsByDepth.end(); it != itEnd; ++it) {
1061 const DisplayObject* item = *it;
1062 if (it != dl._charsByDepth.begin()) os << " | ";
1063 os << " name:" << l.debug(item->get_name())
1064 << " depth:" << item->get_depth();
1067 return os;
1070 } // namespace gnash
1073 // Local Variables:
1074 // mode: C++
1075 // indent-tabs-mode: t
1076 // End: