Make automated FSCommand invocation tests show player-side output.
[gnash.git] / libcore / DisplayList.cpp
blob0604186e8ef701c32065068f2a071ab83911cd1c
1 // dlist.cpp: Display lists, for Gnash.
2 //
3 // Copyright (C) 2005, 2006, 2007, 2008, 2009, 2010, 2011, 2012
4 // Free Software 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 "DisplayList.h"
23 #include <ostream>
24 #include <sstream>
25 #include <algorithm>
26 #include <stack>
27 #include <cassert>
28 #include <functional>
29 #include <boost/format.hpp>
31 #include "log.h"
32 #include "Renderer.h"
33 #include "StringPredicates.h"
34 #include "MovieClip.h"
35 #include "ObjectURI.h"
36 #include "utility.h"
38 namespace gnash {
40 // Forward declarations
41 namespace {
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);
51 #endif
53 /// Return the first element in the DisplayList whose depth exceeds
54 /// 65535 (-16384).
55 DisplayList::iterator dlistTagsEffectiveZoneEnd(
56 DisplayList::container_type& c);
60 /// Anonymous namespace for generic algorithm functors.
61 namespace {
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;
87 class NameEquals
89 public:
90 NameEquals(string_table& st, const ObjectURI& uri, bool caseless)
92 _ce(st, caseless),
93 _name(uri)
96 bool operator() (const DisplayObject* item) {
97 assert (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);
109 private:
110 const ObjectURI::CaseEquals _ce;
111 const ObjectURI& _name;
114 } // anonymous namespace
117 DisplayList::getNextHighestDepth() const
119 testInvariant();
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;
132 DisplayObject*
133 DisplayList::getDisplayObjectAtDepth(int depth) const
135 testInvariant();
137 for (DisplayObject* ch : _charsByDepth) {
139 // Should not be there!
140 if (ch->isDestroyed()) continue;
142 // found
143 if (ch->get_depth() == depth) return ch;
145 // non-existent (chars are ordered by depth)
146 if (ch->get_depth() > depth) return nullptr;
149 return nullptr;
154 DisplayObject*
155 DisplayList::getDisplayObjectByName(string_table& st, const ObjectURI& uri,
156 bool caseless) const
158 testInvariant();
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;
167 return *it;
171 void
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,
181 depth));
183 if (it == _charsByDepth.end() || (*it)->get_depth() != depth) {
184 // add the new char
185 _charsByDepth.insert(it, ch);
187 else {
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!)
196 *it = ch;
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);
208 testInvariant();
211 void
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,
219 depth));
221 if (it == _charsByDepth.end() || (*it)->get_depth() != depth) {
222 _charsByDepth.insert(it, ch);
224 else if (replace) *it = ch;
226 testInvariant();
229 void
230 DisplayList::replaceDisplayObject(DisplayObject* ch, int depth,
231 bool use_old_cxform, bool use_old_matrix)
233 testInvariant();
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);
248 else {
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)
268 *it = ch;
270 // Unload old char
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);
287 testInvariant();
291 // Updates the transform properties of the DisplayObject at
292 // the specified depth.
293 void
294 DisplayList::moveDisplayObject(int depth, const SWFCxForm* color_xform,
295 const SWFMatrix* mat, std::uint16_t* ratio)
297 testInvariant();
299 DisplayObject* ch = getDisplayObjectAtDepth(depth);
300 if (! ch) {
301 // FIXME, should this be log_aserror?
302 IF_VERBOSE_MALFORMED_SWF(
303 log_swferror(_("moveDisplayObject() -- can't find object at depth %d"),
304 depth);
306 return;
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.
318 return;
321 if (color_xform) ch->setCxForm(*color_xform);
322 if (mat) ch->setMatrix(*mat, true);
323 if (ratio) ch->set_ratio(*ratio);
325 testInvariant();
329 // Removes the object at the specified depth.
330 void
331 DisplayList::removeDisplayObject(int depth)
333 testInvariant();
335 #ifndef NDEBUG
336 container_type::size_type size = _charsByDepth.size();
337 #endif
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());
366 testInvariant();
370 // TODO: take DisplayObject by ref ?
371 void
372 DisplayList::swapDepths(DisplayObject* ch1, int newdepth)
374 testInvariant();
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);
382 return;
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);
396 // upper bound ...
397 container_type::iterator it2 =
398 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
399 std::bind(std::not2(DepthLessThan()), std::placeholders::_1,
400 newdepth));
402 if (it1 == _charsByDepth.end()) {
403 log_error(_("First argument to DisplayList::swapDepth() "
404 "is NOT a DisplayObject in the list. Call ignored."));
405 return;
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
414 // our parent is...
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);
423 else {
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();
444 testInvariant();
446 void
447 DisplayList::insertDisplayObject(DisplayObject* obj, int index)
449 testInvariant();
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,
460 index));
462 // Insert the DisplayObject before that position
463 _charsByDepth.insert(it, obj);
465 // Shift depths upwards until no depths are duplicated. No DisplayObjects
466 // are removed!
467 while (it != _charsByDepth.end() && (*it)->get_depth() == index) {
468 (*it)->set_depth(index + 1);
469 ++index, ++it;
472 testInvariant();
476 bool
477 DisplayList::unload()
479 testInvariant();
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; ) {
489 // make a copy
490 DisplayObject* di = *it;
492 // Destroyed objects should not be there!
493 assert(!di->isDestroyed());
495 // Destroy those with a handler anyway?
496 if (di->unload()) {
497 unloadHandler = true;
498 ++it;
499 continue;
502 if (!unloadHandler) {
503 di->destroy();
504 it = _charsByDepth.erase(it);
506 else ++it;
509 testInvariant();
511 return unloadHandler;
515 void
516 DisplayList::destroy()
518 testInvariant();
520 for (iterator it = _charsByDepth.begin(), itEnd = _charsByDepth.end();
521 it != itEnd; ) {
523 // make a copy
524 DisplayObject* di = *it;
526 // skip if already unloaded
527 if ( di->isDestroyed() ) {
528 ++it;
529 continue;
532 di->destroy();
533 it = _charsByDepth.erase(it);
535 testInvariant();
538 // Display the referenced DisplayObjects. Lower depths
539 // are obscured by higher depths.
540 void
541 DisplayList::display(Renderer& renderer, const Transform& base)
543 testInvariant();
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();
568 p = p->parent();
571 // check for non-mask hidden DisplayObjects
572 if (!renderAsMask && (!ch->visible())) {
573 ch->omit_display();
574 // Don't display non-mask hidden DisplayObjects
575 continue;
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();
608 void
609 DisplayList::omit_display()
611 iterator it = beginNonRemoved(_charsByDepth);
612 for (iterator endIt = _charsByDepth.end(); it != endIt; ++it) {
613 DisplayObject* ch = *it;
614 ch->omit_display();
618 void
619 DisplayList::add_invalidated_bounds(InvalidatedRanges& ranges, bool force)
621 testInvariant();
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
645 // into account
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);
676 else {
677 // copy the top mask
678 rangesStack.push(rangesStack.top());
682 if (drawing_mask) {
684 // --> The child is part of a mask, so add ranges to our
685 // mask ranges stack
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);
699 else {
701 if (rangesStack.empty()) {
702 // --> normal case for unmasked DisplayObjects
703 dobj->add_invalidated_bounds(ranges, force);
705 else {
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;
731 void
732 DisplayList::mergeDisplayList(DisplayList& newList, DisplayObject& o)
734 testInvariant();
736 iterator itOld = beginNonRemoved(_charsByDepth);
737 iterator itNew = beginNonRemoved(newList._charsByDepth);
739 iterator itOldEnd = dlistTagsEffectiveZoneEnd(_charsByDepth);
740 iterator itNewEnd = dlistTagsEffectiveZoneEnd(newList._charsByDepth);
742 // step1.
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) {
760 ++itOld;
761 // unload the DisplayObject if it's in static zone(-16384,0)
762 if (depthOld < 0) {
763 o.set_invalidated();
764 _charsByDepth.erase(itOldBackup);
766 if (chOld->unload()) reinsertRemovedCharacter(chOld);
767 else chOld->destroy();
770 break;
772 // depth is occupied in both lists
774 if (depthOld == depthNew) {
775 ++itOld;
776 ++itNew;
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
785 o.set_invalidated();
786 _charsByDepth.insert(itOldBackup, *itNewBackup);
787 _charsByDepth.erase(itOldBackup);
789 // unload the old DisplayObject
790 if (chOld->unload()) reinsertRemovedCharacter(chOld);
791 else chOld->destroy();
793 else {
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));
802 chNew->unload();
803 chNew->destroy();
806 break;
809 // depth in old list is empty, but occupied in new list.
810 ++itNew;
811 // add the new DisplayObject to the old list.
812 o.set_invalidated();
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;
826 o.set_invalidated();
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) {
837 o.set_invalidated();
838 _charsByDepth.insert(itOld, itNew, itNewEnd);
841 // step4.
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()) {
850 iterator it =
851 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
852 std::bind(std::not2(DepthLessThan()), std::placeholders::_1,
853 depthNew));
855 o.set_invalidated();
856 _charsByDepth.insert(it, *itNew);
860 // clear the new display list after merge
861 // ASSERT:
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()) {
871 iterator found =
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);
882 std::abort();
886 #endif
887 newList._charsByDepth.clear();
889 testInvariant();
893 void
894 DisplayList::reinsertRemovedCharacter(DisplayObject* ch)
896 assert(ch->unloaded());
897 assert(!ch->isDestroyed());
898 testInvariant();
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,
909 newDepth));
911 _charsByDepth.insert(it, ch);
913 testInvariant();
916 void
917 DisplayList::removeUnloaded()
919 testInvariant();
921 _charsByDepth.remove_if(std::mem_fn(&DisplayObject::unloaded));
923 testInvariant();
927 #if GNASH_PARANOIA_LEVEL > 1 && !defined(NDEBUG)
928 DisplayList::const_iterator
929 DisplayList::nonRemoved() const
931 return beginNonRemoved(_charsByDepth);
933 #endif
935 namespace {
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,
946 depth));
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,
959 depth));
961 #endif
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
974 std::ostream&
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";
981 size_t count = 0;
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%")
991 % count
992 % dobj
993 % dobj->get_depth()
994 % typeName(*dobj)
995 % boost::io::group(std::boolalpha, dobj->isDestroyed())
996 % boost::io::group(std::boolalpha, dobj->unloaded());
998 os << fmt.str() << std::endl;
1001 return os;
1004 } // namespace gnash
1007 // Local Variables:
1008 // mode: C++
1009 // indent-tabs-mode: t
1010 // End: