Files
GTASource/rage/scaleform/Src/GFxPlayer/GFxStyles.cpp
expvintl 419f2e4752 init
2025-02-23 17:40:52 +08:00

1425 lines
43 KiB
C++

/**********************************************************************
Filename : GFxStyles.cpp
Content :
Created :
Authors :
Copyright : (c) 2001-2006 Scaleform Corp. All Rights Reserved.
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 "GRenderer.h"
#include "GImage.h"
#include "GFxStyles.h"
#include "GFxLog.h"
#include "GFxStream.h"
#include "GFxFontResource.h"
#include "GFxLoaderImpl.h"
#include "GFxPlayerImpl.h"
#include "GFxSprite.h"
#include "GFxImageResource.h"
#include "GFxLoadProcess.h"
#include "GFxDisplayContext.h"
#include "GFxAmpServer.h"
#include "AMP/GFxAmpViewStats.h"
#ifdef GFC_ASSERT_ON_GRADIENT_BITMAP_GEN
#define GASSERT_ON_GRADIENT_BITMAP_GEN GASSERT(0)
#else
#define GASSERT_ON_GRADIENT_BITMAP_GEN ((void)0)
#endif //GFC_ASSERT_ON_GRADIENT_BITMAP_GEN
//
// *** GFxGradientRecord
//
GFxGradientRecord::GFxGradientRecord()
: Ratio(0)
{
}
void GFxGradientRecord::Read(GFxLoadProcess* p, GFxTagType tagType)
{
Ratio = p->ReadU8();
p->ReadRgbaTag(&Color, tagType);
}
//
// *** GFxGradientData
//
GFxGradientData::GFxGradientData(GFxFillType type, UInt16 recordCount, bool linearRgb)
{
GASSERT(((int)type < 256) && (type & GFxFill_Gradient_TestBit));
Type = (UByte) type;
pRecords = 0;
RecordCount = 0;
SetRecordCount(recordCount);
LinearRGB = linearRgb;
FocalRatio = 0.0f;
}
GFxGradientData::~GFxGradientData()
{
if (pRecords)
GFREE(pRecords);
}
bool GFxGradientData::SetRecordCount(UInt16 count)
{
if (count == RecordCount)
return 1;
GFxGradientRecord* pnewRecords = 0;
if (count)
if ((pnewRecords = (GFxGradientRecord*)GALLOC(sizeof(GFxGradientRecord)*count, GFxStatMD_CharDefs_Mem)) == 0)
return 0;
if (pRecords)
{
UInt copyCount = G_Min(count, RecordCount);
for (UInt i=0; i<copyCount; i++)
pnewRecords[i] = pRecords[i];
GFREE(pRecords);
}
pRecords = pnewRecords;
RecordCount = count;
return 1;
}
UPInt GFxGradientData::GetHashCode() const
{
UInt i;
UPInt hashCode = Type;
for (i = 0; i< RecordCount; i++)
hashCode ^= pRecords[i].GetHashCode();
return hashCode;
}
bool GFxGradientData::operator == (const GFxGradientData& other) const
{
// if both are NaNs then operator != returns true. (!AB)
GASSERT(!(GASNumberUtil::IsNaN(FocalRatio) && GASNumberUtil::IsNaN(other.FocalRatio)));
if ((RecordCount != other.RecordCount) ||
(Type != other.Type) ||
(FocalRatio != other.FocalRatio) ||
(LinearRGB != other.LinearRGB))
return 0;
UInt i;
for (i=0; i< RecordCount; i++)
if (pRecords[i] != other.pRecords[i])
return 0;
return 1;
}
// Creates an image resource from gradient.
GFxImageResource* GFxGradientData::CreateImageResource(
GFxResourceWeakLib *plib,
GFxGradientParams *pparams,
GFxImageCreator *pimageCreator,
GFxRenderConfig* prconfig,
GFxLog* plog,
bool threadedLoading)
{
// Gradient data must live in a global heap since it is used as a key in ResourceLib.
GASSERT(GMemory::GetHeapByAddress(this) == GMemory::GetGlobalHeap());
// Create a key and check it against library.
GFxResourceKey gradientKey = CreateGradientKey();
GFxImageResource * pres = 0;
GFxResourceLib::BindHandle bh;
if (plib->BindResourceKey(&bh, gradientKey) == GFxResourceLib::RS_NeedsResolve)
{
// If resource does not exist, create it and resolve it in library.
GPtr<GImage> pimage = *CreateGradientImage(pparams, plib->GetImageHeap());
if (pimage)
{
GFxImageCreateInfo ico(pimage, GFxResource::Use_Gradient);
ico.SetStates(0, prconfig, plog, 0, 0);
ico.ThreadedLoading = threadedLoading;
ico.pHeap = plib->GetImageHeap();
GPtr<GImageInfoBase> pimageInfo;
if (pimageCreator)
pimageInfo = *pimageCreator->CreateImage(ico);
else
{
GFC_DEBUG_WARNING(1, "GFxGradientData::CreateImageResource failed - GFxImageCreator not installed");
}
// Add bitmap resource, if image was created; otherwise, it will
// be added by loader.
pres = GHEAP_NEW(ico.pHeap) GFxImageResource(pimageInfo, gradientKey,
GFxResource::Use_Gradient);
if (pres)
bh.ResolveResource(pres);
}
if (!pres)
bh.CancelResolve("Failed to create gradient");
}
else
{
// WaitForResolve AddRefs, so we are ok to return result.
pres = (GFxImageResource*) bh.WaitForResolve();
}
return pres;
}
class GFxGradientImageResourceKey : public GFxResourceKey::KeyInterface
{
public:
typedef GFxResourceKey::KeyHandle KeyHandle;
virtual void AddRef(KeyHandle hdata)
{
GASSERT(hdata); ((GFxGradientData*) hdata)->AddRef();
}
virtual void Release(KeyHandle hdata)
{
GASSERT(hdata); ((GFxGradientData*) hdata)->Release();
}
// Key/Hash code implementation.
virtual GFxResourceKey::KeyType GetKeyType(KeyHandle hdata) const
{
GUNUSED(hdata);
return GFxResourceKey::Key_Gradient;
}
virtual UPInt GetHashCode(KeyHandle hdata) const
{
GASSERT(hdata);
return ((GFxGradientData*) hdata)->GetHashCode();
}
virtual bool KeyEquals(KeyHandle hdata, const GFxResourceKey& other)
{
if (this != other.GetKeyInterface())
return 0;
return *((GFxGradientData*) hdata) == *((GFxGradientData*) other.GetKeyData());
}
};
class GFxGradientImageResourceCreator : public GFxResourceData::DataInterface
{
// Creates/Loads resource based on data and loading process
virtual bool CreateResource(GFxResourceData::DataHandle hdata, GFxResourceBindData *pbindData,
GFxLoadStates *pls, GMemoryHeap*) const
{
GFxGradientData *pgd = (GFxGradientData*) hdata;
GASSERT(pgd);
pbindData->pResource = *pgd->CreateImageResource(pls->GetLib(),
pls->GetBindStates()->pGradientParams,
pls->GetBindStates()->pImageCreator,
pls->GetRenderConfig(), pls->GetLog(),
pls->IsThreadedLoading());
return pbindData->pResource ? 1 : 0;
}
};
static GFxGradientImageResourceCreator static_inst_data;
static GFxGradientImageResourceKey static_inst_key;
// Creates a GFxResourceData object from this GFxGradientData;
// this object can be used to create a resource.
GFxResourceData GFxGradientData::CreateGradientResourceData()
{
return GFxResourceData(&static_inst_data, this);
}
GFxResourceKey GFxGradientData::CreateGradientKey()
{
return GFxResourceKey(&static_inst_key, this);
}
//=========================================================================
// The code of this class was taken from the Anti-Grain Geometry
// Project and modified for the use by Scaleform.
// Permission to use without restrictions is hereby granted to
// Scaleform Corporation by the author of Anti-Grain Geometry Project.
//=========================================================================
// This class is called G..., not GFx... because it's independent and
// actually belongs to the renderer.
//=========================================================================
class GFocalRadialGradient
{
public:
//---------------------------------------------------------------------
GFocalRadialGradient() :
Radius(100),
FocusX(0),
FocusY(0)
{
updateValues();
}
//---------------------------------------------------------------------
GFocalRadialGradient(Float r, Float fx, Float fy) :
Radius(r),
FocusX(fx),
FocusY(fy)
{
updateValues();
}
//---------------------------------------------------------------------
void Init(Float r, Float fx, Float fy)
{
Radius = r;
FocusX = fx;
FocusY = fy;
updateValues();
}
Float Calculate(Float x, Float y) const;
private:
void updateValues();
Float Radius;
Float FocusX;
Float FocusY;
Float Radius2;
Float Multiplier;
};
//---------------------------------------------------------------------
Float GFocalRadialGradient::Calculate(Float x, Float y) const
{
Float dx = x - FocusX;
Float dy = y - FocusY;
Float d2 = dx * FocusY - dy * FocusX;
Float d3 = Radius2 * (dx * dx + dy * dy) - d2 * d2;
return (dx * FocusX + dy * FocusY + sqrtf(fabsf(d3))) * Multiplier;
}
//---------------------------------------------------------------------
void GFocalRadialGradient::updateValues()
{
// Calculate the invariant values. In case the focal center
// lies exactly on the gradient circle the divisor degenerates
// into zero. In this case we just move the focal center by
// one subpixel unit possibly in the direction to the origin (0,0)
// and calculate the values again.
//-------------------------
Float fx2 = FocusX * FocusX;
Float fy2 = FocusY * FocusY;
Radius2 = Radius * Radius;
Float d = Radius2 - (fx2 + fy2);
if(d == 0)
{
if(FocusX != 0) { if(FocusX < 0) FocusX += 1; else FocusX -= 1; }
if(FocusY != 0) { if(FocusY < 0) FocusY += 1; else FocusY -= 1; }
fx2 = FocusX * FocusX;
fy2 = FocusY * FocusY;
d = Radius2 - (fx2 + fy2);
}
Multiplier = Radius / d;
}
//---------------------------------------------------------------------
class GFxGradientRamp
{
public:
enum { RampSize = 256 };
GFxGradientRamp() {}
GFxGradientRamp(const GFxGradientRecord* colorStops,
unsigned numColors,
Float gamma=1)
{
Init(colorStops, numColors, gamma);
}
void Init(const GFxGradientRecord* colorStops,
unsigned numColors,
Float gamma=1);
const GColor& GetColor(unsigned i) const
{
return Ramp[(i < RampSize) ? i : RampSize-1];
}
private:
struct ColorType
{
UInt16 r,g,b,a;
ColorType() {}
ColorType(UInt16 r_, UInt16 g_, UInt16 b_, UInt16 a_) :
r(r_), g(g_), b(b_), a(a_) {}
};
static ColorType applyGamma(const GColor& c, const UInt16* gammaLut)
{
int a = c.GetAlpha();
return ColorType(gammaLut[c.GetRed() ],
gammaLut[c.GetGreen()],
gammaLut[c.GetBlue() ],
UInt16((a << 8) | a));
}
static UByte applyGamma(UInt16 v, Float gamma)
{
return (UByte)floor(pow(v / 65535.0f, gamma) * 255.0f + 0.5f);
}
static GColor blendColors(const ColorType& c1, const ColorType& c2,
int ratio, int maxRatio,
Float gamma)
{
int alphaDiv = (maxRatio << 8) | maxRatio;
return GColor(applyGamma(UInt16(c1.r + int(c2.r - c1.r) * ratio / maxRatio), gamma),
applyGamma(UInt16(c1.g + int(c2.g - c1.g) * ratio / maxRatio), gamma),
applyGamma(UInt16(c1.b + int(c2.b - c1.b) * ratio / maxRatio), gamma),
UByte(c1.a + int(c2.a - c1.a) * ratio / alphaDiv));
}
static GColor blendColors(const ColorType& c1, const ColorType& c2,
int ratio, int maxRatio)
{
maxRatio = (maxRatio << 8) | maxRatio;
return GColor(UByte(c1.r + int(c2.r - c1.r) * ratio / maxRatio),
UByte(c1.g + int(c2.g - c1.g) * ratio / maxRatio),
UByte(c1.b + int(c2.b - c1.b) * ratio / maxRatio),
UByte(c1.a + int(c2.a - c1.a) * ratio / maxRatio));
}
GColor Ramp[RampSize];
};
//---------------------------------------------------------------------
void GFxGradientRamp::Init(const GFxGradientRecord* colorStops,
unsigned numColors,
Float gamma)
{
unsigned i;
if (numColors == 0 || colorStops == 0)
{
GFxGradientRecord fakeColor;
fakeColor.Ratio = 0;
fakeColor.Color = 0xFF000000;
numColors = 1;
colorStops = &fakeColor;
}
if(numColors > 1)
{
UInt16 gammaDir[256];
Float gammaInv = 1;
if(gamma == 1)
{
for(i = 0; i < 256; i++)
{
gammaDir[i] = UInt16( (i << 8) | i );
}
}
else
{
// It's a simplified calculation of the linear color gradient LUT
// Replace this LUT with sRGB if necessary
for(i = 0; i < 256; i++)
{
gammaDir[i] = (UInt16)floor(pow(i / 255.0f, gamma) * 65535.0f + 0.5f);
}
gammaInv = 1/gamma;
}
unsigned start = colorStops[0].Ratio;
unsigned end = 0;
GColor c = colorStops[0].Color;
for(i = 0; i < start; i++)
{
Ramp[i] = c;
}
for(i = 1; i < numColors; i++)
{
end = colorStops[i].Ratio;
if(end < start) end = start;
ColorType c1 = applyGamma(colorStops[i-1].Color, gammaDir);
ColorType c2 = applyGamma(colorStops[i].Color, gammaDir);
unsigned x0 = start;
unsigned dx = end - start;
if(gamma == 1)
{
while(start < end)
{
c = Ramp[start] = blendColors(c1, c2, start-x0+1, dx);
++start;
}
}
else
{
while(start < end)
{
c = Ramp[start] = blendColors(c1, c2, start-x0+1, dx, gammaInv);
++start;
}
}
}
for(; end < RampSize; end++)
{
Ramp[end] = c;
}
}
else
{
for(i = 0; i < RampSize; i++)
{
Ramp[i] = colorStops[0].Color;
}
}
}
#ifdef GFX_ADAPTIVE_GRADIENT_SIZE
const UInt GFxGradientData_RadialGradientSizeTable[] =
{
16, 16, 32, 32, 64, 64, 64, 128, 128, 128, 128, 128, 128, 256, 256, 256, 256, 256
};
#endif
UInt GFxGradientData::ComputeRadialGradientImageSize(GFxGradientParams *pparams) const
{
UInt radialGradientImageSize = 0;
if (pparams && !pparams->IsAdaptive())
{
radialGradientImageSize = pparams->GetRadialImageSize() ?
pparams->GetRadialImageSize() :
GFX_GRADIENT_SIZE_DEFAULT;
}
#ifdef GFX_ADAPTIVE_GRADIENT_SIZE
if (radialGradientImageSize == 0)
{
// Adaptive gradient size. The function heuristically
// computes the optimal gradient size depending on the
// maximal slope in the color ramp, focal point, and
// linearRGB flag. See also RadialGradientSizeTable that may
// be modified to improve the result. This table and all other
// coefficients are defined empirically, on the basis of
// the visual result in typical cases. It's a kind of magic...
//----------------------------------
UInt i;
Float maxSlope = 0;
for (i = 1; i < GetRecordCount(); ++i)
{
Float slope;
const GFxGradientRecord& g1 = pRecords[i - 1];
const GFxGradientRecord& g2 = pRecords[i];
Float dx = (Float)g2.Ratio - (Float)g1.Ratio;
if (dx > 0)
{
slope = abs((int)g1.Color.GetRed() - (int)g2.Color.GetRed()) / dx;
if (slope > maxSlope) maxSlope = slope;
slope = abs((int)g1.Color.GetGreen() - (int)g2.Color.GetGreen()) / dx;
if (slope > maxSlope) maxSlope = slope;
slope = abs((int)g1.Color.GetBlue() - (int)g2.Color.GetBlue()) / dx;
if (slope > maxSlope) maxSlope = slope;
slope = abs((int)g1.Color.GetAlpha() - (int)g2.Color.GetAlpha()) / dx;
if (slope > maxSlope) maxSlope = slope;
}
}
if (maxSlope == 0)
return GFX_GRADIENT_SIZE_DEFAULT;
if (LinearRGB)
maxSlope *= 1.5f;
if (Type == GFxFill_FocalPointGradient)
{
Float r = fabsf(FocalRatio);
if (r > 0.5f)
maxSlope /= 1.01f - r;
}
if (maxSlope < 0)
maxSlope = 0;
maxSlope = sqrtf((maxSlope + 0.18f) * 5);
UInt idx = (UInt)maxSlope;
if (idx >= sizeof(GFxGradientData_RadialGradientSizeTable) / sizeof(UInt))
idx = sizeof(GFxGradientData_RadialGradientSizeTable) / sizeof(UInt) - 1;
return GFxGradientData_RadialGradientSizeTable[idx];
}
else
return radialGradientImageSize;
#else
return radialGradientImageSize ? radialGradientImageSize : GFX_GRADIENT_SIZE_DEFAULT;
#endif // GFX_ADAPTIVE_GRADIENT_SIZE
}
// Make a GTexture* corresponding to our gradient.
// We can use this to set the gradient fill style.
GImage* GFxGradientData::CreateGradientImage(GFxGradientParams *pparams,
GMemoryHeap* pimageHeap) const
{
GUNUSED(pimageHeap);
GUNUSED(pparams);
#ifndef GFC_NO_GRADIENT_GEN
GASSERT(Type == GFxFill_LinearGradient ||
Type == GFxFill_RadialGradient ||
Type == GFxFill_FocalPointGradient);
GASSERT_ON_GRADIENT_BITMAP_GEN;
GImage *pim = 0;
UInt i, j, size;
int ratio;
Float center, radius;
GColor sample;
UByte r, g, b, a;
Float x, y;
UInt imgSize;
// Gamma=2.17 is the closest approximation of the sRGB curve.
//--------------------
GFxGradientRamp ramp(pRecords, RecordCount, LinearRGB ? 2.17f : 1.0f);
switch(Type)
{
case GFxFill_LinearGradient: // Linear gradient.
if ((pim = GImage::CreateImage(GImage::Image_ARGB_8888, 256, 1, pimageHeap))!=0)
{
for (i = 0; i < pim->Width; i++)
{
sample = ramp.GetColor(i);
pim->SetPixelRGBA(i, 0,
sample.GetRed(),
sample.GetGreen(),
sample.GetBlue(),
sample.GetAlpha());
}
}
break;
case GFxFill_RadialGradient: // Radial gradient.
imgSize = ComputeRadialGradientImageSize(pparams);
if ((pim = GImage::CreateImage(GImage::Image_ARGB_8888, imgSize, imgSize, pimageHeap))!=0)
{
center = pim->Height / 2.0f;
radius = center - 1;
size = imgSize - 1;
for (j = 1; j < size; j++)
{
for (i = 1; i < size; i++)
{
y = j - center + 0.5f;
x = i - center + 0.5f;
ratio = (int)floorf((float)sqrt(x * x + y * y) * 256 / radius + 0.5f);
if (ratio > 255)
{
ratio = 255;
}
sample = ramp.GetColor(ratio);
pim->SetPixelRGBA(i, j,
sample.GetRed(),
sample.GetGreen(),
sample.GetBlue(),
sample.GetAlpha());
}
}
sample = ramp.GetColor(255);
r = sample.GetRed();
g = sample.GetGreen();
b = sample.GetBlue();
a = sample.GetAlpha();
for(i = 1; i < imgSize; i++)
{
pim->SetPixelRGBA(i, 0, r, g, b, a);
pim->SetPixelRGBA(pim->Width-1, i, r, g, b, a);
pim->SetPixelRGBA(pim->Width-i-1, pim->Height-1, r, g, b, a);
pim->SetPixelRGBA(0, pim->Height-i-1, r, g, b, a);
}
}
break;
case GFxFill_FocalPointGradient: // Radial gradient with focal point.
imgSize = ComputeRadialGradientImageSize(pparams);
if ((pim = GImage::CreateImage(GImage::Image_ARGB_8888, imgSize, imgSize, pimageHeap))!=0)
{
center = pim->Height / 2.0f;
radius = center - 1;
size = imgSize - 1;
GFocalRadialGradient gr(radius, FocalRatio * radius, 0);
for (j = 1; j < size; j++)
{
for (i = 1; i < size; i++)
{
y = j - center + 0.5f;
x = i - center + 0.5f;
ratio = (int)floorf(gr.Calculate(x, y) * 256 / radius + 0.5f);
if (ratio > 255)
{
ratio = 255;
}
sample = ramp.GetColor(ratio);
pim->SetPixelRGBA(i, j,
sample.GetRed(),
sample.GetGreen(),
sample.GetBlue(),
sample.GetAlpha());
}
}
sample = ramp.GetColor(255);
r = sample.GetRed();
g = sample.GetGreen();
b = sample.GetBlue();
a = sample.GetAlpha();
for(i = 1; i < imgSize; i++)
{
pim->SetPixelRGBA(i, 0, r, g, b, a);
pim->SetPixelRGBA(pim->Width-1, i, r, g, b, a);
pim->SetPixelRGBA(pim->Width-i-1, pim->Height-1, r, g, b, a);
pim->SetPixelRGBA(0, pim->Height-i-1, r, g, b, a);
}
}
break;
}
return pim;
#else //GFC_NO_GRADIENT_GEN
return 0;
#endif //GFC_NO_GRADIENT_GEN
}
//
// *** GFxFillStyle
//
GFxFillStyle::GFxFillStyle()
{
Type = GFxFill_Solid;
FillFlags = FF_HasAlpha;
pGradientData = 0;
}
GFxFillStyle::GFxFillStyle(const GFxFillStyle& other)
{
Type = GFxFill_Solid;
// We need to take care of gradient assignment, etc;
// so just call operator ==.
*this = other;
}
GFxFillStyle::~GFxFillStyle()
{
if (IsGradientFill())
{
if (pGradientData)
pGradientData->Release();
}
}
void GFxFillStyle::Read(GFxLoadProcess* p, GFxTagType tagType)
{
Type = p->ReadU8();
FillFlags = 0;
p->LogParse(" FillStyle read type = 0x%X\n", Type);
if (Type == GFxFill_Solid)
{
// Read solid fill color.
GColor color;
p->ReadRgbaTag(&color, tagType);
p->LogParse(" color: ");
p->GetStream()->LogParseClass(color);
if (color.GetAlpha() < 255)
FillFlags |= FF_HasAlpha;
RawColor = color.Raw;
}
else if (IsGradientFill())
{
// 0x10: linear gradient fill
// 0x12: radial gradient fill
// 0x13: focal-point gradient fill for SWF 8
p->GetStream()->ReadMatrix(&ImageMatrix);
// GRADIENT
UInt16 numGradients = p->ReadU8();
// For DefineShape5 gradient records are interpreted differently.
// Note that Flash 8 can include these extra bits even for old tag types.
if (numGradients & 0x10)
FillFlags |= FF_LinearRGB;
// TBD: Other bits may be set here, such as 0x80 for focal point
// Do they need custom processing?
numGradients &= 0xF;
// SWF 8 allows up to 15 points.
GASSERT(numGradients >= 1 && numGradients <= 15);
// Allocate gradients.
pGradientData = GNEW GFxGradientData((GFxFillType)Type, numGradients, IsLinearRGB());
if (!pGradientData ||
pGradientData->GetRecordCount() != numGradients)
{
// Memory error...
if (pGradientData)
pGradientData->Release();
RawColor = 0;
Type = GFxFill_Solid;
return;
}
for (int i = 0; i < numGradients; i++)
{
(*pGradientData)[i].Read(p, tagType);
// Flag if the gradient has alpha channel.
if ((*pGradientData)[i].Color.GetAlpha() < 255)
FillFlags |= FF_HasAlpha;
}
p->LogParse(" gradients: numGradients = %d\n", numGradients);
// Record focal point.
if (Type == GFxFill_FocalPointGradient)
pGradientData->SetFocalRatio((float) (p->ReadS16()) / 256.0f);
// The image gradient must be replaced by gradient data,
// which controls how it will be generated during binding.
// 1. Get gradient id.
GFxResourceId charId = p->GetNextGradientId();
// If the gradient corresponds to an image in a file,
// cache it. Otherwise get the corresponding handle
const GFxExporterInfo* pexporterInfo = p->GetExporterInfo();
bool imageFromFile = pexporterInfo &&
(pexporterInfo->ExportFlags & GFxExporterInfo::EXF_GradientTexturesExported);
if (imageFromFile)
{
// We do not have to use bitmap lookup table any longer since
// gradient file loading is matched through GFxResourceLib.
GFxResourceHandle rh;
if (p->GetResourceHandle(&rh, charId))
pImage.SetFromHandle(rh);
}
else
{
// 2. Create an image handle for gradient
GFxResourceData resData = pGradientData->CreateGradientResourceData();
GFxResourceHandle rh = p->AddDataResource(charId, resData);
if (!rh.IsNull())
pImage.SetFromHandle(rh);
}
// for 'planeta.swf' loading
// GASSERT(!(pImage.IsIndex() && pImage.GetBindIndex() == 1));
// We used to pre-compute parts of image matrices here, so that inversion
// did not need to happen in GetTexture. However, two reasons caused this
// logic to be moved to rendering side:
// - the original matrix has to be lerped for correct gradient morph results
// - with ResourceLibn we don't know image sizes are they are computed later
// based on GFxGradientParams, so those sizes can not be factored into the
// image matrix.
}
else if (IsImageFill())
{
// 0x40: tiled bitmap fill
// 0x41: clipped bitmap fill
// 0x42: non-smoothed tiled fill
// 0x43: non-smoothed clipped bitmap fill
GFxResourceId bitmapResourceId = GFxResourceId(p->ReadU16());
p->LogParse(" BitmapChar = %d\n", bitmapResourceId.GetIdIndex());
//
if (!p->GetResourceHandle(&pImage, bitmapResourceId))
{
// Should not happen..
// New image ??
}
else
{
GFxTempBindData* bd = p->GetTempBindData();
if (bd && (Type == GFxFill_TiledImage || Type == GFxFill_TiledSmoothImage))
bd->FillStyleImageWrap.Add(pImage.GetBindIndex());
}
// Look up the bitmap character.
/*
pBitmap = md->GetBitmapResource(bitmapResourceId);
if (!pBitmap && bitmapResourceId != 0xFFFF)
{
// probably, this bitmap will be imported later.
// we need to create an empty bitmap resource that will be
// filled in the ImportResolve.
pBitmap = *new GFxBitmapResource(0);
md->AddBitmapResource(bitmapResourceId, pBitmap);
}
*/
p->GetStream()->ReadMatrix(&ImageMatrix);
// Always 1 for now, should probably check formats in the future.
FillFlags |= FF_HasAlpha;
p->GetStream()->LogParseClass(ImageMatrix);
}
}
// Push our style parameters into the renderer.
void GFxFillStyle::Apply(const GFxDisplayContext& dc,
Float scaleMultiplier,
const GRenderer::Matrix* s9gImgAdjust) const
{
GRenderer *prenderer = dc.GetRenderer();
if (Type == GFxFill_Solid)
{
// 0x00: solid fill
prenderer->FillStyleColor(GColor(RawColor));
}
else
{
GRenderer::FillTexture ft;
ft.pTexture = 0;
// Get the texture for gradient/bitmap fill and use it.
GetFillTexture(&ft, dc, scaleMultiplier, s9gImgAdjust);
if (ft.pTexture)
prenderer->FillStyleBitmap(&ft);
}
}
// Creates a FillTexture for a style
bool GFxFillStyle::GetFillTexture(GRenderer::FillTexture *pftexture,
const GFxDisplayContext& dc,
Float scaleMultiplier,
const GRenderer::Matrix* s9gImgAdjust) const
{
if (IsGradientFill())
{
return GetGradientFillTexture(pftexture, dc, scaleMultiplier, s9gImgAdjust);
}
else if (IsImageFill())
{
return GetImageFillTexture(pftexture, dc, scaleMultiplier, s9gImgAdjust);
}
return 0;
}
bool GFxFillStyle::GetGradientFillTexture(GRenderer::FillTexture *pftexture,
const GFxDisplayContext& dc,
Float scaleMultiplier,
const GRenderer::Matrix* s9gImgAdjust) const
{
#ifdef GFX_AMP_SERVER
ScopeFunctionTimer fillTimer(dc.pStats, NativeCodeSwdHandle, Func_GFxFillStyle_GetGradientFillTexture, Amp_Profile_Level_Low);
#endif
GRenderer *prenderer = dc.GetRenderer();
GRenderer::Matrix imgMtx;
GASSERT(prenderer);
GASSERT(dc.pResourceBinding);
// Type == GFxFill_LinearGradient ||
// Type == GFxFill_RadialGradient ||
// Type == GFxFill_FocalPointGradient (SWF 8)
GFxImageResource* pimageRes = (*dc.pResourceBinding)[pImage];
GASSERT(!pimageRes || (pimageRes->GetResourceType() == GFxResource::RT_Image));
if (!pimageRes)
{
// If our gradient image IS an index and is NOT valid it means that
// it is being rendered before being properly resolved at bind time.
if (pImage.IsIndex())
{
GASSERT(0);
return 0;
}
// We can get here when morphing gradient styles.
// TBD: May need to do locking here!
pimageRes = pGradientData->CreateImageResource(
dc.pWeakLib, dc.pGradientParams, dc.pImageCreator,
dc.GetRenderConfig(),dc.pLog, false);
if (pimageRes)
{
// TBD: Bad because we shouldn't we storing temporary
// instance - related data in fill style.
// Threading - the fact that we can only display in one thread
// may help us.
GFxFillStyle* thisNonConst = const_cast<GFxFillStyle*>(this);
thisNonConst->pImage = pimageRes;
// Release the extra gradient reference created above.
pimageRes->Release();
}
}
if (pimageRes)
{
GImageInfoBase* pimageInfo = pimageRes->GetImageInfo();
if (pimageInfo)
{
pftexture->pTexture = pimageInfo->GetTexture(prenderer);
pftexture->WrapMode = GRenderer::Wrap_Clamp;
pftexture->SampleMode = GRenderer::Sample_Linear;
pftexture->TextureMatrix.SetIdentity();
if (Type == GFxFill_LinearGradient)
{
pftexture->TextureMatrix.AppendScaling(1.0f / 32768.F);
pftexture->TextureMatrix.AppendTranslation(0.5F, 0.F);
}
else
{
// TBD: Some of these matrix computations could have been pre-computed;
// however, that is hard to do since we don't know the image size at load time.
// Furthermore, morphing requires original matrices to be used.
pftexture->TextureMatrix.AppendScaling(1.0f / 32768.F);
pftexture->TextureMatrix.AppendTranslation(0.5F, 0.5F);
// gradient's square size is 32768x32768 (-16,384 - 16,384)
}
// When using scale9grid with gradients or images,
// the image matrix has to be adjusted according to the
// "logical difference" between the world matrix and the
// central part of the scale9grid. The s9gImgAdjust (if not null)
// represents this adjustment matrix.
imgMtx = ImageMatrix;
if (s9gImgAdjust)
imgMtx.Append(*s9gImgAdjust);
pftexture->TextureMatrix.Prepend(imgMtx.GetInverse());
pftexture->TextureMatrix.PrependScaling(scaleMultiplier);
return 1;
}
}
return 0;
}
// Bitmap Fill (either tiled or clipped)
bool GFxFillStyle::GetImageFillTexture(GRenderer::FillTexture *pftexture,
const GFxDisplayContext& dc,
Float scaleMultiplier,
const GRenderer::Matrix* s9gImgAdjust) const
{
#ifdef GFX_AMP_SERVER
ScopeFunctionTimer fillTimer(dc.pStats, NativeCodeSwdHandle, Func_GFxFillStyle_GetImageFillTexture, Amp_Profile_Level_Low);
#endif
GRenderer *prenderer = dc.GetRenderer();
GRenderer::Matrix imgMtx;
GASSERT(prenderer);
GFxImageResource* pimageRes = static_cast<GFxImageResource*>(pImage.GetResourcePtr());
if (!pimageRes)
{
GASSERT(dc.pResourceBinding);
pimageRes = (*dc.pResourceBinding)[pImage];
}
GImageInfoBase* pimageInfo = pimageRes ? pimageRes->GetImageInfo() : 0;
if (pimageInfo)
{
GTexture* ptexture = pimageInfo->GetTexture(prenderer);
if (ptexture)
{
GRenderer::BitmapWrapMode wm = GRenderer::Wrap_Repeat;
GRenderer::BitmapSampleMode sm = GRenderer::Sample_Linear;
switch(Type)
{
case GFxFill_TiledSmoothImage: wm = GRenderer::Wrap_Repeat; sm = GRenderer::Sample_Linear; break;
case GFxFill_ClippedSmoothImage: wm = GRenderer::Wrap_Clamp; sm = GRenderer::Sample_Linear; break;
case GFxFill_TiledImage: wm = GRenderer::Wrap_Repeat; sm = GRenderer::Sample_Point; break;
case GFxFill_ClippedImage: wm = GRenderer::Wrap_Clamp; sm = GRenderer::Sample_Point; break;
default:
GASSERT(0);
}
// When using scale9grid with gradients or images,
// the image matrix has to be adjusted according to the
// "logical difference" between the world matrix and the
// central part of the scale9grid. The s9gImgAdjust (if not null)
// represents this adjustment matrix.
imgMtx = ImageMatrix;
if (s9gImgAdjust)
imgMtx.Append(*s9gImgAdjust);
GRect<SInt> rect = pimageInfo->GetRect();
pftexture->pTexture = ptexture;
pftexture->TextureMatrix.SetInverse(imgMtx);
pftexture->TextureMatrix.PrependScaling(scaleMultiplier);
pftexture->TextureMatrix.AppendTranslation(Float(rect.Left), Float(rect.Top));
pftexture->TextureMatrix.AppendScaling(1.0f/pimageInfo->GetWidth(), 1.0f/pimageInfo->GetHeight());
pftexture->WrapMode = wm;
pftexture->SampleMode= sm;
return 1;
}
}
return 0;
}
GImageInfoBase* GFxFillStyle::GetImageInfo(const GFxDisplayContext& dc) const
{
if (IsImageFill() || IsGradientFill())
{
GFxImageResource* pimageRes = (*dc.pResourceBinding)[pImage];
if (pimageRes)
return pimageRes->GetImageInfo();
}
return 0;
}
// Sets this style to a blend of a and b. t = [0,1]
void GFxFillStyle::SetLerp(const GFxFillStyle& a, const GFxFillStyle& b, Float t)
{
GASSERT(t >= 0 && t <= 1);
// fill style type
Type = (UByte)a.GetType();
GASSERT(Type == b.GetType());
if (Type == GFxFill_Solid)
{
// Fill style color
RawColor = GColor::Blend(a.GetColor(), b.GetColor(), t).Raw;
return;
}
else if (IsGradientFill())
{
GASSERT(a.pGradientData && b.pGradientData);
GASSERT(a.pGradientData->GetRecordCount() == b.pGradientData->GetRecordCount());
GFxGradientData &agd = *a.pGradientData,
&bgd = *b.pGradientData;
// We have to create a new GradientData object here because
// gradient data objects are used as keys when generating gradient
// image resources and thus can not be modified!!
if (pGradientData)
pGradientData->Release();
pGradientData = GNEW GFxGradientData(a.GetType(),
agd.GetRecordCount(),
a.IsLinearRGB());
if (pGradientData)
{
for (UInt j=0; j < pGradientData->GetRecordCount(); j++)
{
(*pGradientData)[j].Ratio =
(UByte) gfrnd(gflerp(agd[j].Ratio, bgd[j].Ratio, t));
(*pGradientData)[j].Color = GColor::Blend(agd[j].Color,
bgd[j].Color, t);
}
}
pGradientData->SetFocalRatio(gflerp(agd.GetFocalRatio(),
bgd.GetFocalRatio(), t));
// Reset image handle ??? So that it is cached??
// Need to test gradient morphing
// NULL Handle -> uninitialized image pointer ??
pImage = 0;
// We will need to do this in thread safe manner
// and coordinate with the library...
}
else if (IsImageFill())
{
pImage = a.pImage;
GASSERT(pImage == b.pImage);
}
// fill style bitmap GRenderer::Matrix
ImageMatrix.SetLerp(a.ImageMatrix, b.ImageMatrix, t);
}
GFxFillStyle& GFxFillStyle::operator = (const GFxFillStyle& src)
{
// Implement correct ref-counting for pGradientData.
if (src.IsGradientFill() && src.pGradientData)
src.pGradientData->AddRef();
if (IsGradientFill() && pGradientData)
pGradientData->Release();
Type = src.Type;
FillFlags = src.FillFlags;
pGradientData = src.pGradientData;
GCOMPILER_ASSERT(sizeof(GFxGradientData*) >= sizeof(RawColor));
pImage = src.pImage;
ImageMatrix = src.ImageMatrix;
return *this;
}
void GFxFillStyle::SetFillType(GFxFillType type)
{
if (IsGradientFill() && pGradientData)
pGradientData->Release();
GASSERT((int)type <= 255);
Type = (UByte)type;
}
// Sets fill style to an image, used to support externally loaded images.
void GFxFillStyle::SetImageFill(GFxFillType fillType,
GFxImageResource *pimage,
const GRenderer::Matrix& mtx)
{
SetFillType(fillType);
GASSERT(IsImageFill());
/*
if (bilinear)
{
if (tiled) Type = (UByte) GFxFill_TiledSmoothImage;
else Type = (UByte) GFxFill_ClippedSmoothImage;
}
else
{
if (tiled) Type = (UByte) GFxFill_TiledImage;
else Type = (UByte) GFxFill_ClippedImage;
}
*/
FillFlags = FF_HasAlpha;
ImageMatrix = mtx;
ImageMatrix.AppendScaling(20);
pImage = pimage;
}
void GFxFillStyle::SetGradientFill(GFxFillType fillType,
GFxGradientData *pgradientData,
const GRenderer::Matrix& mtx)
{
if (!(IsGradientFill() && pgradientData == pGradientData))
{
SetFillType(fillType);
GASSERT(pgradientData->GetFillType() == fillType);
GASSERT(IsGradientFill());
// Gradient data must live in a global heap since it is used as a key in ResourceLib.
GASSERT(GMemory::GetHeapByAddress(pgradientData) == GMemory::GetGlobalHeap());
pGradientData = pgradientData;
if (pGradientData)
pGradientData->AddRef();
}
ImageMatrix = mtx;
ImageMatrix.M_[0][2] = PixelsToTwips(ImageMatrix.M_[0][2]);
ImageMatrix.M_[1][2] = PixelsToTwips(ImageMatrix.M_[1][2]);
}
//
// GFxLineStyle
//
GFxLineStyle::GFxLineStyle()
{
Width = 0;
pComplexFill= 0;
StyleFlags = 0;
#ifndef GFC_NO_FXPLAYER_STROKER
MiterSize = 1.0f; // Must be 1.0 for GetStrokeExtent() to work correctly.
#endif
}
GFxLineStyle::GFxLineStyle(const GFxLineStyle& other)
{
pComplexFill = 0;
*this = other;
}
GFxLineStyle::~GFxLineStyle()
{
if (pComplexFill)
delete pComplexFill;
}
// Line styles are stored in an array, so implement =
// for safety of data structures on assignment.
GFxLineStyle& GFxLineStyle::operator = (const GFxLineStyle& src)
{
Width = src.Width;
Color = src.Color;
#ifndef GFC_NO_FXPLAYER_STROKER
StyleFlags = src.StyleFlags;
MiterSize = src.MiterSize;
#endif
if (pComplexFill)
{
delete pComplexFill;
pComplexFill = 0;
}
if (src.pComplexFill)
pComplexFill = GNEW GFxFillStyle(*src.pComplexFill);
return *this;
}
void GFxLineStyle::SetComplexFill(const GFxFillStyle& f)
{
if (pComplexFill)
delete pComplexFill;
pComplexFill = GNEW GFxFillStyle(f);
}
GFxFillStyle* GFxLineStyle::CreateComplexFill()
{
if (pComplexFill == 0)
pComplexFill = GNEW GFxFillStyle;
return pComplexFill;
}
void GFxLineStyle::Read(GFxLoadProcess* p, GFxTagType tagType)
{
Width = p->ReadU16();
UInt16 styleFlags = 0;
// Extra values come here for DefineShape5.
if (tagType == GFxTag_DefineShape4)
{
styleFlags = p->ReadU16();
StyleFlags = styleFlags;
// Miter is followed by a special 16.16 value
if (styleFlags & LineJoin_Miter)
{
#ifndef GFC_NO_FXPLAYER_STROKER
MiterSize = (Float)(p->ReadU16()) / 256.0f;
#else
p->ReadU16();
#endif
}
}
// If we have complex SWF 8 fill style, read it in.
if (styleFlags & LineFlag_ComplexFill)
{
GFxFillStyle fs, *pfill;
if ((pComplexFill = GHEAP_NEW(p->GetLoadHeap()) GFxFillStyle)!=0)
pfill = pComplexFill;
else
pfill = &fs;
pfill->Read(p, tagType);
// Avoid ASSERT in GetColor() for gradient strokes.
if (pfill->IsSolidFill())
Color = pfill->GetColor();
else if (pfill->IsGradientFill())
{
GFxGradientData *pgdata = pfill->GetGradientData();
if (pgdata && pgdata->GetRecordCount() > 0)
Color = (*pgdata)[0].Color;
}
}
else
{
// Usual case: solid color line.
p->ReadRgbaTag(&Color, tagType);
}
}
void GFxLineStyle::Apply(GRenderer *prenderer) const
{
prenderer->LineStyleColor(Color);
//prenderer->LineStyleWidth(Width);
}