932 lines
32 KiB
C++
932 lines
32 KiB
C++
![]() |
/**********************************************************************
|
|||
|
|
|||
|
Filename : GFxDlist.cpp
|
|||
|
Content : Implementation of Diplay List
|
|||
|
Created :
|
|||
|
Authors : Artem Bolgar, Michael Antonov, TU
|
|||
|
|
|||
|
Copyright : (c) 2001-2008 Scaleform Corp. All Rights Reserved.
|
|||
|
|
|||
|
Notes : Significantly modified for GFx 3.0
|
|||
|
|
|||
|
Licensees may use this file in accordance with the valid Scaleform
|
|||
|
Commercial License Agreement provided with the software.
|
|||
|
|
|||
|
This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING
|
|||
|
THE WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR ANY PURPOSE.
|
|||
|
|
|||
|
**********************************************************************/
|
|||
|
|
|||
|
#include "GFxLog.h"
|
|||
|
#include "GFxPlayer.h"
|
|||
|
|
|||
|
#include "GFxDlist.h"
|
|||
|
#include "GFxDisplayContext.h"
|
|||
|
#include "GFxAction.h"
|
|||
|
#include "GFxFilterDesc.h"
|
|||
|
#include "AMP/GFxAmpViewStats.h"
|
|||
|
|
|||
|
|
|||
|
// ***** GFxDisplayList - Character entry access.
|
|||
|
|
|||
|
// Like the above, but looks for an exact match, and returns -1 if failed.
|
|||
|
UPInt GFxDisplayList::GetDisplayIndex(int depth)
|
|||
|
{
|
|||
|
UPInt index = FindDisplayIndex(depth);
|
|||
|
if (index >= DisplayObjectArray.GetSize()
|
|||
|
|| GetDisplayObject(index).GetDepth() != depth)
|
|||
|
{
|
|||
|
// No object at that depth.
|
|||
|
return GFC_MAX_UPINT;
|
|||
|
}
|
|||
|
return index;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
GFxCharacter* GFxDisplayList::GetCharacterAtDepth(int depth, bool* pisMarkedForRemove)
|
|||
|
{
|
|||
|
UPInt index = GetDisplayIndex(depth);
|
|||
|
if (index != GFC_MAX_UPINT)
|
|||
|
{
|
|||
|
GFxCharacter* ch = DisplayObjectArray[index].GetCharacter();
|
|||
|
GASSERT(ch);
|
|||
|
if (ch->GetDepth() == depth)
|
|||
|
{
|
|||
|
if (pisMarkedForRemove)
|
|||
|
*pisMarkedForRemove = DisplayObjectArray[index].IsMarkedForRemove();
|
|||
|
return ch;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
GFxCharacter* GFxDisplayList::GetCharacterAtDepthAndUnmark(int depth)
|
|||
|
{
|
|||
|
UPInt index = GetDisplayIndex(depth);
|
|||
|
if (index != GFC_MAX_UPINT)
|
|||
|
{
|
|||
|
GFxCharacter* ch = DisplayObjectArray[index].GetCharacter();
|
|||
|
GASSERT(ch);
|
|||
|
if (ch->GetDepth() == depth)
|
|||
|
{
|
|||
|
DisplayObjectArray[index].MarkForRemove(false);
|
|||
|
return ch;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
return NULL;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Swaps two objects at depths.
|
|||
|
bool GFxDisplayList::SwapDepths(int depth1, int depth2, UInt frame)
|
|||
|
{
|
|||
|
if (depth1 == depth2)
|
|||
|
return 1;
|
|||
|
|
|||
|
UPInt index1 = GetDisplayIndex(depth1);
|
|||
|
if (index1 == GFC_MAX_UPINT)
|
|||
|
return 0;
|
|||
|
UPInt index2 = FindDisplayIndex(depth2);
|
|||
|
|
|||
|
pCachedChar = NULL;
|
|||
|
if (index2 >= DisplayObjectArray.GetSize() ||
|
|||
|
GetDisplayObject(index2).GetDepth() != depth2)
|
|||
|
{
|
|||
|
// Index 2 must be used for insertion.
|
|||
|
// Move the object and set depth.
|
|||
|
DisplayEntry de(DisplayObjectArray[index1]);
|
|||
|
DisplayObjectArray.RemoveAt(index1);
|
|||
|
if (index1 < index2)
|
|||
|
index2--;
|
|||
|
DisplayObjectArray.InsertAt(index2, de);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Otherwise, we are swapping depths with both existing entries.
|
|||
|
G_Swap(DisplayObjectArray[index1], DisplayObjectArray[index2]);
|
|||
|
GFxCharacter* ch1 = DisplayObjectArray[index1].GetCharacter();
|
|||
|
if (ch1)
|
|||
|
{
|
|||
|
ch1->SetDepth(depth1);
|
|||
|
// if depth is changed and char is a timeline object then this
|
|||
|
// char will be removed at the timeline's loop
|
|||
|
ch1->SetCreateFrame(frame + 1);
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
GFxCharacter* ch2 = DisplayObjectArray[index2].GetCharacter();
|
|||
|
if (ch2)
|
|||
|
{
|
|||
|
ch2->SetDepth(depth2);
|
|||
|
// if depth is changed and char is a timeline object then this
|
|||
|
// char will be removed at the timeline's loop
|
|||
|
ch2->SetCreateFrame(frame + 1);
|
|||
|
}
|
|||
|
return 1;
|
|||
|
}
|
|||
|
|
|||
|
// Returns maximum used depth.
|
|||
|
int GFxDisplayList::GetLargestDepthInUse() const
|
|||
|
{
|
|||
|
// Display array is sorted by size, so get last value
|
|||
|
UPInt size = DisplayObjectArray.GetSize();
|
|||
|
return size ? DisplayObjectArray[size-1].GetDepth() : -1;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
GFxASCharacter* GFxDisplayList::GetCharacterByName(GASStringContext* psc, const GASString& name)
|
|||
|
{
|
|||
|
if (name.IsEmpty())
|
|||
|
return NULL;
|
|||
|
GFxCharacter* pch = NULL;
|
|||
|
|
|||
|
// See if we have a match on the display list.
|
|||
|
if (psc->IsCaseSensitive())
|
|||
|
{
|
|||
|
if (pCachedChar && pCachedChar->GetName() == name)
|
|||
|
return pCachedChar;
|
|||
|
|
|||
|
UPInt i, n = GetCharacterCount();
|
|||
|
for (i = 0; i < n; i++)
|
|||
|
{
|
|||
|
GFxCharacter* p = GetCharacter(i);
|
|||
|
GASSERT(p);
|
|||
|
if (p && p->IsCharASCharacter() && p->CharToASCharacter_Unsafe()->GetName() == name)
|
|||
|
{
|
|||
|
pch = p;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{ // Case-insensitive version.
|
|||
|
name.ResolveLowercase();
|
|||
|
if (pCachedChar && name.Compare_CaseInsensitive_Resolved(pCachedChar->GetName()))
|
|||
|
return pCachedChar;
|
|||
|
|
|||
|
UPInt i, n = GetCharacterCount();
|
|||
|
for (i = 0; i < n; i++)
|
|||
|
{
|
|||
|
GFxCharacter* p = GetCharacter(i);
|
|||
|
GASSERT(p);
|
|||
|
if (p && p->IsCharASCharacter() && name.Compare_CaseInsensitive_Resolved(p->CharToASCharacter_Unsafe()->GetName()))
|
|||
|
{
|
|||
|
pch = p;
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
// Only GFxASCharacter's have names.
|
|||
|
GASSERT((pch == 0) || pch->IsCharASCharacter());
|
|||
|
|
|||
|
pCachedChar = (GFxASCharacter*)pch;
|
|||
|
return pCachedChar;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// ***** GFxDisplayList - Display list management
|
|||
|
|
|||
|
|
|||
|
void GFxDisplayList::AddDisplayObject(const GFxCharPosInfo &pos, GFxCharacter* ch, UInt32 addFlags)
|
|||
|
{
|
|||
|
GASSERT(ch);
|
|||
|
|
|||
|
int depth = pos.Depth;
|
|||
|
UPInt size = DisplayObjectArray.GetSize();
|
|||
|
UPInt index = FindDisplayIndex(depth);
|
|||
|
pCachedChar = NULL;
|
|||
|
|
|||
|
if (addFlags & Flags_ReplaceIfDepthIsOccupied)
|
|||
|
{
|
|||
|
// Eliminate an existing object if it's in the way.
|
|||
|
|
|||
|
if (index < size)
|
|||
|
{
|
|||
|
DisplayEntry & dobj = DisplayObjectArray[index];
|
|||
|
|
|||
|
if (dobj.GetDepth() == depth)
|
|||
|
{
|
|||
|
// Unload the object at the occupied depth.
|
|||
|
UnloadEntryAtIndex(index);
|
|||
|
// correct size and index since they might be invalid
|
|||
|
// after UnloadEntryAtIndex.
|
|||
|
size = DisplayObjectArray.GetSize();
|
|||
|
index = FindDisplayIndex(depth);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Basically, the display list should contain the only one character at one particular depth.
|
|||
|
// But there are several exceptions from the depth<74>s uniqueness rule and for the short period
|
|||
|
// of time the display list may contain characters at the same depth:
|
|||
|
// 1. There could be several characters at the same depth < -32768. The <20>semi-dead<61> characters
|
|||
|
// (being removed but having the unload handler) are being moved to the depth = (-32769 <20> curDepth).
|
|||
|
// If several characters with onUnload handler were created and removed at the same depth in the same
|
|||
|
// frame then we may get several <20>semi-dead<61> characters at the same negative depth. These characters
|
|||
|
// will be cleaned up once their onUnload handlers are invoked.
|
|||
|
// 2. It is possible to get characters at the same depth during gotoFrame, when the current character
|
|||
|
// is marked for remove and a new character is being added at the same depth. These characters will be
|
|||
|
// cleaned up once UnloadMarkedForRemove() function is called right after the gotoFrame is finished.
|
|||
|
// 3. Another possibility of existence of several characters at the same depth is the gotoFrame backward.
|
|||
|
// For example if the character was created at frame 5 and we at the frame 10. Frame 4 contains another
|
|||
|
// character at the same depth. Now, we got gotoFrame(5). Since the CreateFrame of the existing character
|
|||
|
// is not greater than 5 this object will not be marked for remove. When gotoFrame(5) is executed we
|
|||
|
// create character for frame 4 at the same depth as the currently existing frame 5 instance has.
|
|||
|
// This extra object will be eliminated once ExecuteFrameTags(5) is executed (right after ExecuteSnapshot
|
|||
|
// is finished), because the tags for frame 5 will contain RemoveObject for the frame 4 character and
|
|||
|
// then AddObject for frame 5. New character will not be re-created here because the existing one will
|
|||
|
// be reused. Thus, these two characters will have different CreateFrame. It is also important that
|
|||
|
// AddObject adds record for the same depth character IN FRONT OF existing one (so, lower bound algorithm
|
|||
|
// should be used). Otherwise, the RemoveObject tag might remove incorrect character (it should also use
|
|||
|
// the lower bound algorithm when searching for the character to remove).
|
|||
|
GASSERT(!(size > 0 && (index < size) &&
|
|||
|
DisplayObjectArray[index].GetDepth() == depth && !DisplayObjectArray[index].IsMarkedForRemove() &&
|
|||
|
(DisplayObjectArray[index].GetCharacter() && DisplayObjectArray[index].GetCharacter()->GetCreateFrame() == ch->GetCreateFrame())));
|
|||
|
}
|
|||
|
|
|||
|
ch->SetDepth(depth);
|
|||
|
|
|||
|
DisplayEntry di;
|
|||
|
|
|||
|
di.SetCharacter(ch);
|
|||
|
ch->SetCxform(pos.ColorTransform);
|
|||
|
ch->SetMatrix(pos.Matrix_1);
|
|||
|
ch->SetRatio(pos.Ratio);
|
|||
|
ch->SetClipDepth(pos.ClipDepth);
|
|||
|
ch->SetBlendMode((GFxCharacter::BlendType)pos.BlendMode);
|
|||
|
ch->SetFilters(pos.Filters);
|
|||
|
|
|||
|
// Insert into the display list...
|
|||
|
GASSERT(index == FindDisplayIndex(depth));
|
|||
|
|
|||
|
DisplayObjectArray.InsertAt(index, di);
|
|||
|
|
|||
|
// Do the frame1 Actions (if applicable) and the "OnClipEvent (load)" event.
|
|||
|
ch->OnEventLoad();
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Updates the transform properties of the object at the specified depth.
|
|||
|
void GFxDisplayList::MoveDisplayObject(const GFxCharPosInfo &pos)
|
|||
|
{
|
|||
|
int depth = pos.Depth;
|
|||
|
UPInt size = DisplayObjectArray.GetSize();
|
|||
|
UPInt index = FindDisplayIndex(depth);
|
|||
|
|
|||
|
// Bounds check.
|
|||
|
if (index >= size)
|
|||
|
{
|
|||
|
if (size == 0)
|
|||
|
GFC_DEBUG_WARNING(1, "MoveDisplayObject() - no objects on display list");
|
|||
|
else
|
|||
|
GFC_DEBUG_WARNING1(1, "MoveDisplayObject() - can't find object at depth %d", depth);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
DisplayEntry& di = DisplayObjectArray[index];
|
|||
|
GFxCharacter* ch = di.GetCharacter();
|
|||
|
GASSERT(ch);
|
|||
|
if (ch->GetDepth() != depth)
|
|||
|
{
|
|||
|
GFC_DEBUG_WARNING1(1, "MoveDisplayObject() - no object at depth %d", depth);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
// Object re-added, unmark for remove.
|
|||
|
di.MarkForRemove(0);
|
|||
|
|
|||
|
if (!ch->GetAcceptAnimMoves())
|
|||
|
{
|
|||
|
// This GFxCharacter is rejecting anim moves. This happens after it
|
|||
|
// has been manipulated by ActionScript.
|
|||
|
if (ch->GetContinueAnimationFlag())
|
|||
|
ch->SetAcceptAnimMoves(true); // reset the flag
|
|||
|
else
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
if (pos.HasCxform())
|
|||
|
ch->SetCxform(pos.ColorTransform);
|
|||
|
if (pos.HasMatrix())
|
|||
|
ch->SetMatrix(pos.Matrix_1);
|
|||
|
if (pos.HasBlendMode())
|
|||
|
ch->SetBlendMode((GFxCharacter::BlendType)pos.BlendMode);
|
|||
|
if (pos.HasFilters())
|
|||
|
ch->SetFilters(pos.Filters);
|
|||
|
|
|||
|
ch->SetRatio(pos.Ratio);
|
|||
|
|
|||
|
// MoveDisplayObject apparently does not change clip depth! Thanks to Alexeev Vitaly.
|
|||
|
// ch->SetClipDepth(ClipDepth);
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Puts a new GFxCharacter at the specified depth, replacing any
|
|||
|
// existing GFxCharacter. If HasCxform or HasMatrix are false,
|
|||
|
// then keep those respective properties from the existing character.
|
|||
|
void GFxDisplayList::ReplaceDisplayObject(const GFxCharPosInfo &pos, GFxCharacter* ch)
|
|||
|
{
|
|||
|
UPInt size = DisplayObjectArray.GetSize();
|
|||
|
int depth = pos.Depth;
|
|||
|
UPInt index = FindDisplayIndex(depth);
|
|||
|
|
|||
|
// Bounds check.
|
|||
|
if (index >= size)
|
|||
|
{
|
|||
|
// Error, no existing object found at depth.
|
|||
|
// Fallback -- add the object.
|
|||
|
AddDisplayObject(pos, ch, Flags_ReplaceIfDepthIsOccupied);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
DisplayEntry& di = DisplayObjectArray[index];
|
|||
|
if (di.GetDepth() != depth)
|
|||
|
{
|
|||
|
// This may occur if we are seeking *back* in a movie and find a ReplaceObject tag.
|
|||
|
// In that case, a previous object will not exist, since it would have been removed,
|
|||
|
// if it did.
|
|||
|
AddDisplayObject(pos, ch, Flags_ReplaceIfDepthIsOccupied);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
GPtr<GFxCharacter> poldCh = di.GetCharacter();
|
|||
|
GASSERT(poldCh);
|
|||
|
|
|||
|
// Put the new GFxCharacter in its place.
|
|||
|
GASSERT(ch);
|
|||
|
ch->SetDepth(depth);
|
|||
|
ch->Restart();
|
|||
|
|
|||
|
// Set the display properties.
|
|||
|
di.MarkForRemove(0);
|
|||
|
di.SetCharacter(ch);
|
|||
|
|
|||
|
// Use old characters matrices if new ones not specified.
|
|||
|
ch->SetCxform(pos.HasCxform() ? pos.ColorTransform : poldCh->GetCxform());
|
|||
|
ch->SetMatrix(pos.HasMatrix() ? pos.Matrix_1 : poldCh->GetMatrix());
|
|||
|
ch->SetBlendMode((pos.HasBlendMode()) ? (GFxCharacter::BlendType)pos.BlendMode : poldCh->GetBlendMode());
|
|||
|
ch->SetRatio(pos.Ratio);
|
|||
|
ch->SetClipDepth(pos.ClipDepth);
|
|||
|
ch->SetFilters(pos.Filters);
|
|||
|
|
|||
|
poldCh->OnEventUnload();
|
|||
|
pCachedChar = NULL;
|
|||
|
}
|
|||
|
|
|||
|
void GFxDisplayList::ReplaceDisplayObjectAtIndex(UPInt index, GFxCharacter* ch)
|
|||
|
{
|
|||
|
// 1st validity checks.
|
|||
|
if (index >= DisplayObjectArray.GetSize())
|
|||
|
{
|
|||
|
// Error -- no GFxCharacter at the given depth.
|
|||
|
GFC_DEBUG_WARNING1(1, "ReplaceDisplayObjectAtIndex - invalid index %d", (UInt)index);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
pCachedChar = NULL;
|
|||
|
GFxDisplayList::DisplayEntry &e = GetDisplayObject(index);
|
|||
|
e.SetCharacter(ch);
|
|||
|
}
|
|||
|
|
|||
|
// Removes the object at the specified depth.
|
|||
|
void GFxDisplayList::RemoveDisplayObject(int depth, GFxResourceId id)
|
|||
|
{
|
|||
|
UPInt size = DisplayObjectArray.GetSize();
|
|||
|
UPInt index = FindDisplayIndex(depth);
|
|||
|
|
|||
|
// 1st validity checks.
|
|||
|
if (index >= size)
|
|||
|
{
|
|||
|
// Error -- no GFxCharacter at the given depth.
|
|||
|
GFC_DEBUG_WARNING1(1, "RemoveDisplayObject - no GFxCharacter at depth %d", depth);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
GPtr<GFxCharacter> ch = GetCharacter(index);
|
|||
|
GASSERT(ch);
|
|||
|
|
|||
|
// 2nd validity checks.
|
|||
|
if (ch->GetDepth() != depth)
|
|||
|
{
|
|||
|
// Error -- no GFxCharacter at the given depth.
|
|||
|
GFC_DEBUG_WARNING1(1, "RemoveDisplayObject - no GFxCharacter at depth %d", depth);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
GASSERT(ch->GetDepth() == depth);
|
|||
|
pCachedChar = NULL;
|
|||
|
|
|||
|
if (id != GFxResourceId::InvalidId)
|
|||
|
{
|
|||
|
// Caller specifies a specific id; scan forward til we find it.
|
|||
|
for (;;)
|
|||
|
{
|
|||
|
if (GetCharacter(index)->GetId() == id)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
if (index + 1 >= size || GetCharacter(index + 1)->GetDepth() != depth)
|
|||
|
{
|
|||
|
// Didn't find a match!
|
|||
|
GFC_DEBUG_WARNING2(1, "RemoveDisplayObject - no GFxCharacter at depth %d with id %d", depth, id.GetIdValue());
|
|||
|
return;
|
|||
|
}
|
|||
|
index++;
|
|||
|
}
|
|||
|
GASSERT(index < size);
|
|||
|
GASSERT(GetCharacter(index)->GetDepth() == depth);
|
|||
|
GASSERT(GetCharacter(index)->GetId() == id);
|
|||
|
}
|
|||
|
|
|||
|
UnloadEntryAtIndex(index);
|
|||
|
}
|
|||
|
|
|||
|
bool GFxDisplayList::RemoveCharacter(GFxCharacter* ch)
|
|||
|
{
|
|||
|
GASSERT(ch);
|
|||
|
int depth = ch->GetDepth();
|
|||
|
UPInt size = DisplayObjectArray.GetSize();
|
|||
|
UPInt index = FindDisplayIndex(depth);
|
|||
|
|
|||
|
// 1st validity checks.
|
|||
|
if (index >= size)
|
|||
|
{
|
|||
|
return false;
|
|||
|
}
|
|||
|
// we need to find a character to remove. The following loop is to iterate through
|
|||
|
// potential set of different characters at the same depth.
|
|||
|
GFxCharacter* pcurCh = GetCharacter(index);
|
|||
|
while (pcurCh && pcurCh != ch && depth == pcurCh->GetDepth() && index + 1 < size)
|
|||
|
{
|
|||
|
pcurCh = GetCharacter(++index);
|
|||
|
}
|
|||
|
if (index < size && pcurCh == ch)
|
|||
|
{
|
|||
|
// we've found a character to remove
|
|||
|
DisplayObjectArray.RemoveAt(index);
|
|||
|
pCachedChar = NULL;
|
|||
|
return true;
|
|||
|
}
|
|||
|
return false;
|
|||
|
}
|
|||
|
|
|||
|
void GFxDisplayList::MarkForRemoval(int depth, GFxResourceId id)
|
|||
|
{
|
|||
|
UPInt size = DisplayObjectArray.GetSize();
|
|||
|
UPInt index = FindDisplayIndex(depth);
|
|||
|
|
|||
|
// Validity checks.
|
|||
|
if (index >= size || GetCharacter(index)->GetDepth() != depth)
|
|||
|
{
|
|||
|
// Error -- no GFxCharacter at the given depth.
|
|||
|
GFC_DEBUG_WARNING1(1, "RemoveDisplayObject - no GFxCharacter at depth %d", depth);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
GASSERT(GetCharacter(index) && GetCharacter(index)->GetDepth() == depth);
|
|||
|
|
|||
|
if (id != GFxResourceId::InvalidId)
|
|||
|
{
|
|||
|
// Caller specifies a specific id; scan forward til we find it.
|
|||
|
for (;;)
|
|||
|
{
|
|||
|
if (GetCharacter(index)->GetId() == id)
|
|||
|
{
|
|||
|
break;
|
|||
|
}
|
|||
|
if (index + 1 >= size || GetCharacter(index + 1)->GetDepth() != depth)
|
|||
|
{
|
|||
|
// Didn't find a match!
|
|||
|
GFC_DEBUG_WARNING2(1, "RemoveDisplayObject - no GFxCharacter at depth %d with id %d", depth, id.GetIdValue());
|
|||
|
return;
|
|||
|
}
|
|||
|
index++;
|
|||
|
}
|
|||
|
GASSERT(index < size);
|
|||
|
GASSERT(GetCharacter(index)->GetDepth() == depth);
|
|||
|
GASSERT(GetCharacter(index)->GetId() == id);
|
|||
|
}
|
|||
|
|
|||
|
// Removing the GFxCharacter at GetDisplayObject(index).
|
|||
|
DisplayEntry& di = DisplayObjectArray[index];
|
|||
|
di.MarkForRemove(true);
|
|||
|
}
|
|||
|
|
|||
|
// Clear the display list.
|
|||
|
void GFxDisplayList::Clear()
|
|||
|
{
|
|||
|
UPInt i, n = DisplayObjectArray.GetSize();
|
|||
|
for (i = 0; i < n; i++)
|
|||
|
{
|
|||
|
DisplayEntry& di = DisplayObjectArray[i];
|
|||
|
GASSERT(di.GetCharacter());
|
|||
|
di.GetCharacter()->OnEventUnload();
|
|||
|
di.GetCharacter()->SetParent(NULL); //!AB: clear the parent for children in the display list
|
|||
|
// being cleared, since their parent used to be removed,
|
|||
|
// but children might stay alive for sometime.
|
|||
|
}
|
|||
|
pCachedChar = NULL;
|
|||
|
DisplayObjectArray.Clear();
|
|||
|
}
|
|||
|
|
|||
|
bool GFxDisplayList::UnloadAll()
|
|||
|
{
|
|||
|
pCachedChar = NULL;
|
|||
|
bool mayRemove = true;
|
|||
|
// don't replace DisplayObjectArray.GetSize() by pre-saved variable here!
|
|||
|
for (UPInt i = 0; i < DisplayObjectArray.GetSize(); )
|
|||
|
{
|
|||
|
// we need to correct index only if UnloadEntryAtIndex returns true;
|
|||
|
// this means, that the element at the index 'i' was removed and
|
|||
|
// we don't need to increment the index to get the next element.
|
|||
|
// If it returns false, then the element at index 'i' was moved
|
|||
|
// to the beginning of the array (since depth will be negative) and thus
|
|||
|
// we still need to increment the index.
|
|||
|
bool mayRemoveTheChild = UnloadEntryAtIndex(i);
|
|||
|
if (!mayRemoveTheChild)
|
|||
|
{
|
|||
|
++i;
|
|||
|
mayRemove = false;
|
|||
|
}
|
|||
|
}
|
|||
|
GASSERT(!pCachedChar);
|
|||
|
return mayRemove;
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
// Mark all entries for removal.
|
|||
|
void GFxDisplayList::MarkAllEntriesForRemoval(UInt targetFrame)
|
|||
|
{
|
|||
|
UPInt i, n = DisplayObjectArray.GetSize();
|
|||
|
for (i = 0; i < n; i++)
|
|||
|
{
|
|||
|
GPtr<GFxCharacter> ch = DisplayObjectArray[i].GetCharacter();
|
|||
|
|
|||
|
GASSERT(ch);
|
|||
|
|
|||
|
// do not mark characters created by ActionScript. But we still need to mark
|
|||
|
// characters, which were touched by AS, if they were created on frame other
|
|||
|
// than 0.
|
|||
|
int depth = ch->GetDepth();
|
|||
|
if (!ch || (depth >= 0 && depth < 16384 && ch->GetCreateFrame() > targetFrame))
|
|||
|
{
|
|||
|
//@DBG
|
|||
|
//if (ch && ch->IsASCharacter())
|
|||
|
// printf(" dlist.markForRemoval: %s, depth = %d, createFrame = %d\n",
|
|||
|
// ch->ToASCharacter()->GetCharacterHandle()->GetNamePath().ToCStr(),
|
|||
|
// ch->GetDepth(), ch->GetCreateFrame());
|
|||
|
//else if (ch)
|
|||
|
// printf(" dlist.markForRemoval: depth = %d, createFrame = %d\n",
|
|||
|
// ch->GetDepth(), ch->GetCreateFrame());
|
|||
|
//else
|
|||
|
// printf(" dlist.markForRemoval: <empty>\n");
|
|||
|
DisplayObjectArray[i].MarkForRemove(true);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Removes all objects that are marked for removal.
|
|||
|
void GFxDisplayList::UnloadMarkedObjects()
|
|||
|
{
|
|||
|
// Must be in first-to-last order.
|
|||
|
// don't replace DisplayObjectArray.GetSize() by pre-saved variable here!
|
|||
|
for (UPInt i = 0; i < DisplayObjectArray.GetSize(); i++)
|
|||
|
{
|
|||
|
if (DisplayObjectArray[i].IsMarkedForRemove())
|
|||
|
{
|
|||
|
// remove "marked-for-removal" flag
|
|||
|
DisplayObjectArray[i].ClearMarkForRemove();
|
|||
|
|
|||
|
// we need to correct index only if UnloadEntryAtIndex returns true;
|
|||
|
// this means, that the element at the index 'i' was removed and
|
|||
|
// we don't need to increment the index to get the next element.
|
|||
|
// If it returns false, then the element at index 'i' was moved
|
|||
|
// to the beginning of the array (since depth will be negative) and thus
|
|||
|
// we still need to increment the index.
|
|||
|
if (UnloadEntryAtIndex(i))
|
|||
|
i--;
|
|||
|
}
|
|||
|
}
|
|||
|
pCachedChar = NULL;
|
|||
|
}
|
|||
|
|
|||
|
bool GFxDisplayList::UnloadEntryAtIndex(UPInt index)
|
|||
|
{
|
|||
|
const DisplayEntry & dobj = DisplayObjectArray[index];
|
|||
|
|
|||
|
// If entry is already marked for remove, do nothing at the point,
|
|||
|
// let the UnloadMarkedObjects do its job later.
|
|||
|
if (dobj.IsMarkedForRemove())
|
|||
|
return false;
|
|||
|
|
|||
|
GFxCharacter* pch = dobj.GetCharacter();
|
|||
|
GASSERT(pch);
|
|||
|
|
|||
|
if (pch)
|
|||
|
{
|
|||
|
// It is possible that UnloadEntryAtIndex is called multiple times for the
|
|||
|
// same character. Do nothing if the pch is already being unloaded, return
|
|||
|
// 'false' since it can't be removed immediately.
|
|||
|
if (pch->IsUnloading())
|
|||
|
return false;
|
|||
|
|
|||
|
// OnUnloading handler determines the possibility of instant unloading.
|
|||
|
// It also queues up the onUnload AS event, if it is defined.
|
|||
|
bool mayRemove = pch->OnUnloading();
|
|||
|
pch->SetUnloading(true);
|
|||
|
if (mayRemove)
|
|||
|
{
|
|||
|
// We can remove the object instantly, so, do it now.
|
|||
|
pch->OnEventUnload();
|
|||
|
DisplayObjectArray.RemoveAt(index);
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
//GASSERT(dobj.GetDepth() >= 0);
|
|||
|
// if the depth is already negative this means the clip is already being removed.
|
|||
|
// Such situation is possible when a child clip has onUnload handler and when
|
|||
|
// removeMovieClip is called for the child clip first, and then removeMovieClip for
|
|||
|
// the parent.
|
|||
|
if (dobj.GetDepth() >= 0)
|
|||
|
{
|
|||
|
// ok, we can't remove the object right now, because of unload handler.
|
|||
|
// In this case we need to move the object to the depth = -1 - curDepth.
|
|||
|
// Note, GFx shifts all depths by +16384, so the real new depth of the
|
|||
|
// character will be -32769 - curDepth.
|
|||
|
int newDepth = -1 - dobj.GetDepth();
|
|||
|
|
|||
|
// Index 2 must be used for insertion.
|
|||
|
// Move the object and set depth.
|
|||
|
DisplayEntry de(dobj);
|
|||
|
DisplayObjectArray.RemoveAt(index);
|
|||
|
de.GetCharacter()->SetDepth(newDepth);
|
|||
|
UPInt index2 = FindDisplayIndex(newDepth);
|
|||
|
DisplayObjectArray.InsertAt(index2, de);
|
|||
|
}
|
|||
|
}
|
|||
|
pCachedChar = NULL;
|
|||
|
return mayRemove;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
DisplayObjectArray.RemoveAt(index);
|
|||
|
pCachedChar = NULL;
|
|||
|
}
|
|||
|
GASSERT(!pCachedChar);
|
|||
|
return true;
|
|||
|
}
|
|||
|
|
|||
|
// *** Frame processing.
|
|||
|
|
|||
|
#ifdef GFC_USE_OLD_ADVANCE
|
|||
|
// Advance referenced characters.
|
|||
|
void GFxDisplayList::AdvanceFrame(bool nextFrame, Float framePos)
|
|||
|
{
|
|||
|
// Objects further on display list get processed first in Flash.
|
|||
|
UPInt n = DisplayObjectArray.GetSize();
|
|||
|
|
|||
|
for (SPInt i = (SPInt)(n - 1); i >= 0; i--)
|
|||
|
{
|
|||
|
// @@@@ TODO FIX: If GArray changes size due to
|
|||
|
// GFxCharacter actions, the iteration may not be
|
|||
|
// correct!
|
|||
|
//
|
|||
|
// What's the correct thing to do here? Options:
|
|||
|
//
|
|||
|
// * copy the display list at the beginning,
|
|||
|
// iterate through the copy
|
|||
|
//
|
|||
|
// * Use (or emulate) a linked list instead of
|
|||
|
// an GArray (still has problems; e.G. what
|
|||
|
// happens if the next or current object gets
|
|||
|
// removed from the dlist?)
|
|||
|
//
|
|||
|
// * iterate through current GArray in depth
|
|||
|
// order. Always find the next object using a
|
|||
|
// search of current GArray (but optimize the
|
|||
|
// common case where nothing has changed).
|
|||
|
//
|
|||
|
// * ???
|
|||
|
//
|
|||
|
// Need to test to see what Flash does.
|
|||
|
if (n != DisplayObjectArray.GetSize())
|
|||
|
{
|
|||
|
GFC_DEBUG_WARNING(1, "GFxPlayer bug - dlist size changed due to GFxCharacter actions, bailing on update!");
|
|||
|
break;
|
|||
|
}
|
|||
|
|
|||
|
DisplayEntry & dobj = DisplayObjectArray[i];
|
|||
|
GFxCharacter* pch = dobj.GetCharacter();
|
|||
|
GASSERT(pch);
|
|||
|
if (pch && pch->IsUnloaded())
|
|||
|
{
|
|||
|
pch->OnEventUnload();
|
|||
|
DisplayObjectArray.RemoveAt(i);
|
|||
|
pCachedChar = NULL; // should we check if we remove exactly the pCachedChar?
|
|||
|
n = DisplayObjectArray.GetSize();
|
|||
|
continue;
|
|||
|
}
|
|||
|
|
|||
|
if (dobj.CanAdvanceChar())
|
|||
|
{
|
|||
|
pch->AdvanceFrame(nextFrame, framePos);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
#endif
|
|||
|
|
|||
|
|
|||
|
// Display the referenced characters. Lower depths
|
|||
|
// are obscured by higher depths.
|
|||
|
void GFxDisplayList::Display(GFxDisplayContext &context)
|
|||
|
{
|
|||
|
UInt maskCount = 0;
|
|||
|
int highestMaskedLayer = 0;
|
|||
|
GRenderer* prenderer = context.GetRenderer();
|
|||
|
|
|||
|
for (UPInt i = 0, n = DisplayObjectArray.GetSize(); i < n; i++)
|
|||
|
{
|
|||
|
DisplayEntry& dobj = DisplayObjectArray[i];
|
|||
|
GFxCharacter* ch = dobj.GetCharacter();
|
|||
|
GASSERT(ch);
|
|||
|
|
|||
|
if (ch->IsUnloading())
|
|||
|
continue;
|
|||
|
|
|||
|
// Don't display non-visible items, or items, used as a mask (MovieClip.setMask()).
|
|||
|
if (!ch->GetVisible() || ch->IsUsedAsMask() || ch->IsTopmostLevelFlagSet())
|
|||
|
continue;
|
|||
|
|
|||
|
// If mask ended before this layer, remove it.
|
|||
|
if (maskCount && (ch->GetDepth() > highestMaskedLayer))
|
|||
|
{
|
|||
|
maskCount--;
|
|||
|
context.PopMask();
|
|||
|
}
|
|||
|
|
|||
|
// Check to ensure that mask state is correct.
|
|||
|
if ((context.MaskStackIndexMax > context.MaskStackIndex) && !context.MaskRenderCount)
|
|||
|
{
|
|||
|
// A different mask was rendered before this one; hence we must restore the mask state.
|
|||
|
// Draw mask with Decrement op.
|
|||
|
GRenderer::Matrix* poldmatrix = context.pParentMatrix;
|
|||
|
GRenderer::Cxform* poldcxform = context.pParentCxform;
|
|||
|
context.MaskRenderCount++;
|
|||
|
while(context.MaskStackIndexMax > context.MaskStackIndex)
|
|||
|
{
|
|||
|
if (context.MaskStackIndexMax <= GFxDisplayContext::Mask_MaxStackSize)
|
|||
|
{
|
|||
|
GFxCharacter* pparentmask = context.MaskStack[context.MaskStackIndexMax-1]->GetParent();
|
|||
|
GRenderer::Matrix matrix;
|
|||
|
|
|||
|
#ifndef GFC_NO_3D
|
|||
|
if (pparentmask->Is3D() || context.Is3D)
|
|||
|
context.pParentMatrix = &GMatrix2D::Identity;
|
|||
|
else
|
|||
|
#endif
|
|||
|
{
|
|||
|
pparentmask->GetWorldMatrix(&matrix);
|
|||
|
context.pParentMatrix = &matrix;
|
|||
|
}
|
|||
|
|
|||
|
prenderer->BeginSubmitMask(GRenderer::Mask_Decrement);
|
|||
|
context.MaskStack[context.MaskStackIndexMax-1]->Display(context);
|
|||
|
prenderer->EndSubmitMask();
|
|||
|
context.MaskStack[context.MaskStackIndexMax-1] = 0;
|
|||
|
}
|
|||
|
context.MaskStackIndexMax--;
|
|||
|
}
|
|||
|
context.MaskRenderCount--;
|
|||
|
context.pParentMatrix = poldmatrix;
|
|||
|
context.pParentCxform = poldcxform;
|
|||
|
}
|
|||
|
|
|||
|
// Check whether this object should become mask; if so - apply.
|
|||
|
if ((ch->GetClipDepth() > 0) && !context.MaskRenderCount)
|
|||
|
{
|
|||
|
context.PushAndDrawMask(ch);
|
|||
|
|
|||
|
highestMaskedLayer = ch->GetClipDepth();
|
|||
|
maskCount++;
|
|||
|
}
|
|||
|
else
|
|||
|
{
|
|||
|
// Rendering of regular shapes (not masks).
|
|||
|
prenderer->PushBlendMode(ch->GetBlendMode());
|
|||
|
ch->Display(context);
|
|||
|
prenderer->PopBlendMode();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// If a mask masks the scene all the way up to the highest layer, it will not be
|
|||
|
// disabled at the end of drawing the display list, so disable it manually.
|
|||
|
while (maskCount > 0)
|
|||
|
{
|
|||
|
context.PopMask();
|
|||
|
maskCount--;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
|
|||
|
// Calls all onClipEvent handlers due to a mouse event.
|
|||
|
void GFxDisplayList::PropagateMouseEvent(const GFxEventId& id)
|
|||
|
{
|
|||
|
// Objects further on display list get mouse messages first.
|
|||
|
|
|||
|
for (SPInt i = (SPInt)(DisplayObjectArray.GetSize() - 1); i >= 0; i--)
|
|||
|
{
|
|||
|
DisplayEntry& dobj = DisplayObjectArray[i];
|
|||
|
GFxCharacter* ch = dobj.GetCharacter();
|
|||
|
GASSERT(ch);
|
|||
|
|
|||
|
if (!ch->GetVisible())
|
|||
|
continue;
|
|||
|
|
|||
|
ch->PropagateMouseEvent(id);
|
|||
|
|
|||
|
// Can display list change during an event ? check for bounds.
|
|||
|
// This probably isn't the right behavior, since some of characters might miss
|
|||
|
// the mouse event.
|
|||
|
// Copy pointers first, and process them later ? @TODO
|
|||
|
if (i >= (SPInt)DisplayObjectArray.GetSize())
|
|||
|
i = (SPInt)DisplayObjectArray.GetSize(); //? reset to the end?
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Calls all onClipEvent handlers due to a key event.
|
|||
|
void GFxDisplayList::PropagateKeyEvent(const GFxEventId& id, int* pkeyMask)
|
|||
|
{
|
|||
|
// Objects at the beginning of display list get key messages first.
|
|||
|
|
|||
|
for (UPInt i = 0, n = DisplayObjectArray.GetSize(); i < n; ++i)
|
|||
|
{
|
|||
|
DisplayEntry& dobj = DisplayObjectArray[i];
|
|||
|
GFxCharacter* ch = dobj.GetCharacter();
|
|||
|
GASSERT(ch);
|
|||
|
|
|||
|
if (!ch->GetVisible())
|
|||
|
continue;
|
|||
|
|
|||
|
ch->PropagateKeyEvent(id, pkeyMask);
|
|||
|
|
|||
|
// Can display list change during an event ? (answer: yes!) check for recent bounds.
|
|||
|
// This probably isn't the right behavior, since some of characters might miss
|
|||
|
// the key event.
|
|||
|
// Copy pointers first, and process them later ? @TODO.
|
|||
|
if (i + 1 >= DisplayObjectArray.GetSize())
|
|||
|
break;
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
// Visit all display list members that have a name.
|
|||
|
void GFxDisplayList::VisitMembers(GASObjectInterface::MemberVisitor *pvisitor, UInt visitFlags) const
|
|||
|
{
|
|||
|
GUNUSED(visitFlags);
|
|||
|
|
|||
|
// Enumeration Op lists children in-order.
|
|||
|
for (UPInt i = 0, n = DisplayObjectArray.GetSize(); i < n; ++i)
|
|||
|
{
|
|||
|
const DisplayEntry& dobj = DisplayObjectArray[i];
|
|||
|
GFxCharacter* ch = dobj.GetCharacter();
|
|||
|
GASSERT(ch);
|
|||
|
|
|||
|
// This is used in enumeration op, which returns values by name only.
|
|||
|
if (ch->IsCharASCharacter())
|
|||
|
{
|
|||
|
const GASString& name = ch->CharToASCharacter_Unsafe()->GetName();
|
|||
|
if (!name.IsEmpty())
|
|||
|
pvisitor->Visit(name, GASValue(ch->CharToASCharacter_Unsafe()), 0);
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
#ifdef GFC_BUILD_DEBUG
|
|||
|
// checks for consistency of the display list
|
|||
|
void GFxDisplayList::CheckConsistency() const
|
|||
|
{
|
|||
|
if (DisplayObjectArray.GetSize() > 0)
|
|||
|
{
|
|||
|
// check for multiple characters at the same depth. Note, sometimes
|
|||
|
// multiple characters at the same depth are allowed (see AddDisplayObject).
|
|||
|
|
|||
|
GFxCharacter* curch = DisplayObjectArray[0].GetCharacter();
|
|||
|
GASSERT(curch);
|
|||
|
int curdepth = DisplayObjectArray[0].GetDepth();
|
|||
|
for (UInt i = 1; i < DisplayObjectArray.GetSize(); i++)
|
|||
|
{
|
|||
|
GFxCharacter* ich = DisplayObjectArray[i].GetCharacter();
|
|||
|
GASSERT(ich);
|
|||
|
if (DisplayObjectArray[i].GetDepth() >= 0)
|
|||
|
GASSERT(DisplayObjectArray[i].GetDepth() != curdepth);
|
|||
|
curdepth = DisplayObjectArray[i].GetDepth();
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
#endif // GFC_BUILD_DEBUG
|
|||
|
|