Drop the bool operator for ObjectURI, to avoid getting the kind of side-effect that...
[gnash.git] / libcore / DisplayList.cpp
blob7044bf47f4291f83bac716644537fe2beee88a05
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" // GNASH_USE_GC
22 #include "DisplayList.h"
23 #include "log.h"
24 #include "Renderer.h"
25 #include "StringPredicates.h"
26 #include "MovieClip.h"
28 #include <typeinfo>
29 #include <iostream>
30 #include <algorithm>
31 #include <stack>
32 #include <cassert>
33 #include <boost/bind.hpp>
35 namespace gnash {
37 // Forward declarations
38 namespace {
39 /// Return an iterator to the first element of the container NOT
40 /// in the "removed" depth zone
41 DisplayList::iterator beginNonRemoved(DisplayList::container_type& c);
43 /// Return an constant iterator to the first element of the
44 /// container NOT in the "removed" depth zone
45 DisplayList::const_iterator beginNonRemoved(
46 const DisplayList::container_type& c);
48 /// Return an iterator succeeding the last element in zone
49 /// (-16384, 0xffff-16384)
50 DisplayList::iterator dlistTagsEffectiveZoneEnd(
51 DisplayList::container_type& c);
53 /// Return an constant iterator succeeding the last element
54 /// in (-16384, 0xffff-16384)
55 DisplayList::const_iterator dlistTagsEffectiveZoneEnd(
56 const DisplayList::container_type& c);
59 /// Anonymous namespace for generic algorithm functors.
60 namespace {
62 class DepthEquals
64 public:
66 DepthEquals(int depth) : _depth(depth) {}
68 bool operator() (const DisplayObject* item) {
69 if (!item) return false;
70 return item->get_depth() == _depth;
73 private:
74 int _depth;
77 struct DepthGreaterThan
79 bool operator()(const DisplayObject* a, const DisplayObject* b) {
80 return a->get_depth() > b->get_depth();
84 struct DepthLessThan
86 bool operator()(const DisplayObject* a, const DisplayObject* b) {
87 return a->get_depth() < b->get_depth();
91 class DepthGreaterOrEqual
93 public:
95 DepthGreaterOrEqual(int depth) : _depth(depth) {}
97 bool operator() (const DisplayObject* item) {
98 if (!item) return false;
99 return item->get_depth() >= _depth;
101 private:
102 int _depth;
106 class NameEquals
108 public:
109 NameEquals(string_table& st, const ObjectURI& uri, bool caseless)
111 _st(st),
112 _caseless(caseless),
113 _name(uri)
116 bool operator() (const DisplayObject* item) {
117 assert (item);
119 // TODO: this is necessary because destroy() is called in
120 // movie_root, leaving destroyed items on the DisplayList. They
121 // shouldn't be found. A better fix would be to stop destroying
122 // objects there and add to the invariant that there are never
123 // destroyed DisplayObjects in the DisplayList.
124 if (item->isDestroyed()) return false;
126 if ( _caseless ) {
127 return equalsNoCase(_st, item->get_name(), _name);
128 } else {
129 return item->get_name() == _name;
133 private:
134 string_table& _st;
135 const bool _caseless;
136 const ObjectURI& _name;
139 } // anonymous namespace
142 DisplayList::getNextHighestDepth() const
144 testInvariant();
146 int nexthighestdepth=0;
147 for (const_iterator it = _charsByDepth.begin(),
148 itEnd = _charsByDepth.end(); it != itEnd; ++it) {
150 DisplayObject* ch = *it;
152 int chdepth = ch->get_depth();
153 if (chdepth >= nexthighestdepth) {
154 nexthighestdepth = chdepth+1;
157 return nexthighestdepth;
160 DisplayObject*
161 DisplayList::getDisplayObjectAtDepth(int depth) const
163 testInvariant();
165 for (const_iterator it = _charsByDepth.begin(), itEnd = _charsByDepth.end();
166 it != itEnd; ++it) {
168 DisplayObject* ch = *it;
170 // Should not be there!
171 if (ch->isDestroyed()) continue;
173 // found
174 if (ch->get_depth() == depth) return ch;
176 // non-existent (chars are ordered by depth)
177 if (ch->get_depth() > depth) return 0;
180 return 0;
185 DisplayObject*
186 DisplayList::getDisplayObjectByName(string_table& st, const ObjectURI& uri,
187 bool caseless) const
189 testInvariant();
191 const container_type::const_iterator e = _charsByDepth.end();
193 container_type::const_iterator it =
194 std::find_if(_charsByDepth.begin(), e, NameEquals(st, uri, caseless));
196 if (it == e) return 0;
198 return *it;
202 void
203 DisplayList::placeDisplayObject(DisplayObject* ch, int depth)
205 assert(!ch->unloaded());
206 ch->set_invalidated();
207 ch->set_depth(depth);
209 container_type::iterator it =
210 std::find_if( _charsByDepth.begin(), _charsByDepth.end(),
211 DepthGreaterOrEqual(depth));
213 if (it == _charsByDepth.end() || (*it)->get_depth() != depth) {
214 // add the new char
215 _charsByDepth.insert(it, ch);
217 else {
218 // remember bounds of old char
219 InvalidatedRanges old_ranges;
220 (*it)->add_invalidated_bounds(old_ranges, true);
222 // make a copy (before replacing)
223 DisplayObject* oldCh = *it;
225 // replace existing char (before calling unload!)
226 *it = ch;
228 if (oldCh->unload()) {
229 // reinsert removed DisplayObject if needed
230 reinsertRemovedCharacter(oldCh);
232 else oldCh->destroy();
234 // extend invalidated bounds
235 ch->extend_invalidated_bounds(old_ranges);
238 testInvariant();
241 void
242 DisplayList::add(DisplayObject* ch, bool replace)
244 int depth = ch->get_depth();
246 container_type::iterator it =
247 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
248 DepthGreaterOrEqual(depth));
250 if (it == _charsByDepth.end() || (*it)->get_depth() != depth) {
251 _charsByDepth.insert(it, ch);
253 else if (replace) *it = ch;
255 testInvariant();
258 void
259 DisplayList::replaceDisplayObject(DisplayObject* ch, int depth,
260 bool use_old_cxform, bool use_old_matrix)
262 testInvariant();
264 //GNASH_REPORT_FUNCTION;
265 assert(!ch->unloaded());
267 ch->set_invalidated();
268 ch->set_depth(depth);
270 container_type::iterator it =
271 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
272 DepthGreaterOrEqual(depth));
274 if (it == _charsByDepth.end() || (*it)->get_depth() != depth) {
275 _charsByDepth.insert(it, ch);
277 else {
278 // Make a copy (before replacing)
279 DisplayObject* oldch = *it;
281 InvalidatedRanges old_ranges;
283 if (use_old_cxform) {
284 // Use the SWFCxForm from the old DisplayObject.
285 ch->setCxForm(getCxForm(*oldch));
288 if (use_old_matrix) {
289 // Use the SWFMatrix from the old DisplayObject.
290 ch->setMatrix(getMatrix(*oldch), true);
293 // remember bounds of old char
294 oldch->add_invalidated_bounds(old_ranges, true);
296 // replace existing char (before calling unload)
297 *it = ch;
299 // Unload old char
300 if (oldch->unload()) {
301 // reinsert removed DisplayObject if needed
302 reinsertRemovedCharacter(oldch);
304 else oldch->destroy();
306 // extend invalidated bounds
307 // WARNING: when a new Button DisplayObject is added,
308 // the invalidated bounds computation will likely
309 // be bogus, as the actual DisplayObject shown is not
310 // instantiated until stagePlacementCallback for buttons
311 // (I'd say this is a bug in Button).
312 ch->extend_invalidated_bounds(old_ranges);
316 testInvariant();
320 // Updates the transform properties of the DisplayObject at
321 // the specified depth.
322 void
323 DisplayList::moveDisplayObject( int depth, const SWFCxForm* color_xform,
324 const SWFMatrix* mat, int* ratio, int* /* clip_depth */)
326 testInvariant();
328 DisplayObject* ch = getDisplayObjectAtDepth(depth);
329 if (! ch) {
330 // FIXME, should this be log_aserror?
331 IF_VERBOSE_MALFORMED_SWF(
332 log_swferror(_("moveDisplayObject() -- can't find object at depth %d"),
333 depth);
335 return;
338 if (ch->unloaded()) {
339 log_error("Request to move an unloaded DisplayObject");
340 assert(!ch->unloaded());
343 // TODO: is sign of depth related to accepting anim moves ?
344 if (!ch->get_accept_anim_moves()) {
345 // This DisplayObject is rejecting anim moves. This happens after it
346 // has been manipulated by ActionScript.
347 return;
350 if (color_xform) ch->setCxForm(*color_xform);
351 if (mat) ch->setMatrix(*mat, true);
352 if (ratio) ch->set_ratio(*ratio);
354 testInvariant();
358 // Removes the object at the specified depth.
359 void
360 DisplayList::removeDisplayObject(int depth)
362 //GNASH_REPORT_FUNCTION;
364 testInvariant();
366 #ifndef NDEBUG
367 container_type::size_type size = _charsByDepth.size();
368 #endif
370 // TODO: would it be legal to call removeDisplayObject with a depth
371 // in the "removed" zone ?
372 // TODO: optimize to take by-depth order into account
373 container_type::iterator it =
374 std::find_if( _charsByDepth.begin(), _charsByDepth.end(),
375 DepthEquals(depth));
377 if (it != _charsByDepth.end()) {
378 // Make a copy (before erasing)
379 DisplayObject* oldCh = *it;
381 // Erase (before calling unload)
382 _charsByDepth.erase(it);
384 if (oldCh->unload()) {
385 // reinsert removed DisplayObject if needed
386 // NOTE: could be optimized if we knew exactly how
387 // to handle the case in which the target depth
388 // (after the shift) is occupied already
390 reinsertRemovedCharacter(oldCh);
392 else oldCh->destroy();
395 assert(size >= _charsByDepth.size());
397 testInvariant();
401 // TODO: take DisplayObject by ref ?
402 void
403 DisplayList::swapDepths(DisplayObject* ch1, int newdepth)
405 testInvariant();
407 if (newdepth < DisplayObject::staticDepthOffset) {
408 IF_VERBOSE_ASCODING_ERRORS(
409 log_aserror("%s.swapDepth(%d) : ignored call with target depth "
410 "less then %d", ch1->getTarget(), newdepth,
411 DisplayObject::staticDepthOffset);
413 return;
416 const int srcdepth = ch1->get_depth();
418 // what if source char is at a lower depth ?
419 assert(srcdepth >= DisplayObject::staticDepthOffset);
421 assert(srcdepth != newdepth);
423 // TODO: optimize this scan by taking ch1 depth into account ?
424 container_type::iterator it1 =
425 std::find(_charsByDepth.begin(), _charsByDepth.end(), ch1);
427 // upper bound ...
428 container_type::iterator it2 =
429 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
430 DepthGreaterOrEqual(newdepth));
432 if (it1 == _charsByDepth.end()) {
433 log_error("First argument to DisplayList::swapDepth() "
434 "is NOT a DisplayObject in the list. Call ignored.");
435 return;
438 // Found another DisplayObject at the given depth
439 if (it2 != _charsByDepth.end() && (*it2)->get_depth() == newdepth)
441 DisplayObject* ch2 = *it2;
443 ch2->set_depth(srcdepth);
445 // TODO: we're not actually invalidated ourselves, rather
446 // our parent is...
447 ch2->set_invalidated();
449 // We won't accept static transforms after a depth swap.
450 // See displaylist_depths_test6.swf for more info.
451 ch2->transformedByScript();
453 std::iter_swap(it1, it2);
456 // No DisplayObject found at the given depth
457 else {
458 // Move the DisplayObject to the new position
459 // NOTE: insert *before* erasing, in case the list is
460 // the only referer of the ref-counted DisplayObject
461 _charsByDepth.insert(it2, ch1);
462 _charsByDepth.erase(it1);
465 // don't change depth before the iter_swap case above, as
466 // we'll need it to assign to the new DisplayObject
467 ch1->set_depth(newdepth);
469 // TODO: we're not actually invalidated ourselves, rather our parent is...
470 // UdoG ? Want to verify this ?
471 ch1->set_invalidated();
473 // We won't accept static transforms after a depth swap.
474 // See displaylist_depths_test6.swf for more info.
475 ch1->transformedByScript();
477 testInvariant();
481 DisplayObject*
482 DisplayList::removeDisplayObjectAt(int index)
484 container_type::iterator it =
485 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
486 DepthEquals(index));
488 if (it == _charsByDepth.end()) return 0;
490 DisplayObject* obj = *it;
491 _charsByDepth.erase(it);
492 return obj;
495 void
496 DisplayList::removeDisplayObject(DisplayObject* obj)
498 container_type::iterator it = std::find(_charsByDepth.begin(),
499 _charsByDepth.end(), obj);
501 if (it != _charsByDepth.end()) {
502 _charsByDepth.erase(it);
506 void
507 DisplayList::insertDisplayObject(DisplayObject* obj, int index)
509 testInvariant();
511 assert(!obj->unloaded());
513 obj->set_invalidated();
514 obj->set_depth(index);
516 // Find the first index greater than or equal to the required index
517 container_type::iterator it =
518 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
519 DepthGreaterOrEqual(index));
521 // Insert the DisplayObject before that position
522 _charsByDepth.insert(it, obj);
524 // Shift depths upwards until no depths are duplicated. No DisplayObjects
525 // are removed!
526 while (it != _charsByDepth.end() && (*it)->get_depth() == index) {
527 (*it)->set_depth(index + 1);
528 ++index, ++it;
531 testInvariant();
535 void
536 DisplayList::addDisplayObject(DisplayObject* obj)
538 testInvariant();
540 assert(!obj->unloaded());
542 obj->set_invalidated();
544 int index;
546 if (_charsByDepth.empty()) {
547 index = 0;
549 else {
550 container_type::const_reverse_iterator it = _charsByDepth.rbegin();
551 index = (*it)->get_depth() + 1;
554 obj->set_depth(index);
556 // Insert the DisplayObject at the end
557 _charsByDepth.insert(_charsByDepth.end(), obj);
559 testInvariant();
564 bool
565 DisplayList::unload()
567 testInvariant();
569 bool unloadHandler = false;
571 // All children with an unload handler should be unloaded. As soon as
572 // the first unload handler is encountered, subsequent children should
573 // not be destroyed or removed from the display list. This affects
574 // children without an unload handler.
575 for (iterator it = beginNonRemoved(_charsByDepth),
576 itEnd = _charsByDepth.end(); it != itEnd; )
578 // make a copy
579 DisplayObject* di = *it;
581 // Destroyed objects should not be there!
582 assert(!di->isDestroyed());
584 // Destroy those with a handler anyway?
585 if (di->unload()) {
586 unloadHandler = true;
587 ++it;
588 continue;
591 if (!unloadHandler) {
592 di->destroy();
593 it = _charsByDepth.erase(it);
595 else ++it;
599 testInvariant();
601 return unloadHandler;
606 void
607 DisplayList::destroy()
609 //GNASH_REPORT_FUNCTION;
611 testInvariant();
613 for (iterator it = _charsByDepth.begin(), itEnd = _charsByDepth.end();
614 it != itEnd; ) {
616 // make a copy
617 DisplayObject* di = *it;
619 // skip if already unloaded
620 if ( di->isDestroyed() ) {
621 ++it;
622 continue;
625 di->destroy();
626 it = _charsByDepth.erase(it);
628 testInvariant();
631 // Display the referenced DisplayObjects. Lower depths
632 // are obscured by higher depths.
633 void
634 DisplayList::display(Renderer& renderer, const Transform& base)
636 testInvariant();
638 std::stack<int> clipDepthStack;
640 // We only display DisplayObjects which are out of the "removed" zone
641 // (or should we check unloaded?)
642 iterator it = beginNonRemoved(_charsByDepth);
643 for (iterator endIt = _charsByDepth.end(); it != endIt; ++it)
645 DisplayObject* ch = *it;
646 assert(!ch->isDestroyed());
648 // Don't display dynamic masks
649 if (ch->isDynamicMask()) continue;
651 assert(!ch->unloaded()); // we don't advance unloaded chars
653 // Check if this charater or any of its parents is a mask.
654 // Characters acting as masks should always be rendered to the
655 // mask buffer despite their visibility.
657 DisplayObject* p = ch->parent();
658 bool renderAsMask = ch->isMaskLayer();
660 while (!renderAsMask && p) {
661 renderAsMask = p->isMaskLayer();
662 p = p->parent();
665 // check for non-mask hiden DisplayObjects
666 if (!renderAsMask && (!ch->visible())) {
667 ch->omit_display();
668 // Don't display non-mask hidden DisplayObjects
669 continue;
672 int depth = ch->get_depth();
673 // Discard useless masks
674 while (!clipDepthStack.empty() && (depth > clipDepthStack.top())) {
675 clipDepthStack.pop();
676 renderer.disable_mask();
679 // Push a new mask to the masks stack
680 if (ch->isMaskLayer()) {
681 const int clipDepth = ch->get_clip_depth();
682 clipDepthStack.push(clipDepth);
683 renderer.begin_submit_mask();
686 if (ch->boundsInClippingArea(renderer)) {
687 ch->display(renderer, base);
689 else ch->omit_display();
691 // Notify the renderer that mask drawing has finished.
692 if (ch->isMaskLayer()) renderer.end_submit_mask();
695 // Discard any remaining masks
696 while (!clipDepthStack.empty()) {
697 clipDepthStack.pop();
698 renderer.disable_mask();
704 void
705 DisplayList::omit_display()
707 iterator it = beginNonRemoved(_charsByDepth);
708 for (iterator endIt = _charsByDepth.end(); it != endIt; ++it) {
709 DisplayObject* ch = *it;
710 ch->omit_display();
714 void
715 DisplayList::dump() const
717 //testInvariant();
719 if ( _charsByDepth.empty() ) return;
721 string_table& st = getStringTable(*getObject(_charsByDepth.front()));
722 ObjectURI::Logger l(st);
724 int num=0;
725 for (const_iterator it = _charsByDepth.begin(),
726 endIt = _charsByDepth.end(); it != endIt; ++it) {
728 const DisplayObject* dobj = *it;
729 log_debug(_("Item %d(%s) at depth %d (char name %s, type %s)"
730 "Destroyed: %s, unloaded: %s"),
731 num, dobj, dobj->get_depth(), l.debug(dobj->get_name()), typeName(*dobj),
732 dobj->isDestroyed(), dobj->unloaded());
733 num++;
738 void
739 DisplayList::add_invalidated_bounds(InvalidatedRanges& ranges, bool force)
741 testInvariant();
743 // This function generally has nothing else to do than calling the
744 // add_invalidated_bounds() function of all items in the display list.
745 // However, special optimization is included for masks, which makes it
746 // look a bit more complicated. We want to avoid that a masked DisplayObject
747 // invalidates an area where the DisplayObject is invisible because of it's
748 // mask (which is quite common). For example, think of a large bitmap that
749 // covers the entire stage and is masked by a very small circle in the
750 // middle of the stage (ie. it's only visible there). Changes in the
751 // bitmap sprite would invalidate the whole stage even if only the small
752 // masked portion in the middle really needs to be drawn again.
754 // So, like display(), we keep a stack of masks. Instead of drawing the
755 // mask we keep a separate list of InvalidatedRanges for the masks which
756 // later are intersected with the masked DisplayObjects' InvalidatedRanges.
758 // The code is much based on the display() function, so some references
759 // in comments have been added for convenience.
761 // For a simpler implementation (that doesn't care about masks, but
762 // still produces correct results) see CVS revision 1.96
764 // TODO: review this function to take "dynamic" mask and maskees
765 // into account
767 std::stack<int> clipDepthStack; // same method used in display()
768 std::stack<InvalidatedRanges> rangesStack;
769 bool drawing_mask = false;
771 iterator it = beginNonRemoved(_charsByDepth);
772 for (iterator endIt = _charsByDepth.end(); it != endIt; ++it) {
773 DisplayObject* dobj = *it;
775 #ifndef GNASH_USE_GC
776 assert(dobj->get_ref_count() > 0);
777 #endif // ndef GNASH_USE_GC
780 const int depth = dobj->get_depth();
782 // Discard useless masks
783 while (!clipDepthStack.empty() && (depth > clipDepthStack.top())) {
784 clipDepthStack.pop();
785 rangesStack.pop(); // disable_mask() equivalent
789 // Push a new mask to the masks stack
790 if (dobj->isMaskLayer()) {
792 const int clipDepth = dobj->get_clip_depth();
793 clipDepthStack.push(clipDepth);
795 drawing_mask = true; // begin_submit_mask equivalent
797 if (rangesStack.empty()) {
798 InvalidatedRanges item;
799 rangesStack.push(item);
801 else {
802 // copy the top mask
803 rangesStack.push(rangesStack.top());
807 if (drawing_mask) {
809 // --> The child is part of a mask, so add ranges to our
810 // mask ranges stack
812 assert(!rangesStack.empty());
813 dobj->add_invalidated_bounds(rangesStack.top(), true);
815 // need to call add_invalidated_bounds again because the previous
816 // call needs force==true. Changes to the mask itself may also
817 // require re-rendering of the mask area, so we have to
818 // add the mask itself to the global ranges, but this time
819 // with normal "force" value...
820 // As long the mask has not been invalidated and force==false this
821 // call won't modify the "ranges" list.
822 dobj->add_invalidated_bounds(ranges, force);
825 else {
827 if (rangesStack.empty()) {
829 // --> normal case for unmasked DisplayObjects
830 dobj->add_invalidated_bounds(ranges, force);
833 else {
835 // --> DisplayObject is masked, so intersect with "mask"
837 // first get the ranges of the child in a separate list
838 InvalidatedRanges childRanges;
839 childRanges.inheritConfig(ranges);
841 dobj->add_invalidated_bounds(childRanges, force);
843 // then intersect ranges with topmost "mask"
844 childRanges.intersect(rangesStack.top());
846 // add result to the global ranges
847 ranges.add(childRanges);
851 } // not drawing mask
853 // <== end of display() equivalent
856 // Mask "drawing" has finished
857 if (dobj->isMaskLayer()) {
858 // end_submit_mask equivalent
859 drawing_mask = false;
866 void
867 DisplayList::sort()
869 _charsByDepth.sort(DepthLessThan());
872 void
873 DisplayList::mergeDisplayList(DisplayList & newList)
875 testInvariant();
877 iterator itOld = beginNonRemoved(_charsByDepth);
878 iterator itNew = beginNonRemoved(newList._charsByDepth);
880 iterator itOldEnd = dlistTagsEffectiveZoneEnd(_charsByDepth);
881 iterator itNewEnd = newList._charsByDepth.end();
882 assert(itNewEnd == dlistTagsEffectiveZoneEnd(newList._charsByDepth) );
884 // step1.
885 // starting scanning both lists.
886 while (itOld != itOldEnd)
888 iterator itOldBackup = itOld;
890 DisplayObject* chOld = *itOldBackup;
891 int depthOld = chOld->get_depth();
893 while (itNew != itNewEnd) {
894 iterator itNewBackup = itNew;
896 DisplayObject* chNew = *itNewBackup;
897 int depthNew = chNew->get_depth();
899 // depth in old list is occupied, and empty in new list.
900 if (depthOld < depthNew) {
902 itOld++;
903 // unload the DisplayObject if it's in static zone(-16384,0)
904 if (depthOld < 0) {
905 _charsByDepth.erase(itOldBackup);
907 if (chOld->unload()) reinsertRemovedCharacter(chOld);
908 else chOld->destroy();
911 break;
913 // depth is occupied in both lists
915 if (depthOld == depthNew) {
916 ++itOld;
917 ++itNew;
919 bool is_ratio_compatible =
920 (chOld->get_ratio() == chNew->get_ratio());
922 if (!is_ratio_compatible || chOld->isDynamic() ||
923 !isReferenceable(*chOld)) {
924 // replace the DisplayObject in old list with
925 // corresponding DisplayObject in new list
926 _charsByDepth.insert(itOldBackup, *itNewBackup);
927 _charsByDepth.erase(itOldBackup);
929 // unload the old DisplayObject
930 if (chOld->unload()) reinsertRemovedCharacter(chOld);
931 else chOld->destroy();
933 else {
934 newList._charsByDepth.erase(itNewBackup);
936 // replace the transformation SWFMatrix if the old
937 // DisplayObject accepts static transformation.
938 if (chOld->get_accept_anim_moves()) {
939 chOld->setMatrix(getMatrix(*chNew), true);
940 chOld->setCxForm(getCxForm(*chNew));
942 chNew->unload();
943 chNew->destroy();
946 break;
949 // depth in old list is empty, but occupied in new list.
950 ++ itNew;
951 // add the new DisplayObject to the old list.
952 _charsByDepth.insert(itOldBackup, *itNewBackup );
955 // break if finish scanning the new list
956 if (itNew == itNewEnd) break;
959 // step2(only required if scanning of new list finished earlier in step1).
960 // continue to scan the static zone of the old list.
961 // unload remaining DisplayObjects directly.
962 while((itOld != itOldEnd) && ((*itOld)->get_depth() < 0)) {
964 DisplayObject* chOld = *itOld;
965 itOld = _charsByDepth.erase(itOld);
967 if (chOld->unload()) reinsertRemovedCharacter(chOld);
968 else chOld->destroy();
971 // step3(only required if scanning of old list finished earlier in step1).
972 // continue to scan the new list.
973 // add remaining DisplayObjects directly.
974 if (itNew != itNewEnd) _charsByDepth.insert(itOld, itNew, itNewEnd);
976 // step4.
977 // Copy all unloaded DisplayObjects from the new display list to the
978 // old display list, and clear the new display list
979 for (itNew = newList._charsByDepth.begin(); itNew != itNewEnd; ++itNew) {
981 DisplayObject* chNew = *itNew;
982 int depthNew = chNew->get_depth();
984 if (chNew->unloaded()) {
985 iterator it =
986 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
987 DepthGreaterOrEqual(depthNew));
989 _charsByDepth.insert(it, *itNew);
993 // clear the new display list after merge
994 // ASSERT:
995 // - Any element in newList._charsByDepth is either marked as unloaded
996 // or found in this list
997 #if GNASH_PARANOIA_LEVEL > 1
998 for (iterator i = newList._charsByDepth.begin(),
999 e = newList._charsByDepth.end(); i != e; ++i) {
1001 DisplayObject* ch = *i;
1002 if (!ch->unloaded()) {
1004 iterator found =
1005 std::find(_charsByDepth.begin(), _charsByDepth.end(), ch);
1007 if (found == _charsByDepth.end())
1009 log_error("mergeDisplayList: DisplayObject %s (%s at depth "
1010 "%d [%d]) about to be discarded in given display list"
1011 " is not marked as unloaded and not found in the"
1012 " merged current displaylist",
1013 ch->getTarget(), typeName(*ch), ch->get_depth(),
1014 ch->get_depth()-DisplayObject::staticDepthOffset);
1015 std::abort();
1019 #endif
1020 newList._charsByDepth.clear();
1022 testInvariant();
1026 void
1027 DisplayList::reinsertRemovedCharacter(DisplayObject* ch)
1029 assert(ch->unloaded());
1030 assert(!ch->isDestroyed());
1031 testInvariant();
1033 // TODO: have this done by DisplayObject::unload() instead ?
1034 int oldDepth = ch->get_depth();
1035 int newDepth = DisplayObject::removedDepthOffset - oldDepth;
1036 ch->set_depth(newDepth);
1038 // TODO: optimize this by searching from the end(lowest depth).
1039 container_type::iterator it =
1040 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
1041 DepthGreaterOrEqual(newDepth));
1043 _charsByDepth.insert(it, ch);
1045 testInvariant();
1048 void
1049 DisplayList::removeUnloaded()
1051 testInvariant();
1053 _charsByDepth.remove_if(boost::mem_fn(&DisplayObject::unloaded));
1055 testInvariant();
1058 bool
1059 DisplayList::isSorted() const
1061 if (_charsByDepth.empty()) return true;
1062 return std::adjacent_find(_charsByDepth.begin(), _charsByDepth.end(),
1063 DepthGreaterThan()) == _charsByDepth.end();
1067 #if GNASH_PARANOIA_LEVEL > 1 && !defined(NDEBUG)
1068 DisplayList::const_iterator
1069 DisplayList::nonRemoved() const
1071 return beginNonRemoved(_charsByDepth);
1073 #endif
1075 namespace {
1077 DisplayList::iterator
1078 beginNonRemoved(DisplayList::container_type& c)
1080 const int depth = DisplayObject::removedDepthOffset -
1081 DisplayObject::staticDepthOffset;
1083 return std::find_if(c.begin(), c.end(), DepthGreaterOrEqual(depth));
1086 DisplayList::const_iterator
1087 beginNonRemoved(const DisplayList::container_type& c)
1089 const int depth = DisplayObject::removedDepthOffset -
1090 DisplayObject::staticDepthOffset;
1092 return std::find_if(c.begin(), c.end(), DepthGreaterOrEqual(depth));
1095 DisplayList::iterator
1096 dlistTagsEffectiveZoneEnd(DisplayList::container_type& c)
1098 return std::find_if(c.begin(), c.end(),
1099 DepthGreaterOrEqual(0xffff + DisplayObject::staticDepthOffset));
1102 DisplayList::const_iterator
1103 dlistTagsEffectiveZoneEnd(const DisplayList::container_type& c)
1105 return std::find_if(c.begin(), c.end(),
1106 DepthGreaterOrEqual(0xffff + DisplayObject::staticDepthOffset));
1109 } // anonymous namespace
1112 std::ostream&
1113 operator<< (std::ostream& os, const DisplayList& dl)
1115 os << "By depth: ";
1117 if ( dl._charsByDepth.empty() ) return os;
1119 string_table& st = getStringTable(*getObject(dl._charsByDepth.front()));
1120 ObjectURI::Logger l(st);
1122 for (DisplayList::const_iterator it = dl._charsByDepth.begin(),
1123 itEnd = dl._charsByDepth.end(); it != itEnd; ++it) {
1125 const DisplayObject* item = *it;
1126 if (it != dl._charsByDepth.begin()) os << " | ";
1127 os << " name:" << l.debug(item->get_name())
1128 << " depth:" << item->get_depth();
1131 return os;
1134 } // namespace gnash
1137 // Local Variables:
1138 // mode: C++
1139 // indent-tabs-mode: t
1140 // End: