3982 lines
129 KiB
C++
3982 lines
129 KiB
C++
/**********************************************************************
|
|
|
|
Filename : GFxShape.cpp
|
|
Content : Shape character definition with paths and edges
|
|
Created :
|
|
Authors : Michael Antonov, Artem Bolgar
|
|
|
|
Copyright : (c) 2005-2008 Scaleform Corp. All Rights Reserved.
|
|
Patent Pending. Contact Scaleform for more information.
|
|
|
|
Notes :
|
|
|
|
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 "GFile.h"
|
|
#include "GFxLog.h"
|
|
#include "GFxStream.h"
|
|
#include "GFxShape.h"
|
|
#include "GFxMesh.h"
|
|
#include "GFxCharacter.h"
|
|
#include "GFxPlayerImpl.h"
|
|
#include "GRenderer.h"
|
|
#include "GFxRenderGen.h"
|
|
|
|
#include "GFxLoadProcess.h"
|
|
|
|
#include "GFxImageResource.h"
|
|
#include "GFxDisplayContext.h"
|
|
#include "GFxMeshCacheManager.h"
|
|
#include "AMP/GFxAmpViewStats.h"
|
|
|
|
#include <float.h>
|
|
#include <stdlib.h>
|
|
|
|
//#if defined(GFC_OS_WIN32)
|
|
// #include <windows.h>
|
|
//#endif
|
|
|
|
#ifdef GFC_BUILD_DEBUG
|
|
//#define GFX_SHAPE_MEM_TRACKING
|
|
#endif //GFC_BUILD_DEBUG
|
|
|
|
#ifdef GFX_SHAPE_MEM_TRACKING
|
|
UInt GFx_MT_SwfGeomMem = 0;
|
|
UInt GFx_MT_PathsCount = 0;
|
|
UInt GFx_MT_GeomMem = 0;
|
|
UInt GFx_MT_ShapesCount = 0;
|
|
#endif //GFX_SHAPE_MEM_TRACKING
|
|
|
|
// Debugging macro: Define this to generate "shapes" file, used for
|
|
// shape extraction and external tessellator testing.
|
|
|
|
//
|
|
// Shape I/O helper functions.
|
|
//
|
|
|
|
// Read fill styles, and push them onto the given style GArray.
|
|
static int GFx_ReadFillStyles(GFxFillStyleArrayTemp* styles, GFxLoadProcess* p, GFxTagType tagType)
|
|
{
|
|
//GASSERT(styles);
|
|
|
|
// Get the count.
|
|
UInt fillStyleCount = p->ReadU8();
|
|
if (tagType > GFxTag_DefineShape)
|
|
{
|
|
if (fillStyleCount == 0xFF)
|
|
fillStyleCount = p->ReadU16();
|
|
}
|
|
int off = p->Tell();
|
|
|
|
if (styles)
|
|
{
|
|
p->LogParse(" GFx_ReadFillStyles: count = %d\n", fillStyleCount);
|
|
|
|
// Read the styles.
|
|
UInt baseIndex = (UInt)styles->GetSize();
|
|
if (fillStyleCount)
|
|
styles->Resize(baseIndex + fillStyleCount);
|
|
for (UInt i = 0; i < fillStyleCount; i++)
|
|
{
|
|
p->AlignStream(); //!AB
|
|
(*styles)[baseIndex + i].Read(p, tagType);
|
|
}
|
|
}
|
|
else if (fillStyleCount > 0)
|
|
{
|
|
p->LogError("Error: GFx_ReadFillStyles, trying to read %d fillstyles into no-style shape\n", fillStyleCount);
|
|
}
|
|
return off;
|
|
}
|
|
|
|
// Read line styles and push them onto the back of the given GArray.
|
|
static int GFx_ReadLineStyles(GFxLineStyleArrayTemp* styles, GFxLoadProcess* p, GFxTagType tagType)
|
|
{
|
|
//GASSERT(styles);
|
|
|
|
// Get the count.
|
|
UInt lineStyleCount = p->ReadU8();
|
|
|
|
p->LogParse(" GFx_ReadLineStyles: count = %d\n", lineStyleCount);
|
|
|
|
// @@ does the 0xFF flag apply to all tag types?
|
|
// if (TagType > GFxTag_DefineShape)
|
|
// {
|
|
if (lineStyleCount == 0xFF)
|
|
{
|
|
lineStyleCount = p->ReadU16();
|
|
p->LogParse(" GFx_ReadLineStyles: count2 = %d\n", lineStyleCount);
|
|
}
|
|
// }
|
|
int off = p->Tell();
|
|
|
|
if (styles)
|
|
{
|
|
// Read the styles.
|
|
UInt baseIndex = (UInt)styles->GetSize();
|
|
styles->Resize(baseIndex + lineStyleCount);
|
|
|
|
for (UInt i = 0; i < lineStyleCount; i++)
|
|
{
|
|
p->AlignStream(); //!AB
|
|
(*styles)[baseIndex + i].Read(p, tagType);
|
|
}
|
|
}
|
|
else if (lineStyleCount > 0)
|
|
{
|
|
p->LogError("Error: GFx_ReadLineStyles, trying to read %d linestyles into no-style shape\n", lineStyleCount);
|
|
}
|
|
return off;
|
|
}
|
|
|
|
// Implementation of virtual path Iterator, that may be used for shapes
|
|
// with unknown or mixed path data formats.
|
|
//
|
|
template<class ShapeDef, class PathData>
|
|
class GFxVirtualPathIterator : public GFxShapeBase::PathsIterator
|
|
{
|
|
typedef typename PathData::PathsIterator NativePathsIterator;
|
|
typedef typename PathData::EdgesIterator NativeEdgesIterator;
|
|
NativePathsIterator PathIter;
|
|
NativeEdgesIterator EdgeIter;
|
|
StateInfo State;
|
|
public:
|
|
GFxVirtualPathIterator(const ShapeDef* pdef) :
|
|
PathIter(pdef->GetNativePathsIterator())
|
|
{
|
|
if(PathIter.IsFinished())
|
|
{
|
|
State.State = StateInfo::St_Finished;
|
|
}
|
|
else
|
|
{
|
|
State.State = StateInfo::St_Setup;
|
|
PathIter.GetStyles(&State.Setup.Fill0, &State.Setup.Fill1, &State.Setup.Line);
|
|
EdgeIter = PathIter.GetEdgesIterator();
|
|
EdgeIter.GetMoveXY(&State.Setup.MoveX, &State.Setup.MoveY);
|
|
}
|
|
}
|
|
virtual bool GetNext(StateInfo* pcontext)
|
|
{
|
|
*pcontext = State;
|
|
switch(State.State)
|
|
{
|
|
case StateInfo::St_Setup:
|
|
case StateInfo::St_Edge:
|
|
if (EdgeIter.IsFinished())
|
|
{
|
|
State.State = StateInfo::St_NewPath;
|
|
PathIter.AdvanceBy(EdgeIter);
|
|
}
|
|
else
|
|
{
|
|
typename NativeEdgesIterator::Edge edge;
|
|
EdgeIter.GetEdge(&edge);
|
|
State.State = StateInfo::St_Edge;
|
|
State.Edge.Cx = edge.Cx;
|
|
State.Edge.Cy = edge.Cy;
|
|
State.Edge.Ax = edge.Ax;
|
|
State.Edge.Ay = edge.Ay;
|
|
State.Edge.Curve = edge.Curve;
|
|
}
|
|
break;
|
|
case StateInfo::St_NewPath:
|
|
if (PathIter.IsFinished())
|
|
State.State = StateInfo::St_Finished;
|
|
else
|
|
{
|
|
if (PathIter.IsNewShape())
|
|
PathIter.Skip();
|
|
State.State = StateInfo::St_Setup;
|
|
PathIter.GetStyles(&State.Setup.Fill0, &State.Setup.Fill1, &State.Setup.Line);
|
|
EdgeIter = NativeEdgesIterator(PathIter);
|
|
EdgeIter.GetMoveXY(&State.Setup.MoveX, &State.Setup.MoveY);
|
|
}
|
|
break;
|
|
default:;
|
|
}
|
|
return pcontext->State != StateInfo::St_Finished;
|
|
}
|
|
};
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// GFxShapeBase
|
|
//
|
|
GFxShapeBase::GFxShapeBase() :
|
|
RefCount(1), pMeshCache(0), pMeshSetBag(0), pPreTessMesh(0)
|
|
{
|
|
Bound = GRectF(0,0,0,0);
|
|
Flags = 0;
|
|
HintedGlyphSize = 0;
|
|
}
|
|
|
|
GFxShapeBase::~GFxShapeBase()
|
|
{
|
|
ResetCache();
|
|
}
|
|
|
|
// Thread-Safe ref-count implementation.
|
|
void GFxShapeBase::AddRef()
|
|
{
|
|
RefCount.Increment_NoSync();
|
|
// SInt32 val = RefCount.ExchangeAdd_NoSync(1);
|
|
// if (ResType == 100)
|
|
// printf("Thr %4d, %8x : GFxResource::AddRef (%d -> %d)\n", GetCurrentThreadId(), this, val, val+1);
|
|
}
|
|
void GFxShapeBase::Release()
|
|
{
|
|
if ((RefCount.ExchangeAdd_Acquire(-1) - 1) == 0)
|
|
// if (--RefCount == 0)
|
|
{
|
|
delete this;
|
|
}
|
|
}
|
|
|
|
void GFxShapeBase::ResetCache()
|
|
{
|
|
if (pMeshCache)
|
|
pMeshCache->AddShapeToKillList(this);
|
|
pMeshCache = 0;
|
|
delete pPreTessMesh;
|
|
pPreTessMesh = 0;
|
|
}
|
|
|
|
const GFxFillStyle* GFxShapeBase::GetFillStyles(UInt* pstylesNum) const
|
|
{
|
|
*pstylesNum = 0;
|
|
return NULL;
|
|
}
|
|
|
|
const GFxLineStyle* GFxShapeBase::GetLineStyles(UInt* pstylesNum) const
|
|
{
|
|
*pstylesNum = 0;
|
|
return NULL;
|
|
}
|
|
|
|
void GFxShapeBase::GetFillAndLineStyles
|
|
(const GFxFillStyle** ppfillStyles, UInt* pfillStylesNum,
|
|
const GFxLineStyle** pplineStyles, UInt* plineStylesNum) const
|
|
{
|
|
*ppfillStyles = NULL;
|
|
*pfillStylesNum = 0;
|
|
*pplineStyles = NULL;
|
|
*plineStylesNum = 0;
|
|
}
|
|
|
|
void GFxShapeBase::GetFillAndLineStyles(GFxDisplayParams* pparams) const
|
|
{
|
|
pparams->pFillStyles = NULL;
|
|
pparams->pLineStyles = NULL;
|
|
pparams->FillStylesNum = 0;
|
|
pparams->LineStylesNum = 0;
|
|
}
|
|
|
|
void GFxShapeBase::ApplyScale9Grid(GCompoundShape* shape,
|
|
const GFxScale9GridInfo& sg)
|
|
{
|
|
UInt i;
|
|
for (i = 0; i < shape->GetNumVertices(); ++i)
|
|
{
|
|
GPointType& v = shape->GetVertex(i);
|
|
sg.Transform(&v.x, &v.y);
|
|
}
|
|
}
|
|
|
|
// Draw the shape using our own inherent styles.
|
|
void GFxShapeBase::Display(GFxDisplayContext &context, GFxCharacter* inst)
|
|
{
|
|
GMatrix2D matrix = *context.pParentMatrix;
|
|
matrix.Prepend(inst->GetMatrix());
|
|
GRenderer::Cxform cxform = *context.pParentCxform;
|
|
cxform.Concatenate(inst->GetCxform());
|
|
|
|
GFxDisplayParams params(context, matrix,
|
|
cxform, inst->GetActiveBlendMode());
|
|
|
|
// Do viewport culling if bounds are available (and not 3D).
|
|
if (
|
|
#ifndef GFC_NO_3D
|
|
context.pParentMatrix3D == NULL &&
|
|
#endif
|
|
HasValidBounds())
|
|
{
|
|
GRectF tbounds;
|
|
|
|
params.Mat.EncloseTransform(&tbounds, Bound);
|
|
if (!inst->GetMovieRoot()->GetVisibleFrameRectInTwips().Intersects(tbounds))
|
|
{
|
|
if (!(context.GetRenderFlags() & GFxRenderConfig::RF_NoViewCull))
|
|
return;
|
|
}
|
|
}
|
|
|
|
if (Flags & Flags_StylesSupport)
|
|
{
|
|
// fill fill and line styles
|
|
GetFillAndLineStyles(¶ms.pFillStyles, ¶ms.FillStylesNum,
|
|
¶ms.pLineStyles, ¶ms.LineStylesNum);
|
|
}
|
|
Display(params, (inst->GetClipDepth() > 0), inst);
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// Display our shape. Use the FillStyles arg to
|
|
// override our default set of fill Styles (e.G. when
|
|
// rendering text).
|
|
void GFxShapeBase::Display (GFxDisplayParams& params,
|
|
bool edgeAADisabled,
|
|
GFxCharacter* inst)
|
|
{
|
|
const GFxRenderConfig &rconfig = *params.Context.GetRenderConfig();
|
|
GFxMeshCache* cache = params.Context.pMeshCacheManager->GetMeshCache();
|
|
|
|
#ifdef GFC_NO_FXPLAYER_EDGEAA
|
|
GUNUSED(edgeAADisabled);
|
|
bool useEdgeAA = 0;
|
|
#else
|
|
bool useEdgeAA = rconfig.IsUsingEdgeAA() &&
|
|
(rconfig.IsEdgeAATextured() || !HasTexturedFill()) &&
|
|
!edgeAADisabled && params.Context.MaskRenderCount == 0;
|
|
#endif
|
|
|
|
GPtr<GFxScale9GridInfo> s9g;
|
|
|
|
if ((Flags & Flags_S9GSupport) && inst && inst->DoesScale9GridExist())
|
|
s9g = *inst->CreateScale9Grid(params.Context.GetPixelScale() * rconfig.GetMaxCurvePixelError());
|
|
|
|
// Check to see if it's possible to use the pre-tessellated mesh.
|
|
// The pre-tessellated mesh must exist, plus there should not be
|
|
// scale9grid, plus, if the shape is used as a mask, there should not
|
|
// be EdgeAA.
|
|
if (pPreTessMesh && s9g.GetPtr() == 0)
|
|
{
|
|
if (useEdgeAA == pPreTessMesh->IsUsingEdgeAA())
|
|
{
|
|
pPreTessMesh->Display(params, false);
|
|
return;
|
|
}
|
|
}
|
|
|
|
GFxMeshSet* meshSet = 0;
|
|
|
|
// Compute the error tolerance in object-space.
|
|
Float maxScale = params.Mat.GetMaxScale();
|
|
|
|
//Float sx = mat.M_[0][0] + mat.M_[0][1];
|
|
//Float sy = mat.M_[1][0] + mat.M_[1][1];
|
|
//Float maxScale = sqrtf(sx*sx + sy*sy) * 0.707106781f;
|
|
|
|
if (fabsf(maxScale) < 1e-6f)
|
|
{
|
|
// Scale is essentially zero.
|
|
return;
|
|
}
|
|
|
|
bool cxformHasAddAlpha = (fabs(params.Cx.M_[3][1]) >= 1.0f);
|
|
|
|
// If Alpha is Zero, don't draw.
|
|
// Though, if the mask is being drawn, don't consider the alpha. Tested with MovieClip.setMask (!AB)
|
|
if ((fabs(params.Cx.M_[3][0]) < 0.001f) && !cxformHasAddAlpha && params.Context.MaskRenderCount == 0)
|
|
return;
|
|
|
|
Float masterScale = params.Context.GetPixelScale() * maxScale;
|
|
|
|
GFxMeshSet::KeyCategoryType meshKeyCat;
|
|
UInt meshKeyLen;
|
|
UInt meshKeyFlags =
|
|
(useEdgeAA != false) |
|
|
((cxformHasAddAlpha != false) << 1) |
|
|
((rconfig.IsOptimizingTriangles() != false) << 2);
|
|
UInt8 meshKey[GFxScale9GridInfo::KeySize];
|
|
|
|
// Make the meshKey. If scale9grid is in use we look for the
|
|
// meshSet whose key fits the given scale9grid.
|
|
// Otherwise just use the mesh that corresponds to the scaleKey.
|
|
//-------------------
|
|
if (s9g.GetPtr() == 0)
|
|
{
|
|
meshKey[0] = UInt8(meshKeyFlags);
|
|
meshKey[1] = UInt8(GFx_ComputeShapeScaleKey(masterScale, useEdgeAA));
|
|
meshKey[2] = UInt8(GFx_ComputeShapeScaleKey(rconfig.GetMaxCurvePixelError(), false));
|
|
meshKeyLen = 3;
|
|
meshKeyCat = GFxMeshSet::KeyCategory_Regular;
|
|
}
|
|
else
|
|
{
|
|
s9g->MakeKey(meshKey, masterScale, 0, (UInt8)meshKeyFlags);
|
|
meshKeyLen = GFxScale9GridInfo::KeySize;
|
|
meshKeyCat = GFxMeshSet::KeyCategory_Scale9Grid;
|
|
}
|
|
|
|
meshSet = cache->GetMeshSetAndLock(this, inst, meshKeyCat, meshKey, meshKeyLen);
|
|
if (meshSet)
|
|
{
|
|
meshSet->Display(params, s9g.GetPtr() != 0);
|
|
meshSet->Unlock();
|
|
return;
|
|
}
|
|
|
|
// Construct A new GFxMesh to handle this pixel size and the
|
|
// other visual parameters. Then tessellate it and store in
|
|
// the proper cache, depending on whether or not Scale9grid is in use.
|
|
//----------------
|
|
{
|
|
Float screenPixelSize = 20.0f / masterScale;
|
|
Float curveError = screenPixelSize * 0.75f * rconfig.GetMaxCurvePixelError();
|
|
meshSet = GHEAP_NEW(cache->GetHeap())
|
|
GFxMeshSet(screenPixelSize,
|
|
curveError, !useEdgeAA, rconfig.IsOptimizingTriangles());
|
|
#ifndef GFC_NO_FXPLAYER_EDGEAA
|
|
meshSet->SetAllowCxformAddAlpha(cxformHasAddAlpha);
|
|
#endif
|
|
Tessellate(meshSet, curveError, params.Context, s9g);
|
|
}
|
|
|
|
meshSet->SetMeshKey(meshKeyCat, meshKey, meshKeyLen, inst);
|
|
meshSet->Display(params, s9g.GetPtr() != 0);
|
|
cache->AddMeshSet(this, meshSet);
|
|
|
|
//printf("T");// DBG
|
|
}
|
|
|
|
// Find the bounds of this shape, and store them in
|
|
// the given rectangle.
|
|
template <class PathData>
|
|
void GFxShapeBase::ComputeBoundImpl(GRectF* r) const
|
|
{
|
|
r->Left = 1e10f;
|
|
r->Top = 1e10f;
|
|
r->Right = -1e10f;
|
|
r->Bottom = -1e10f;
|
|
|
|
typename PathData::PathsIterator it = typename PathData::PathsIterator(this);
|
|
while(!it.IsFinished())
|
|
{
|
|
if (it.IsNewShape())
|
|
{
|
|
it.Skip();
|
|
continue;
|
|
}
|
|
|
|
typename PathData::EdgesIterator edgesIt = it.GetEdgesIterator();
|
|
Float ax, ay;
|
|
edgesIt.GetMoveXY(&ax, &ay);
|
|
r->ExpandToPoint(ax, ay);
|
|
|
|
while (!edgesIt.IsFinished())
|
|
{
|
|
typename PathData::EdgesIterator::Edge edge;
|
|
edgesIt.GetEdge(&edge);
|
|
if (edge.Curve)
|
|
{
|
|
Float t, x, y;
|
|
t = GMath2D::CalcQuadCurveExtremum(ax, edge.Cx, edge.Ax);
|
|
if (t > 0 && t < 1)
|
|
{
|
|
GMath2D::CalcPointOnQuadCurve(
|
|
ax, ay,
|
|
edge.Cx, edge.Cy,
|
|
edge.Ax, edge.Ay,
|
|
t, &x, &y);
|
|
r->ExpandToPoint(x, y);
|
|
}
|
|
t = GMath2D::CalcQuadCurveExtremum(ay, edge.Cy, edge.Ay);
|
|
if (t > 0 && t < 1)
|
|
{
|
|
GMath2D::CalcPointOnQuadCurve(
|
|
ax, ay,
|
|
edge.Cx, edge.Cy,
|
|
edge.Ax, edge.Ay,
|
|
t, &x, &y);
|
|
r->ExpandToPoint(x, y);
|
|
}
|
|
}
|
|
r->ExpandToPoint(edge.Ax, edge.Ay);
|
|
ax = edge.Ax;
|
|
ay = edge.Ay;
|
|
}
|
|
it.AdvanceBy(edgesIt);
|
|
}
|
|
}
|
|
|
|
|
|
namespace GMath2D
|
|
{
|
|
//--------------------------------------------------------------------
|
|
struct QuadCoordType
|
|
{
|
|
Float x1, y1, x2, y2, x3, y3;
|
|
};
|
|
|
|
//--------------------------------------------------------------------
|
|
template<class CurveType>
|
|
void SubdivideQuadCurve(GCoordType x1, GCoordType y1,
|
|
GCoordType x2, GCoordType y2,
|
|
GCoordType x3, GCoordType y3,
|
|
GCoordType t,
|
|
CurveType* c1, CurveType* c2)
|
|
{
|
|
GCoordType x12 = x1 + t*(x2 - x1);
|
|
GCoordType y12 = y1 + t*(y2 - y1);
|
|
GCoordType x23 = x2 + t*(x3 - x2);
|
|
GCoordType y23 = y2 + t*(y3 - y2);
|
|
GCoordType x123 = x12 + t*(x23 - x12);
|
|
GCoordType y123 = y12 + t*(y23 - y12);
|
|
c1->x1 = x1;
|
|
c1->y1 = y1;
|
|
c1->x2 = x12;
|
|
c1->y2 = y12;
|
|
c1->x3 = x123;
|
|
c1->y3 = y123;
|
|
c2->x1 = x123;
|
|
c2->y1 = y123;
|
|
c2->x2 = x23;
|
|
c2->y2 = y23;
|
|
c2->x3 = x3;
|
|
c2->y3 = y3;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
inline GCoordType CalcPointOnQuadCurve1D(GCoordType x1,
|
|
GCoordType x2,
|
|
GCoordType x3,
|
|
GCoordType t)
|
|
{
|
|
x1 += t*(x2 - x1);
|
|
x2 += t*(x3 - x2);
|
|
return x1 + t*(x2 - x1);
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
inline bool CheckMonoCurveIntersection(GCoordType x1, GCoordType y1,
|
|
GCoordType x2, GCoordType y2,
|
|
GCoordType x3, GCoordType y3,
|
|
GCoordType x, GCoordType y)
|
|
{
|
|
// Check the Y-monotone quadratic curve for the intersection
|
|
// with a horizontal ray from (x,y) to the left. First check Y.
|
|
//----------------------
|
|
if (y >= y1 && y < y3) // Conditions >= && < are IMPORTANT!
|
|
{
|
|
// Early out. Check if all tree edges (triangle) lie on the left
|
|
// or on the right. First means "definitely no intersection",
|
|
// second - "definitely there is an intersection".
|
|
// It's OK to use bitwise expressions rather than logical
|
|
// as potentially more efficient.
|
|
//-----------------------
|
|
unsigned cp1 = GMath2D::CrossProduct(x1, y1, x2, y2, x, y) > 0;
|
|
unsigned cp2 = GMath2D::CrossProduct(x2, y2, x3, y3, x, y) > 0;
|
|
unsigned cp3 = GMath2D::CrossProduct(x1, y1, x3, y3, x, y) > 0;
|
|
|
|
if (cp1 & cp2 & cp3) // cp1>0 && cp2>0 && cp3>0, on the right
|
|
return true;
|
|
|
|
if ((cp1^1) & (cp2^1) & (cp3^1)) // cp1<=0 && cp2<=0 && cp3<=0, on the left
|
|
return false;
|
|
|
|
// Intermediate case, the point is inside the triangle.
|
|
// Calculate the real intersection point between the curve and
|
|
// the horizontal line at Y. Then check if the intersection point
|
|
// lies on the left of the given x.
|
|
//-----------------------
|
|
Float den = y1 - 2*y2 + y3;
|
|
Float t = -1;
|
|
if(den == 0)
|
|
{
|
|
den = y3 - y1;
|
|
if (den != 0)
|
|
t = (y - y1) / den;
|
|
}
|
|
else
|
|
{
|
|
// The initial expression is:
|
|
// t = (sqrtf(-y1 * (y3 - y) + y2*y2 - 2*y*y2 + y*y3) + y1 - y2) / den;
|
|
// Theoretically the expression under the sqrtf
|
|
// (-y1 * (y3 - y) + y2*y2 - 2*y*y2 + y*y3) must never be negative.
|
|
// However, it may occur because of finite precision of
|
|
// float/double. So that, it's a good idea to clamp the value to ]0...oo]
|
|
//------------------------
|
|
t = -y1 * (y3 - y) + y2*y2 - 2*y*y2 + y*y3;
|
|
t = (t > 0) ? sqrtf(t) : 0;
|
|
t = (t + y1 - y2) / den;
|
|
}
|
|
return GMath2D::CalcPointOnQuadCurve1D(x1, x2, x3, t) < x;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
//--------------------------------------------------------------------
|
|
bool CheckCurveIntersection(GCoordType x1, GCoordType y1,
|
|
GCoordType x2, GCoordType y2,
|
|
GCoordType x3, GCoordType y3,
|
|
GCoordType x, GCoordType y)
|
|
{
|
|
if(y2 >= y1 && y2 <= y3)
|
|
{
|
|
// A simple (Y-monotone) case
|
|
//-----------------
|
|
return CheckMonoCurveIntersection(x1, y1, x2, y2, x3, y3, x, y);
|
|
}
|
|
|
|
// The curve has a Y-extreme. Subdivide it at the
|
|
// extreme point and process separately.
|
|
//--------------------
|
|
Float dy = (2*y2 - y1 - y3);
|
|
Float ty = (dy == 0) ? -1 : (y2 - y1) / dy;
|
|
|
|
// Subdivide the curves they needs to be normalized
|
|
// (y1 must be less than y2). To do so it's theoretically
|
|
// enough to check only one of (c1.y1, c1.y3) or (c2.y1, c2.y3),
|
|
// but in practice it's better to check them both for the sake
|
|
// of numerical stability.
|
|
//--------------------
|
|
QuadCoordType c1, c2;
|
|
SubdivideQuadCurve(x1, y1, x2, y2, x3, y3, ty, &c1, &c2);
|
|
if(c1.y1 > c1.y3)
|
|
{
|
|
G_Swap(c1.x1, c1.x3);
|
|
G_Swap(c1.y1, c1.y3);
|
|
}
|
|
if(c2.y1 > c2.y3)
|
|
{
|
|
G_Swap(c2.x1, c2.x3);
|
|
G_Swap(c2.y1, c2.y3);
|
|
}
|
|
|
|
// The results of these calls must differ. That is, if the hit-test point
|
|
// lies on the left or on the right of both sub-curves, it definitely
|
|
// means that the curve is not contributing (0 or 2 intersections).
|
|
// Different results mean that the point lies between the sub-curves
|
|
// ("inside" the original curve).
|
|
//-------------------
|
|
return CheckMonoCurveIntersection(c1.x1, c1.y1, c1.x2, c1.y2, c1.x3, c1.y3, x, y) !=
|
|
CheckMonoCurveIntersection(c2.x1, c2.y1, c2.x2, c2.y2, c2.x3, c2.y3, x, y);
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<class PathData, class PathIterator>
|
|
bool GFxShapeBase::PointInShape(PathIterator& it, const GFxScale9GridInfo* s9g, Float x, Float y) const
|
|
{
|
|
#ifndef GFC_NO_FXPLAYER_STROKER
|
|
UInt lineStylesNum = 0;
|
|
const GFxLineStyle* lineStyles = GetLineStyles(&lineStylesNum);
|
|
bool strokeFlag = (lineStylesNum && (Flags & Flags_StylesSupport) != 0);
|
|
GPtr<GFxRenderGenStroker> str;
|
|
|
|
#endif
|
|
|
|
int styleCount = 0;
|
|
while(!it.IsFinished())
|
|
{
|
|
if (it.IsNewShape())
|
|
{
|
|
if (styleCount)
|
|
return true;
|
|
it.Skip();
|
|
styleCount = 0;
|
|
}
|
|
else
|
|
{
|
|
UInt fill0, fill1, line;
|
|
it.GetStyles(&fill0, &fill1, &line);
|
|
if (line > 0 || (fill0 == 0) != (fill1 == 0))
|
|
{
|
|
typedef typename PathData::EdgesIterator EdgesIteratorType;
|
|
EdgesIteratorType ei = it.GetEdgesIterator();
|
|
Float ax, ay;
|
|
ei.GetMoveXY(&ax, &ay);
|
|
if (s9g)
|
|
s9g->Transform(&ax, &ay);
|
|
|
|
#ifndef GFC_NO_FXPLAYER_STROKER
|
|
if (strokeFlag && line > 0)
|
|
{
|
|
if(str.GetPtr() == 0)
|
|
{
|
|
str = *GNEW GFxRenderGenStroker;
|
|
str->Shape.SetCurveTolerance(20.0f);
|
|
str->Stroke.SetCurveTolerance(str->Shape.GetCurveTolerance());
|
|
}
|
|
str->Shape.BeginPath(-1, -1, line - 1, ax, ay);
|
|
}
|
|
#endif
|
|
typename EdgesIteratorType::Edge edge;
|
|
while(!ei.IsFinished())
|
|
{
|
|
ei.GetEdge(&edge);
|
|
if (s9g)
|
|
s9g->Transform(&edge.Ax, &edge.Ay);
|
|
Float x1 = ax;
|
|
Float y1 = ay;
|
|
Float x3 = edge.Ax;
|
|
Float y3 = edge.Ay;
|
|
if(y1 > y3)
|
|
{
|
|
G_Swap(x1, x3);
|
|
G_Swap(y1, y3);
|
|
}
|
|
if (edge.Curve)
|
|
{
|
|
if (s9g)
|
|
s9g->Transform(&edge.Cx, &edge.Cy);
|
|
|
|
if ((fill0 == 0) != (fill1 == 0) &&
|
|
GMath2D::CheckCurveIntersection(x1, y1, edge.Cx, edge.Cy, x3, y3, x, y))
|
|
{
|
|
styleCount ^= 1;
|
|
}
|
|
}
|
|
else
|
|
if ((fill0 == 0) != (fill1 == 0) &&
|
|
y >= y1 && y < y3 &&
|
|
GMath2D::CrossProduct(x1, y1, x3, y3, x, y) > 0)
|
|
{
|
|
styleCount ^= 1;
|
|
}
|
|
|
|
#ifndef GFC_NO_FXPLAYER_STROKER
|
|
if (strokeFlag && line > 0)
|
|
{
|
|
if (edge.Curve)
|
|
str->Shape.AddCurve(edge.Cx, edge.Cy, edge.Ax, edge.Ay);
|
|
else
|
|
str->Shape.AddVertex(edge.Ax, edge.Ay);
|
|
}
|
|
#endif
|
|
ax = edge.Ax;
|
|
ay = edge.Ay;
|
|
}
|
|
it.AdvanceBy(ei);
|
|
}
|
|
else
|
|
{
|
|
it.SkipComplex();
|
|
}
|
|
}
|
|
}
|
|
if (styleCount)
|
|
return true;
|
|
|
|
#ifndef GFC_NO_FXPLAYER_STROKER
|
|
if(str.GetPtr())
|
|
{
|
|
for(UInt i = 0; i < str->Shape.GetNumPaths(); ++i)
|
|
{
|
|
const GCompoundShape::SPath& path = str->Shape.GetPath(i);
|
|
const GFxLineStyle& style = lineStyles[path.GetLineStyle()];
|
|
str->Stroker.SetWidth((style.GetWidth() < 20.0f) ? 20.0f : style.GetWidth());
|
|
str->Stroker.SetLineJoin(GFx_SWFToFxStroke_LineJoin(style.GetJoin()));
|
|
str->Stroker.SetStartLineCap(GFx_SWFToFxStroke_LineCap(style.GetStartCap()));
|
|
str->Stroker.SetEndLineCap(GFx_SWFToFxStroke_LineCap(style.GetEndCap()));
|
|
if (style.GetJoin() == GFxLineStyle::LineJoin_Miter)
|
|
str->Stroker.SetMiterLimit(style.GetMiterSize());
|
|
|
|
str->Stroke.Clear();
|
|
str->Stroker.GenerateStroke(path, str->Stroke);
|
|
if (str->Stroke.PointInShape(x, y, true))
|
|
return true;
|
|
}
|
|
}
|
|
#endif
|
|
return false;
|
|
}
|
|
|
|
/*
|
|
struct GFxPathBounds
|
|
{
|
|
float x1,y1,x2,y2;
|
|
};
|
|
|
|
bool GFxCmpPathBounds(const GFxPathBounds& a, const GFxPathBounds& b)
|
|
{
|
|
if (a.y1 != b.y1) return a.y1 < b.y1;
|
|
return a.x1 < b.x1;
|
|
}
|
|
*/
|
|
|
|
// Return true if the specified GRenderer::Point is on the interior of our shape.
|
|
// Incoming coords are local coords.
|
|
template <class PathData>
|
|
bool GFxShapeBase::DefPointTestLocalImpl(const GPointF &pt,
|
|
bool testShape,
|
|
const GFxCharacter* pinst) const
|
|
{
|
|
//printf("%f %f\n", pt.x, pt.y);// DBG
|
|
GPtr<GFxScale9GridInfo> s9g;
|
|
|
|
if (pinst && pinst->DoesScale9GridExist())
|
|
{
|
|
s9g = *pinst->CreateScale9Grid(1.0f);
|
|
if (s9g.GetPtr())
|
|
s9g->Compute();
|
|
}
|
|
|
|
GRectF b2 = Bound;
|
|
if (!HasValidBounds())
|
|
ComputeBound(&b2);
|
|
|
|
if (s9g.GetPtr())
|
|
b2 = s9g->AdjustBounds(b2);
|
|
|
|
// Early out.
|
|
if (!b2.Contains(pt))
|
|
return false;
|
|
|
|
// If we are within bounds and not testing shape, return true.
|
|
if (!testShape)
|
|
return true;
|
|
|
|
if (pinst && s9g.GetPtr() == 0 && pinst->CheckLastHitResult(pt.x, pt.y))
|
|
{
|
|
//printf("-");// DBG
|
|
return pinst->WasLastHitPositive();
|
|
}
|
|
|
|
typedef typename PathData::PathsIterator PathIteratorType;
|
|
PathIteratorType it = typename PathData::PathsIterator(this);
|
|
|
|
bool result = PointInShape<PathData, PathIteratorType>(it, s9g, pt.x, pt.y);
|
|
|
|
if (pinst)
|
|
pinst->SetLastHitResult(pt.x, pt.y, result);
|
|
|
|
//printf("*"); // DBG
|
|
return result;
|
|
}
|
|
|
|
|
|
|
|
// Push our shape data through the tessellator.
|
|
template<class PathData>
|
|
void GFxShapeBase::TessellateImpl(GFxMeshSet *meshSet, Float tolerance,
|
|
GFxDisplayContext &context,
|
|
GFxScale9GridInfo* s9g,
|
|
Float maxStrokeExtent) const
|
|
{
|
|
#ifdef GFX_AMP_SERVER_OR_RAGE_STATS
|
|
ScopeFunctionTimer tessTimer(context.pStats, NativeCodeSwdHandle, Func_GFxShapeBase_TessellateImpl, Amp_Profile_Level_Low);
|
|
#endif
|
|
|
|
GFxRenderGen* rg = context.pMeshCacheManager->GetRenderGen();
|
|
|
|
rg->Shape1.Clear(); // GCompoundShape cs;
|
|
rg->Shape1.SetCurveTolerance(tolerance);
|
|
rg->Shape1.SetNonZeroFill(IsNonZeroFill());
|
|
|
|
// Character shapes may have invalid bounds.
|
|
if (HasValidBounds())
|
|
meshSet->SetShapeBounds(Bound, maxStrokeExtent);
|
|
|
|
if (s9g)
|
|
{
|
|
s9g->Compute();
|
|
if (HasValidBounds())
|
|
meshSet->SetShapeBounds(s9g->AdjustBounds(Bound), maxStrokeExtent);
|
|
|
|
// Merging compatible image scale9grid generated by Grant Skinner's
|
|
// JSFL command. See http://www.gskinner.com/blog/archives/2010/04/bitmapslice9_sc.html
|
|
// The plug-in generates 9 rectangles in one shape, separated by NewShape flag.
|
|
// The conditions are:
|
|
// - the shape must have exactly 9 image fill styles
|
|
// - the shape must have exactly 9 subshapes
|
|
// - the sum area of all 9 rectangles must be equal to the area
|
|
// of the bounding box, with 2x2 pixels tolerance.
|
|
//--------------------------------------
|
|
unsigned imgStyleCount = 0;
|
|
if (Flags & Flags_StylesSupport)
|
|
{
|
|
unsigned numFillStyles = 0;
|
|
const GFxFillStyle* fillStyles = GetFillStyles(&numFillStyles);
|
|
if (numFillStyles == 9)
|
|
{
|
|
for(unsigned i = 0; i < 9; ++i)
|
|
{
|
|
// TO DO: revise and possibly check for the same image/texture
|
|
if (fillStyles[i].IsImageFill())
|
|
imgStyleCount++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (imgStyleCount == 9)
|
|
{
|
|
typename PathData::PathsIterator it = typename PathData::PathsIterator(this);
|
|
while(!it.IsFinished())
|
|
{
|
|
it.AddForTessellation(&rg->Shape1);
|
|
if (it.IsNewShape())
|
|
{
|
|
it.Skip();
|
|
}
|
|
}
|
|
if (rg->Shape1.GetNumPaths() == 9)
|
|
{
|
|
float x1, y1, x2, y2;
|
|
float area1 = 0;
|
|
for(unsigned i = 0; i < rg->Shape1.GetNumPaths(); ++i)
|
|
{
|
|
x1 = 1e30f;
|
|
y1 = 1e30f;
|
|
x2 = -1e30f;
|
|
y2 = -1e30f;
|
|
rg->Shape1.ExpandPathBounds(rg->Shape1.GetPath(i), &x1, &y1, &x2, &y2);
|
|
area1 += (x2-x1)*(y2-y1);
|
|
}
|
|
rg->Shape1.PerceiveBounds(&x1, &y1, &x2, &y2);
|
|
float area2 = (x2-x1)*(y2-y1);
|
|
const float areaTolerance = 20*2*20*2; // 2x2 pixels tolerance
|
|
|
|
if (fabsf(area1-area2) <= areaTolerance)
|
|
{
|
|
const GCompoundShape::SPath& path = rg->Shape1.GetPath(0);
|
|
if ((path.GetLeftStyle() < 0) != (path.GetRightStyle() < 0) && path.GetLineStyle() < 0)
|
|
{
|
|
int styleIdx = (path.GetLeftStyle() >= 0) ? path.GetLeftStyle() : path.GetRightStyle();
|
|
rg->Shape1.Clear();
|
|
rg->Shape1.BeginPath(styleIdx, -1, -1);
|
|
rg->Shape1.AddVertex(x1, y1);
|
|
rg->Shape1.AddVertex(x2, y1);
|
|
rg->Shape1.AddVertex(x2, y2);
|
|
rg->Shape1.AddVertex(x1, y2);
|
|
rg->Shape1.ClosePath();
|
|
unsigned numStyles;
|
|
GFxFillStyle* fillStyles = (GFxFillStyle*)GetFillStyles(&numStyles);
|
|
|
|
GRenderer::FillTexture texture;
|
|
fillStyles[styleIdx].GetFillTexture(&texture, context, 1, 0);
|
|
if (!texture.TextureMatrix.IsFreeRotation())
|
|
{
|
|
fillStyles[styleIdx].SetImageMatrix(GMatrix2D().AppendScaling(0.01f));
|
|
|
|
// The automatic slicing only works for clipped images,
|
|
// while in this case they are tiled. So, replace the type.
|
|
unsigned type = fillStyles[styleIdx].GetType();
|
|
if (type == GFxFill_TiledImage)
|
|
type = GFxFill_ClippedImage;
|
|
|
|
if (type == GFxFill_TiledSmoothImage)
|
|
type = GFxFill_ClippedSmoothImage;
|
|
|
|
fillStyles[styleIdx].SetFillType((GFxFillType)type);
|
|
|
|
// Done, store last set of meshes.
|
|
AddShapeToMesh(meshSet, &rg->Shape1, context, s9g);
|
|
rg->Shape1.Clear();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
rg->Shape1.Clear();
|
|
typename PathData::PathsIterator it = typename PathData::PathsIterator(this);
|
|
while(!it.IsFinished())
|
|
{
|
|
if (it.IsNewShape())
|
|
{
|
|
// Process the compound shape and prepare it for next set of paths.
|
|
AddShapeToMesh(meshSet, &rg->Shape1, context, s9g);
|
|
rg->Shape1.Clear();
|
|
it.Skip();
|
|
}
|
|
else
|
|
{
|
|
it.AddForTessellation(&rg->Shape1);
|
|
}
|
|
}
|
|
|
|
// Done, store last set of meshes.
|
|
AddShapeToMesh(meshSet, &rg->Shape1, context, s9g);
|
|
rg->Shape1.Clear();
|
|
|
|
if (s9g && s9g->ImgAdjustments.GetSize())
|
|
{
|
|
s9g->ComputeImgAdjustMatrices();
|
|
meshSet->SetImgAdjustMatrices(s9g->ImgAdjustments);
|
|
}
|
|
}
|
|
|
|
|
|
template<class PathData>
|
|
void GFxShapeBase::PreTessellateImpl(Float masterScale,
|
|
const GFxRenderConfig& config,
|
|
Float maxStrokeExtent)
|
|
{
|
|
if (pPreTessMesh)
|
|
return;
|
|
|
|
#ifdef GFC_NO_FXPLAYER_EDGEAA
|
|
bool useEdgeAA = 0;
|
|
#else
|
|
bool useEdgeAA = config.IsUsingEdgeAA() &&
|
|
(config.IsEdgeAATextured() || !HasTexturedFill());
|
|
#endif
|
|
|
|
GPtr<GFxRenderGen> rg = *GNEW GFxRenderGen(GMemory::GetGlobalHeap());
|
|
|
|
Float screenPixelSize = 20.0f / masterScale;
|
|
Float curveError = screenPixelSize * 0.75f * config.GetMaxCurvePixelError();
|
|
|
|
// Construct A new GFxMesh to handle this error tolerance.
|
|
pPreTessMesh = GHEAP_AUTO_NEW(this) GFxMeshSet(screenPixelSize,
|
|
curveError, !useEdgeAA, false);
|
|
|
|
rg->Shape1.Clear();
|
|
rg->Shape1.SetCurveTolerance(curveError);
|
|
rg->Shape1.SetNonZeroFill(IsNonZeroFill());
|
|
|
|
// Character shapes may have invalid bounds.
|
|
if (HasValidBounds())
|
|
pPreTessMesh->SetShapeBounds(Bound, maxStrokeExtent);
|
|
|
|
//PathsIterator it = GetPathsIterator();
|
|
typename PathData::PathsIterator it = typename PathData::PathsIterator(this);
|
|
UInt fillStylesNum = 0;
|
|
const GFxFillStyle* pfillStyles = GetFillStyles(&fillStylesNum);
|
|
while(!it.IsFinished())
|
|
{
|
|
if (it.IsNewShape())
|
|
{
|
|
// Add and Tessellate shape, including styles and line strips.
|
|
rg->Shape1.RemoveShortSegments(curveError / 4);
|
|
pPreTessMesh->AddTessellatedShape(rg->Shape1,
|
|
pfillStyles,
|
|
fillStylesNum,
|
|
(GFxDisplayContext*)0,
|
|
config);
|
|
// Prepare compound shape for nest set of paths.
|
|
rg->Shape1.Clear();
|
|
it.Skip();
|
|
}
|
|
else
|
|
{
|
|
it.AddForTessellation(&rg->Shape1);
|
|
}
|
|
}
|
|
rg->Shape1.RemoveShortSegments(curveError / 4);
|
|
pPreTessMesh->AddTessellatedShape(rg->Shape1,
|
|
pfillStyles,
|
|
fillStylesNum,
|
|
(GFxDisplayContext*)0,
|
|
config);
|
|
|
|
//printf("P"); // DBG
|
|
}
|
|
|
|
// Convert the paths to GCompoundShape, flattening the curves.
|
|
// The function ignores the NewShape flag and adds all paths
|
|
// GFxShapeBase contains.
|
|
template<class PathData>
|
|
void GFxShapeBase::MakeCompoundShapeImpl(GCompoundShape *cs, Float tolerance) const
|
|
{
|
|
cs->Clear();
|
|
cs->SetCurveTolerance(tolerance);
|
|
cs->SetNonZeroFill(IsNonZeroFill());
|
|
typename PathData::PathsIterator it = typename PathData::PathsIterator(this);
|
|
while(!it.IsFinished())
|
|
{
|
|
it.AddForTessellation(cs);
|
|
}
|
|
}
|
|
|
|
|
|
//------------------------------------------------------------------------------
|
|
static UInt32 GFx_ComputeBernsteinHash(const void* buf, UInt size, UInt32 seed = 5381)
|
|
{
|
|
const UByte* pdata = (const UByte*) buf;
|
|
UInt32 hash = seed;
|
|
while (size)
|
|
{
|
|
size--;
|
|
hash = ((hash << 5) + hash) ^ (UInt)pdata[size];
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
template<class PathData>
|
|
UInt32 GFxShapeBase::ComputeGeometryHashImpl() const
|
|
{
|
|
UInt32 hash = 5381;
|
|
UInt shapesCnt = 0, pathsCnt = 0;
|
|
GetShapeAndPathCounts(&shapesCnt, &pathsCnt);
|
|
hash = GFx_ComputeBernsteinHash(&shapesCnt, sizeof(shapesCnt), hash);
|
|
hash = GFx_ComputeBernsteinHash(&pathsCnt, sizeof(pathsCnt), hash);
|
|
typename PathData::PathsIterator pit = typename PathData::PathsIterator(this);
|
|
|
|
while(!pit.IsFinished())
|
|
{
|
|
UInt styles[3];
|
|
pit.GetStyles(&styles[0], &styles[1], &styles[2]);
|
|
hash = GFx_ComputeBernsteinHash(&styles, sizeof(styles), hash);
|
|
|
|
typename PathData::EdgesIterator eit = pit.GetEdgesIterator();
|
|
|
|
Float mx[2];
|
|
eit.GetMoveXY(&mx[0], &mx[1]);
|
|
hash = GFx_ComputeBernsteinHash(&mx, sizeof(mx), hash);
|
|
|
|
// Counting num of edges might be expensive! Especially for Swf paths.
|
|
//UInt numEdges = eit.GetEdgesCount();
|
|
//hash = GFx_ComputeBernsteinHash(&numEdges, sizeof(numEdges), hash);
|
|
while (!eit.IsFinished())
|
|
{
|
|
typename PathData::EdgesIterator::PlainEdge edge;
|
|
eit.GetPlainEdge(&edge);
|
|
hash = GFx_ComputeBernsteinHash(&edge.Data, edge.Size * sizeof(SInt32), hash);
|
|
}
|
|
pit.AdvanceBy(eit);
|
|
}
|
|
return hash;
|
|
}
|
|
|
|
template<class PathData>
|
|
bool GFxShapeBase::IsEqualGeometryImpl(const GFxShapeBase& cmpWith) const
|
|
{
|
|
UInt shapesCnt = 0, pathsCnt = 0;
|
|
GetShapeAndPathCounts(&shapesCnt, &pathsCnt);
|
|
UInt cmpWithShapesCnt = 0, cmpWithPathsCnt = 0;
|
|
cmpWith.GetShapeAndPathCounts(&cmpWithShapesCnt, &cmpWithPathsCnt);
|
|
if (shapesCnt != cmpWithShapesCnt || pathsCnt != cmpWithPathsCnt)
|
|
return false;
|
|
|
|
typename PathData::PathsIterator pit1 = typename PathData::PathsIterator(this);
|
|
typename PathData::PathsIterator pit2 = typename PathData::PathsIterator(&cmpWith);
|
|
while(!pit1.IsFinished())
|
|
{
|
|
if (pit2.IsFinished())
|
|
return false;
|
|
|
|
UInt styles1[3], styles2[3];
|
|
pit1.GetStyles(&styles1[0], &styles1[1], &styles1[2]);
|
|
pit2.GetStyles(&styles2[0], &styles2[1], &styles2[2]);
|
|
if (memcmp(styles1, styles2, sizeof(styles1)) != 0)
|
|
return false;
|
|
|
|
// !AB: theoretically, we need to compare fill styles too, but
|
|
// for font glyphs this is not critical.
|
|
|
|
typename PathData::EdgesIterator eit1 = pit1.GetEdgesIterator();
|
|
typename PathData::EdgesIterator eit2 = pit2.GetEdgesIterator();
|
|
|
|
Float ax1, ay1, ax2, ay2;
|
|
eit1.GetMoveXY(&ax1, &ay1);
|
|
eit2.GetMoveXY(&ax2, &ay2);
|
|
if (ax1 != ax2 || ay1 != ay2)
|
|
return false;
|
|
|
|
if (eit1.GetEdgesCount() != eit2.GetEdgesCount())
|
|
return false;
|
|
|
|
typename PathData::EdgesIterator::PlainEdge edge1;
|
|
typename PathData::EdgesIterator::PlainEdge edge2;
|
|
while (!eit1.IsFinished())
|
|
{
|
|
if (eit2.IsFinished())
|
|
return false;
|
|
|
|
eit1.GetPlainEdge(&edge1);
|
|
eit2.GetPlainEdge(&edge2);
|
|
|
|
if (edge1.Size != edge2.Size ||
|
|
memcmp(edge1.Data, edge2.Data, edge1.Size * sizeof(SInt)) != 0)
|
|
return false;
|
|
}
|
|
pit1.AdvanceBy(eit1);
|
|
pit2.AdvanceBy(eit2);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void GFxShapeBase::AddShapeToMesh(GFxMeshSet *meshSet,
|
|
GCompoundShape* cs,
|
|
GFxDisplayContext &context,
|
|
GFxScale9GridInfo* s9g) const
|
|
{
|
|
GRenderer::FillTexture texture;
|
|
GFxTexture9Grid t9g;
|
|
GRectF t9gClip;
|
|
bool useTiling = false;
|
|
|
|
const GFxFillStyle* pfillStyles = NULL;
|
|
UInt fillStylesNum = 0;
|
|
if (Flags & Flags_StylesSupport)
|
|
{
|
|
pfillStyles = GetFillStyles(&fillStylesNum);
|
|
}
|
|
|
|
// Add and tessellate shape, including styles and line strips.
|
|
|
|
if ((Flags & (Flags_S9GSupport | Flags_StylesSupport)) && s9g && s9g->CanUseTiling)
|
|
{
|
|
// The images can be drawn with automatic tiling according to the
|
|
// scale9grid. It is possible when:
|
|
// 1. The image is on the level of the scale9grid movie clip
|
|
// (s9g->CanUseTiling == true), and
|
|
// 2. The shape has only one path, one fill style, and no line style
|
|
// (which basically represents a Flash image as opposed to a shape
|
|
// with image fill style), and
|
|
// 3. The shape fill is ClippedImageFill (not tiled), and
|
|
// 4. The texture matrix has no free rotation.
|
|
//----------------------
|
|
int texture9GridStyle = GetTexture9GridStyle(*cs);
|
|
if (texture9GridStyle >= 0)
|
|
{
|
|
pfillStyles[texture9GridStyle].GetFillTexture(&texture, context, 1, 0);
|
|
if (!texture.TextureMatrix.IsFreeRotation())
|
|
{
|
|
// The clipping bounds for the texture9grid must be calculated for this
|
|
// particular part of the shape. It's incorrect to take just RectBound.
|
|
//--------------------------
|
|
cs->PerceiveBounds(&t9gClip.Left, &t9gClip.Top, &t9gClip.Right, &t9gClip.Bottom);
|
|
t9g.Compute(*s9g, t9gClip, meshSet->GetScaleMultiplier(), texture9GridStyle);
|
|
meshSet->AddTexture9Grid(t9g);
|
|
useTiling = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!useTiling)
|
|
{
|
|
if (s9g)
|
|
{
|
|
s9g->ComputeImgAdjustRects(*cs, pfillStyles, fillStylesNum);
|
|
ApplyScale9Grid(cs, *s9g);
|
|
}
|
|
cs->RemoveShortSegments(meshSet->GetCurveError() / 4);
|
|
meshSet->AddTessellatedShape(*cs, pfillStyles, fillStylesNum, context);
|
|
}
|
|
}
|
|
|
|
int GFxShapeBase::GetTexture9GridStyle(const GCompoundShape& cs) const
|
|
{
|
|
int styleIdx = -1;
|
|
if ((Flags & Flags_StylesSupport) && cs.GetNumPaths() == 1)
|
|
{
|
|
const GCompoundShape::SPath& path = cs.GetPath(0);
|
|
if ((path.GetLeftStyle() < 0) != (path.GetRightStyle() < 0) && path.GetLineStyle() < 0)
|
|
{
|
|
styleIdx = (path.GetLeftStyle() >= 0) ? path.GetLeftStyle() : path.GetRightStyle();
|
|
|
|
UInt fillStylesNum = 0;
|
|
const GFxFillStyle* pfillStyles = GetFillStyles(&fillStylesNum);
|
|
if (styleIdx < (int)fillStylesNum && !pfillStyles[styleIdx].IsClippedImageFill())
|
|
styleIdx = -1;
|
|
}
|
|
}
|
|
return styleIdx;
|
|
}
|
|
|
|
|
|
GFxPathAllocator::GFxPathAllocator(UInt pageSize) :
|
|
pFirstPage(0), pLastPage(0), FreeBytes(0), DefaultPageSize((UInt16)pageSize)
|
|
{
|
|
}
|
|
|
|
GFxPathAllocator::~GFxPathAllocator()
|
|
{
|
|
Clear();
|
|
}
|
|
|
|
void GFxPathAllocator::Clear()
|
|
{
|
|
for(Page* pcurPage = pFirstPage; pcurPage; )
|
|
{
|
|
Page* pnextPage = pcurPage->pNext;
|
|
GFREE(pcurPage);
|
|
pcurPage = pnextPage;
|
|
}
|
|
|
|
pFirstPage = 0;
|
|
pLastPage = 0;
|
|
FreeBytes = 0;
|
|
}
|
|
|
|
// edgesDataSize - size of geometrical data including the optional edge count, edge infos and vertices.
|
|
// Can be 0 if new shape.
|
|
// pathSize - 0 - new shape, 1 - 8-bit, 2 - 16-bit (aligned), 4 - 32-bit (aligned)
|
|
// edgeSize - 2 - 16-bit, 4 - 32-bit
|
|
UByte* GFxPathAllocator::AllocPath(UInt edgesDataSize, UInt pathSize, UInt edgeSize)
|
|
{
|
|
UInt freeBytes = FreeBytes;
|
|
UInt size = 1 + pathSize*3 + edgesDataSize;
|
|
UInt sizeForCurrentPage = size;
|
|
|
|
if (edgesDataSize > 0)
|
|
{
|
|
// calculate the actual size of path taking into account both alignments (for path and for edges)
|
|
UInt delta = 0;
|
|
if (pLastPage)
|
|
{
|
|
UByte* ptr = pLastPage->GetBufferPtr(freeBytes) + 1; // +1 is for first byte (bit flags)
|
|
|
|
// The first delta represents alignment for path
|
|
delta = (UInt)(((UPInt)ptr) & (pathSize - 1));
|
|
delta = ((delta + pathSize - 1) & (~(pathSize - 1))) - delta; // aligned delta
|
|
|
|
// The second delta (delta2) represents the alignment for edges, taking into account
|
|
// the previous alignment.
|
|
UInt delta2 = (UInt)((UPInt)ptr + delta + pathSize*3) & (edgeSize - 1);
|
|
delta2 = ((delta2 + edgeSize - 1) & (~(edgeSize - 1))) - delta2;
|
|
|
|
delta += delta2;
|
|
}
|
|
sizeForCurrentPage += delta;
|
|
if (!pLastPage || FreeBytes < sizeForCurrentPage)
|
|
{
|
|
// new page will be allocated, calculate new delta size
|
|
delta = (1 + (pathSize - 1)) & (~(pathSize - 1));
|
|
delta = ((delta + pathSize*3 + (edgeSize - 1)) & (~(edgeSize - 1))) + edgesDataSize - size;
|
|
}
|
|
size += delta;
|
|
}
|
|
UByte* ptr = AllocMemoryBlock(sizeForCurrentPage, size);
|
|
return ptr;
|
|
}
|
|
|
|
UByte* GFxPathAllocator::AllocRawPath(UInt32 sizeInBytes)
|
|
{
|
|
return AllocMemoryBlock(sizeInBytes, sizeInBytes);
|
|
}
|
|
|
|
UByte* GFxPathAllocator::AllocMemoryBlock(UInt32 sizeForCurrentPage, UInt32 sizeInNewPage)
|
|
{
|
|
UInt size = sizeInNewPage;
|
|
UInt freeBytes = FreeBytes;
|
|
if (pLastPage == NULL || FreeBytes < sizeForCurrentPage)
|
|
{
|
|
UInt pageSize = DefaultPageSize;
|
|
Page* pnewPage;
|
|
if (size > pageSize)
|
|
{
|
|
pageSize = size;
|
|
}
|
|
freeBytes = pageSize;
|
|
pnewPage = (Page*)GHEAP_AUTO_ALLOC(this, sizeof(Page) + pageSize);
|
|
// Was: GFxStatMD_ShapeData_Mem);
|
|
#ifdef GFC_BUILD_DEBUG
|
|
memset(pnewPage, 0xba, sizeof(Page) + pageSize);
|
|
#endif
|
|
pnewPage->pNext = NULL;
|
|
pnewPage->PageSize = pageSize;
|
|
if (pLastPage)
|
|
{
|
|
pLastPage->pNext = pnewPage;
|
|
// correct page size
|
|
pLastPage->PageSize = pLastPage->PageSize - FreeBytes;
|
|
}
|
|
pLastPage = pnewPage;
|
|
if (!pFirstPage)
|
|
pFirstPage = pnewPage;
|
|
}
|
|
else
|
|
{
|
|
size = sizeForCurrentPage;
|
|
}
|
|
|
|
UByte* ptr = pLastPage->GetBufferPtr(freeBytes);
|
|
#ifdef GFC_BUILD_DEBUG
|
|
memset(ptr, 0xcc, size);
|
|
#endif
|
|
FreeBytes = (UInt16)(freeBytes - size);
|
|
|
|
return ptr;
|
|
}
|
|
|
|
bool GFxPathAllocator::ReallocLastBlock(UByte* ptr, UInt32 oldSize, UInt32 newSize)
|
|
{
|
|
GASSERT(newSize <= oldSize);
|
|
if (newSize < oldSize && pLastPage && IsInPage(pLastPage, ptr))
|
|
{
|
|
UPInt off = ptr - pLastPage->GetBufferPtr();
|
|
if (pLastPage->PageSize - (off + oldSize) == FreeBytes)
|
|
{
|
|
UPInt newFreeBytes = pLastPage->PageSize - (off + newSize);
|
|
if (newFreeBytes < 65536)
|
|
FreeBytes = UInt16(newFreeBytes);
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void GFxPathAllocator::SetDefaultPageSize(UInt dps)
|
|
{
|
|
if (pFirstPage == NULL)
|
|
{
|
|
GASSERT(dps < 65536);
|
|
DefaultPageSize = (UInt16)dps;
|
|
}
|
|
}
|
|
|
|
GFxPathPacker::GFxPathPacker()
|
|
{
|
|
Reset();
|
|
}
|
|
|
|
void GFxPathPacker::Reset()
|
|
{
|
|
Fill0 = Fill1 = Line = 0;
|
|
Ax = Ay = Ex = Ey = 0; // moveTo
|
|
EdgesIndex = 0;
|
|
CurvesNum = 0;
|
|
LinesNum = 0;
|
|
EdgesNumBits = 0;
|
|
NewShape = 0;
|
|
}
|
|
|
|
static GINLINE UInt GFx_BitsToUInt(SInt value)
|
|
{
|
|
//return (value >= 0) ? (UInt)value : (((UInt)G_Abs(value))<<1);
|
|
// always need to add one bit for a sign, even if value is positive,
|
|
// since this value will be assigned to signed int (8, 16 or 32 bits)
|
|
return (value == 0) ? 0 : (((UInt)G_Abs(value))<<1);
|
|
}
|
|
|
|
static GINLINE UByte GFx_BitCountSInt(SInt value)
|
|
{
|
|
return GBitsUtil::BitCount32(GFx_BitsToUInt(value));
|
|
}
|
|
|
|
static GINLINE UByte GFx_BitCountSInt(SInt value1, SInt value2)
|
|
{
|
|
return GBitsUtil::BitCount32(GFx_BitsToUInt(value1) | GFx_BitsToUInt(value2));
|
|
}
|
|
|
|
static GINLINE UByte GFx_BitCountSInt(SInt value1, SInt value2, SInt value3, SInt value4)
|
|
{
|
|
return GBitsUtil::BitCount32(GFx_BitsToUInt(value1) | GFx_BitsToUInt(value2) |
|
|
GFx_BitsToUInt(value3) | GFx_BitsToUInt(value4));
|
|
}
|
|
|
|
template <UPInt alignSize, typename T>
|
|
GINLINE static T GFx_AlignedPtr(T ptr)
|
|
{
|
|
return (T)(UPInt(ptr + alignSize - 1) & (~(alignSize - 1)));
|
|
}
|
|
|
|
template <typename T>
|
|
GINLINE static T GFx_AlignedPtr(T ptr, UPInt alignSize)
|
|
{
|
|
return (T)(UPInt(ptr + alignSize - 1) & (~(alignSize - 1)));
|
|
}
|
|
|
|
void GFxPathPacker::Pack(GFxPathAllocator* pallocator, GFxPathData::PathsInfo* ppathsInfo)
|
|
{
|
|
// RAGE - Warn about overly complex shapes
|
|
const int RageDrawAPIVertLimit = 1000;
|
|
if (EdgesIndex > RageDrawAPIVertLimit)
|
|
{
|
|
Warningf("Drawing overly complex shape with the drawing API (%d edges)", EdgesIndex);
|
|
}
|
|
|
|
/* There are several types of packed paths:
|
|
1) Path8
|
|
2) Path16
|
|
3) Path32
|
|
4) NewShape
|
|
5) Path8 + up to 4 16-bit edges.
|
|
|
|
Octet 0 represents bit flags:
|
|
Bit 0: 0 - complex type (path types 1 - 4)
|
|
1 - path type 5 (Path8 + up to 4 16-bit edges)
|
|
For complex type (if bit 0 is 0):
|
|
Bits 1-2: type:
|
|
0 - NewShape
|
|
1 - path 8
|
|
2 - path 16
|
|
3 - path 32
|
|
Bit 3: edge size:
|
|
0 - 16 bit
|
|
1 - 32 bit
|
|
Bits 4-7: edge count:
|
|
0 - edge count is stored separately
|
|
1 - 15 - actual edge count (no additional counter is stored)
|
|
For path type 5:
|
|
Bits 1-2: edge count
|
|
0 - 1
|
|
1 - 2
|
|
2 - 3
|
|
3 - 4 edges
|
|
Bits 3-6: edge types mask, bit set to 1 indicates curve, otherwise - line.
|
|
Bit 7: spare.
|
|
Flags' octet is not aligned.
|
|
The following part is aligned accrodingly to path type: path8 - 8-bit alignment, path16 - 16-bit, path32 - 32-bit
|
|
(aligned accordingly);
|
|
NewShape consists of only one octet.
|
|
Octet 1/Word 1/Dword 1 - Fill0
|
|
Octet 2/Word 2/Dword 2 - Fill1
|
|
Octet 3/Word 3/Dword 3 - Line
|
|
|
|
Edges aligned accordingly to bit 3 ("edge size").
|
|
Edges are encoded as follows: optional edge count, "moveto" starting point, edge info, edge coordinates, edge info, edge coords, etc.
|
|
Edge count is encoded for path types 1 - 3, if it is greater than 15 (see bits 4-7 for complex type path).
|
|
The size of count depends on "edge size" bit - 16 or 32-bit.
|
|
|
|
"moveto" starting point is encoded as set of 2 16 or 32-bit integers.
|
|
|
|
Edge info represented by bit array encoded as 16 or 32-bit integer (depending on "edge size" bit).
|
|
Each bit indicates the type of edge - 1 - curve, 0 - line.
|
|
|
|
Line edges are encoded as set of 2 16- or 32-bit integers (depending on "edge size" bit).
|
|
Curve edges are encoded as set of 4 16- or 32-bit integers (depending on "edge size" bit).
|
|
|
|
If there are more than 16 (32) edges then another edge info bit array is encoded for the next 16 (32) edges
|
|
followed by encoded edges.
|
|
*/
|
|
GASSERT((Fill0 & 0x80000000) == 0); // prevent from "negative" styles
|
|
GASSERT((Fill1 & 0x80000000) == 0); // prevent from "negative" styles
|
|
GASSERT((Line & 0x80000000) == 0); // prevent from "negative" styles
|
|
|
|
UInt fillNumBits = GBitsUtil::BitCount32(Fill0 | Fill1 | Line);
|
|
UByte* pbuf = 0;
|
|
|
|
if (NewShape)
|
|
{
|
|
pbuf = pallocator->AllocPath(0, 0, 0);
|
|
*pbuf = 0;
|
|
}
|
|
else if (fillNumBits <= 8 && EdgesNumBits <= 16 && EdgesIndex <= 4)
|
|
{
|
|
// special case, Path8 and up to 4 16-bits edges
|
|
// encode flags
|
|
UByte flags = 0;
|
|
flags |= GFxPathConsts::PathTypeMask;
|
|
flags |= ((EdgesIndex - 1) << GFxPathConsts::Path8EdgesCountShift) & GFxPathConsts::Path8EdgesCountMask;
|
|
UByte edgesTypes = 0;
|
|
pbuf = pallocator->AllocPath((LinesNum + 1)*2*2 + CurvesNum*4*2, 1, 2);
|
|
pbuf[1] = (UByte)Fill0;
|
|
pbuf[2] = (UByte)Fill1;
|
|
pbuf[3] = (UByte)Line;
|
|
|
|
SInt16* pbuf16 = (SInt16*)(GFx_AlignedPtr<2>(pbuf + 4));
|
|
|
|
*pbuf16++ = (SInt16)Ax; // move to
|
|
*pbuf16++ = (SInt16)Ay;
|
|
|
|
UByte i, mask = 0x01;
|
|
for(i = 0; i < EdgesIndex; ++i, mask <<= 1)
|
|
{
|
|
if (Edges[i].IsCurve())
|
|
{
|
|
*pbuf16++ = (SInt16)Edges[i].Cx;
|
|
*pbuf16++ = (SInt16)Edges[i].Cy;
|
|
edgesTypes |= mask;
|
|
}
|
|
*pbuf16++ = (SInt16)Edges[i].Ax;
|
|
*pbuf16++ = (SInt16)Edges[i].Ay;
|
|
}
|
|
flags |= (edgesTypes << GFxPathConsts::Path8EdgeTypesShift) & GFxPathConsts::Path8EdgeTypesMask;
|
|
pbuf[0] = flags;
|
|
}
|
|
else
|
|
{
|
|
UByte flags = 0;
|
|
UInt pathSizeFactor = 1;
|
|
if (fillNumBits <= 8)
|
|
flags |= (1 << GFxPathConsts::PathSizeShift);
|
|
else if (fillNumBits <= 16)
|
|
{
|
|
flags |= (2 << GFxPathConsts::PathSizeShift);
|
|
pathSizeFactor = 2;
|
|
}
|
|
else
|
|
{ // 32-bit path
|
|
flags |= (3 << GFxPathConsts::PathSizeShift);
|
|
pathSizeFactor = 4;
|
|
}
|
|
UInt bytesToAllocate = 0;
|
|
// encode edge count
|
|
UInt edgesSizeFactor;
|
|
if (EdgesNumBits > 16 || EdgesIndex > 65535)
|
|
{
|
|
flags |= GFxPathConsts::PathEdgeSizeMask; // 32-bit edges
|
|
edgesSizeFactor = 4;
|
|
}
|
|
else
|
|
edgesSizeFactor = 2;
|
|
if (EdgesIndex < 16)
|
|
flags |= (EdgesIndex & 0xF) << GFxPathConsts::PathEdgesCountShift;
|
|
else
|
|
bytesToAllocate += edgesSizeFactor; // space for size
|
|
|
|
// calculate space needed for edgeinfo
|
|
if (EdgesNumBits <= 16)
|
|
bytesToAllocate += (EdgesIndex + 15)/16*2;
|
|
else
|
|
bytesToAllocate += (EdgesIndex + 31)/32*4;
|
|
//
|
|
pbuf = pallocator->AllocPath(bytesToAllocate + (LinesNum + 1)*2*edgesSizeFactor + CurvesNum * 4 *edgesSizeFactor, pathSizeFactor, edgesSizeFactor);
|
|
pbuf[0] = flags;
|
|
UByte* palignedBuf = GFx_AlignedPtr(pbuf + 1, pathSizeFactor);
|
|
switch (pathSizeFactor)
|
|
{
|
|
case 1:
|
|
palignedBuf[0] = (UByte)Fill0;
|
|
palignedBuf[1] = (UByte)Fill1;
|
|
palignedBuf[2] = (UByte)Line;
|
|
break;
|
|
case 2:
|
|
{
|
|
SInt16* pbuf16 = (SInt16*)palignedBuf;
|
|
pbuf16[0] = (SInt16)Fill0;
|
|
pbuf16[1] = (SInt16)Fill1;
|
|
pbuf16[2] = (SInt16)Line;
|
|
break;
|
|
}
|
|
case 4:
|
|
{
|
|
SInt32* pbuf32 = (SInt32*)palignedBuf;
|
|
pbuf32[0] = (SInt32)Fill0;
|
|
pbuf32[1] = (SInt32)Fill1;
|
|
pbuf32[2] = (SInt32)Line;
|
|
break;
|
|
}
|
|
}
|
|
UInt bufIndex = 3 * pathSizeFactor;
|
|
if (edgesSizeFactor == 2)
|
|
{
|
|
SInt16* pbuf16 = (SInt16*)(GFx_AlignedPtr(palignedBuf + bufIndex, edgesSizeFactor));
|
|
if (EdgesIndex >= 16)
|
|
*pbuf16++ = (SInt16)EdgesIndex; // edge count
|
|
|
|
*pbuf16++ = (SInt16)Ax; // moveto
|
|
*pbuf16++ = (SInt16)Ay;
|
|
|
|
SInt16* pedgeInfo = 0;
|
|
UInt i, mask = 0;
|
|
for(i = 0; i < EdgesIndex; ++i, mask <<= 1)
|
|
{
|
|
if (i % 16 == 0)
|
|
{
|
|
// edge info
|
|
pedgeInfo = pbuf16++;
|
|
*pedgeInfo = 0;
|
|
mask = 1;
|
|
}
|
|
if (Edges[i].IsCurve())
|
|
{
|
|
*pbuf16++ = (SInt16)Edges[i].Cx;
|
|
*pbuf16++ = (SInt16)Edges[i].Cy;
|
|
*pedgeInfo |= mask;
|
|
}
|
|
*pbuf16++ = (SInt16)Edges[i].Ax;
|
|
*pbuf16++ = (SInt16)Edges[i].Ay;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SInt32* pbuf32 = (SInt32*)(GFx_AlignedPtr(palignedBuf + bufIndex, edgesSizeFactor));
|
|
if (EdgesIndex >= 16)
|
|
*pbuf32++ = (SInt32)EdgesIndex;
|
|
|
|
*pbuf32++ = (SInt32)Ax; // moveto
|
|
*pbuf32++ = (SInt32)Ay;
|
|
|
|
SInt32* pedgeInfo = 0;
|
|
UInt i, mask = 0;
|
|
for(i = 0; i < EdgesIndex; ++i, mask <<= 1)
|
|
{
|
|
if (i % 32 == 0)
|
|
{
|
|
// edge info
|
|
pedgeInfo = pbuf32++;
|
|
*pedgeInfo = 0;
|
|
mask = 1;
|
|
}
|
|
if (Edges[i].IsCurve())
|
|
{
|
|
*pbuf32++ = (SInt32)Edges[i].Cx;
|
|
*pbuf32++ = (SInt32)Edges[i].Cy;
|
|
*pedgeInfo |= mask;
|
|
}
|
|
*pbuf32++ = (SInt32)Edges[i].Ax;
|
|
*pbuf32++ = (SInt32)Edges[i].Ay;
|
|
}
|
|
}
|
|
}
|
|
if (pbuf && ppathsInfo)
|
|
{
|
|
if (ppathsInfo->pPaths == NULL)
|
|
{
|
|
ppathsInfo->PathsPageOffset = pallocator->GetPageOffset(pbuf);
|
|
ppathsInfo->pPaths = pbuf;
|
|
}
|
|
if (NewShape)
|
|
++ppathsInfo->ShapesCount;
|
|
++ppathsInfo->PathsCount;
|
|
}
|
|
ResetEdges();
|
|
}
|
|
|
|
void GFxPathPacker::SetMoveTo(SInt x, SInt y, UInt numBits)
|
|
{
|
|
Ax = Ex = x;
|
|
Ay = Ey = y;
|
|
if (numBits > EdgesNumBits)
|
|
EdgesNumBits = (UByte)numBits;
|
|
else if (numBits == 0)
|
|
EdgesNumBits = G_Max(GFx_BitCountSInt(x, y), EdgesNumBits);
|
|
}
|
|
|
|
void GFxPathPacker::AddLineTo(SInt x, SInt y, UInt numBits)
|
|
{
|
|
Edge e(x, y);
|
|
if (EdgesIndex < Edges.GetSize())
|
|
Edges[EdgesIndex] = e;
|
|
else
|
|
Edges.PushBack(e);
|
|
++EdgesIndex;
|
|
++LinesNum;
|
|
if (numBits > EdgesNumBits)
|
|
EdgesNumBits = (UByte)numBits;
|
|
else if (numBits == 0)
|
|
EdgesNumBits = G_Max(GFx_BitCountSInt(x, y), EdgesNumBits);
|
|
Ex += x;
|
|
Ey += y;
|
|
}
|
|
|
|
void GFxPathPacker::AddCurve (SInt cx, SInt cy, SInt ax, SInt ay, UInt numBits)
|
|
{
|
|
Edge e(cx, cy, ax, ay);
|
|
if (EdgesIndex < Edges.GetSize())
|
|
Edges[EdgesIndex] = e;
|
|
else
|
|
Edges.PushBack(e);
|
|
++EdgesIndex;
|
|
++CurvesNum;
|
|
if (numBits > EdgesNumBits)
|
|
EdgesNumBits = (UByte)numBits;
|
|
else if (numBits == 0)
|
|
EdgesNumBits = G_Max(GFx_BitCountSInt(cx, cy, ax, ay), EdgesNumBits);
|
|
Ex += cx + ax;
|
|
Ey += cy + ay;
|
|
}
|
|
|
|
|
|
|
|
void GFxPathPacker::LineToAbs(SInt x, SInt y)
|
|
{
|
|
AddLineTo(x - Ex, y - Ey, 0);
|
|
}
|
|
|
|
void GFxPathPacker::CurveToAbs(SInt cx, SInt cy, SInt ax, SInt ay)
|
|
{
|
|
AddCurve(cx - Ex, cy - Ey, ax - cx, ay - cy, 0);
|
|
}
|
|
|
|
void GFxPathPacker::ClosePath()
|
|
{
|
|
if(Ex != Ax || Ey != Ay)
|
|
{
|
|
LineToAbs(Ax, Ay);
|
|
}
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// GFxConstShapeNoStyles
|
|
//
|
|
bool GFxConstShapeNoStyles::DefPointTestLocal(const GPointF &pt,
|
|
bool testShape,
|
|
const GFxCharacter* pinst) const
|
|
{
|
|
return DefPointTestLocalImpl<GFxSwfPathData>(pt, testShape, pinst);
|
|
}
|
|
|
|
// Push our shape data through the tessellator.
|
|
void GFxConstShapeNoStyles::Tessellate(GFxMeshSet *meshSet, Float tolerance,
|
|
GFxDisplayContext &context,
|
|
GFxScale9GridInfo* s9g) const
|
|
{
|
|
TessellateImpl<GFxSwfPathData>(meshSet, tolerance, context, s9g);
|
|
}
|
|
|
|
|
|
// Convert the paths to GCompoundShape, flattening the curves.
|
|
// The function ignores the NewShape flag and adds all paths
|
|
// GFxShapeBase contains.
|
|
void GFxConstShapeNoStyles::MakeCompoundShape(GCompoundShape *cs, Float tolerance) const
|
|
{
|
|
MakeCompoundShapeImpl<GFxSwfPathData>(cs, tolerance);
|
|
}
|
|
|
|
|
|
UInt32 GFxConstShapeNoStyles::ComputeGeometryHash() const
|
|
{
|
|
// just compute hash of piece of memory
|
|
UInt memsz = GFxSwfPathData::GetMemorySize(pPaths);
|
|
return GFx_ComputeBernsteinHash(pPaths, memsz, 0);
|
|
}
|
|
|
|
bool GFxConstShapeNoStyles::IsEqualGeometry(const GFxShapeBase& cmpWith) const
|
|
{
|
|
if (cmpWith.GetPathDataType() == GFxConstShapeNoStyles::GetPathDataType())
|
|
{
|
|
// just compare two memory blocks
|
|
const GFxConstShapeNoStyles* cmpWithShape =
|
|
static_cast<const GFxConstShapeNoStyles*>(&cmpWith);
|
|
UInt memsz = GFxSwfPathData::GetMemorySize(pPaths);
|
|
UInt cmpWithMemsz = GFxSwfPathData::GetMemorySize(cmpWithShape->pPaths);
|
|
if (memsz != cmpWithMemsz)
|
|
return false;
|
|
bool res = (memcmp(pPaths, cmpWithShape->pPaths, memsz) == 0);
|
|
GASSERT(res == IsEqualGeometryImpl<GFxSwfPathData>(cmpWith));
|
|
return res;
|
|
}
|
|
return IsEqualGeometryImpl<GFxSwfPathData>(cmpWith);
|
|
}
|
|
|
|
|
|
|
|
// Find the bounds of this shape, and store them in
|
|
// the given rectangle.
|
|
void GFxConstShapeNoStyles::ComputeBound(GRectF* r) const
|
|
{
|
|
ComputeBoundImpl<GFxSwfPathData>(r);
|
|
}
|
|
|
|
void GFxConstShapeNoStyles::Read(GFxLoadProcess* p, GFxTagType tagType, UInt lenInBytes, bool withStyle, GFxPathAllocator* pAllocator)
|
|
{
|
|
Read(p, tagType, lenInBytes, withStyle, NULL, NULL,pAllocator);
|
|
}
|
|
|
|
static UByte* GFx_WriteUInt(UByte* p, UInt value, UInt sz)
|
|
{
|
|
UByte* dp = p;
|
|
for (UInt i = 0, shift = 0; i < sz; ++i, shift += 8)
|
|
{
|
|
*dp++ = UByte((value >> shift) & 0xFF);
|
|
}
|
|
return dp;
|
|
}
|
|
|
|
static UInt GFx_ReadUInt(const UByte* p, UInt sz)
|
|
{
|
|
UInt res = 0;
|
|
for (UInt i = 0, shift = 0; i < sz; ++i, shift += 8)
|
|
{
|
|
res |= ((UInt)p[i]) << shift;
|
|
}
|
|
return res;
|
|
}
|
|
|
|
void GFxConstShapeNoStyles::Read(GFxLoadProcess* p, GFxTagType tagType, UInt lenInBytes, bool withStyle,
|
|
GFxFillStyleArrayTemp* pfillStyles, GFxLineStyleArrayTemp* plineStyles, GFxPathAllocator* pAllocator)
|
|
{
|
|
GFxPathAllocator* ppathAllocator = pAllocator? pAllocator : p->GetPathAllocator();
|
|
GFxStream* in = p->GetStream();
|
|
GASSERT(ppathAllocator);
|
|
|
|
int stylesLen;
|
|
if (withStyle)
|
|
{
|
|
int curOffset = in->Tell();
|
|
|
|
SetValidBoundsFlag(true);
|
|
in->ReadRect(&Bound);
|
|
|
|
// SWF 8 contains bounds without a stroke: MovieClip.getRect()
|
|
if ((tagType == GFxTag_DefineShape4) || (tagType == GFxTag_DefineFont3))
|
|
{
|
|
GRectF rectBound;
|
|
in->ReadRect(&rectBound);
|
|
SetRectBoundsLocal(rectBound);
|
|
// MA: What does this byte do?
|
|
// I've seen it take on values of 0x01 and 0x00
|
|
//UByte mysteryByte =
|
|
in->ReadU8();
|
|
}
|
|
else
|
|
SetRectBoundsLocal(Bound);
|
|
|
|
GFx_ReadFillStyles(pfillStyles, p, tagType);
|
|
GFx_ReadLineStyles(plineStyles, p, tagType);
|
|
stylesLen = in->Tell() - curOffset;
|
|
GASSERT((int)stylesLen >= 0);
|
|
GASSERT((int)(lenInBytes - stylesLen) >= 0);
|
|
}
|
|
else
|
|
stylesLen = 0;
|
|
|
|
#ifndef GFX_USE_CUSTOM_PACKER
|
|
// Format of shape data is as follows:
|
|
// 1 byte - PathFlags (see GFxSwfPathData::Flags_<>)
|
|
// 4 bytes - signature (in DEBUG build only)
|
|
// N bytes [1..4] - size of whole shape data memory block (N depends on Mask_NumBytesInMemCnt)
|
|
// X bytes - original SWF shape data
|
|
// Y bytes [1..4] - number of shapes in swf shape data (Y depends on Mask_NumBytesInGeomCnt)
|
|
// Z bytes [1..4] - number of paths in swf shape data (Z depends on Mask_NumBytesInGeomCnt)
|
|
|
|
UInt shapeLen = (UInt)lenInBytes - stylesLen;
|
|
UInt memBlockSize = shapeLen;
|
|
UInt originalShapeLen = shapeLen;
|
|
memBlockSize += 1; // mandatory 1 extra byte with flags (see GFxSwfPathData::Flags enum)
|
|
UByte pathFlags = (UByte)
|
|
((tagType > GFxTag_DefineShape) ? GFxSwfPathData::Flags_HasExtendedFillNum : 0);
|
|
if (withStyle)
|
|
{
|
|
if (pfillStyles && pfillStyles->GetSize() > 0)
|
|
{
|
|
pathFlags |= GFxSwfPathData::Flags_HasFillStyles;
|
|
memBlockSize += 2;
|
|
}
|
|
if (plineStyles && plineStyles->GetSize() > 0)
|
|
{
|
|
pathFlags |= GFxSwfPathData::Flags_HasLineStyles;
|
|
memBlockSize += 2;
|
|
}
|
|
}
|
|
#ifdef GFC_BUILD_DEBUG
|
|
// put a 4-bytes signature at the beginning for debugging purposes
|
|
// to catch improper iterator usage.
|
|
memBlockSize += 4;
|
|
#endif
|
|
|
|
// we need to reserve space for shapes & paths counter
|
|
// these counter might be 1, 2, 3 or 4 bytes length
|
|
// depending on their size. PathFlags should contain
|
|
// appropriate bits set (see Mask_NumBytesInGeomCnt).
|
|
// At the beginning we will reserve maximum - 4 bytes per each
|
|
// and will correct the final size at the end of read.
|
|
// These counters will be stored at the end of whole memory block.
|
|
// To access them it would be necessary to decode memory counter
|
|
// first and add it the pointer and subtract two sizes of geometry
|
|
// counters.
|
|
UInt geomCntInBytes = 4;
|
|
memBlockSize += geomCntInBytes * 2;
|
|
|
|
// we need to reserve space for memory counter
|
|
// memory counter might be 1, 2, 3 or 4 bytes length
|
|
// depending on its size. PathFlags should contain
|
|
// appropriate bits set (see Mask_NumBytesInMemCnt).
|
|
UInt memBlockSizeInBytes;
|
|
if (memBlockSize < (1U<<8))
|
|
memBlockSizeInBytes = 1;
|
|
else if (memBlockSize < (1U<<16))
|
|
memBlockSizeInBytes = 2;
|
|
else if (memBlockSize < (1U<<24))
|
|
memBlockSizeInBytes = 3;
|
|
else
|
|
memBlockSizeInBytes = 4;
|
|
memBlockSize += memBlockSizeInBytes;
|
|
pathFlags |= ((memBlockSizeInBytes - 1) << GFxSwfPathData::Shift_NumBytesInMemCnt) &
|
|
GFxSwfPathData::Mask_NumBytesInMemCnt;
|
|
|
|
UByte *pmemBlock = ppathAllocator->AllocRawPath(memBlockSize);
|
|
UByte *ptr = pmemBlock;
|
|
|
|
#ifdef GFC_BUILD_DEBUG
|
|
// put a 4-bytes signature at the beginning for debugging purposes
|
|
// to catch improper iterator usage. Put it in little endian format
|
|
// like all swf's integers.
|
|
*ptr++ = GFxSwfPathData::Signature & 0xFF;
|
|
*ptr++ = (GFxSwfPathData::Signature >> 8) & 0xFF;
|
|
*ptr++ = (GFxSwfPathData::Signature >> 16) & 0xFF;
|
|
*ptr++ = (GFxSwfPathData::Signature >> 24) & 0xFF;
|
|
#endif
|
|
|
|
*ptr++ = pathFlags;
|
|
|
|
// actual value of memory size will be written at the end of read
|
|
// after all corrections are done.
|
|
ptr += memBlockSizeInBytes;
|
|
|
|
if (pathFlags & GFxSwfPathData::Flags_HasFillStyles)
|
|
{
|
|
UPInt fn = pfillStyles->GetSize();
|
|
GASSERT(fn <= 65535);
|
|
*ptr++ = UInt8(fn & 0xFF);
|
|
*ptr++ = UInt8((fn >> 8) & 0x7F);
|
|
}
|
|
if (pathFlags & GFxSwfPathData::Flags_HasLineStyles)
|
|
{
|
|
UPInt fn = plineStyles->GetSize();
|
|
GASSERT(fn <= 65535);
|
|
*ptr++ = UInt8(fn & 0xFF);
|
|
*ptr++ = UInt8((fn >> 8) & 0x7F);
|
|
}
|
|
|
|
in->Align();
|
|
in->ReadToBuffer(ptr, shapeLen);
|
|
|
|
// Multiplier used for scaling.
|
|
// In SWF 8, font shape resolution (tag 75) was increased by 20.
|
|
Float sfactor = 1.0f;
|
|
if (tagType == GFxTag_DefineFont3)
|
|
{
|
|
sfactor = 1.0f / 20.0f;
|
|
Flags |= Flags_Sfactor20;
|
|
}
|
|
|
|
GFxStream* poriginalStream = in;
|
|
GFxStream ss(ptr, shapeLen, p->GetLoadHeap(), in->GetLog(), in->GetParseControl());
|
|
in = &ss;
|
|
p->SetAltStream(in);
|
|
|
|
// need to preprocess shape to parse and remove all additional fill & line styles.
|
|
//
|
|
// SHAPE
|
|
//
|
|
in->Align(); //!AB
|
|
int numFillBits = in->ReadUInt4(); // RAGE - use specialized (faster) read function
|
|
int numLineBits = in->ReadUInt4();
|
|
|
|
if (withStyle) // do not trace, if !withStyle (for example, for font glyphs)
|
|
in->LogParse(" ShapeCharacter read: nfillbits = %d, nlinebits = %d\n", numFillBits, numLineBits);
|
|
|
|
// These are state variables that keep the
|
|
// current position & style of the shape
|
|
// outline, and vary as we read the GFxEdge data.
|
|
//
|
|
// At the moment we just store each GFxEdge with
|
|
// the full necessary info to render it, which
|
|
// is simple but not optimally efficient.
|
|
int fillBase = 0;
|
|
int lineBase = 0;
|
|
int moveX = 0, moveY = 0;
|
|
SInt numMoveBits = 0;
|
|
UInt totalPathsCount = 0;
|
|
UInt totalShapesCount = 0;
|
|
int curEdgesCount = 0;
|
|
int curPathsCount = 0;
|
|
bool shapeIsCorrupted = false;
|
|
|
|
#define SHAPE_LOG 0
|
|
#ifdef GFX_SHAPE_MEM_TRACKING
|
|
int __startingFileOffset = in->Tell();
|
|
#endif //GFX_SHAPE_MEM_TRACKING
|
|
// SHAPERECORDS
|
|
while(!shapeIsCorrupted)
|
|
{
|
|
int typeFlag = in->ReadUInt1();
|
|
if (typeFlag == 0)
|
|
{
|
|
// Parse the record.
|
|
int flags = in->ReadUInt(5);
|
|
if (flags == 0) {
|
|
// End of shape records.
|
|
if (curEdgesCount > 0)
|
|
{
|
|
++totalPathsCount;
|
|
curEdgesCount = 0;
|
|
++curPathsCount;
|
|
}
|
|
break;
|
|
}
|
|
if (flags & 0x01)
|
|
{
|
|
// MoveTo = 1;
|
|
if (curEdgesCount > 0)
|
|
{
|
|
++totalPathsCount;
|
|
curEdgesCount = 0;
|
|
++curPathsCount;
|
|
}
|
|
|
|
numMoveBits = in->ReadUInt(5);
|
|
moveX = in->ReadSInt(numMoveBits);
|
|
moveY = in->ReadSInt(numMoveBits);
|
|
|
|
if (in->IsVerboseParseShape())
|
|
in->LogParseShape(" ShapeCharacter read: moveto %4g %4g\n", (Float) moveX * sfactor, (Float) moveY * sfactor);
|
|
}
|
|
if ((flags & 0x02) && numFillBits > 0)
|
|
{
|
|
// FillStyle0_change = 1;
|
|
if (curEdgesCount > 0)
|
|
{
|
|
++totalPathsCount;
|
|
curEdgesCount = 0;
|
|
++curPathsCount;
|
|
}
|
|
|
|
int style = in->ReadUInt(numFillBits);
|
|
if (in->IsVerboseParseShape())
|
|
{
|
|
if (style > 0)
|
|
{
|
|
style += fillBase;
|
|
}
|
|
in->LogParseShape(" ShapeCharacter read: fill0 = %d\n", style);
|
|
}
|
|
}
|
|
if ((flags & 0x04) && numFillBits > 0)
|
|
{
|
|
// FillStyle1_change = 1;
|
|
if (curEdgesCount > 0)
|
|
{
|
|
++totalPathsCount;
|
|
curEdgesCount = 0;
|
|
++curPathsCount;
|
|
}
|
|
|
|
int style = in->ReadUInt(numFillBits);
|
|
if (in->IsVerboseParseShape())
|
|
{
|
|
if (style > 0)
|
|
{
|
|
style += fillBase;
|
|
}
|
|
in->LogParseShape(" ShapeCharacter read: fill1 = %d\n", style);
|
|
}
|
|
}
|
|
if ((flags & 0x08) && numLineBits > 0)
|
|
{
|
|
// LineStyleChange = 1;
|
|
if (curEdgesCount > 0)
|
|
{
|
|
++totalPathsCount;
|
|
curEdgesCount = 0;
|
|
++curPathsCount;
|
|
}
|
|
|
|
int style = in->ReadUInt(numLineBits);
|
|
if (style > 0)
|
|
{
|
|
style += lineBase;
|
|
}
|
|
|
|
if (in->IsVerboseParseShape())
|
|
in->LogParseShape(" ShapeCharacter read: line = %d\n", style);
|
|
}
|
|
if (flags & 0x10)
|
|
{
|
|
GASSERT(tagType >= 22);
|
|
|
|
in->LogParse(" ShapeCharacter read: more fill styles\n");
|
|
|
|
if (curPathsCount > 0)
|
|
{
|
|
++totalShapesCount;
|
|
curPathsCount = 0;
|
|
}
|
|
if (curEdgesCount > 0)
|
|
{
|
|
++totalPathsCount;
|
|
curEdgesCount = 0;
|
|
++curPathsCount;
|
|
}
|
|
|
|
#ifdef GFX_SHAPE_MEM_TRACKING
|
|
int __offset = in->Tell();
|
|
#endif
|
|
fillBase = pfillStyles ? (int)pfillStyles->GetSize() : 0;
|
|
lineBase = plineStyles ? (int)plineStyles->GetSize() : 0;
|
|
|
|
int foff = GFx_ReadFillStyles(pfillStyles, p, tagType);
|
|
int moff = in->Tell();
|
|
int loff = GFx_ReadLineStyles(plineStyles, p, tagType);
|
|
int eoff = in->Tell();
|
|
|
|
if (moff != foff)
|
|
{
|
|
if (foff > moff || UInt(moff) > originalShapeLen)
|
|
{
|
|
shapeIsCorrupted = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// collapse array of fill styles and correct all offsets
|
|
memmove(ptr + foff, ptr + moff, loff - moff);
|
|
loff -= moff - foff;
|
|
moff = foff;
|
|
}
|
|
}
|
|
if (loff != eoff)
|
|
{
|
|
if (loff > eoff || UInt(eoff) > originalShapeLen)
|
|
{
|
|
shapeIsCorrupted = true;
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
// collapse array of lines
|
|
memmove(ptr + loff, ptr + eoff, shapeLen - eoff);
|
|
shapeLen -= eoff - loff;
|
|
}
|
|
}
|
|
in->SetPosition(loff); // do we need to set new DataSize in the stream? (!AB)
|
|
|
|
numFillBits = in->ReadUInt4(); // RAGE - use specialized (faster) read function
|
|
numLineBits = in->ReadUInt4();
|
|
#ifdef GFX_SHAPE_MEM_TRACKING
|
|
__startingFileOffset -= (in->Tell() - __offset);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// EDGERECORD
|
|
int edgeFlag = in->ReadUInt1();
|
|
++curEdgesCount;
|
|
if (edgeFlag == 0)
|
|
{
|
|
// curved GFxEdge
|
|
UInt numbits = 2 + in->ReadUInt4(); // RAGE - use specialized (faster) read function
|
|
SInt cx = in->ReadSInt(numbits);
|
|
SInt cy = in->ReadSInt(numbits);
|
|
SInt ax = in->ReadSInt(numbits);
|
|
SInt ay = in->ReadSInt(numbits);
|
|
|
|
if (in->IsVerboseParseShape())
|
|
{
|
|
in->LogParseShape(" ShapeCharacter read: curved edge = %4g %4g - %4g %4g - %4g %4g\n",
|
|
(Float) moveX * sfactor, (Float) moveY * sfactor,
|
|
(Float) (moveX + cx) * sfactor, (Float) (moveY + cy) * sfactor,
|
|
(Float) (moveX + cx + ax ) * sfactor, (Float) (moveY + cy + ay ) * sfactor);
|
|
}
|
|
moveX += ax + cx;
|
|
moveY += ay + cy;
|
|
}
|
|
else
|
|
{
|
|
// straight GFxEdge
|
|
UInt numbits = 2 + in->ReadUInt4(); // RAGE - use specialized (faster) read function
|
|
int lineFlag = in->ReadUInt1();
|
|
SInt dx = 0, dy = 0;
|
|
if (lineFlag)
|
|
{
|
|
// General line.
|
|
dx = in->ReadSInt(numbits);
|
|
dy = in->ReadSInt(numbits);
|
|
}
|
|
else
|
|
{
|
|
int vertFlag = in->ReadUInt1();
|
|
if (vertFlag == 0) {
|
|
// Horizontal line.
|
|
dx = in->ReadSInt(numbits);
|
|
} else {
|
|
// Vertical line.
|
|
dy = in->ReadSInt(numbits);
|
|
}
|
|
}
|
|
|
|
if (in->IsVerboseParseShape())
|
|
{
|
|
in->LogParseShape(" ShapeCharacter read: straight edge = %4g %4g - %4g %4g\n",
|
|
(Float)moveX * sfactor, (Float)moveY * sfactor,
|
|
(Float)(moveX + dx) * sfactor, (Float)(moveY + dy) * sfactor);
|
|
}
|
|
moveX += dx;
|
|
moveY += dy;
|
|
}
|
|
}
|
|
// detect if shape record is corrupted
|
|
shapeIsCorrupted = ((UInt)in->Tell() > originalShapeLen);
|
|
}
|
|
if (!shapeIsCorrupted)
|
|
{
|
|
if (curPathsCount > 0)
|
|
++totalShapesCount;
|
|
|
|
// make correction for new shape length (if built-in fill & line styles
|
|
// were removed).
|
|
UInt newMemBlockSize = memBlockSize - (originalShapeLen - shapeLen);
|
|
|
|
// now we need to calculate how much memory would be necessary
|
|
// to store totalShapeCount & totalPathCount
|
|
UInt maxGeomCnt = G_Max(totalShapesCount, totalPathsCount);
|
|
if (maxGeomCnt < (1U<<8))
|
|
geomCntInBytes = 1;
|
|
else if (maxGeomCnt < (1U<<16))
|
|
geomCntInBytes = 2;
|
|
else if (maxGeomCnt < (1U<<24))
|
|
geomCntInBytes = 3;
|
|
else
|
|
geomCntInBytes = 4;
|
|
|
|
// correct allocated memory once again
|
|
newMemBlockSize = newMemBlockSize - 2*4 + 2*geomCntInBytes;
|
|
UByte* pgeomCnt = pmemBlock + newMemBlockSize - 2*geomCntInBytes;
|
|
pgeomCnt = GFx_WriteUInt(pgeomCnt, totalShapesCount, geomCntInBytes);
|
|
pgeomCnt = GFx_WriteUInt(pgeomCnt, totalPathsCount, geomCntInBytes);
|
|
|
|
if (memBlockSize > newMemBlockSize)
|
|
{
|
|
ppathAllocator->ReallocLastBlock(pmemBlock, memBlockSize, newMemBlockSize);
|
|
}
|
|
// now we can put correct memory size
|
|
UInt offset = 0; // path flags
|
|
#ifdef GFC_BUILD_DEBUG
|
|
// erase tail of reallocated memory block to make sure data is not used
|
|
if (memBlockSize > newMemBlockSize)
|
|
memset(pmemBlock + newMemBlockSize, 0xFB, memBlockSize - newMemBlockSize);
|
|
offset += 4; // signature
|
|
#endif
|
|
// update PathFlags to reflect correct size of shape&path counters
|
|
pmemBlock[offset++] |= ((geomCntInBytes - 1) << GFxSwfPathData::Shift_NumBytesInGeomCnt) &
|
|
GFxSwfPathData::Mask_NumBytesInGeomCnt;
|
|
|
|
GASSERT(newMemBlockSize != 0);
|
|
GFx_WriteUInt(pmemBlock + offset, newMemBlockSize, memBlockSizeInBytes);
|
|
|
|
// just make sure all values are stored correctly
|
|
GASSERT(newMemBlockSize == GFxSwfPathData::GetMemorySize(pmemBlock));
|
|
#ifdef GFC_BUILD_DEBUG
|
|
{
|
|
UInt sc = 0, pc = 0;
|
|
GFxSwfPathData::GetShapeAndPathCounts(pmemBlock, &sc, &pc);
|
|
GASSERT(totalShapesCount == sc && totalPathsCount == pc);
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// corrupted shape; just make an empty shape and display warning message.
|
|
in->LogWarning("Error: Corrupted shape detected in file %s\n",
|
|
poriginalStream->GetFileName().ToCStr());
|
|
|
|
UInt offset = 0;
|
|
|
|
// pathFlags, memsz, numbits fill and line (1 b), end-of-shape record (1 b), shapes cnt, paths cnt
|
|
UInt newMemBlockSize = 1 + 1 + 1 + 1 + 1 + 1;
|
|
#ifdef GFC_BUILD_DEBUG
|
|
newMemBlockSize += 4; // signature
|
|
offset += 4;
|
|
#endif
|
|
pmemBlock[offset++] = 0; // pathFlags, just set to 0
|
|
pmemBlock[offset++] = 0; // mem size
|
|
pmemBlock[offset++] = 0; // num bits in fill & line styles
|
|
pmemBlock[offset++] = 0; // end-of-shape record
|
|
pmemBlock[offset++] = 0; // shapes cnt
|
|
pmemBlock[offset++] = 0; // paths cnt
|
|
if (memBlockSize > newMemBlockSize)
|
|
{
|
|
ppathAllocator->ReallocLastBlock(pmemBlock, memBlockSize, newMemBlockSize);
|
|
}
|
|
}
|
|
pPaths = pmemBlock;
|
|
#ifdef GFC_BUILD_DEBUG
|
|
{
|
|
// Make sure signature is still correct
|
|
// Read a 4-bytes signature to ensure we use correct path format.
|
|
UInt32 v = (UInt32(pmemBlock[0]) | (UInt32(pmemBlock[1])<<8) |
|
|
(UInt32(pmemBlock[2])<<16) | (UInt32(pmemBlock[3])<<24));
|
|
GUNUSED(v);
|
|
GASSERT(v == GFxSwfPathData::Signature);
|
|
}
|
|
#endif
|
|
|
|
p->SetAltStream(NULL);
|
|
#else // GFX_USE_CUSTOM_PACKER
|
|
//
|
|
// SHAPE
|
|
//
|
|
in->Align(); //!AB
|
|
int NumFillBits = in->ReadUInt4(); // RAGE - use specialized (faster) read function
|
|
int NumLineBits = in->ReadUInt4();
|
|
|
|
if (withStyle) // do not trace, if !withStyle (for example, for font glyphs)
|
|
in->LogParse(" ShapeCharacter read: nfillbits = %d, nlinebits = %d\n", NumFillBits, NumLineBits);
|
|
|
|
// These are state variables that keep the
|
|
// current position & style of the shape
|
|
// outline, and vary as we read the GFxEdge data.
|
|
//
|
|
// At the moment we just store each GFxEdge with
|
|
// the full necessary info to render it, which
|
|
// is simple but not optimally efficient.
|
|
int FillBase = 0;
|
|
int LineBase = 0;
|
|
int moveX = 0, moveY = 0;
|
|
SInt numMoveBits = 0;
|
|
GFxPathPacker CurrentPath;
|
|
|
|
#define SHAPE_LOG 0
|
|
#ifdef GFX_SHAPE_MEM_TRACKING
|
|
int __startingFileOffset = in->Tell();
|
|
#endif //GFX_SHAPE_MEM_TRACKING
|
|
// SHAPERECORDS
|
|
for (;;) {
|
|
int TypeFlag = in->ReadUInt1();
|
|
if (TypeFlag == 0)
|
|
{
|
|
// Parse the record.
|
|
int flags = in->ReadUInt(5);
|
|
if (flags == 0) {
|
|
// End of shape records.
|
|
|
|
// Store the current GFxPath if any.
|
|
if (! CurrentPath.IsEmpty())
|
|
{
|
|
CurrentPath.Pack(ppathAllocator, &Paths);
|
|
}
|
|
|
|
break;
|
|
}
|
|
if (flags & 0x01)
|
|
{
|
|
// MoveTo = 1;
|
|
|
|
// Store the current GFxPath if any, and prepare a fresh one.
|
|
if (! CurrentPath.IsEmpty())
|
|
{
|
|
CurrentPath.Pack(ppathAllocator, &Paths);
|
|
}
|
|
|
|
numMoveBits = in->ReadUInt(5);
|
|
moveX = in->ReadSInt(numMoveBits);
|
|
moveY = in->ReadSInt(numMoveBits);
|
|
|
|
// Set the beginning of the GFxPath.
|
|
CurrentPath.SetMoveTo(moveX, moveY, numMoveBits);
|
|
|
|
in->LogParseShape(" ShapeCharacter read: moveto %4g %4g\n", (Float) moveX * sfactor, (Float) moveY * sfactor);
|
|
}
|
|
if ((flags & 0x02)
|
|
&& NumFillBits > 0)
|
|
{
|
|
// FillStyle0_change = 1;
|
|
if (! CurrentPath.IsEmpty())
|
|
{
|
|
CurrentPath.Pack(ppathAllocator, &Paths);
|
|
CurrentPath.SetMoveTo(moveX, moveY, numMoveBits);
|
|
}
|
|
int style = in->ReadUInt(NumFillBits);
|
|
if (style > 0)
|
|
{
|
|
style += FillBase;
|
|
}
|
|
CurrentPath.SetFill0(style);
|
|
|
|
in->LogParseShape(" ShapeCharacter read: fill0 = %d\n", style);
|
|
}
|
|
if ((flags & 0x04)
|
|
&& NumFillBits > 0)
|
|
{
|
|
// FillStyle1_change = 1;
|
|
if (! CurrentPath.IsEmpty())
|
|
{
|
|
CurrentPath.Pack(ppathAllocator, &Paths);
|
|
CurrentPath.SetMoveTo(moveX, moveY, numMoveBits);
|
|
}
|
|
int style = in->ReadUInt(NumFillBits);
|
|
if (style > 0)
|
|
{
|
|
style += FillBase;
|
|
}
|
|
CurrentPath.SetFill1(style);
|
|
|
|
in->LogParseShape(" ShapeCharacter read: fill1 = %d\n", style);
|
|
}
|
|
if ((flags & 0x08)
|
|
&& NumLineBits > 0)
|
|
{
|
|
// LineStyleChange = 1;
|
|
if (! CurrentPath.IsEmpty())
|
|
{
|
|
CurrentPath.Pack(ppathAllocator, &Paths);
|
|
CurrentPath.SetMoveTo(moveX, moveY, numMoveBits);
|
|
}
|
|
int style = in->ReadUInt(NumLineBits);
|
|
if (style > 0)
|
|
{
|
|
style += LineBase;
|
|
}
|
|
CurrentPath.SetLine(style);
|
|
|
|
in->LogParseShape(" ShapeCharacter read: line = %d\n", style);
|
|
}
|
|
if (flags & 0x10)
|
|
{
|
|
GASSERT(tagType >= 22);
|
|
|
|
in->LogParse(" ShapeCharacter read: more fill styles\n");
|
|
|
|
// Store the current GFxPath if any.
|
|
if (! CurrentPath.IsEmpty())
|
|
{
|
|
CurrentPath.Pack(ppathAllocator, &Paths);
|
|
// Clear styles.
|
|
CurrentPath.SetFill0(0);
|
|
CurrentPath.SetFill1(0);
|
|
CurrentPath.SetLine(0);
|
|
}
|
|
// Tack on an empty GFxPath signaling a new shape.
|
|
// @@ need better understanding of whether this is correct??!?!!
|
|
// @@ i.E., we should just start a whole new shape here, right?
|
|
|
|
CurrentPath.SetNewShape();
|
|
CurrentPath.Pack(ppathAllocator, &Paths);
|
|
#ifdef GFX_SHAPE_MEM_TRACKING
|
|
int __offset = in->Tell();
|
|
#endif
|
|
FillBase = pfillStyles ? (int)pfillStyles->GetSize() : 0;
|
|
LineBase = plineStyles ? (int)plineStyles->GetSize() : 0;
|
|
GFx_ReadFillStyles(pfillStyles, p, tagType);
|
|
GFx_ReadLineStyles(plineStyles, p, tagType);
|
|
NumFillBits = in->ReadUInt4(); // RAGE - use specialized (faster) read function
|
|
NumLineBits = in->ReadUInt4();
|
|
#ifdef GFX_SHAPE_MEM_TRACKING
|
|
__startingFileOffset -= (in->Tell() - __offset);
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// EDGERECORD
|
|
int EdgeFlag = in->ReadUInt1();
|
|
if (EdgeFlag == 0)
|
|
{
|
|
// curved GFxEdge
|
|
UInt numbits = 2 + in->ReadUInt4(); // RAGE - use specialized (faster) read function
|
|
SInt cx = in->ReadSInt(numbits);
|
|
SInt cy = in->ReadSInt(numbits);
|
|
SInt ax = in->ReadSInt(numbits);
|
|
SInt ay = in->ReadSInt(numbits);
|
|
|
|
CurrentPath.AddCurve(cx, cy, ax, ay, numbits);
|
|
|
|
in->LogParseShape(" ShapeCharacter read: curved edge = %4g %4g - %4g %4g - %4g %4g\n",
|
|
(Float) moveX * sfactor, (Float) moveY * sfactor,
|
|
(Float) (moveX + cx) * sfactor, (Float) (moveY + cy) * sfactor,
|
|
(Float) (moveX + cx + ax ) * sfactor, (Float) (moveY + cy + ay ) * sfactor);
|
|
|
|
moveX += ax + cx;
|
|
moveY += ay + cy;
|
|
}
|
|
else
|
|
{
|
|
// straight GFxEdge
|
|
UInt numbits = 2 + in->ReadUInt4(); // RAGE - use specialized (faster) read function
|
|
int LineFlag = in->ReadUInt1();
|
|
//Float dx = 0, dy = 0;
|
|
SInt dx = 0, dy = 0;
|
|
if (LineFlag)
|
|
{
|
|
// General line.
|
|
dx = in->ReadSInt(numbits);
|
|
dy = in->ReadSInt(numbits);
|
|
}
|
|
else
|
|
{
|
|
int VertFlag = in->ReadUInt1();
|
|
if (VertFlag == 0) {
|
|
// Horizontal line.
|
|
dx = in->ReadSInt(numbits);
|
|
} else {
|
|
// Vertical line.
|
|
dy = in->ReadSInt(numbits);
|
|
}
|
|
}
|
|
|
|
in->LogParseShape(" ShapeCharacter read: straight edge = %4g %4g - %4g %4g\n",
|
|
(Float)moveX * sfactor, (Float)moveY * sfactor,
|
|
(Float)(moveX + dx) * sfactor, (Float)(moveY + dy) * sfactor);
|
|
|
|
// Must have a half of dx,dy for the curve control point
|
|
// to perform shape tween (morph) correctly.
|
|
CurrentPath.AddLineTo(dx, dy, numbits);
|
|
|
|
moveX += dx;
|
|
moveY += dy;
|
|
}
|
|
}
|
|
}
|
|
#endif // GFX_USE_CUSTOM_PACKER
|
|
|
|
#ifdef GFX_SHAPE_MEM_TRACKING
|
|
GFx_MT_SwfGeomMem += (in->Tell() - __startingFileOffset);
|
|
if(withStyle)
|
|
{
|
|
printf("Shapes memory statistics:\n"
|
|
" ShapesCount = %d, PathsCount = %d, SwfGeomMem = %d, AllocatedMem = %d\n",
|
|
GFx_MT_ShapesCount, GFx_MT_PathsCount, GFx_MT_SwfGeomMem, GFx_MT_GeomMem);
|
|
}
|
|
#endif
|
|
|
|
UPInt i, n;
|
|
// Determine if we have any non-solid color fills.
|
|
Flags &= (~Flags_TexturedFill);
|
|
if (pfillStyles)
|
|
{
|
|
for (i = 0, n = pfillStyles->GetSize(); i < n; i++)
|
|
{
|
|
if ((*pfillStyles)[i].GetType() != 0)
|
|
{
|
|
Flags |= Flags_TexturedFill;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
// Compete maximum stroke extent
|
|
if (Flags & Flags_StylesSupport)
|
|
{
|
|
Float maxStrokeExtent = 1.0f;
|
|
if (plineStyles)
|
|
{
|
|
for(i = 0, n = plineStyles->GetSize(); i < n; i++)
|
|
maxStrokeExtent = G_Max(maxStrokeExtent, (*plineStyles)[i].GetStrokeExtent());
|
|
}
|
|
SetMaxStrokeExtent(maxStrokeExtent);
|
|
}
|
|
}
|
|
|
|
void GFxConstShapeNoStyles::GetShapeAndPathCounts(UInt* pshapesCnt, UInt* ppathsCnt) const
|
|
{
|
|
return GFxSwfPathData::GetShapeAndPathCounts(pPaths, pshapesCnt, ppathsCnt);
|
|
}
|
|
|
|
GFxShapeBase::PathsIterator* GFxConstShapeNoStyles::GetPathsIterator() const
|
|
{
|
|
GFxVirtualPathIterator<GFxConstShapeNoStyles, GFxSwfPathData>* pswfIter =
|
|
GHEAP_AUTO_NEW(this) GFxVirtualPathIterator<GFxConstShapeNoStyles, GFxSwfPathData>(this);
|
|
return pswfIter;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
//
|
|
// GFxSwfPathData
|
|
//
|
|
GFxSwfPathData::PathsIterator::PathsIterator(const GFxShapeBase* pdef) :
|
|
pShapeDef(static_cast<const GFxConstShapeNoStyles*>(pdef)),
|
|
Stream(pShapeDef->GetNativePathData()),
|
|
Record(Rec_None)
|
|
{
|
|
if (Stream.pData)
|
|
{
|
|
#ifdef GFC_BUILD_DEBUG
|
|
// Read a 4-bytes signature to ensure we use correct path format.
|
|
UInt32 sig;
|
|
sig = Stream.ReadU32();
|
|
GUNUSED(sig);
|
|
GASSERT(sig == Signature);
|
|
#endif
|
|
|
|
PathFlags = Stream.ReadU8();
|
|
if (pdef->GetFlags() & GFxShapeBase::Flags_Sfactor20)
|
|
{
|
|
Sfactor = 1.0f/20.0f;
|
|
PathFlags |= Flags_HasScaleFactor;
|
|
}
|
|
else
|
|
{
|
|
Sfactor = 1.0f;
|
|
}
|
|
|
|
// need to skip memory counter
|
|
UInt memSizeInBytes = ((PathFlags & GFxSwfPathData::Mask_NumBytesInMemCnt) >>
|
|
GFxSwfPathData::Shift_NumBytesInMemCnt) + 1;
|
|
Stream.Skip(memSizeInBytes); // just skip for now
|
|
|
|
if (PathFlags & Flags_HasFillStyles)
|
|
CurFillStylesNum = Stream.ReadU16();
|
|
else
|
|
CurFillStylesNum = 0;
|
|
if (PathFlags & Flags_HasLineStyles)
|
|
CurLineStylesNum = Stream.ReadU16();
|
|
else
|
|
CurLineStylesNum = 0;
|
|
FillBase = LineBase = 0;
|
|
NumFillBits = Stream.ReadUInt4(); // RAGE - use specialized (faster) read function
|
|
NumLineBits = Stream.ReadUInt4();
|
|
MoveX = MoveY = 0;
|
|
CurEdgesCount = 0;
|
|
CurPathsCount = 0;
|
|
Fill0 = Fill1 = Line = 0;
|
|
PathsCount = ShapesCount = 0;
|
|
ReadNextEdge();
|
|
}
|
|
else
|
|
{
|
|
// empty shape? init by default values
|
|
Record = Rec_EndOfShape; // forces IsFinished to ret true
|
|
PathFlags = 0;
|
|
Sfactor = 1.0f;
|
|
CurFillStylesNum = CurLineStylesNum = 0;
|
|
FillBase = LineBase = 0;
|
|
NumFillBits = NumLineBits = 0;
|
|
MoveX = MoveY = 0;
|
|
CurEdgesCount = 0;
|
|
CurPathsCount = 0;
|
|
Fill0 = Fill1 = Line = 0;
|
|
PathsCount = ShapesCount = 0;
|
|
}
|
|
}
|
|
|
|
GFxSwfPathData::PathsIterator::PathsIterator(const GFxSwfPathData::PathsIterator& p) :
|
|
Stream(p.Stream), Fill0(p.Fill0), Fill1(p.Fill1), Line(p.Line), Record(p.Record),
|
|
PathsCount(p.PathsCount), ShapesCount(p.ShapesCount), MoveX(p.MoveX), MoveY(p.MoveY),
|
|
FillBase(p.FillBase), LineBase(p.LineBase), CurFillStylesNum(p.CurFillStylesNum),
|
|
CurLineStylesNum(p.CurLineStylesNum), NumFillBits(p.NumFillBits),
|
|
NumLineBits(p.NumLineBits), CurEdgesCount(p.CurEdgesCount), CurPathsCount(p.CurPathsCount),
|
|
Ax(p.Ax), Ay(p.Ay), Cx(p.Cx), Cy(p.Cy), Dx(p.Dx), Dy(p.Dy),
|
|
Sfactor(p.Sfactor), PathFlags(p.PathFlags)
|
|
{}
|
|
|
|
void GFxSwfPathData::PathsIterator::AddForTessellation(GCompoundShape *cs)
|
|
{
|
|
EdgesIterator it = GetEdgesIterator();
|
|
|
|
Float ax, ay;
|
|
it.GetMoveXY(&ax, &ay);
|
|
UInt fill0, fill1, line;
|
|
GetStyles(&fill0, &fill1, &line);
|
|
cs->BeginPath(fill0 - 1, fill1 - 1, line - 1, ax, ay);
|
|
|
|
EdgesIterator::Edge edge;
|
|
while(!it.IsFinished())
|
|
{
|
|
it.GetEdge(&edge);
|
|
if (edge.Curve)
|
|
cs->AddCurve(edge.Cx, edge.Cy, edge.Ax, edge.Ay);
|
|
else
|
|
cs->AddVertex(edge.Ax, edge.Ay);
|
|
}
|
|
AdvanceBy(it);
|
|
}
|
|
|
|
void GFxSwfPathData::PathsIterator::SkipComplex()
|
|
{
|
|
// this call will skip whole path
|
|
EdgesIterator ei(*this);
|
|
while(!ei.IsFinished())
|
|
{
|
|
GASSERT(Record == PathsIterator::Rec_StraightEdge ||
|
|
Record == PathsIterator::Rec_CurveEdge);
|
|
switch(Record)
|
|
{
|
|
case PathsIterator::Rec_CurveEdge:
|
|
MoveX += Cx + Ax;
|
|
MoveY += Cy + Ay;
|
|
break;
|
|
case PathsIterator::Rec_StraightEdge:
|
|
MoveX += Dx;
|
|
MoveY += Dy;
|
|
break;
|
|
default:;
|
|
}
|
|
++ei.EdgeIndex;
|
|
ReadNext();
|
|
}
|
|
AdvanceBy(ei);
|
|
}
|
|
|
|
void GFxSwfPathData::PathsIterator::ReadNextEdge()
|
|
{
|
|
while(!IsFinished())
|
|
{
|
|
ReadNext();
|
|
if (Record != Rec_NonEdge)
|
|
break;
|
|
}
|
|
}
|
|
|
|
#if 0//defined(GFC_BUILD_DEBUG)
|
|
#define LOG_SHAPE0(s) printf(s)
|
|
#define LOG_SHAPE1(s,p1) printf(s,p1)
|
|
#define LOG_SHAPE2(s,p1,p2) printf(s,p1,p2)
|
|
#define LOG_SHAPE3(s,p1,p2,p3) printf(s,p1,p2,p3)
|
|
#define LOG_SHAPE4(s,p1,p2,p3,p4) printf(s,p1,p2,p3,p4)
|
|
#define LOG_SHAPE5(s,p1,p2,p3,p4,p5) printf(s,p1,p2,p3,p4,p5)
|
|
#define LOG_SHAPE6(s,p1,p2,p3,p4,p5,p6) printf(s,p1,p2,p3,p4,p5,p6)
|
|
#else
|
|
#define LOG_SHAPE0(s)
|
|
#define LOG_SHAPE1(s,p1)
|
|
#define LOG_SHAPE2(s,p1,p2)
|
|
#define LOG_SHAPE3(s,p1,p2,p3)
|
|
#define LOG_SHAPE4(s,p1,p2,p3,p4)
|
|
#define LOG_SHAPE5(s,p1,p2,p3,p4,p5)
|
|
#define LOG_SHAPE6(s,p1,p2,p3,p4,p5,p6)
|
|
#endif
|
|
|
|
void GFxSwfPathData::PathsIterator::ReadNext()
|
|
{
|
|
|
|
// GFxStream* in = &Stream;
|
|
|
|
LOG_SHAPE0("SHAPE decode: start\n");
|
|
|
|
// These are state variables that keep the
|
|
// current position & style of the shape
|
|
// outline, and vary as we read the GFxEdge data.
|
|
//
|
|
// At the moment we just store each GFxEdge with
|
|
// the full necessary info to render it, which
|
|
// is simple but not optimally efficient.
|
|
|
|
// SHAPERECORDS
|
|
do
|
|
{
|
|
bool typeFlag = Stream.ReadUInt1();
|
|
if (!typeFlag)
|
|
{
|
|
// Parse the record.
|
|
int flags = Stream.ReadUInt5();
|
|
if (flags == 0)
|
|
{
|
|
// End of shape records.
|
|
Record = Rec_EndOfShape;
|
|
LOG_SHAPE0(" SHAPE decode: end-of-shape\n");
|
|
if (CurEdgesCount > 0)
|
|
{
|
|
++PathsCount;
|
|
CurEdgesCount = 0;
|
|
++CurPathsCount;
|
|
}
|
|
break;
|
|
}
|
|
if (flags & 0x01)
|
|
{
|
|
// MoveTo = 1;
|
|
Record = Rec_NonEdge;
|
|
if (CurEdgesCount > 0)
|
|
{
|
|
++PathsCount;
|
|
CurEdgesCount = 0;
|
|
++CurPathsCount;
|
|
}
|
|
|
|
UInt numMoveBits = Stream.ReadUInt5();
|
|
MoveX = Stream.ReadSInt(numMoveBits);
|
|
MoveY = Stream.ReadSInt(numMoveBits);
|
|
|
|
LOG_SHAPE3(" SHAPE decode: numbits = %d, moveto %d %d\n",
|
|
numMoveBits, MoveX, MoveY);
|
|
}
|
|
if ((flags & 0x02) && NumFillBits > 0)
|
|
{
|
|
Record = Rec_NonEdge;
|
|
// FillStyle0_change = 1;
|
|
if (CurEdgesCount > 0)
|
|
{
|
|
++PathsCount;
|
|
CurEdgesCount = 0;
|
|
++CurPathsCount;
|
|
}
|
|
|
|
UInt style = Stream.ReadUInt(NumFillBits);
|
|
if (style > 0)
|
|
{
|
|
style += FillBase;
|
|
}
|
|
Fill0 = style;
|
|
|
|
LOG_SHAPE1(" SHAPE decode: fill0 = %d\n", style);
|
|
}
|
|
if ((flags & 0x04) && NumFillBits > 0)
|
|
{
|
|
Record = Rec_NonEdge;
|
|
// FillStyle1_change = 1;
|
|
if (CurEdgesCount > 0)
|
|
{
|
|
++PathsCount;
|
|
CurEdgesCount = 0;
|
|
++CurPathsCount;
|
|
}
|
|
|
|
int style = Stream.ReadUInt(NumFillBits);
|
|
if (style > 0)
|
|
{
|
|
style += FillBase;
|
|
}
|
|
Fill1 = style;
|
|
|
|
LOG_SHAPE1(" SHAPE decode: fill1 = %d\n", style);
|
|
}
|
|
if ((flags & 0x08) && NumLineBits > 0)
|
|
{
|
|
Record = Rec_NonEdge;
|
|
// LineStyleChange = 1;
|
|
if (CurEdgesCount > 0)
|
|
{
|
|
++PathsCount;
|
|
CurEdgesCount = 0;
|
|
++CurPathsCount;
|
|
}
|
|
|
|
int style = Stream.ReadUInt(NumLineBits);
|
|
if (style > 0)
|
|
{
|
|
style += LineBase;
|
|
}
|
|
Line = style;
|
|
|
|
LOG_SHAPE1(" SHAPE decode: line = %d\n", style);
|
|
}
|
|
if (flags & 0x10)
|
|
{
|
|
Record = Rec_NewShape;
|
|
|
|
if (CurPathsCount > 0)
|
|
{
|
|
++ShapesCount;
|
|
CurPathsCount = 0;
|
|
}
|
|
if (CurEdgesCount > 0)
|
|
{
|
|
++PathsCount;
|
|
CurEdgesCount = 0;
|
|
++CurPathsCount;
|
|
}
|
|
|
|
Fill0 = Fill1 = Line = 0;
|
|
|
|
Stream.Align();
|
|
UInt fsn = Stream.ReadU8();
|
|
if (fsn == 0xFF && PathFlags & Flags_HasExtendedFillNum)
|
|
fsn = Stream.ReadU16();
|
|
UInt lsn = Stream.ReadU8();
|
|
if (lsn == 0xFF) // ? PathFlags & Flags_HasExtendedFillNum ?
|
|
lsn = Stream.ReadU16();
|
|
FillBase = CurFillStylesNum;
|
|
LineBase = CurLineStylesNum;
|
|
CurFillStylesNum += fsn;
|
|
CurLineStylesNum += lsn;
|
|
|
|
NumFillBits = Stream.ReadUInt4(); // RAGE - use specialized (faster) read function
|
|
NumLineBits = Stream.ReadUInt4();
|
|
|
|
LOG_SHAPE2(" SHAPE decode: more fill styles, numfillbits = %d, numlinebint = %d\n",
|
|
NumFillBits, NumLineBits);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// EDGERECORD
|
|
int edgeFlag = Stream.ReadUInt1();
|
|
++CurEdgesCount;
|
|
if (edgeFlag == 0)
|
|
{
|
|
// curved GFxEdge
|
|
UInt numbits = 2 + Stream.ReadUInt4(); // RAGE - use specialized (faster) read function
|
|
Cx = Stream.ReadSInt(numbits);
|
|
Cy = Stream.ReadSInt(numbits);
|
|
Ax = Stream.ReadSInt(numbits);
|
|
Ay = Stream.ReadSInt(numbits);
|
|
|
|
Record = Rec_CurveEdge;
|
|
|
|
LOG_SHAPE5(" SHAPE decode: curved edge, numbits = %d, %d %d - %d %d\n",
|
|
numbits, Cx, Cy, Ax, Ay);
|
|
}
|
|
else
|
|
{
|
|
// straight GFxEdge
|
|
UInt numbits = 2 + Stream.ReadUInt4(); // RAGE - use specialized (faster) read function
|
|
int lineFlag = Stream.ReadUInt1();
|
|
//Float dx = 0, dy = 0;
|
|
Dx = 0, Dy = 0;
|
|
if (lineFlag)
|
|
{
|
|
// General line.
|
|
Dx = Stream.ReadSInt(numbits);
|
|
Dy = Stream.ReadSInt(numbits);
|
|
}
|
|
else
|
|
{
|
|
int vertFlag = Stream.ReadUInt1();
|
|
if (vertFlag == 0) {
|
|
// Horizontal line.
|
|
Dx = Stream.ReadSInt(numbits);
|
|
} else {
|
|
// Vertical line.
|
|
Dy = Stream.ReadSInt(numbits);
|
|
}
|
|
}
|
|
|
|
LOG_SHAPE4(" SHAPE decode: straight edge, numbits = %d, lineFl = %d, Dx = %d, Dy = %d\n",
|
|
numbits, lineFlag, Dx, Dy);
|
|
Record = Rec_StraightEdge;
|
|
}
|
|
}
|
|
} while(0);
|
|
LOG_SHAPE0("SHAPE decode: end\n");
|
|
}
|
|
|
|
UInt GFxSwfPathData::PathsIterator::GetEdgesCount() const
|
|
{
|
|
PathsIterator pit(*this);
|
|
return EdgesIterator(pit).CalcEdgesCount();
|
|
}
|
|
|
|
GFxSwfPathData::EdgesIterator::EdgesIterator(GFxSwfPathData::PathsIterator& pathIter) :
|
|
pPathIter(&pathIter)
|
|
{
|
|
EdgeIndex = 0;
|
|
if (!(pPathIter->Record & PathsIterator::Rec_EdgeMask) && !pPathIter->IsFinished())
|
|
pPathIter->ReadNextEdge();
|
|
}
|
|
|
|
void GFxSwfPathData::EdgesIterator::GetMoveXY(Float* px, Float* py)
|
|
{
|
|
*px = Float(pPathIter->MoveX);
|
|
*py = Float(pPathIter->MoveY);
|
|
if (pPathIter->PathFlags & Flags_HasScaleFactor)
|
|
{
|
|
*px *= pPathIter->Sfactor;
|
|
*py *= pPathIter->Sfactor;
|
|
}
|
|
}
|
|
|
|
UInt GFxSwfPathData::EdgesIterator::GetEdgesCount() const
|
|
{
|
|
PathsIterator pit(*pPathIter);
|
|
EdgesIterator eit = pit.GetEdgesIterator();
|
|
return eit.CalcEdgesCount();
|
|
}
|
|
|
|
UInt GFxSwfPathData::EdgesIterator::CalcEdgesCount()
|
|
{
|
|
while(!IsFinished())
|
|
{
|
|
PlainEdge edge;
|
|
GetPlainEdge(&edge);
|
|
}
|
|
return EdgeIndex;
|
|
}
|
|
|
|
void GFxSwfPathData::EdgesIterator::GetEdge(Edge* pedge, bool doLines2CurveConv)
|
|
{
|
|
GASSERT(pPathIter->Record == PathsIterator::Rec_StraightEdge ||
|
|
pPathIter->Record == PathsIterator::Rec_CurveEdge);
|
|
switch(pPathIter->Record)
|
|
{
|
|
case PathsIterator::Rec_CurveEdge:
|
|
{
|
|
pedge->Cx = Float(pPathIter->Cx + pPathIter->MoveX);
|
|
pedge->Cy = Float(pPathIter->Cy + pPathIter->MoveY);
|
|
pPathIter->MoveX += pPathIter->Cx + pPathIter->Ax;
|
|
pPathIter->MoveY += pPathIter->Cy + pPathIter->Ay;
|
|
pedge->Ax = Float(pPathIter->MoveX);
|
|
pedge->Ay = Float(pPathIter->MoveY);
|
|
if (pPathIter->PathFlags & Flags_HasScaleFactor)
|
|
{
|
|
pedge->Cx *= pPathIter->Sfactor;
|
|
pedge->Cy *= pPathIter->Sfactor;
|
|
pedge->Ax *= pPathIter->Sfactor;
|
|
pedge->Ay *= pPathIter->Sfactor;
|
|
}
|
|
pedge->Curve = 1;
|
|
}
|
|
break;
|
|
case PathsIterator::Rec_StraightEdge:
|
|
{
|
|
pedge->Ax = Float(pPathIter->Dx + pPathIter->MoveX);
|
|
pedge->Ay = Float(pPathIter->Dy + pPathIter->MoveY);
|
|
if (doLines2CurveConv)
|
|
{
|
|
pedge->Cx = (pPathIter->Dx * 0.5f + pPathIter->MoveX);
|
|
pedge->Cy = (pPathIter->Dy * 0.5f + pPathIter->MoveY);
|
|
if (pPathIter->PathFlags & Flags_HasScaleFactor)
|
|
{
|
|
pedge->Cx *= pPathIter->Sfactor;
|
|
pedge->Cy *= pPathIter->Sfactor;
|
|
pedge->Ax *= pPathIter->Sfactor;
|
|
pedge->Ay *= pPathIter->Sfactor;
|
|
}
|
|
pedge->Curve = 1;
|
|
}
|
|
else
|
|
{
|
|
if (pPathIter->PathFlags & Flags_HasScaleFactor)
|
|
{
|
|
pedge->Ax *= pPathIter->Sfactor;
|
|
pedge->Ay *= pPathIter->Sfactor;
|
|
}
|
|
pedge->Curve = 0;
|
|
}
|
|
pPathIter->MoveX += pPathIter->Dx;
|
|
pPathIter->MoveY += pPathIter->Dy;
|
|
}
|
|
break;
|
|
default:;
|
|
}
|
|
++EdgeIndex;
|
|
pPathIter->ReadNext();
|
|
}
|
|
|
|
|
|
void GFxSwfPathData::EdgesIterator::GetPlainEdge(PlainEdge* pedge)
|
|
{
|
|
GASSERT(pPathIter->Record == PathsIterator::Rec_StraightEdge ||
|
|
pPathIter->Record == PathsIterator::Rec_CurveEdge);
|
|
SInt32* p = pedge->Data;
|
|
switch(pPathIter->Record)
|
|
{
|
|
case PathsIterator::Rec_CurveEdge:
|
|
*p++ = pPathIter->Cx;
|
|
*p++ = pPathIter->Cy;
|
|
*p++ = pPathIter->Ax;
|
|
*p++ = pPathIter->Ay;
|
|
pPathIter->MoveX += pPathIter->Cx + pPathIter->Ax;
|
|
pPathIter->MoveY += pPathIter->Cy + pPathIter->Ay;
|
|
break;
|
|
case PathsIterator::Rec_StraightEdge:
|
|
*p++ = pPathIter->Dx;
|
|
*p++ = pPathIter->Dy;
|
|
pPathIter->MoveX += pPathIter->Dx;
|
|
pPathIter->MoveY += pPathIter->Dy;
|
|
break;
|
|
default:;
|
|
}
|
|
pedge->Size = (UInt)(p - pedge->Data);
|
|
++EdgeIndex;
|
|
pPathIter->ReadNext();
|
|
}
|
|
|
|
UInt GFxSwfPathData::GetMemorySize(const UByte* ppaths)
|
|
{
|
|
UInt offset = 0;
|
|
#ifdef GFC_BUILD_DEBUG
|
|
offset += 4; // signature
|
|
#endif
|
|
UByte pathFlags = ppaths[offset];
|
|
++offset; // path flags
|
|
UInt memSizeInBytes = ((pathFlags & GFxSwfPathData::Mask_NumBytesInMemCnt) >>
|
|
GFxSwfPathData::Shift_NumBytesInMemCnt) + 1;
|
|
const UByte* pmemSize = ppaths + offset;
|
|
UInt memSize = GFx_ReadUInt(pmemSize, memSizeInBytes);
|
|
return memSize;
|
|
}
|
|
|
|
void GFxSwfPathData::GetShapeAndPathCounts(const UByte* ppaths, UInt* pshapesCnt, UInt* ppathsCnt)
|
|
{
|
|
UInt offset = 0;
|
|
#ifdef GFC_BUILD_DEBUG
|
|
offset += 4; // signature
|
|
#endif
|
|
UByte pathFlags = ppaths[offset];
|
|
++offset; // path flags
|
|
UInt memSizeInBytes = ((pathFlags & GFxSwfPathData::Mask_NumBytesInMemCnt) >>
|
|
GFxSwfPathData::Shift_NumBytesInMemCnt) + 1;
|
|
UInt geomCntInBytes = ((pathFlags & GFxSwfPathData::Mask_NumBytesInGeomCnt) >>
|
|
GFxSwfPathData::Shift_NumBytesInGeomCnt) + 1;
|
|
const UByte* pmemSize = ppaths + offset;
|
|
UInt memSize = GFx_ReadUInt(pmemSize, memSizeInBytes);
|
|
const UByte* pcnts = ppaths + memSize - 2 * geomCntInBytes;
|
|
if (pshapesCnt)
|
|
*pshapesCnt = GFx_ReadUInt(pcnts, geomCntInBytes);
|
|
pcnts += geomCntInBytes;
|
|
if (ppathsCnt)
|
|
*ppathsCnt = GFx_ReadUInt(pcnts, geomCntInBytes);
|
|
}
|
|
|
|
///////////////////////////////////
|
|
// GFxShapeNoStyles
|
|
//
|
|
GFxShapeNoStyles::GFxShapeNoStyles(UInt pageSize) :
|
|
PathAllocator(pageSize)
|
|
{
|
|
|
|
}
|
|
|
|
// Push our shape data through the tessellator.
|
|
void GFxShapeNoStyles::Tessellate(GFxMeshSet *meshSet, Float tolerance,
|
|
GFxDisplayContext &context,
|
|
GFxScale9GridInfo* s9g) const
|
|
{
|
|
TessellateImpl<GFxPathData>(meshSet, tolerance, context, s9g);
|
|
}
|
|
|
|
bool GFxShapeNoStyles::DefPointTestLocal(const GPointF &pt,
|
|
bool testShape,
|
|
const GFxCharacter* pinst) const
|
|
{
|
|
return DefPointTestLocalImpl<GFxPathData>(pt, testShape, pinst);
|
|
}
|
|
|
|
|
|
|
|
// Convert the paths to GCompoundShape, flattening the curves.
|
|
// The function ignores the NewShape flag and adds all paths
|
|
// GFxShapeBase contains.
|
|
void GFxShapeNoStyles::MakeCompoundShape(GCompoundShape *cs, Float tolerance) const
|
|
{
|
|
MakeCompoundShapeImpl<GFxPathData>(cs, tolerance);
|
|
}
|
|
|
|
|
|
UInt32 GFxShapeNoStyles::ComputeGeometryHash() const
|
|
{
|
|
return ComputeGeometryHashImpl<GFxPathData>();
|
|
}
|
|
|
|
|
|
bool GFxShapeNoStyles::IsEqualGeometry(const GFxShapeBase& cmpWith) const
|
|
{
|
|
return IsEqualGeometryImpl<GFxPathData>(cmpWith);
|
|
}
|
|
|
|
|
|
|
|
// Find the bounds of this shape, and store them in
|
|
// the given rectangle.
|
|
void GFxShapeNoStyles::ComputeBound(GRectF* r) const
|
|
{
|
|
ComputeBoundImpl<GFxPathData>(r);
|
|
}
|
|
|
|
GFxShapeBase::PathsIterator* GFxShapeNoStyles::GetPathsIterator() const
|
|
{
|
|
GFxVirtualPathIterator<GFxShapeNoStyles, GFxPathData>* pswfIter =
|
|
GHEAP_AUTO_NEW(this) GFxVirtualPathIterator<GFxShapeNoStyles, GFxPathData>(this);
|
|
return pswfIter;
|
|
}
|
|
|
|
void GFxShapeNoStyles::GetShapeAndPathCounts(UInt* pshapesCnt, UInt* ppathsCnt) const
|
|
{
|
|
if (pshapesCnt)
|
|
*pshapesCnt = Paths.ShapesCount;
|
|
if (ppathsCnt)
|
|
*ppathsCnt = Paths.PathsCount;
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// GFxConstShapeWithStyles
|
|
//
|
|
GFxConstShapeWithStyles::GFxConstShapeWithStyles()
|
|
{
|
|
MaxStrokeExtent = 100.0f; // Max Flash width = 200; so divide by 2.
|
|
Flags |= Flags_StylesSupport | Flags_S9GSupport;
|
|
pFillStyles = NULL;
|
|
pLineStyles = NULL;
|
|
FillStylesNum = LineStylesNum = 0;
|
|
RectBound = GRectF(0,0,0,0);
|
|
}
|
|
|
|
GFxConstShapeWithStyles::~GFxConstShapeWithStyles()
|
|
{
|
|
for (UInt i = 0; i < (UInt)FillStylesNum; ++i)
|
|
pFillStyles[i].~GFxFillStyle();
|
|
GFREE(const_cast<GFxFillStyle*>(pFillStyles));
|
|
|
|
for (UInt i = 0; i < (UInt)LineStylesNum; ++i)
|
|
pLineStyles[i].~GFxLineStyle();
|
|
GFREE(const_cast<GFxLineStyle*>(pLineStyles));
|
|
}
|
|
|
|
void GFxConstShapeWithStyles::Tessellate(GFxMeshSet *meshSet, Float tolerance,
|
|
GFxDisplayContext &context,
|
|
GFxScale9GridInfo* s9g) const
|
|
{
|
|
TessellateImpl<GFxSwfPathData>(meshSet, tolerance, context, s9g, MaxStrokeExtent);
|
|
}
|
|
|
|
void GFxConstShapeWithStyles::Read(GFxLoadProcess* p, GFxTagType tagType, UInt lenInBytes, bool withStyle)
|
|
{
|
|
GFxFillStyleArrayTemp fillStyles;
|
|
GFxLineStyleArrayTemp lineStyles;
|
|
GFxConstShapeNoStyles::Read(p, tagType, lenInBytes, withStyle, &fillStyles, &lineStyles);
|
|
|
|
// convert fillStyles & lineStyles to regular arrays
|
|
GMemoryHeap* pheap = p->GetLoadHeap();
|
|
|
|
if (fillStyles.GetSize())
|
|
{
|
|
GASSERT(fillStyles.GetSize() < 65536);
|
|
GASSERT(FillStylesNum == 0 && pFillStyles == NULL);
|
|
FillStylesNum = (UInt32)fillStyles.GetSize();
|
|
GFxFillStyle* pfillStyles = (GFxFillStyle*)
|
|
GHEAP_ALLOC(pheap, sizeof(GFxFillStyle)*FillStylesNum, GFxStatMD_ShapeData_Mem);
|
|
pFillStyles = pfillStyles;
|
|
for(UInt i = 0; i < (UInt)FillStylesNum; ++i)
|
|
{
|
|
G_Construct<GFxFillStyle>(&pfillStyles[i], fillStyles[i]);
|
|
}
|
|
}
|
|
if (lineStyles.GetSize())
|
|
{
|
|
GASSERT(lineStyles.GetSize() < 65536);
|
|
GASSERT(LineStylesNum == 0 && pLineStyles == NULL);
|
|
LineStylesNum = (UInt32)lineStyles.GetSize();
|
|
GFxLineStyle* plineStyles = (GFxLineStyle*)
|
|
GHEAP_ALLOC(pheap, sizeof(GFxLineStyle)*LineStylesNum, GFxStatMD_ShapeData_Mem);
|
|
pLineStyles = plineStyles;
|
|
for(UInt i = 0; i < (UInt)LineStylesNum; ++i)
|
|
{
|
|
G_Construct<GFxLineStyle>(&plineStyles[i], lineStyles[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
////////////////////////////////
|
|
// GFxShapeWithStyles
|
|
|
|
GFxShapeWithStyles::GFxShapeWithStyles(UInt pageSize) : GFxShapeNoStyles(pageSize)
|
|
{
|
|
MaxStrokeExtent = 100.0f; // Max Flash width = 200; so divide by 2.
|
|
Flags |= Flags_StylesSupport | Flags_S9GSupport;
|
|
RectBound = GRectF(0,0,0,0);
|
|
}
|
|
|
|
GFxShapeWithStyles::~GFxShapeWithStyles()
|
|
{
|
|
}
|
|
|
|
// Creates a definition relying on in-memory image.
|
|
void GFxShapeWithStyles::SetToImage(GFxImageResource *pimage, bool bilinear)
|
|
{
|
|
GRect<SInt> r = pimage->GetImageInfo()->GetRect();
|
|
SInt iw = r.Width();
|
|
SInt ih = r.Height();
|
|
Float width = (Float)PixelsToTwips(iw);
|
|
Float height = (Float)PixelsToTwips(ih);
|
|
|
|
// Create fill style
|
|
FillStyles.Resize(1);
|
|
GMatrix2D m;
|
|
m.Translation((Float)PixelsToTwips(r.Left), (Float)PixelsToTwips(r.Top));
|
|
FillStyles[0].SetImageFill(bilinear ? GFxFill_ClippedSmoothImage : GFxFill_ClippedImage,
|
|
pimage, m);
|
|
Flags |= GFxShapeBase::Flags_TexturedFill;
|
|
|
|
// Set bounds
|
|
Bound.SetRect(width, height);
|
|
SetRectBoundsLocal(Bound);
|
|
SetValidBoundsFlag(true);
|
|
MaxStrokeExtent = 0;
|
|
|
|
// Path with fill 0 on one side; clockwise.
|
|
PathAllocator.SetDefaultPageSize(32);
|
|
Flags |= GFxShapeBase::Flags_LocallyAllocated;
|
|
|
|
GFxPathPacker currentPath;
|
|
currentPath.SetFill0(0);
|
|
currentPath.SetFill1(1);
|
|
currentPath.SetLine(0);
|
|
|
|
currentPath.SetMoveTo(0, 0);
|
|
currentPath.AddLineTo(PixelsToTwips(iw), 0);
|
|
currentPath.AddLineTo(0, PixelsToTwips(ih));
|
|
currentPath.AddLineTo(-PixelsToTwips(iw), 0);
|
|
currentPath.AddLineTo(0, -PixelsToTwips(ih));
|
|
|
|
currentPath.Pack(&PathAllocator, &Paths);
|
|
}
|
|
|
|
void GFxShapeWithStyles::Tessellate(GFxMeshSet *meshSet, Float tolerance,
|
|
GFxDisplayContext &context,
|
|
GFxScale9GridInfo* s9g) const
|
|
{
|
|
TessellateImpl<GFxPathData>(meshSet, tolerance, context, s9g, MaxStrokeExtent);
|
|
}
|
|
|
|
|
|
//
|
|
// ***** GFxPathData::EdgesIterator
|
|
//
|
|
|
|
GFxPathData::EdgesIterator::EdgesIterator(const GFxPathData::PathsIterator& pathIter)
|
|
{
|
|
// pre-parse the path
|
|
pPath = pathIter.CurPathsPtr;
|
|
|
|
EdgeIndex = 0;
|
|
Sfactor = pathIter.Sfactor;
|
|
CurEdgesInfoMask = 1;
|
|
if (!pPath)
|
|
{
|
|
EdgesCount = 0;
|
|
CurEdgesInfo = 0;
|
|
pVertices16 = 0;
|
|
pVertices32 = 0;
|
|
return;
|
|
}
|
|
|
|
UByte flags = *pPath;
|
|
|
|
if (flags & GFxPathConsts::PathTypeMask)
|
|
{
|
|
EdgesCount = ((flags & GFxPathConsts::Path8EdgesCountMask) >> GFxPathConsts::Path8EdgesCountShift) + 1;
|
|
CurEdgesInfo = (flags & GFxPathConsts::Path8EdgeTypesMask) >> GFxPathConsts::Path8EdgeTypesShift;
|
|
pVertices16 = (const SInt16*)(GFx_AlignedPtr<2>(pPath + 4));
|
|
MoveX = *pVertices16++;
|
|
MoveY = *pVertices16++;
|
|
}
|
|
else
|
|
{
|
|
UInt vertexSFactor = (flags & GFxPathConsts::PathEdgeSizeMask) ? 4 : 2;
|
|
UByte pathSizeType = (UByte)((flags & GFxPathConsts::PathSizeMask) >> GFxPathConsts::PathSizeShift);
|
|
if (pathSizeType == 0) // newShape
|
|
{
|
|
EdgesCount = 0;
|
|
return;
|
|
}
|
|
static const UInt pathSFactorTable[] = { 0, 1, 2, 4 };
|
|
const UByte* paligned = GFx_AlignedPtr(pPath + 1, pathSizeType);
|
|
paligned = GFx_AlignedPtr(paligned + pathSFactorTable[pathSizeType] * 3, vertexSFactor);
|
|
EdgesCount = (flags & GFxPathConsts::PathEdgesCountMask) >> GFxPathConsts::PathEdgesCountShift;
|
|
if (vertexSFactor == 2)
|
|
{
|
|
pVertices16 = (const SInt16*)(paligned);
|
|
if (EdgesCount == 0)
|
|
EdgesCount = (UInt16)(*pVertices16++);
|
|
GASSERT(EdgesCount > 0);
|
|
MoveX = *pVertices16++;
|
|
MoveY = *pVertices16++;
|
|
CurEdgesInfo = (UInt16)(*pVertices16++);
|
|
|
|
pVertices32 = 0;
|
|
}
|
|
else
|
|
{
|
|
pVertices32 = (const SInt32*)(paligned);
|
|
if (EdgesCount == 0)
|
|
EdgesCount = (UInt32)(*pVertices32++);
|
|
GASSERT(EdgesCount > 0);
|
|
MoveX = *pVertices32++;
|
|
MoveY = *pVertices32++;
|
|
CurEdgesInfo = (UInt32)(*pVertices32++);
|
|
|
|
pVertices16 = 0;
|
|
}
|
|
}
|
|
GASSERT(EdgesCount < (1UL<<31));
|
|
}
|
|
|
|
void GFxPathData::EdgesIterator::GetEdge(Edge* pedge, bool doLines2CurveConv)
|
|
{
|
|
if (EdgeIndex < EdgesCount)
|
|
{
|
|
SInt32 cx, cy, ax, ay;
|
|
bool curve = false;
|
|
if (pVertices16)
|
|
{
|
|
if (CurEdgesInfoMask > 0x8000)
|
|
{
|
|
CurEdgesInfo = *pVertices16++;
|
|
CurEdgesInfoMask = 1;
|
|
}
|
|
if (CurEdgesInfo & CurEdgesInfoMask) // curve
|
|
{
|
|
cx = *pVertices16++;
|
|
cy = *pVertices16++;
|
|
ax = *pVertices16++;
|
|
ay = *pVertices16++;
|
|
curve = true;
|
|
}
|
|
else
|
|
{
|
|
cx = ax = *pVertices16++;
|
|
cy = ay = *pVertices16++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GASSERT(pVertices32);
|
|
|
|
if ((CurEdgesInfoMask & 0xFFFFFFFFu) == 0) // care just about 32-bits
|
|
{
|
|
CurEdgesInfo = *pVertices32++;
|
|
CurEdgesInfoMask = 1;
|
|
}
|
|
if (CurEdgesInfo & CurEdgesInfoMask) // curve
|
|
{
|
|
cx = *pVertices32++;
|
|
cy = *pVertices32++;
|
|
ax = *pVertices32++;
|
|
ay = *pVertices32++;
|
|
curve = true;
|
|
}
|
|
else
|
|
{
|
|
cx = ax = *pVertices32++;
|
|
cy = ay = *pVertices32++;
|
|
}
|
|
}
|
|
if (curve)
|
|
{
|
|
pedge->Cx = (cx + MoveX) * Sfactor;
|
|
pedge->Cy = (cy + MoveY) * Sfactor;
|
|
pedge->Ax = (ax + cx + MoveX) * Sfactor;
|
|
pedge->Ay = (ay + cy + MoveY) * Sfactor;
|
|
pedge->Curve = 1;
|
|
MoveX += cx + ax;
|
|
MoveY += cy + ay;
|
|
}
|
|
else
|
|
{
|
|
pedge->Ax = (ax + MoveX) * Sfactor;
|
|
pedge->Ay = (ay + MoveY) * Sfactor;
|
|
if (doLines2CurveConv)
|
|
{
|
|
pedge->Cx = (ax * 0.5f + MoveX) * Sfactor;
|
|
pedge->Cy = (ay * 0.5f + MoveY) * Sfactor;
|
|
pedge->Curve = 1;
|
|
}
|
|
else
|
|
pedge->Curve = 0;
|
|
MoveX += ax;
|
|
MoveY += ay;
|
|
}
|
|
++EdgeIndex;
|
|
CurEdgesInfoMask <<= 1;
|
|
}
|
|
}
|
|
|
|
|
|
void GFxPathData::EdgesIterator::GetPlainEdge(PlainEdge* pedge)
|
|
{
|
|
if (EdgeIndex < EdgesCount)
|
|
{
|
|
SInt32* p = pedge->Data;
|
|
if (pVertices16)
|
|
{
|
|
if (CurEdgesInfoMask > 0x8000)
|
|
{
|
|
CurEdgesInfo = *pVertices16++;
|
|
CurEdgesInfoMask = 1;
|
|
}
|
|
if (CurEdgesInfo & CurEdgesInfoMask) // curve
|
|
{
|
|
*p++ = *pVertices16++;
|
|
*p++ = *pVertices16++;
|
|
*p++ = *pVertices16++;
|
|
*p++ = *pVertices16++;
|
|
}
|
|
else
|
|
{
|
|
*p++ = *pVertices16++;
|
|
*p++ = *pVertices16++;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
GASSERT(pVertices32);
|
|
|
|
if ((CurEdgesInfoMask & 0xFFFFFFFFu) == 0) // care just about 32-bits
|
|
{
|
|
CurEdgesInfo = *pVertices32++;
|
|
CurEdgesInfoMask = 1;
|
|
}
|
|
if (CurEdgesInfo & CurEdgesInfoMask) // curve
|
|
{
|
|
*p++ = *pVertices32++;
|
|
*p++ = *pVertices32++;
|
|
*p++ = *pVertices32++;
|
|
*p++ = *pVertices32++;
|
|
}
|
|
else
|
|
{
|
|
*p++ = *pVertices32++;
|
|
*p++ = *pVertices32++;
|
|
}
|
|
}
|
|
++EdgeIndex;
|
|
CurEdgesInfoMask <<= 1;
|
|
pedge->Size = (UInt)(p - pedge->Data);
|
|
}
|
|
}
|
|
|
|
|
|
|
|
//
|
|
// ***** GFxPathData::PathsIterator
|
|
//
|
|
|
|
GFxPathData::PathsIterator::PathsIterator(const GFxShapeBase* pdef) :
|
|
pShapeDef(static_cast<const GFxShapeNoStyles*>(pdef)),
|
|
CurPathsPtr(pShapeDef->GetNativePathData()->pPaths), Paths(*pShapeDef->GetNativePathData()),
|
|
CurIndex(0), Count(pShapeDef->GetNativePathData()->PathsCount),
|
|
Sfactor((pdef->GetFlags() & GFxShapeBase::Flags_Sfactor20) ? 1.0f/20.0f : 1.0f)
|
|
{
|
|
}
|
|
|
|
void GFxPathData::PathsIterator::AddForTessellation(GCompoundShape *cs)
|
|
{
|
|
EdgesIterator it = GetEdgesIterator();
|
|
|
|
Float ax, ay;
|
|
it.GetMoveXY(&ax, &ay);
|
|
UInt fill0, fill1, line;
|
|
GetStyles(&fill0, &fill1, &line);
|
|
cs->BeginPath(fill0 - 1, fill1 - 1, line - 1, ax, ay);
|
|
|
|
EdgesIterator::Edge edge;
|
|
while(!it.IsFinished())
|
|
{
|
|
it.GetEdge(&edge);
|
|
if (edge.Curve)
|
|
cs->AddCurve(edge.Cx, edge.Cy, edge.Ax, edge.Ay);
|
|
else
|
|
cs->AddVertex(edge.Ax, edge.Ay);
|
|
}
|
|
AdvanceBy(it);
|
|
}
|
|
|
|
void GFxPathData::PathsIterator::GetStyles(UInt* pfill0, UInt* pfill1, UInt* pline) const
|
|
{
|
|
UByte flags = *CurPathsPtr;
|
|
if (flags & GFxPathConsts::PathTypeMask ||
|
|
(flags & GFxPathConsts::PathSizeMask) == (1 << GFxPathConsts::PathSizeShift))
|
|
{
|
|
const UByte* ptr = CurPathsPtr + 1;
|
|
*pfill0 = *ptr++;
|
|
*pfill1 = *ptr++;
|
|
*pline = *ptr;
|
|
}
|
|
else if (!IsNewShape())
|
|
{
|
|
if ((flags & GFxPathConsts::PathSizeMask) == (2 << GFxPathConsts::PathSizeShift))
|
|
{
|
|
const UInt16* ptr16 = (const UInt16*)GFx_AlignedPtr<2>(CurPathsPtr + 1);
|
|
*pfill0 = *ptr16++;
|
|
*pfill1 = *ptr16++;
|
|
*pline = *ptr16;
|
|
}
|
|
else
|
|
{
|
|
const UInt32* ptr32 = (const UInt32*)GFx_AlignedPtr<4>(CurPathsPtr + 1);
|
|
*pfill0 = *ptr32++;
|
|
*pfill1 = *ptr32++;
|
|
*pline = *ptr32;
|
|
}
|
|
}
|
|
else
|
|
GASSERT(0);
|
|
}
|
|
|
|
void GFxPathData::PathsIterator::AdvanceBy(const EdgesIterator& edgeIt)
|
|
{
|
|
if (edgeIt.IsFinished())
|
|
{
|
|
CurPathsPtr = (edgeIt.pVertices16) ? (const UByte*)edgeIt.pVertices16 : (const UByte*)edgeIt.pVertices32;
|
|
++CurIndex;
|
|
CheckPage();
|
|
}
|
|
}
|
|
|
|
void GFxPathData::PathsIterator::CheckPage()
|
|
{
|
|
const void* ppage = GFxPathAllocator::GetPagePtr(Paths.pPaths, Paths.PathsPageOffset);
|
|
if (!GFxPathAllocator::IsInPage(ppage, CurPathsPtr))
|
|
{
|
|
const void* pnextPage;
|
|
if ((pnextPage = GFxPathAllocator::GetNextPage(ppage, &CurPathsPtr)) != NULL)
|
|
{
|
|
Paths.pPaths = CurPathsPtr;
|
|
Paths.PathsPageOffset = GFxPathAllocator::GetPageOffset(pnextPage, CurPathsPtr);
|
|
}
|
|
}
|
|
}
|
|
|
|
void GFxPathData::PathsIterator::SkipComplex()
|
|
{
|
|
UInt32 edgesCount, curEdgesInfo = 0;
|
|
UInt32 curEdgesInfoMask = 1;
|
|
UByte flags = *CurPathsPtr;
|
|
const SInt16* pvertices16 = 0;
|
|
const SInt32* pvertices32 = 0;
|
|
|
|
if (flags & GFxPathConsts::PathTypeMask)
|
|
{
|
|
edgesCount = ((flags & GFxPathConsts::Path8EdgesCountMask) >> GFxPathConsts::Path8EdgesCountShift) + 1;
|
|
curEdgesInfo = (flags & GFxPathConsts::Path8EdgeTypesMask) >> GFxPathConsts::Path8EdgeTypesShift;
|
|
pvertices16 = (const SInt16*)(GFx_AlignedPtr<2>(CurPathsPtr + 4));
|
|
// skip moveX, moveY
|
|
pvertices16 += 2;
|
|
}
|
|
else
|
|
{
|
|
UInt vertexSFactor = (flags & GFxPathConsts::PathEdgeSizeMask) ? 4 : 2;
|
|
UByte pathSizeType = (UByte)((flags & GFxPathConsts::PathSizeMask) >> GFxPathConsts::PathSizeShift);
|
|
if (pathSizeType == 0) // newShape
|
|
{
|
|
edgesCount = 0;
|
|
++CurPathsPtr;
|
|
return;
|
|
}
|
|
static const UInt pathSFactorTable[] = { 0, 1, 2, 4 };
|
|
const UByte* paligned = GFx_AlignedPtr(CurPathsPtr + 1, pathSizeType);
|
|
paligned = GFx_AlignedPtr(paligned + pathSFactorTable[pathSizeType] * 3, vertexSFactor);
|
|
edgesCount = (flags & GFxPathConsts::PathEdgesCountMask) >> GFxPathConsts::PathEdgesCountShift;
|
|
if (vertexSFactor == 2)
|
|
{
|
|
pvertices16 = (const SInt16*)(paligned);
|
|
if (edgesCount == 0)
|
|
edgesCount = (UInt16)(*pvertices16++);
|
|
GASSERT(edgesCount > 0);
|
|
// skip moveX, Y
|
|
pvertices16 += 2;
|
|
curEdgesInfo = (UInt16)(*pvertices16++);
|
|
}
|
|
else
|
|
{
|
|
pvertices32 = (const SInt32*)(paligned);
|
|
if (edgesCount == 0)
|
|
edgesCount = (UInt32)(*pvertices32++);
|
|
GASSERT(edgesCount > 0);
|
|
// skip moveX, Y
|
|
pvertices32 += 2;
|
|
curEdgesInfo = (UInt32)(*pvertices32++);
|
|
}
|
|
}
|
|
// skip vertices, edgeinfos
|
|
if (pvertices16)
|
|
{
|
|
for (UInt i = 0; i < edgesCount; ++i)
|
|
{
|
|
if (curEdgesInfoMask > 0x8000)
|
|
{
|
|
curEdgesInfo = *pvertices16++;
|
|
curEdgesInfoMask = 1;
|
|
}
|
|
if (curEdgesInfo & curEdgesInfoMask) // curve
|
|
{
|
|
// skip curve
|
|
pvertices16 += 4;
|
|
}
|
|
else
|
|
{
|
|
// skip line
|
|
pvertices16 += 2;
|
|
}
|
|
curEdgesInfoMask <<= 1;
|
|
}
|
|
CurPathsPtr = (const UByte*)pvertices16;
|
|
}
|
|
else
|
|
{
|
|
GASSERT(pvertices32);
|
|
for (UInt i = 0; i < edgesCount; ++i)
|
|
{
|
|
//GASSERT(*pvertices32 != 0xbabababa);
|
|
|
|
if ((curEdgesInfoMask & 0xFFFFFFFFu) == 0) // care just about 32-bits
|
|
{
|
|
curEdgesInfo = *pvertices32++;
|
|
curEdgesInfoMask = 1;
|
|
}
|
|
if (curEdgesInfo & curEdgesInfoMask) // curve
|
|
{
|
|
// skip curve
|
|
pvertices32 += 4;
|
|
}
|
|
else
|
|
{
|
|
// skip line
|
|
pvertices32 += 2;
|
|
}
|
|
curEdgesInfoMask <<= 1;
|
|
}
|
|
CurPathsPtr = (const UByte*)pvertices32;
|
|
}
|
|
++CurIndex;
|
|
CheckPage();
|
|
}
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// GFxShapeCharacterDef
|
|
//
|
|
// Creates a definition relying on in-memory image.
|
|
void GFxShapeCharacterDef::SetToImage(GFxImageResource *pimage, bool bilinear)
|
|
{
|
|
Shape.SetToImage(pimage, bilinear);
|
|
}
|
|
|
|
void GFxShapeCharacterDef::Display(GFxDisplayContext &context, GFxCharacter* inst)
|
|
{
|
|
Shape.Display(context, inst);
|
|
}
|
|
|
|
void GFxShapeCharacterDef::ComputeBound(GRectF* r) const
|
|
{
|
|
Shape.ComputeBoundImpl<GFxPathData>(r);
|
|
}
|
|
|
|
bool GFxShapeCharacterDef::DefPointTestLocal(const GPointF &pt, bool testShape, const GFxCharacter *pinst) const
|
|
{
|
|
return Shape.DefPointTestLocalImpl<GFxPathData>(pt, testShape, pinst);
|
|
}
|
|
|
|
// Convert the paths to GCompoundShape, flattening the curves.
|
|
void GFxShapeCharacterDef::MakeCompoundShape(GCompoundShape *cs, Float tolerance) const
|
|
{
|
|
Shape.MakeCompoundShapeImpl<GFxPathData>(cs, tolerance);
|
|
|
|
}
|
|
|
|
UInt32 GFxShapeCharacterDef::ComputeGeometryHash() const
|
|
{
|
|
return Shape.ComputeGeometryHashImpl<GFxPathData>();
|
|
}
|
|
|
|
bool GFxShapeCharacterDef::IsEqualGeometry(const GFxShapeBaseCharacterDef& cmpWith) const
|
|
{
|
|
return Shape.IsEqualGeometryImpl<GFxPathData>(*cmpWith.GetShape());
|
|
}
|
|
|
|
void GFxShapeCharacterDef::PreTessellate(Float masterScale, const GFxRenderConfig& config)
|
|
{
|
|
Shape.PreTessellateImpl<GFxPathData>(masterScale, config);
|
|
}
|
|
|
|
GFxShapeCharacterDef::PathsIterator* GFxShapeCharacterDef::GetPathsIterator() const
|
|
{
|
|
return Shape.GetPathsIterator();
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// GFxConstShapeCharacterDef
|
|
//
|
|
void GFxConstShapeCharacterDef::Display(GFxDisplayContext &context, GFxCharacter* inst)
|
|
{
|
|
Shape.Display(context, inst);
|
|
}
|
|
|
|
void GFxConstShapeCharacterDef::ComputeBound(GRectF* r) const
|
|
{
|
|
Shape.ComputeBoundImpl<GFxSwfPathData>(r);
|
|
}
|
|
|
|
bool GFxConstShapeCharacterDef::DefPointTestLocal(const GPointF &pt, bool testShape, const GFxCharacter *pinst) const
|
|
{
|
|
return Shape.DefPointTestLocalImpl<GFxSwfPathData>(pt, testShape, pinst);
|
|
}
|
|
|
|
// Convert the paths to GCompoundShape, flattening the curves.
|
|
void GFxConstShapeCharacterDef::MakeCompoundShape(GCompoundShape *cs, Float tolerance) const
|
|
{
|
|
Shape.MakeCompoundShapeImpl<GFxSwfPathData>(cs, tolerance);
|
|
|
|
}
|
|
|
|
UInt32 GFxConstShapeCharacterDef::ComputeGeometryHash() const
|
|
{
|
|
return Shape.ComputeGeometryHashImpl<GFxSwfPathData>();
|
|
}
|
|
|
|
bool GFxConstShapeCharacterDef::IsEqualGeometry(const GFxShapeBaseCharacterDef& cmpWith) const
|
|
{
|
|
return Shape.IsEqualGeometryImpl<GFxSwfPathData>(*cmpWith.GetShape());
|
|
}
|
|
|
|
void GFxConstShapeCharacterDef::PreTessellate(Float masterScale, const GFxRenderConfig& config)
|
|
{
|
|
Shape.PreTessellateImpl<GFxSwfPathData>(masterScale, config);
|
|
}
|
|
|
|
GFxConstShapeCharacterDef::PathsIterator* GFxConstShapeCharacterDef::GetPathsIterator() const
|
|
{
|
|
return Shape.GetPathsIterator();
|
|
}
|
|
|
|
|
|
|
|
#if 0
|
|
// DBG
|
|
//static void GFX_DrawQuad(Float x1, Float y1, Float x2, Float y2,
|
|
// Float x3, Float y3, Float x4, Float y4,
|
|
// UInt32 rgba,
|
|
// GRenderer* prenderer)
|
|
//{
|
|
// const SInt16 icoords[18] =
|
|
// {
|
|
// // Strip (fill in)
|
|
// (SInt16) x1, (SInt16) y1,
|
|
// (SInt16) x2, (SInt16) y2,
|
|
// (SInt16) x3, (SInt16) y3,
|
|
// (SInt16) x4, (SInt16) y4
|
|
// };
|
|
// static const UInt16 indices[6] = { 0, 1, 2, 0, 2, 3 };
|
|
//
|
|
// prenderer->FillStyleColor(rgba);
|
|
// prenderer->SetVertexData(icoords, 9, GRenderer::Vertex_XY16i);
|
|
//
|
|
// // Fill the inside
|
|
// prenderer->SetIndexData(indices, 6, GRenderer::Index_16);
|
|
// prenderer->DrawIndexedTriList(0, 0, 6, 0, 2);
|
|
//
|
|
// // Done
|
|
// prenderer->SetVertexData(0, 0, GRenderer::Vertex_None);
|
|
// prenderer->SetIndexData(0, 0, GRenderer::Index_None);
|
|
//}
|
|
//static void GFX_DrawParl(Float x1, Float y1, Float x2, Float y2,
|
|
// Float x3, Float y3,
|
|
// UInt32 rgba,
|
|
// GRenderer* prenderer)
|
|
//{
|
|
// GFX_DrawQuad(x1, y1, x2, y2, x3, y3, x1 + (x3-x2), y1 + (y3-y2), rgba, prenderer);
|
|
//}
|
|
|
|
|
|
|
|
|
|
|
|
|
|
template<class Container>
|
|
static void GFx_DumpFillStyles(FILE* fd, const Container& s)
|
|
{
|
|
unsigned i;
|
|
for(i = 0; i < s.GetSize(); i++)
|
|
{
|
|
fprintf(fd, "Fill %d %d %d %d %d\n",
|
|
i,
|
|
s[i].GetColor().GetRed(),
|
|
s[i].GetColor().GetGreen(),
|
|
s[i].GetColor().GetBlue(),
|
|
s[i].GetColor().GetAlpha());
|
|
}
|
|
}
|
|
|
|
template<class Container>
|
|
static void GFx_DumpLineStyles(FILE* fd, const Container& s)
|
|
{
|
|
unsigned i;
|
|
for(i = 0; i < s.GetSize(); i++)
|
|
{
|
|
fprintf(fd, "Stroke %d %d.0 %u %d %d %d %d\n",
|
|
i,
|
|
s[i].GetWidth(),
|
|
s[i].GetStyle(),
|
|
s[i].GetColor().GetRed(),
|
|
s[i].GetColor().GetGreen(),
|
|
s[i].GetColor().GetBlue(),
|
|
s[i].GetColor().GetAlpha());
|
|
}
|
|
}
|
|
|
|
/*
|
|
// Dump our precomputed GFxMesh data to the given GFxStream.
|
|
void GFxShapeBase::OutputCachedData(GFile* out, const GFxCacheOptions& options)
|
|
{
|
|
GUNUSED(options);
|
|
|
|
UInt n = CachedMeshes.GetSize();
|
|
out->WriteSInt32(n);
|
|
|
|
for (UInt i = 0; i < n; i++)
|
|
{
|
|
CachedMeshes[i]->OutputCachedData(out);
|
|
}
|
|
}
|
|
|
|
|
|
// Initialize our GFxMesh data from the given GFxStream.
|
|
void GFxShapeBase::InputCachedData(GFile* in)
|
|
{
|
|
int n = in->ReadSInt32();
|
|
|
|
CachedMeshes.Resize(n);
|
|
|
|
for (int i = 0; i < n; i++)
|
|
{
|
|
GFxMeshSet* ms = GHEAP_AUTO_NEW(this) GFxMeshSet();
|
|
ms->InputCachedData(in);
|
|
CachedMeshes[i] = ms;
|
|
}
|
|
}
|
|
*/
|
|
|
|
#endif
|