Flag an error if the FB gui is configured without the rawfb device. For #108015.
[gnash.git] / libcore / DisplayList.cpp
blob1568b5215c26428aa1592a9d8bc0f83c390f4f1f
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 <boost/bind.hpp>
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 /// Return an constant iterator to the first element of the
47 /// container NOT in the "removed" depth zone
48 DisplayList::const_iterator beginNonRemoved(
49 const DisplayList::container_type& c);
51 /// Return the first element in the DisplayList whose depth exceeds
52 /// 65535 (-16384).
53 DisplayList::iterator dlistTagsEffectiveZoneEnd(
54 DisplayList::container_type& c);
58 /// Anonymous namespace for generic algorithm functors.
59 namespace {
61 struct DepthEquals : std::binary_function<const DisplayObject*, int, bool>
63 bool operator()(const DisplayObject* item, int depth) const {
64 if (!item) return false;
65 return item->get_depth() == depth;
69 struct DepthLessThan : std::binary_function<const DisplayObject*, int, bool>
71 bool operator()(const DisplayObject* item, int depth) const {
72 if (!item) return false;
73 return item->get_depth() < depth;
77 struct DepthGreaterThan : std::binary_function<const DisplayObject*, int, bool>
79 bool operator()(const DisplayObject* item, int depth) const {
80 if (!item) return false;
81 return item->get_depth() > depth;
85 class NameEquals
87 public:
88 NameEquals(string_table& st, const ObjectURI& uri, bool caseless)
90 _ce(st, caseless),
91 _name(uri)
94 bool operator() (const DisplayObject* item) {
95 assert (item);
97 // TODO: this is necessary because destroy() is called in
98 // movie_root, leaving destroyed items on the DisplayList. They
99 // shouldn't be found. A better fix would be to stop destroying
100 // objects there and add to the invariant that there are never
101 // destroyed DisplayObjects in the DisplayList.
102 if (item->isDestroyed()) return false;
104 return _ce(item->get_name(), _name);
107 private:
108 const ObjectURI::CaseEquals _ce;
109 const ObjectURI& _name;
112 } // anonymous namespace
115 DisplayList::getNextHighestDepth() const
117 testInvariant();
119 int nexthighestdepth=0;
120 for (const_iterator it = _charsByDepth.begin(),
121 itEnd = _charsByDepth.end(); it != itEnd; ++it) {
123 DisplayObject* ch = *it;
125 const int chdepth = ch->get_depth();
126 if (chdepth >= nexthighestdepth) {
127 nexthighestdepth = chdepth+1;
130 return nexthighestdepth;
133 DisplayObject*
134 DisplayList::getDisplayObjectAtDepth(int depth) const
136 testInvariant();
138 for (const_iterator it = _charsByDepth.begin(), itEnd = _charsByDepth.end();
139 it != itEnd; ++it) {
141 DisplayObject* ch = *it;
143 // Should not be there!
144 if (ch->isDestroyed()) continue;
146 // found
147 if (ch->get_depth() == depth) return ch;
149 // non-existent (chars are ordered by depth)
150 if (ch->get_depth() > depth) return 0;
153 return 0;
158 DisplayObject*
159 DisplayList::getDisplayObjectByName(string_table& st, const ObjectURI& uri,
160 bool caseless) const
162 testInvariant();
164 const container_type::const_iterator e = _charsByDepth.end();
166 container_type::const_iterator it =
167 std::find_if(_charsByDepth.begin(), e, NameEquals(st, uri, caseless));
169 if (it == e) return 0;
171 return *it;
175 void
176 DisplayList::placeDisplayObject(DisplayObject* ch, int depth)
178 assert(!ch->unloaded());
179 ch->set_invalidated();
180 ch->set_depth(depth);
182 container_type::iterator it =
183 std::find_if( _charsByDepth.begin(), _charsByDepth.end(),
184 boost::bind(std::not2(DepthLessThan()), _1, depth));
186 if (it == _charsByDepth.end() || (*it)->get_depth() != depth) {
187 // add the new char
188 _charsByDepth.insert(it, ch);
190 else {
191 // remember bounds of old char
192 InvalidatedRanges old_ranges;
193 (*it)->add_invalidated_bounds(old_ranges, true);
195 // make a copy (before replacing)
196 DisplayObject* oldCh = *it;
198 // replace existing char (before calling unload!)
199 *it = ch;
201 if (oldCh->unload()) {
202 // reinsert removed DisplayObject if needed
203 reinsertRemovedCharacter(oldCh);
205 else oldCh->destroy();
207 // extend invalidated bounds
208 ch->extend_invalidated_bounds(old_ranges);
211 testInvariant();
214 void
215 DisplayList::add(DisplayObject* ch, bool replace)
217 const int depth = ch->get_depth();
219 container_type::iterator it =
220 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
221 boost::bind(std::not2(DepthLessThan()), _1, depth));
223 if (it == _charsByDepth.end() || (*it)->get_depth() != depth) {
224 _charsByDepth.insert(it, ch);
226 else if (replace) *it = ch;
228 testInvariant();
231 void
232 DisplayList::replaceDisplayObject(DisplayObject* ch, int depth,
233 bool use_old_cxform, bool use_old_matrix)
235 testInvariant();
237 //GNASH_REPORT_FUNCTION;
238 assert(!ch->unloaded());
240 ch->set_invalidated();
241 ch->set_depth(depth);
243 container_type::iterator it =
244 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
245 boost::bind(std::not2(DepthLessThan()), _1, depth));
247 if (it == _charsByDepth.end() || (*it)->get_depth() != depth) {
248 _charsByDepth.insert(it, ch);
250 else {
251 // Make a copy (before replacing)
252 DisplayObject* oldch = *it;
254 InvalidatedRanges old_ranges;
256 if (use_old_cxform) {
257 // Use the SWFCxForm from the old DisplayObject.
258 ch->setCxForm(getCxForm(*oldch));
261 if (use_old_matrix) {
262 // Use the SWFMatrix from the old DisplayObject.
263 ch->setMatrix(getMatrix(*oldch), true);
266 // remember bounds of old char
267 oldch->add_invalidated_bounds(old_ranges, true);
269 // replace existing char (before calling unload)
270 *it = ch;
272 // Unload old char
273 if (oldch->unload()) {
274 // reinsert removed DisplayObject if needed
275 reinsertRemovedCharacter(oldch);
277 else oldch->destroy();
279 // extend invalidated bounds
280 // WARNING: when a new Button DisplayObject is added,
281 // the invalidated bounds computation will likely
282 // be bogus, as the actual DisplayObject shown is not
283 // instantiated until stagePlacementCallback for buttons
284 // (I'd say this is a bug in Button).
285 ch->extend_invalidated_bounds(old_ranges);
289 testInvariant();
293 // Updates the transform properties of the DisplayObject at
294 // the specified depth.
295 void
296 DisplayList::moveDisplayObject(int depth, const SWFCxForm* color_xform,
297 const SWFMatrix* mat, boost::uint16_t* ratio)
299 testInvariant();
301 DisplayObject* ch = getDisplayObjectAtDepth(depth);
302 if (! ch) {
303 // FIXME, should this be log_aserror?
304 IF_VERBOSE_MALFORMED_SWF(
305 log_swferror(_("moveDisplayObject() -- can't find object at depth %d"),
306 depth);
308 return;
311 if (ch->unloaded()) {
312 log_error(_("Request to move an unloaded DisplayObject"));
313 assert(!ch->unloaded());
316 // TODO: is sign of depth related to accepting anim moves ?
317 if (!ch->get_accept_anim_moves()) {
318 // This DisplayObject is rejecting anim moves. This happens after it
319 // has been manipulated by ActionScript.
320 return;
323 if (color_xform) ch->setCxForm(*color_xform);
324 if (mat) ch->setMatrix(*mat, true);
325 if (ratio) ch->set_ratio(*ratio);
327 testInvariant();
331 // Removes the object at the specified depth.
332 void
333 DisplayList::removeDisplayObject(int depth)
335 testInvariant();
337 #ifndef NDEBUG
338 container_type::size_type size = _charsByDepth.size();
339 #endif
341 // TODO: would it be legal to call removeDisplayObject with a depth
342 // in the "removed" zone ?
343 // TODO: optimize to take by-depth order into account
344 container_type::iterator it =
345 std::find_if( _charsByDepth.begin(), _charsByDepth.end(),
346 boost::bind(DepthEquals(), _1, depth));
348 if (it != _charsByDepth.end()) {
349 // Make a copy (before erasing)
350 DisplayObject* oldCh = *it;
352 // Erase (before calling unload)
353 _charsByDepth.erase(it);
355 if (oldCh->unload()) {
356 // reinsert removed DisplayObject if needed
357 // NOTE: could be optimized if we knew exactly how
358 // to handle the case in which the target depth
359 // (after the shift) is occupied already
361 reinsertRemovedCharacter(oldCh);
363 else oldCh->destroy();
366 assert(size >= _charsByDepth.size());
368 testInvariant();
372 // TODO: take DisplayObject by ref ?
373 void
374 DisplayList::swapDepths(DisplayObject* ch1, int newdepth)
376 testInvariant();
378 if (newdepth < DisplayObject::staticDepthOffset) {
379 IF_VERBOSE_ASCODING_ERRORS(
380 log_aserror(_("%s.swapDepth(%d) : ignored call with target depth "
381 "less then %d"), ch1->getTarget(), newdepth,
382 DisplayObject::staticDepthOffset);
384 return;
387 const int srcdepth = ch1->get_depth();
389 // what if source char is at a lower depth ?
390 assert(srcdepth >= DisplayObject::staticDepthOffset);
392 assert(srcdepth != newdepth);
394 // TODO: optimize this scan by taking ch1 depth into account ?
395 container_type::iterator it1 =
396 std::find(_charsByDepth.begin(), _charsByDepth.end(), ch1);
398 // upper bound ...
399 container_type::iterator it2 =
400 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
401 boost::bind(std::not2(DepthLessThan()), _1, newdepth));
403 if (it1 == _charsByDepth.end()) {
404 log_error(_("First argument to DisplayList::swapDepth() "
405 "is NOT a DisplayObject in the list. Call ignored."));
406 return;
409 // Found another DisplayObject at the given depth
410 if (it2 != _charsByDepth.end() && (*it2)->get_depth() == newdepth) {
411 DisplayObject* ch2 = *it2;
412 ch2->set_depth(srcdepth);
414 // TODO: we're not actually invalidated ourselves, rather
415 // our parent is...
416 ch2->set_invalidated();
418 // We won't accept static transforms after a depth swap.
419 // See displaylist_depths_test6.swf for more info.
420 ch2->transformedByScript();
422 std::iter_swap(it1, it2);
424 else {
425 // No DisplayObject found at the given depth
426 // Move the DisplayObject to the new position
427 // NOTE: insert *before* erasing, in case the list is
428 // the only referer of the ref-counted DisplayObject
429 _charsByDepth.insert(it2, ch1);
430 _charsByDepth.erase(it1);
433 // don't change depth before the iter_swap case above, as
434 // we'll need it to assign to the new DisplayObject
435 ch1->set_depth(newdepth);
437 // TODO: we're not actually invalidated ourselves, rather our parent is...
438 // UdoG ? Want to verify this ?
439 ch1->set_invalidated();
441 // We won't accept static transforms after a depth swap.
442 // See displaylist_depths_test6.swf for more info.
443 ch1->transformedByScript();
445 testInvariant();
447 void
448 DisplayList::insertDisplayObject(DisplayObject* obj, int index)
450 testInvariant();
452 assert(!obj->unloaded());
454 obj->set_invalidated();
455 obj->set_depth(index);
457 // Find the first index greater than or equal to the required index
458 container_type::iterator it =
459 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
460 boost::bind(std::not2(DepthLessThan()), _1, 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 boost::bind(std::not2(DepthLessThan()), _1, depthNew));
854 o.set_invalidated();
855 _charsByDepth.insert(it, *itNew);
859 // clear the new display list after merge
860 // ASSERT:
861 // - Any element in newList._charsByDepth is either marked as unloaded
862 // or found in this list
863 #if GNASH_PARANOIA_LEVEL > 1
864 for (iterator i = newList._charsByDepth.begin(),
865 e = newList._charsByDepth.end(); i != e; ++i) {
867 DisplayObject* ch = *i;
868 if (!ch->unloaded()) {
870 iterator found =
871 std::find(_charsByDepth.begin(), _charsByDepth.end(), ch);
873 if (found == _charsByDepth.end())
875 log_error)_("mergeDisplayList: DisplayObject %s (%s at depth "
876 "%d [%d]) about to be discarded in given display list"
877 " is not marked as unloaded and not found in the"
878 " merged current displaylist"),
879 ch->getTarget(), typeName(*ch), ch->get_depth(),
880 ch->get_depth()-DisplayObject::staticDepthOffset);
881 std::abort();
885 #endif
886 newList._charsByDepth.clear();
888 testInvariant();
892 void
893 DisplayList::reinsertRemovedCharacter(DisplayObject* ch)
895 assert(ch->unloaded());
896 assert(!ch->isDestroyed());
897 testInvariant();
899 // TODO: have this done by DisplayObject::unload() instead ?
900 int oldDepth = ch->get_depth();
901 int newDepth = DisplayObject::removedDepthOffset - oldDepth;
902 ch->set_depth(newDepth);
904 // TODO: optimize this by searching from the end(lowest depth).
905 container_type::iterator it =
906 std::find_if(_charsByDepth.begin(), _charsByDepth.end(),
907 boost::bind(std::not2(DepthLessThan()), _1, newDepth));
909 _charsByDepth.insert(it, ch);
911 testInvariant();
914 void
915 DisplayList::removeUnloaded()
917 testInvariant();
919 _charsByDepth.remove_if(boost::mem_fn(&DisplayObject::unloaded));
921 testInvariant();
925 #if GNASH_PARANOIA_LEVEL > 1 && !defined(NDEBUG)
926 DisplayList::const_iterator
927 DisplayList::nonRemoved() const
929 return beginNonRemoved(_charsByDepth);
931 #endif
933 namespace {
935 DisplayList::iterator
936 beginNonRemoved(DisplayList::container_type& c)
938 // Depths from -32678 to -16384 are removed
939 const int depth = 1 + DisplayObject::removedDepthOffset -
940 DisplayObject::staticDepthOffset;
942 return std::find_if(c.begin(), c.end(),
943 boost::bind(std::not2(DepthLessThan()), _1, depth));
946 DisplayList::const_iterator
947 beginNonRemoved(const DisplayList::container_type& c)
949 // Depths from -32678 to -16384 are removed
950 const int depth = 1 + DisplayObject::removedDepthOffset -
951 DisplayObject::staticDepthOffset;
953 return std::find_if(c.begin(), c.end(),
954 boost::bind(std::not2(DepthLessThan()), _1, depth));
957 DisplayList::iterator
958 dlistTagsEffectiveZoneEnd(DisplayList::container_type& c)
960 return std::find_if(c.begin(), c.end(),
961 boost::bind(DepthGreaterThan(), _1,
962 0xffff + DisplayObject::staticDepthOffset));
965 } // anonymous namespace
968 std::ostream&
969 operator<<(std::ostream& os, const DisplayList& dl)
971 if (dl._charsByDepth.empty()) return os << "Empty DisplayList";
973 os << "DisplayList size " << dl._charsByDepth.size() << "\n";
975 size_t count = 0;
977 for (DisplayList::const_iterator it = dl._charsByDepth.begin(),
978 itEnd = dl._charsByDepth.end(); it != itEnd; ++it, ++count) {
980 const DisplayObject* dobj = *it;
982 boost::format fmt = boost::format(
983 "Item %1% (%2%) at depth %3% (type %4%) "
984 "Destroyed: %5%, unloaded: %6%")
985 % count
986 % dobj
987 % dobj->get_depth()
988 % typeName(*dobj)
989 % boost::io::group(std::boolalpha, dobj->isDestroyed())
990 % boost::io::group(std::boolalpha, dobj->unloaded());
992 os << fmt.str() << std::endl;
995 return os;
998 } // namespace gnash
1001 // Local Variables:
1002 // mode: C++
1003 // indent-tabs-mode: t
1004 // End: