1 // dlist.cpp: Display lists, for Gnash.
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010 Free Software
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.
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"
25 #include "StringPredicates.h"
26 #include "MovieClip.h"
33 #include <boost/bind.hpp>
37 // Forward declarations
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.
66 DepthEquals(int depth
) : _depth(depth
) {}
68 bool operator() (const DisplayObject
* item
) {
69 if (!item
) return false;
70 return item
->get_depth() == _depth
;
77 struct DepthGreaterThan
79 bool operator()(const DisplayObject
* a
, const DisplayObject
* b
) {
80 return a
->get_depth() > b
->get_depth();
86 bool operator()(const DisplayObject
* a
, const DisplayObject
* b
) {
87 return a
->get_depth() < b
->get_depth();
91 class DepthGreaterOrEqual
95 DepthGreaterOrEqual(int depth
) : _depth(depth
) {}
97 bool operator() (const DisplayObject
* item
) {
98 if (!item
) return false;
99 return item
->get_depth() >= _depth
;
109 NameEquals(string_table
& st
, string_table::key name
, bool caseless
)
113 _name(caseless
? _st
.noCase(name
) : name
)
116 bool operator() (const DisplayObject
* 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 const string_table::key itname
=
127 _caseless
? _st
.noCase(item
->get_name()) : item
->get_name();
129 return itname
== _name
;
135 const bool _caseless
;
136 const string_table::key _name
;
139 } // anonymous namespace
142 DisplayList::getNextHighestDepth() const
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
;
161 DisplayList::getDisplayObjectAtDepth(int depth
) const
165 for (const_iterator it
= _charsByDepth
.begin(), itEnd
= _charsByDepth
.end();
168 DisplayObject
* ch
= *it
;
170 // Should not be there!
171 if (ch
->isDestroyed()) continue;
174 if (ch
->get_depth() == depth
) return ch
;
176 // non-existent (chars are ordered by depth)
177 if (ch
->get_depth() > depth
) return 0;
186 DisplayList::getDisplayObjectByName(string_table
& st
, string_table::key name
,
191 const container_type::const_iterator e
= _charsByDepth
.end();
193 container_type::const_iterator it
=
194 std::find_if(_charsByDepth
.begin(), e
, NameEquals(st
, name
, caseless
));
196 if (it
== e
) return 0;
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
) {
215 _charsByDepth
.insert(it
, ch
);
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!)
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
);
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
;
259 DisplayList::replaceDisplayObject(DisplayObject
* ch
, int depth
,
260 bool use_old_cxform
, bool use_old_matrix
)
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
);
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)
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
);
320 // Updates the transform properties of the DisplayObject at
321 // the specified depth.
323 DisplayList::moveDisplayObject( int depth
, const SWFCxForm
* color_xform
,
324 const SWFMatrix
* mat
, int* ratio
, int* /* clip_depth */)
328 DisplayObject
* ch
= getDisplayObjectAtDepth(depth
);
330 // FIXME, should this be log_aserror?
331 IF_VERBOSE_MALFORMED_SWF(
332 log_swferror(_("moveDisplayObject() -- can't find object at depth %d"),
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.
350 if (color_xform
) ch
->setCxForm(*color_xform
);
351 if (mat
) ch
->setMatrix(*mat
, true);
352 if (ratio
) ch
->set_ratio(*ratio
);
358 // Removes the object at the specified depth.
360 DisplayList::removeDisplayObject(int depth
)
362 //GNASH_REPORT_FUNCTION;
367 container_type::size_type size
= _charsByDepth
.size();
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(),
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());
401 // TODO: take DisplayObject by ref ?
403 DisplayList::swapDepths(DisplayObject
* ch1
, int newdepth
)
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
);
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
);
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.");
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
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
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();
482 DisplayList::removeDisplayObjectAt(int index
)
484 container_type::iterator it
=
485 std::find_if(_charsByDepth
.begin(), _charsByDepth
.end(),
488 if (it
== _charsByDepth
.end()) return 0;
490 DisplayObject
* obj
= *it
;
491 _charsByDepth
.erase(it
);
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
);
507 DisplayList::insertDisplayObject(DisplayObject
* obj
, int index
)
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
526 while (it
!= _charsByDepth
.end() && (*it
)->get_depth() == index
) {
527 (*it
)->set_depth(index
+ 1);
536 DisplayList::addDisplayObject(DisplayObject
* obj
)
540 assert(!obj
->unloaded());
542 obj
->set_invalidated();
546 if (_charsByDepth
.empty()) {
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
);
565 DisplayList::unload()
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
; )
579 DisplayObject
* di
= *it
;
581 // Destroyed objects should not be there!
582 assert(!di
->isDestroyed());
584 // Destroy those with a handler anyway?
586 unloadHandler
= true;
591 if (!unloadHandler
) {
593 it
= _charsByDepth
.erase(it
);
601 return unloadHandler
;
607 DisplayList::destroy()
609 //GNASH_REPORT_FUNCTION;
613 for (iterator it
= _charsByDepth
.begin(), itEnd
= _charsByDepth
.end();
617 DisplayObject
* di
= *it
;
619 // skip if already unloaded
620 if ( di
->isDestroyed() ) {
626 it
= _charsByDepth
.erase(it
);
631 // Display the referenced DisplayObjects. Lower depths
632 // are obscured by higher depths.
634 DisplayList::display(Renderer
& renderer
, const Transform
& base
)
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();
665 // check for non-mask hiden DisplayObjects
666 if (!renderAsMask
&& (!ch
->visible())) {
668 // Don't display non-mask hidden DisplayObjects
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();
705 DisplayList::omit_display()
707 iterator it
= beginNonRemoved(_charsByDepth
);
708 for (iterator endIt
= _charsByDepth
.end(); it
!= endIt
; ++it
) {
709 DisplayObject
* ch
= *it
;
715 DisplayList::dump() const
720 for (const_iterator it
= _charsByDepth
.begin(),
721 endIt
= _charsByDepth
.end(); it
!= endIt
; ++it
) {
723 const DisplayObject
* dobj
= *it
;
724 log_debug(_("Item %d(%s) at depth %d (char name %s, type %s)"
725 "Destroyed: %s, unloaded: %s"),
726 num
, dobj
, dobj
->get_depth(), dobj
->get_name(), typeName(*dobj
),
727 dobj
->isDestroyed(), dobj
->unloaded());
734 DisplayList::add_invalidated_bounds(InvalidatedRanges
& ranges
, bool force
)
738 // This function generally has nothing else to do than calling the
739 // add_invalidated_bounds() function of all items in the display list.
740 // However, special optimization is included for masks, which makes it
741 // look a bit more complicated. We want to avoid that a masked DisplayObject
742 // invalidates an area where the DisplayObject is invisible because of it's
743 // mask (which is quite common). For example, think of a large bitmap that
744 // covers the entire stage and is masked by a very small circle in the
745 // middle of the stage (ie. it's only visible there). Changes in the
746 // bitmap sprite would invalidate the whole stage even if only the small
747 // masked portion in the middle really needs to be drawn again.
749 // So, like display(), we keep a stack of masks. Instead of drawing the
750 // mask we keep a separate list of InvalidatedRanges for the masks which
751 // later are intersected with the masked DisplayObjects' InvalidatedRanges.
753 // The code is much based on the display() function, so some references
754 // in comments have been added for convenience.
756 // For a simpler implementation (that doesn't care about masks, but
757 // still produces correct results) see CVS revision 1.96
759 // TODO: review this function to take "dynamic" mask and maskees
762 std::stack
<int> clipDepthStack
; // same method used in display()
763 std::stack
<InvalidatedRanges
> rangesStack
;
764 bool drawing_mask
= false;
766 iterator it
= beginNonRemoved(_charsByDepth
);
767 for (iterator endIt
= _charsByDepth
.end(); it
!= endIt
; ++it
) {
768 DisplayObject
* dobj
= *it
;
771 assert(dobj
->get_ref_count() > 0);
772 #endif // ndef GNASH_USE_GC
775 const int depth
= dobj
->get_depth();
777 // Discard useless masks
778 while (!clipDepthStack
.empty() && (depth
> clipDepthStack
.top())) {
779 clipDepthStack
.pop();
780 rangesStack
.pop(); // disable_mask() equivalent
784 // Push a new mask to the masks stack
785 if (dobj
->isMaskLayer()) {
787 const int clipDepth
= dobj
->get_clip_depth();
788 clipDepthStack
.push(clipDepth
);
790 drawing_mask
= true; // begin_submit_mask equivalent
792 if (rangesStack
.empty()) {
793 InvalidatedRanges item
;
794 rangesStack
.push(item
);
798 rangesStack
.push(rangesStack
.top());
804 // --> The child is part of a mask, so add ranges to our
807 assert(!rangesStack
.empty());
808 dobj
->add_invalidated_bounds(rangesStack
.top(), true);
810 // need to call add_invalidated_bounds again because the previous
811 // call needs force==true. Changes to the mask itself may also
812 // require re-rendering of the mask area, so we have to
813 // add the mask itself to the global ranges, but this time
814 // with normal "force" value...
815 // As long the mask has not been invalidated and force==false this
816 // call won't modify the "ranges" list.
817 dobj
->add_invalidated_bounds(ranges
, force
);
822 if (rangesStack
.empty()) {
824 // --> normal case for unmasked DisplayObjects
825 dobj
->add_invalidated_bounds(ranges
, force
);
830 // --> DisplayObject is masked, so intersect with "mask"
832 // first get the ranges of the child in a separate list
833 InvalidatedRanges childRanges
;
834 childRanges
.inheritConfig(ranges
);
836 dobj
->add_invalidated_bounds(childRanges
, force
);
838 // then intersect ranges with topmost "mask"
839 childRanges
.intersect(rangesStack
.top());
841 // add result to the global ranges
842 ranges
.add(childRanges
);
846 } // not drawing mask
848 // <== end of display() equivalent
851 // Mask "drawing" has finished
852 if (dobj
->isMaskLayer()) {
853 // end_submit_mask equivalent
854 drawing_mask
= false;
864 _charsByDepth
.sort(DepthLessThan());
868 DisplayList::mergeDisplayList(DisplayList
& newList
)
872 iterator itOld
= beginNonRemoved(_charsByDepth
);
873 iterator itNew
= beginNonRemoved(newList
._charsByDepth
);
875 iterator itOldEnd
= dlistTagsEffectiveZoneEnd(_charsByDepth
);
876 iterator itNewEnd
= newList
._charsByDepth
.end();
877 assert(itNewEnd
== dlistTagsEffectiveZoneEnd(newList
._charsByDepth
) );
880 // starting scanning both lists.
881 while (itOld
!= itOldEnd
)
883 iterator itOldBackup
= itOld
;
885 DisplayObject
* chOld
= *itOldBackup
;
886 int depthOld
= chOld
->get_depth();
888 while (itNew
!= itNewEnd
) {
889 iterator itNewBackup
= itNew
;
891 DisplayObject
* chNew
= *itNewBackup
;
892 int depthNew
= chNew
->get_depth();
894 // depth in old list is occupied, and empty in new list.
895 if (depthOld
< depthNew
) {
898 // unload the DisplayObject if it's in static zone(-16384,0)
900 _charsByDepth
.erase(itOldBackup
);
902 if (chOld
->unload()) reinsertRemovedCharacter(chOld
);
903 else chOld
->destroy();
908 // depth is occupied in both lists
910 if (depthOld
== depthNew
) {
914 bool is_ratio_compatible
=
915 (chOld
->get_ratio() == chNew
->get_ratio());
917 if (!is_ratio_compatible
|| chOld
->isDynamic() ||
918 !isReferenceable(*chOld
)) {
919 // replace the DisplayObject in old list with
920 // corresponding DisplayObject in new list
921 _charsByDepth
.insert(itOldBackup
, *itNewBackup
);
922 _charsByDepth
.erase(itOldBackup
);
924 // unload the old DisplayObject
925 if (chOld
->unload()) reinsertRemovedCharacter(chOld
);
926 else chOld
->destroy();
929 newList
._charsByDepth
.erase(itNewBackup
);
931 // replace the transformation SWFMatrix if the old
932 // DisplayObject accepts static transformation.
933 if (chOld
->get_accept_anim_moves()) {
934 chOld
->setMatrix(getMatrix(*chNew
), true);
935 chOld
->setCxForm(getCxForm(*chNew
));
944 // depth in old list is empty, but occupied in new list.
946 // add the new DisplayObject to the old list.
947 _charsByDepth
.insert(itOldBackup
, *itNewBackup
);
950 // break if finish scanning the new list
951 if (itNew
== itNewEnd
) break;
954 // step2(only required if scanning of new list finished earlier in step1).
955 // continue to scan the static zone of the old list.
956 // unload remaining DisplayObjects directly.
957 while((itOld
!= itOldEnd
) && ((*itOld
)->get_depth() < 0)) {
959 DisplayObject
* chOld
= *itOld
;
960 itOld
= _charsByDepth
.erase(itOld
);
962 if (chOld
->unload()) reinsertRemovedCharacter(chOld
);
963 else chOld
->destroy();
966 // step3(only required if scanning of old list finished earlier in step1).
967 // continue to scan the new list.
968 // add remaining DisplayObjects directly.
969 if (itNew
!= itNewEnd
) _charsByDepth
.insert(itOld
, itNew
, itNewEnd
);
972 // Copy all unloaded DisplayObjects from the new display list to the
973 // old display list, and clear the new display list
974 for (itNew
= newList
._charsByDepth
.begin(); itNew
!= itNewEnd
; ++itNew
) {
976 DisplayObject
* chNew
= *itNew
;
977 int depthNew
= chNew
->get_depth();
979 if (chNew
->unloaded()) {
981 std::find_if(_charsByDepth
.begin(), _charsByDepth
.end(),
982 DepthGreaterOrEqual(depthNew
));
984 _charsByDepth
.insert(it
, *itNew
);
988 // clear the new display list after merge
990 // - Any element in newList._charsByDepth is either marked as unloaded
991 // or found in this list
992 #if GNASH_PARANOIA_LEVEL > 1
993 for (iterator i
= newList
._charsByDepth
.begin(),
994 e
= newList
._charsByDepth
.end(); i
!= e
; ++i
) {
996 DisplayObject
* ch
= *i
;
997 if (!ch
->unloaded()) {
1000 std::find(_charsByDepth
.begin(), _charsByDepth
.end(), ch
);
1002 if (found
== _charsByDepth
.end())
1004 log_error("mergeDisplayList: DisplayObject %s (%s at depth "
1005 "%d [%d]) about to be discarded in given display list"
1006 " is not marked as unloaded and not found in the"
1007 " merged current displaylist",
1008 ch
->getTarget(), typeName(*ch
), ch
->get_depth(),
1009 ch
->get_depth()-DisplayObject::staticDepthOffset
);
1015 newList
._charsByDepth
.clear();
1022 DisplayList::reinsertRemovedCharacter(DisplayObject
* ch
)
1024 assert(ch
->unloaded());
1025 assert(!ch
->isDestroyed());
1028 // TODO: have this done by DisplayObject::unload() instead ?
1029 int oldDepth
= ch
->get_depth();
1030 int newDepth
= DisplayObject::removedDepthOffset
- oldDepth
;
1031 ch
->set_depth(newDepth
);
1033 // TODO: optimize this by searching from the end(lowest depth).
1034 container_type::iterator it
=
1035 std::find_if(_charsByDepth
.begin(), _charsByDepth
.end(),
1036 DepthGreaterOrEqual(newDepth
));
1038 _charsByDepth
.insert(it
, ch
);
1044 DisplayList::removeUnloaded()
1048 _charsByDepth
.remove_if(boost::mem_fn(&DisplayObject::unloaded
));
1054 DisplayList::isSorted() const
1056 if (_charsByDepth
.empty()) return true;
1057 return std::adjacent_find(_charsByDepth
.begin(), _charsByDepth
.end(),
1058 DepthGreaterThan()) == _charsByDepth
.end();
1062 #if GNASH_PARANOIA_LEVEL > 1 && !defined(NDEBUG)
1063 DisplayList::const_iterator
1064 DisplayList::nonRemoved() const
1066 return beginNonRemoved(_charsByDepth
);
1072 DisplayList::iterator
1073 beginNonRemoved(DisplayList::container_type
& c
)
1075 const int depth
= DisplayObject::removedDepthOffset
-
1076 DisplayObject::staticDepthOffset
;
1078 return std::find_if(c
.begin(), c
.end(), DepthGreaterOrEqual(depth
));
1081 DisplayList::const_iterator
1082 beginNonRemoved(const DisplayList::container_type
& c
)
1084 const int depth
= DisplayObject::removedDepthOffset
-
1085 DisplayObject::staticDepthOffset
;
1087 return std::find_if(c
.begin(), c
.end(), DepthGreaterOrEqual(depth
));
1090 DisplayList::iterator
1091 dlistTagsEffectiveZoneEnd(DisplayList::container_type
& c
)
1093 return std::find_if(c
.begin(), c
.end(),
1094 DepthGreaterOrEqual(0xffff + DisplayObject::staticDepthOffset
));
1097 DisplayList::const_iterator
1098 dlistTagsEffectiveZoneEnd(const DisplayList::container_type
& c
)
1100 return std::find_if(c
.begin(), c
.end(),
1101 DepthGreaterOrEqual(0xffff + DisplayObject::staticDepthOffset
));
1104 } // anonymous namespace
1108 operator<< (std::ostream
& os
, const DisplayList
& dl
)
1111 for (DisplayList::const_iterator it
= dl
._charsByDepth
.begin(),
1112 itEnd
= dl
._charsByDepth
.end(); it
!= itEnd
; ++it
) {
1114 const DisplayObject
* item
= *it
;
1115 if (it
!= dl
._charsByDepth
.begin()) os
<< " | ";
1116 os
<< " name:" << item
->get_name()
1117 << " depth:" << item
->get_depth();
1123 } // namespace gnash
1128 // indent-tabs-mode: t