1 // dlist.cpp: Display lists, for Gnash.
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software Foundation, Inc
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 "DisplayList.h"
29 #include <boost/format.hpp>
33 #include "StringPredicates.h"
34 #include "MovieClip.h"
35 #include "ObjectURI.h"
40 // Forward declarations
42 /// Return an iterator to the first element of the container NOT
43 /// in the "removed" depth zone
44 DisplayList::iterator
beginNonRemoved(DisplayList::container_type
& c
);
46 #if GNASH_PARANOIA_LEVEL > 1 && !defined(NDEBUG)
47 /// Return an constant iterator to the first element of the
48 /// container NOT in the "removed" depth zone
49 DisplayList::const_iterator
beginNonRemoved(
50 const DisplayList::container_type
& c
);
53 /// Return the first element in the DisplayList whose depth exceeds
55 DisplayList::iterator
dlistTagsEffectiveZoneEnd(
56 DisplayList::container_type
& c
);
60 /// Anonymous namespace for generic algorithm functors.
63 struct DepthEquals
: std::binary_function
<const DisplayObject
*, int, bool>
65 bool operator()(const DisplayObject
* item
, int depth
) const {
66 if (!item
) return false;
67 return item
->get_depth() == depth
;
71 struct DepthLessThan
: std::binary_function
<const DisplayObject
*, int, bool>
73 bool operator()(const DisplayObject
* item
, int depth
) const {
74 if (!item
) return false;
75 return item
->get_depth() < depth
;
79 struct DepthGreaterThan
: std::binary_function
<const DisplayObject
*, int, bool>
81 bool operator()(const DisplayObject
* item
, int depth
) const {
82 if (!item
) return false;
83 return item
->get_depth() > depth
;
90 NameEquals(string_table
& st
, const ObjectURI
& uri
, bool caseless
)
96 bool operator() (const DisplayObject
* item
) {
99 // TODO: this is necessary because destroy() is called in
100 // movie_root, leaving destroyed items on the DisplayList. They
101 // shouldn't be found. A better fix would be to stop destroying
102 // objects there and add to the invariant that there are never
103 // destroyed DisplayObjects in the DisplayList.
104 if (item
->isDestroyed()) return false;
106 return _ce(item
->get_name(), _name
);
110 const ObjectURI::CaseEquals _ce
;
111 const ObjectURI
& _name
;
114 } // anonymous namespace
117 DisplayList::getNextHighestDepth() const
121 int nexthighestdepth
=0;
122 for (DisplayObject
* ch
: _charsByDepth
) {
124 const int chdepth
= ch
->get_depth();
125 if (chdepth
>= nexthighestdepth
) {
126 nexthighestdepth
= chdepth
+1;
129 return nexthighestdepth
;
133 DisplayList::getDisplayObjectAtDepth(int depth
) const
137 for (DisplayObject
* ch
: _charsByDepth
) {
139 // Should not be there!
140 if (ch
->isDestroyed()) continue;
143 if (ch
->get_depth() == depth
) return ch
;
145 // non-existent (chars are ordered by depth)
146 if (ch
->get_depth() > depth
) return nullptr;
155 DisplayList::getDisplayObjectByName(string_table
& st
, const ObjectURI
& uri
,
160 const container_type::const_iterator e
= _charsByDepth
.end();
162 container_type::const_iterator it
=
163 std::find_if(_charsByDepth
.begin(), e
, NameEquals(st
, uri
, caseless
));
165 if (it
== e
) return nullptr;
172 DisplayList::placeDisplayObject(DisplayObject
* ch
, int depth
)
174 assert(!ch
->unloaded());
175 ch
->set_invalidated();
176 ch
->set_depth(depth
);
178 container_type::iterator it
=
179 std::find_if( _charsByDepth
.begin(), _charsByDepth
.end(),
180 std::bind(std::not2(DepthLessThan()), std::placeholders::_1
,
183 if (it
== _charsByDepth
.end() || (*it
)->get_depth() != depth
) {
185 _charsByDepth
.insert(it
, ch
);
188 // remember bounds of old char
189 InvalidatedRanges old_ranges
;
190 (*it
)->add_invalidated_bounds(old_ranges
, true);
192 // make a copy (before replacing)
193 DisplayObject
* oldCh
= *it
;
195 // replace existing char (before calling unload!)
198 if (oldCh
->unload()) {
199 // reinsert removed DisplayObject if needed
200 reinsertRemovedCharacter(oldCh
);
202 else oldCh
->destroy();
204 // extend invalidated bounds
205 ch
->extend_invalidated_bounds(old_ranges
);
212 DisplayList::add(DisplayObject
* ch
, bool replace
)
214 const int depth
= ch
->get_depth();
216 container_type::iterator it
=
217 std::find_if(_charsByDepth
.begin(), _charsByDepth
.end(),
218 std::bind(std::not2(DepthLessThan()), std::placeholders::_1
,
221 if (it
== _charsByDepth
.end() || (*it
)->get_depth() != depth
) {
222 _charsByDepth
.insert(it
, ch
);
224 else if (replace
) *it
= ch
;
230 DisplayList::replaceDisplayObject(DisplayObject
* ch
, int depth
,
231 bool use_old_cxform
, bool use_old_matrix
)
235 //GNASH_REPORT_FUNCTION;
236 assert(!ch
->unloaded());
238 ch
->set_invalidated();
239 ch
->set_depth(depth
);
241 container_type::iterator it
=
242 std::find_if(_charsByDepth
.begin(), _charsByDepth
.end(),
243 std::bind(std::not2(DepthLessThan()), std::placeholders::_1
, depth
));
245 if (it
== _charsByDepth
.end() || (*it
)->get_depth() != depth
) {
246 _charsByDepth
.insert(it
, ch
);
249 // Make a copy (before replacing)
250 DisplayObject
* oldch
= *it
;
252 InvalidatedRanges old_ranges
;
254 if (use_old_cxform
) {
255 // Use the SWFCxForm from the old DisplayObject.
256 ch
->setCxForm(getCxForm(*oldch
));
259 if (use_old_matrix
) {
260 // Use the SWFMatrix from the old DisplayObject.
261 ch
->setMatrix(getMatrix(*oldch
), true);
264 // remember bounds of old char
265 oldch
->add_invalidated_bounds(old_ranges
, true);
267 // replace existing char (before calling unload)
271 if (oldch
->unload()) {
272 // reinsert removed DisplayObject if needed
273 reinsertRemovedCharacter(oldch
);
275 else oldch
->destroy();
277 // extend invalidated bounds
278 // WARNING: when a new Button DisplayObject is added,
279 // the invalidated bounds computation will likely
280 // be bogus, as the actual DisplayObject shown is not
281 // instantiated until stagePlacementCallback for buttons
282 // (I'd say this is a bug in Button).
283 ch
->extend_invalidated_bounds(old_ranges
);
291 // Updates the transform properties of the DisplayObject at
292 // the specified depth.
294 DisplayList::moveDisplayObject(int depth
, const SWFCxForm
* color_xform
,
295 const SWFMatrix
* mat
, std::uint16_t* ratio
)
299 DisplayObject
* ch
= getDisplayObjectAtDepth(depth
);
301 // FIXME, should this be log_aserror?
302 IF_VERBOSE_MALFORMED_SWF(
303 log_swferror(_("moveDisplayObject() -- can't find object at depth %d"),
309 if (ch
->unloaded()) {
310 log_error(_("Request to move an unloaded DisplayObject"));
311 assert(!ch
->unloaded());
314 // TODO: is sign of depth related to accepting anim moves ?
315 if (!ch
->get_accept_anim_moves()) {
316 // This DisplayObject is rejecting anim moves. This happens after it
317 // has been manipulated by ActionScript.
321 if (color_xform
) ch
->setCxForm(*color_xform
);
322 if (mat
) ch
->setMatrix(*mat
, true);
323 if (ratio
) ch
->set_ratio(*ratio
);
329 // Removes the object at the specified depth.
331 DisplayList::removeDisplayObject(int depth
)
336 container_type::size_type size
= _charsByDepth
.size();
339 // TODO: would it be legal to call removeDisplayObject with a depth
340 // in the "removed" zone ?
341 // TODO: optimize to take by-depth order into account
342 container_type::iterator it
=
343 std::find_if( _charsByDepth
.begin(), _charsByDepth
.end(),
344 std::bind(DepthEquals(), std::placeholders::_1
, depth
));
346 if (it
!= _charsByDepth
.end()) {
347 // Make a copy (before erasing)
348 DisplayObject
* oldCh
= *it
;
350 // Erase (before calling unload)
351 _charsByDepth
.erase(it
);
353 if (oldCh
->unload()) {
354 // reinsert removed DisplayObject if needed
355 // NOTE: could be optimized if we knew exactly how
356 // to handle the case in which the target depth
357 // (after the shift) is occupied already
359 reinsertRemovedCharacter(oldCh
);
361 else oldCh
->destroy();
364 assert(size
>= _charsByDepth
.size());
370 // TODO: take DisplayObject by ref ?
372 DisplayList::swapDepths(DisplayObject
* ch1
, int newdepth
)
376 if (newdepth
< DisplayObject::staticDepthOffset
) {
377 IF_VERBOSE_ASCODING_ERRORS(
378 log_aserror(_("%s.swapDepth(%d) : ignored call with target depth "
379 "less then %d"), ch1
->getTarget(), newdepth
,
380 DisplayObject::staticDepthOffset
);
385 const int srcdepth
= ch1
->get_depth();
387 // what if source char is at a lower depth ?
388 assert(srcdepth
>= DisplayObject::staticDepthOffset
);
390 assert(srcdepth
!= newdepth
);
392 // TODO: optimize this scan by taking ch1 depth into account ?
393 container_type::iterator it1
=
394 std::find(_charsByDepth
.begin(), _charsByDepth
.end(), ch1
);
397 container_type::iterator it2
=
398 std::find_if(_charsByDepth
.begin(), _charsByDepth
.end(),
399 std::bind(std::not2(DepthLessThan()), std::placeholders::_1
,
402 if (it1
== _charsByDepth
.end()) {
403 log_error(_("First argument to DisplayList::swapDepth() "
404 "is NOT a DisplayObject in the list. Call ignored."));
408 // Found another DisplayObject at the given depth
409 if (it2
!= _charsByDepth
.end() && (*it2
)->get_depth() == newdepth
) {
410 DisplayObject
* ch2
= *it2
;
411 ch2
->set_depth(srcdepth
);
413 // TODO: we're not actually invalidated ourselves, rather
415 ch2
->set_invalidated();
417 // We won't accept static transforms after a depth swap.
418 // See displaylist_depths_test6.swf for more info.
419 ch2
->transformedByScript();
421 std::iter_swap(it1
, it2
);
424 // No DisplayObject found at the given depth
425 // Move the DisplayObject to the new position
426 // NOTE: insert *before* erasing, in case the list is
427 // the only referer of the ref-counted DisplayObject
428 _charsByDepth
.insert(it2
, ch1
);
429 _charsByDepth
.erase(it1
);
432 // don't change depth before the iter_swap case above, as
433 // we'll need it to assign to the new DisplayObject
434 ch1
->set_depth(newdepth
);
436 // TODO: we're not actually invalidated ourselves, rather our parent is...
437 // UdoG ? Want to verify this ?
438 ch1
->set_invalidated();
440 // We won't accept static transforms after a depth swap.
441 // See displaylist_depths_test6.swf for more info.
442 ch1
->transformedByScript();
447 DisplayList::insertDisplayObject(DisplayObject
* obj
, int index
)
451 assert(!obj
->unloaded());
453 obj
->set_invalidated();
454 obj
->set_depth(index
);
456 // Find the first index greater than or equal to the required index
457 container_type::iterator it
=
458 std::find_if(_charsByDepth
.begin(), _charsByDepth
.end(),
459 std::bind(std::not2(DepthLessThan()), std::placeholders::_1
,
462 // Insert the DisplayObject before that position
463 _charsByDepth
.insert(it
, obj
);
465 // Shift depths upwards until no depths are duplicated. No DisplayObjects
467 while (it
!= _charsByDepth
.end() && (*it
)->get_depth() == index
) {
468 (*it
)->set_depth(index
+ 1);
477 DisplayList::unload()
481 bool unloadHandler
= false;
483 // All children with an unload handler should be unloaded. As soon as
484 // the first unload handler is encountered, subsequent children should
485 // not be destroyed or removed from the display list. This affects
486 // children without an unload handler.
487 for (iterator it
= beginNonRemoved(_charsByDepth
),
488 itEnd
= _charsByDepth
.end(); it
!= itEnd
; ) {
490 DisplayObject
* di
= *it
;
492 // Destroyed objects should not be there!
493 assert(!di
->isDestroyed());
495 // Destroy those with a handler anyway?
497 unloadHandler
= true;
502 if (!unloadHandler
) {
504 it
= _charsByDepth
.erase(it
);
511 return unloadHandler
;
516 DisplayList::destroy()
520 for (iterator it
= _charsByDepth
.begin(), itEnd
= _charsByDepth
.end();
524 DisplayObject
* di
= *it
;
526 // skip if already unloaded
527 if ( di
->isDestroyed() ) {
533 it
= _charsByDepth
.erase(it
);
538 // Display the referenced DisplayObjects. Lower depths
539 // are obscured by higher depths.
541 DisplayList::display(Renderer
& renderer
, const Transform
& base
)
545 std::stack
<int> clipDepthStack
;
547 // We only display DisplayObjects which are out of the "removed" zone
548 // (or should we check unloaded?)
549 for (iterator it
= beginNonRemoved(_charsByDepth
),
550 endIt
= _charsByDepth
.end(); it
!= endIt
; ++it
) {
552 DisplayObject
* ch
= *it
;
553 assert(!ch
->isDestroyed());
555 // Don't display dynamic masks
556 if (ch
->isDynamicMask()) continue;
558 assert(!ch
->unloaded()); // we don't advance unloaded chars
560 // Check if this charater or any of its parents is a mask.
561 // Characters acting as masks should always be rendered to the
562 // mask buffer despite their visibility.
563 DisplayObject
* p
= ch
->parent();
564 bool renderAsMask
= ch
->isMaskLayer();
566 while (!renderAsMask
&& p
) {
567 renderAsMask
= p
->isMaskLayer();
571 // check for non-mask hidden DisplayObjects
572 if (!renderAsMask
&& (!ch
->visible())) {
574 // Don't display non-mask hidden DisplayObjects
578 const int depth
= ch
->get_depth();
579 // Discard useless masks
580 while (!clipDepthStack
.empty() && (depth
> clipDepthStack
.top())) {
581 clipDepthStack
.pop();
582 renderer
.disable_mask();
585 // Push a new mask to the masks stack
586 if (ch
->isMaskLayer()) {
587 const int clipDepth
= ch
->get_clip_depth();
588 clipDepthStack
.push(clipDepth
);
589 renderer
.begin_submit_mask();
592 if (ch
->boundsInClippingArea(renderer
)) {
593 ch
->display(renderer
, base
);
595 else ch
->omit_display();
597 // Notify the renderer that mask drawing has finished.
598 if (ch
->isMaskLayer()) renderer
.end_submit_mask();
601 // Discard any remaining masks
602 while (!clipDepthStack
.empty()) {
603 clipDepthStack
.pop();
604 renderer
.disable_mask();
609 DisplayList::omit_display()
611 iterator it
= beginNonRemoved(_charsByDepth
);
612 for (iterator endIt
= _charsByDepth
.end(); it
!= endIt
; ++it
) {
613 DisplayObject
* ch
= *it
;
619 DisplayList::add_invalidated_bounds(InvalidatedRanges
& ranges
, bool force
)
623 // This function generally has nothing else to do than calling the
624 // add_invalidated_bounds() function of all items in the display list.
625 // However, special optimization is included for masks, which makes it
626 // look a bit more complicated. We want to avoid that a masked DisplayObject
627 // invalidates an area where the DisplayObject is invisible because of it's
628 // mask (which is quite common). For example, think of a large bitmap that
629 // covers the entire stage and is masked by a very small circle in the
630 // middle of the stage (ie. it's only visible there). Changes in the
631 // bitmap sprite would invalidate the whole stage even if only the small
632 // masked portion in the middle really needs to be drawn again.
634 // So, like display(), we keep a stack of masks. Instead of drawing the
635 // mask we keep a separate list of InvalidatedRanges for the masks which
636 // later are intersected with the masked DisplayObjects' InvalidatedRanges.
638 // The code is much based on the display() function, so some references
639 // in comments have been added for convenience.
641 // For a simpler implementation (that doesn't care about masks, but
642 // still produces correct results) see CVS revision 1.96
644 // TODO: review this function to take "dynamic" mask and maskees
647 std::stack
<int> clipDepthStack
; // same method used in display()
648 std::stack
<InvalidatedRanges
> rangesStack
;
649 bool drawing_mask
= false;
651 iterator it
= beginNonRemoved(_charsByDepth
);
652 for (iterator endIt
= _charsByDepth
.end(); it
!= endIt
; ++it
) {
653 DisplayObject
* dobj
= *it
;
655 const int depth
= dobj
->get_depth();
657 // Discard useless masks
658 while (!clipDepthStack
.empty() && (depth
> clipDepthStack
.top())) {
659 clipDepthStack
.pop();
660 rangesStack
.pop(); // disable_mask() equivalent
664 // Push a new mask to the masks stack
665 if (dobj
->isMaskLayer()) {
667 const int clipDepth
= dobj
->get_clip_depth();
668 clipDepthStack
.push(clipDepth
);
670 drawing_mask
= true; // begin_submit_mask equivalent
672 if (rangesStack
.empty()) {
673 InvalidatedRanges item
;
674 rangesStack
.push(item
);
678 rangesStack
.push(rangesStack
.top());
684 // --> The child is part of a mask, so add ranges to our
687 assert(!rangesStack
.empty());
688 dobj
->add_invalidated_bounds(rangesStack
.top(), true);
690 // need to call add_invalidated_bounds again because the previous
691 // call needs force==true. Changes to the mask itself may also
692 // require re-rendering of the mask area, so we have to
693 // add the mask itself to the global ranges, but this time
694 // with normal "force" value...
695 // As long the mask has not been invalidated and force==false this
696 // call won't modify the "ranges" list.
697 dobj
->add_invalidated_bounds(ranges
, force
);
701 if (rangesStack
.empty()) {
702 // --> normal case for unmasked DisplayObjects
703 dobj
->add_invalidated_bounds(ranges
, force
);
706 // --> DisplayObject is masked, so intersect with "mask"
708 // first get the ranges of the child in a separate list
709 InvalidatedRanges childRanges
;
710 childRanges
.inheritConfig(ranges
);
712 dobj
->add_invalidated_bounds(childRanges
, force
);
714 // then intersect ranges with topmost "mask"
715 childRanges
.intersect(rangesStack
.top());
717 // add result to the global ranges
718 ranges
.add(childRanges
);
720 } // not drawing mask
722 // <== end of display() equivalent
723 // Mask "drawing" has finished
724 if (dobj
->isMaskLayer()) {
725 // end_submit_mask equivalent
726 drawing_mask
= false;
732 DisplayList::mergeDisplayList(DisplayList
& newList
, DisplayObject
& o
)
736 iterator itOld
= beginNonRemoved(_charsByDepth
);
737 iterator itNew
= beginNonRemoved(newList
._charsByDepth
);
739 iterator itOldEnd
= dlistTagsEffectiveZoneEnd(_charsByDepth
);
740 iterator itNewEnd
= dlistTagsEffectiveZoneEnd(newList
._charsByDepth
);
743 // starting scanning both lists.
744 while (itOld
!= itOldEnd
) {
746 iterator itOldBackup
= itOld
;
748 DisplayObject
* chOld
= *itOldBackup
;
749 const int depthOld
= chOld
->get_depth();
751 while (itNew
!= itNewEnd
) {
752 iterator itNewBackup
= itNew
;
754 DisplayObject
* chNew
= *itNewBackup
;
755 const int depthNew
= chNew
->get_depth();
757 // depth in old list is occupied, and empty in new list.
758 if (depthOld
< depthNew
) {
761 // unload the DisplayObject if it's in static zone(-16384,0)
764 _charsByDepth
.erase(itOldBackup
);
766 if (chOld
->unload()) reinsertRemovedCharacter(chOld
);
767 else chOld
->destroy();
772 // depth is occupied in both lists
774 if (depthOld
== depthNew
) {
778 const bool is_ratio_compatible
=
779 (chOld
->get_ratio() == chNew
->get_ratio());
781 if (!is_ratio_compatible
|| chOld
->isDynamic() ||
782 !isReferenceable(*chOld
)) {
783 // replace the DisplayObject in old list with
784 // corresponding DisplayObject in new list
786 _charsByDepth
.insert(itOldBackup
, *itNewBackup
);
787 _charsByDepth
.erase(itOldBackup
);
789 // unload the old DisplayObject
790 if (chOld
->unload()) reinsertRemovedCharacter(chOld
);
791 else chOld
->destroy();
794 newList
._charsByDepth
.erase(itNewBackup
);
796 // replace the transformation SWFMatrix if the old
797 // DisplayObject accepts static transformation.
798 if (chOld
->get_accept_anim_moves()) {
799 chOld
->setMatrix(getMatrix(*chNew
), true);
800 chOld
->setCxForm(getCxForm(*chNew
));
809 // depth in old list is empty, but occupied in new list.
811 // add the new DisplayObject to the old list.
813 _charsByDepth
.insert(itOldBackup
, *itNewBackup
);
816 // break if finish scanning the new list
817 if (itNew
== itNewEnd
) break;
820 // step2(only required if scanning of new list finished earlier in step1).
821 // continue to scan the static zone of the old list.
822 // unload remaining DisplayObjects directly.
823 while ((itOld
!= itOldEnd
) && ((*itOld
)->get_depth() < 0)) {
825 DisplayObject
* chOld
= *itOld
;
827 itOld
= _charsByDepth
.erase(itOld
);
829 if (chOld
->unload()) reinsertRemovedCharacter(chOld
);
830 else chOld
->destroy();
833 // step3(only required if scanning of old list finished earlier in step1).
834 // continue to scan the new list.
835 // add remaining DisplayObjects directly.
836 if (itNew
!= itNewEnd
) {
838 _charsByDepth
.insert(itOld
, itNew
, itNewEnd
);
842 // Copy all unloaded DisplayObjects from the new display list to the
843 // old display list, and clear the new display list
844 for (itNew
= newList
._charsByDepth
.begin(); itNew
!= itNewEnd
; ++itNew
) {
846 DisplayObject
* chNew
= *itNew
;
847 const int depthNew
= chNew
->get_depth();
849 if (chNew
->unloaded()) {
851 std::find_if(_charsByDepth
.begin(), _charsByDepth
.end(),
852 std::bind(std::not2(DepthLessThan()), std::placeholders::_1
,
856 _charsByDepth
.insert(it
, *itNew
);
860 // clear the new display list after merge
862 // - Any element in newList._charsByDepth is either marked as unloaded
863 // or found in this list
864 #if GNASH_PARANOIA_LEVEL > 1
865 for (iterator i
= newList
._charsByDepth
.begin(),
866 e
= newList
._charsByDepth
.end(); i
!= e
; ++i
) {
868 DisplayObject
* ch
= *i
;
869 if (!ch
->unloaded()) {
872 std::find(_charsByDepth
.begin(), _charsByDepth
.end(), ch
);
874 if (found
== _charsByDepth
.end())
876 log_error
)_("mergeDisplayList: DisplayObject %s (%s at depth "
877 "%d [%d]) about to be discarded in given display list"
878 " is not marked as unloaded and not found in the"
879 " merged current displaylist"),
880 ch
->getTarget(), typeName(*ch
), ch
->get_depth(),
881 ch
->get_depth()-DisplayObject::staticDepthOffset
);
887 newList
._charsByDepth
.clear();
894 DisplayList::reinsertRemovedCharacter(DisplayObject
* ch
)
896 assert(ch
->unloaded());
897 assert(!ch
->isDestroyed());
900 // TODO: have this done by DisplayObject::unload() instead ?
901 int oldDepth
= ch
->get_depth();
902 int newDepth
= DisplayObject::removedDepthOffset
- oldDepth
;
903 ch
->set_depth(newDepth
);
905 // TODO: optimize this by searching from the end(lowest depth).
906 container_type::iterator it
=
907 std::find_if(_charsByDepth
.begin(), _charsByDepth
.end(),
908 std::bind(std::not2(DepthLessThan()), std::placeholders::_1
,
911 _charsByDepth
.insert(it
, ch
);
917 DisplayList::removeUnloaded()
921 _charsByDepth
.remove_if(std::mem_fn(&DisplayObject::unloaded
));
927 #if GNASH_PARANOIA_LEVEL > 1 && !defined(NDEBUG)
928 DisplayList::const_iterator
929 DisplayList::nonRemoved() const
931 return beginNonRemoved(_charsByDepth
);
937 DisplayList::iterator
938 beginNonRemoved(DisplayList::container_type
& c
)
940 // Depths from -32678 to -16384 are removed
941 const int depth
= 1 + DisplayObject::removedDepthOffset
-
942 DisplayObject::staticDepthOffset
;
944 return std::find_if(c
.begin(), c
.end(),
945 std::bind(std::not2(DepthLessThan()), std::placeholders::_1
,
949 #if GNASH_PARANOIA_LEVEL > 1 && !defined(NDEBUG)
950 DisplayList::const_iterator
951 beginNonRemoved(const DisplayList::container_type
& c
)
953 // Depths from -32678 to -16384 are removed
954 const int depth
= 1 + DisplayObject::removedDepthOffset
-
955 DisplayObject::staticDepthOffset
;
957 return std::find_if(c
.begin(), c
.end(),
958 std::bind(std::not2(DepthLessThan()), std::placeholders::_1
,
963 DisplayList::iterator
964 dlistTagsEffectiveZoneEnd(DisplayList::container_type
& c
)
966 return std::find_if(c
.begin(), c
.end(),
967 std::bind(DepthGreaterThan(), std::placeholders::_1
,
968 0xffff + DisplayObject::staticDepthOffset
));
971 } // anonymous namespace
975 operator<<(std::ostream
& os
, const DisplayList
& dl
)
977 if (dl
._charsByDepth
.empty()) return os
<< "Empty DisplayList";
979 os
<< "DisplayList size " << dl
._charsByDepth
.size() << "\n";
983 for (DisplayList::const_iterator it
= dl
._charsByDepth
.begin(),
984 itEnd
= dl
._charsByDepth
.end(); it
!= itEnd
; ++it
, ++count
) {
986 const DisplayObject
* dobj
= *it
;
988 boost::format fmt
= boost::format(
989 "Item %1% (%2%) at depth %3% (type %4%) "
990 "Destroyed: %5%, unloaded: %6%")
995 % boost::io::group(std::boolalpha
, dobj
->isDestroyed())
996 % boost::io::group(std::boolalpha
, dobj
->unloaded());
998 os
<< fmt
.str() << std::endl
;
1004 } // namespace gnash
1009 // indent-tabs-mode: t