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

473 lines
18 KiB
C++

/*********************************************************************
Filename : GFxScale9Grid.cpp
Content :
Created :
Authors : Maxim Shemanarev
Copyright : (c) 2001-2007 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 "GCompoundShape.h"
#include "GFxScale9Grid.h"
#include "GFxDisplayContext.h"
#include "AMP/GFxAmpViewStats.h"
//------------------------------------------------------------------------
GFxScale9GridInfo::GFxScale9GridInfo(const GFxScale9Grid* gr,
const GMatrix2D& s9gMtx,
const GMatrix2D& shapeMtx,
Float pixelScale,
const GRectF& bounds)
{
Scale9Grid = *gr;
S9gMatrix = s9gMtx;
ShapeMatrix = shapeMtx;
PixelScale = pixelScale;
Bounds = bounds;
CanUseTiling = false;
}
//------------------------------------------------------------------------
void GFxScale9GridInfo::Compute()
{
// The images can be drawn with automatic tiling according to the
// scale9grid. It is possible when the image is on the level of the
// scale9grid movie clip; to be more precise, it's possible when the
// ShapeMatrix has no transformations.
CanUseTiling = //(ShapeMatrix == GMatrix2D::Identity);
ShapeMatrix.M_[0][0] == 1.0f &&
ShapeMatrix.M_[0][1] == 0.0f &&
ShapeMatrix.M_[1][0] == 0.0f &&
ShapeMatrix.M_[1][1] == 1.0f;
InverseMatrix = S9gMatrix.GetInverse();
InverseMatrix.Append(ShapeMatrix.GetInverse());
// Scaling Grid
Float gx1 = Scale9Grid.x;
Float gy1 = Scale9Grid.y;
Float gx2 = Scale9Grid.x + Scale9Grid.w;
Float gy2 = Scale9Grid.y + Scale9Grid.h;
// Sprite Bounds
Float bx1 = Bounds.Left;
Float by1 = Bounds.Top;
Float bx2 = Bounds.Right;
Float by2 = Bounds.Bottom;
// Prevent from having degenerate matrices.
// The bounding box must be bigger than the scaling grid
// box. A value of 1/200 of a pixel (one TWIP/10) is quite enough for
// correct calculating of the matrices.
if (bx1 >= gx1) bx1 = gx1-0.1f;
if (by1 >= gy1) by1 = gy1-0.1f;
if (bx2 <= gx2) bx2 = gx1+0.1f;
if (by2 <= gy2) by2 = gy1+0.1f;
// Transformed Bounds
Float tx1 = bx1;
Float ty1 = by1;
Float tx2 = bx2;
Float ty2 = by1;
Float tx3 = bx2;
Float ty3 = by2;
Float tx4 = bx1;
Float ty4 = by2;
// Transform bounds (Y axis goes up)
// 4 3
// *----+-----------------+----*
// / / / /
// +----+-----------------+----+
// / / / /
// / / / /
// / / / /
// +----+-----------------+----+
// / / / /
// *----+-----------------+----*
// 1 2
//------------------------------------------
S9gMatrix.Transform(&tx1, &ty1);
S9gMatrix.Transform(&tx2, &ty2);
S9gMatrix.Transform(&tx3, &ty3);
S9gMatrix.Transform(&tx4, &ty4);
// Compute pseudo width and height of the bounds
Float w = GMath2D::CalcDistance(tx1, ty1, tx2, ty2);
Float h = GMath2D::CalcDistance(tx2, ty2, tx3, ty3);
if (w == 0) w = 0.1f;
if (h == 0) h = 0.1f;
// Coefficients for the margins
Float kx1 = (gx1 - bx1) / w;
Float ky1 = (gy1 - by1) / h;
Float kx2 = (bx2 - gx2) / w;
Float ky2 = (by2 - gy2) / h;
// Prevent from "overlapping"
Float d = kx1 + kx2;
if (d > 1)
{
d += 0.05f;
kx1 /= d;
kx2 /= d;
}
d = ky1 + ky2;
if (d > 1)
{
d += 0.05f;
ky1 /= d;
ky2 /= d;
}
// Compute the outer points (Y axis goes up)
// 6 5
// +----*-----------------*----+
// / / / /
// 7 *----+-----------------+----* 4
// / / / /
// / / / /
// / / / /
// 8 *----+-----------------+----* 3
// / / / /
// +----*-----------------*----+
// 1 2
//------------------------------------------
Float ox1 = tx1 + (tx2-tx1) * kx1;
Float oy1 = ty1 + (ty2-ty1) * kx1;
Float ox2 = tx2 - (tx2-tx1) * kx2;
Float oy2 = ty2 - (ty2-ty1) * kx2;
Float ox3 = tx2 + (tx3-tx2) * ky1;
Float oy3 = ty2 + (ty3-ty2) * ky1;
Float ox4 = tx3 - (tx3-tx2) * ky2;
Float oy4 = ty3 - (ty3-ty2) * ky2;
Float ox5 = tx3 - (tx3-tx4) * kx2;
Float oy5 = ty3 - (ty3-ty4) * kx2;
Float ox6 = tx4 + (tx3-tx4) * kx1;
Float oy6 = ty4 + (ty3-ty4) * kx1;
Float ox7 = tx4 - (tx4-tx1) * ky2;
Float oy7 = ty4 - (ty4-ty1) * ky2;
Float ox8 = tx1 + (tx4-tx1) * ky1;
Float oy8 = ty1 + (ty4-ty1) * ky1;
// Compute the inner points (Y axis goes up)
//
// +----+-----------------+----+
// / / / /
// +----*-----------------*----+
// / / 4 3 / /
// / / / /
// / / 1 2 / /
// +----*-----------------*----+
// / / / /
// +----+-----------------+----+
//
//------------------------------------------
Float ix1 = ox8 + (tx2-tx1) * kx1;
Float iy1 = oy8 + (ty2-ty1) * kx1;
Float ix2 = ox3 - (tx2-tx1) * kx2;
Float iy2 = oy3 - (ty2-ty1) * kx2;
Float ix3 = ox4 - (tx3-tx4) * kx2;
Float iy3 = oy4 - (ty3-ty4) * kx2;
Float ix4 = ox7 + (tx3-tx4) * kx1;
Float iy4 = oy7 + (ty3-ty4) * kx1;
// DBG
//GFx_DrawQuad(tx1, ty1, tx2, ty2, tx3, ty3, tx4, ty4, 0x7FFF0000, context.GetRenderer());
//GFx_DrawQuad(ox1, oy1, ox2, oy2, ox5, oy5, ox6, oy6, 0x7F00FF00, context.GetRenderer());
//GFx_DrawQuad(ox8, oy8, ox3, oy3, ox4, oy4, ox7, oy7, 0x7F0000FF, context.GetRenderer());
//GFx_DrawQuad(ix1, iy1, ix2, iy2, ix3, iy3, ix4, iy4, 0x7F000000, context.GetRenderer());
// Area Matrices (Y-axis goes up)
// | |
// m6 | m7 | m8
// | |
// -------+--------+--------
// | |
// m3 | m4 | m5
// | |
// -------+--------+--------
// | |
// m0 | m1 | m2
// | |
//------------------------------
Float pr[6];
#define GFX_PARL(a,b,c,d,e,f) { pr[0]=a; pr[1]=b; pr[2]=c; pr[3]=d; pr[4]=e; pr[5]=f; }
GFX_PARL(tx1, ty1, ox1, oy1, ix1, iy1); ResultingMatrices[0].SetRectToParl(bx1, by1, gx1, gy1, pr);
GFX_PARL(ox1, oy1, ox2, oy2, ix2, iy2); ResultingMatrices[1].SetRectToParl(gx1, by1, gx2, gy1, pr);
GFX_PARL(ox2, oy2, tx2, ty2, ox3, oy3); ResultingMatrices[2].SetRectToParl(gx2, by1, bx2, gy1, pr);
GFX_PARL(ox8, oy8, ix1, iy1, ix4, iy4); ResultingMatrices[3].SetRectToParl(bx1, gy1, gx1, gy2, pr);
GFX_PARL(ix1, iy1, ix2, iy2, ix3, iy3); ResultingMatrices[4].SetRectToParl(gx1, gy1, gx2, gy2, pr);
GFX_PARL(ix2, iy2, ox3, oy3, ox4, oy4); ResultingMatrices[5].SetRectToParl(gx2, gy1, bx2, gy2, pr);
GFX_PARL(ox7, oy7, ix4, iy4, ox6, oy6); ResultingMatrices[6].SetRectToParl(bx1, gy2, gx1, by2, pr);
GFX_PARL(ix4, iy4, ix3, iy3, ox5, oy5); ResultingMatrices[7].SetRectToParl(gx1, gy2, gx2, by2, pr);
GFX_PARL(ix3, iy3, ox4, oy4, tx3, ty3); ResultingMatrices[8].SetRectToParl(gx2, gy2, bx2, by2, pr);
#undef GFX_PARL
ResultingGrid.Left = gx1;
ResultingGrid.Top = gy1;
ResultingGrid.Right = gx2;
ResultingGrid.Bottom = gy2;
}
void GFxScale9GridInfo::Transform(Float* x, Float* y) const
{
// Area Codes (Y-axis goes up)
// | |
// 0110 | 0010 | 0011
// 6->m6 | 2->m7 | 3->m8
// -------+--------+-------- y2
// | |
// 0100 | 0000 | 0001
// 4->m3 | 0->m4 | 1->m5
// -------+--------+-------- y1
// | |
// 1100 | 1000 | 1001
// 12->m0 | 8->m1 | 9->m2
// x1 x2
//------------------------------
enum {X=0};
static const UInt8 codeToMtx[] = { 4, 5, 7, 8, 3, X, 6, X, 1, 2, X, X, 0 };
// 0 1 2 3 4 5 6 7 8 9 10 11 12
ShapeMatrix.Transform(x, y);
UInt areaCode = (*x > ResultingGrid.Right) | // See "Area Codes"
((*y > ResultingGrid.Bottom) << 1) |
((*x < ResultingGrid.Left) << 2) |
((*y < ResultingGrid.Top) << 3);
ResultingMatrices[codeToMtx[areaCode]].Transform(x, y);
InverseMatrix.Transform(x, y);
}
//------------------------------------------------------------------------
GRectF GFxScale9GridInfo::AdjustBounds(const GRectF& bounds) const
{
Float x1 = bounds.Left;
Float y1 = bounds.Top;
Float x2 = bounds.Right;
Float y2 = bounds.Top;
Float x3 = bounds.Right;
Float y3 = bounds.Bottom;
Float x4 = bounds.Left;
Float y4 = bounds.Bottom;
Transform(&x1, &y1);
Transform(&x2, &y2);
Transform(&x3, &y3);
Transform(&x4, &y4);
Float xb1=x1, yb1=y1, xb2=x1, yb2=y1;
if (x2 < xb1) xb1 = x2; if (y2 < yb1) yb1 = y2;
if (x2 > xb2) xb2 = x2; if (y2 > yb2) yb2 = y2;
if (x3 < xb1) xb1 = x3; if (y3 < yb1) yb1 = y3;
if (x3 > xb2) xb2 = x3; if (y3 > yb2) yb2 = y3;
if (x4 < xb1) xb1 = x4; if (y4 < yb1) yb1 = y4;
if (x4 > xb2) xb2 = x4; if (y4 > yb2) yb2 = y4;
return GRectF(xb1, yb1, xb2, yb2);
}
//------------------------------------------------------------------------
void GFxScale9GridInfo::ComputeImgAdjustRects(const GCompoundShape& cs,
const GFxFillStyle* pfillStyles,
UPInt fillStylesNum)
{
UInt i;
for (i = 0; i < cs.GetNumPaths(); ++i)
{
const GCompoundShape::SPath& path = cs.GetPath(i);
if (path.GetLeftStyle() >= 0)
{
GASSERT(path.GetLeftStyle() < (int)fillStylesNum);
const GFxFillStyle& fill = pfillStyles[path.GetLeftStyle()];
if (fill.IsGradientFill() || fill.IsImageFill())
{
if (ImgAdjustments.GetSize() == 0)
ImgAdjustments.Resize(fillStylesNum);
ImgAdjust& adj = ImgAdjustments[path.GetLeftStyle()];
cs.ExpandPathBounds(path, &adj.x1, &adj.y1, &adj.x2, &adj.y2);
}
}
if (path.GetRightStyle() >= 0)
{
GASSERT(path.GetRightStyle() < (int)fillStylesNum);
const GFxFillStyle& fill = pfillStyles[path.GetRightStyle()];
if (fill.IsGradientFill() || fill.IsImageFill())
{
if (ImgAdjustments.GetSize() == 0)
ImgAdjustments.Resize(fillStylesNum);
ImgAdjust& adj = ImgAdjustments[path.GetRightStyle()];
cs.ExpandPathBounds(path, &adj.x1, &adj.y1, &adj.x2, &adj.y2);
}
}
}
}
//------------------------------------------------------------------------
void GFxScale9GridInfo::ComputeImgAdjustMatrices()
{
UInt i;
for(i = 0; i < ImgAdjustments.GetSize(); ++i)
{
ImgAdjust& adj = ImgAdjustments[i];
if (adj.x1 < adj.x2 && adj.y1 < adj.y2)
{
Float p[6] = { adj.x1, adj.y1, adj.x2, adj.y1, adj.x2, adj.y2 };
Transform(&p[0], &p[1]);
Transform(&p[2], &p[3]);
Transform(&p[4], &p[5]);
adj.Matrix.SetRectToParl(adj.x1, adj.y1, adj.x2, adj.y2, p);
}
}
}
//------------------------------------------------------------------------
void GFxTexture9Grid::Compute(const GFxScale9GridInfo& sg,
const GRectF& clipRect,
Float scaleMultiplier,
UInt styleIdx)
{
ScaleMultiplierInv = 1/scaleMultiplier;
FillStyleIdx = styleIdx;
pTexture = 0;
// (Y axis goes up, so, Top and Bottom are inverse)
// sg.ResultingGrid - the inner scaling grid rectangle
// sg.Bounds - the outer Movie Clip bounding box
// clipRect - clipping rectangle (typically shape bounding box)
// m0...m8 - transformation matrices (sg.ResultingMatrices)
// 0...35 - resulting grid points, transformed accordingly by m[0]...m[8]
//
// *--------*-----------------*--------*
// / 27 26 / 31 30 / 35 34 /
// / m6 / m7 / m8 /
// / 24 25 / 28 29 / 32 33 /
// *--------*-----------------*--------*
// / 15 14 / 19 18 / 23 22 /
// / m3 / m4 / m5 /
// / 12 13 / 16 17 / 20 21 /
// *--------*-----------------*--------*
// / 3 2 / 7 6 / 11 10 /
// / m0 / m1 / m2 /
// / 0 1 / 4 5 / 8 9 /
// *--------*-----------------*--------*
//
// The idea is to compute the grid points for the triangle pairs.
// IMPORTANT: the resulting mesh there must use the same affine matrix,
// otherwise seam pixels will appear. So, the only possibility to draw
// the areas properly is to apply different texture matrices.
//---------------------------------
// The "slices" array corresponds to "sg.ResultingMatrices", then it's
// transformed to the GridPoint array.
//---------------------
GRectF slices[9];
slices[0].SetRect(sg.Bounds.Left, sg.Bounds.Top, sg.ResultingGrid.Left, sg.ResultingGrid.Top);
slices[1].SetRect(sg.ResultingGrid.Left, sg.Bounds.Top, sg.ResultingGrid.Right, sg.ResultingGrid.Top);
slices[2].SetRect(sg.ResultingGrid.Right, sg.Bounds.Top, sg.Bounds.Right, sg.ResultingGrid.Top);
slices[3].SetRect(sg.Bounds.Left, sg.ResultingGrid.Top, sg.ResultingGrid.Left, sg.ResultingGrid.Bottom);
slices[4].SetRect(sg.ResultingGrid.Left, sg.ResultingGrid.Top, sg.ResultingGrid.Right, sg.ResultingGrid.Bottom);
slices[5].SetRect(sg.ResultingGrid.Right, sg.ResultingGrid.Top, sg.Bounds.Right, sg.ResultingGrid.Bottom);
slices[6].SetRect(sg.Bounds.Left, sg.ResultingGrid.Bottom, sg.ResultingGrid.Left, sg.Bounds.Bottom);
slices[7].SetRect(sg.ResultingGrid.Left, sg.ResultingGrid.Bottom, sg.ResultingGrid.Right, sg.Bounds.Bottom);
slices[8].SetRect(sg.ResultingGrid.Right, sg.ResultingGrid.Bottom, sg.Bounds.Right, sg.Bounds.Bottom);
NumBitmaps = 0;
Float ox = clipRect.Left;
Float oy = clipRect.Top;
Float kw = 1 / clipRect.Width();
Float kh = 1 / clipRect.Height();
GMatrix2D invShapeMatrix = sg.ShapeMatrix.GetInverse();
UInt i;
for(i = 0; i < 9; ++i)
{
// Clip the slice and if it's not empty transform the points,
// according to the sg.ResultingMatrices and calculate the BitmapDesc
//--------------------
invShapeMatrix.Transform(&slices[i].Left, &slices[i].Top);
invShapeMatrix.Transform(&slices[i].Right, &slices[i].Bottom);
slices[i].Intersect(clipRect);
if (!slices[i].IsEmpty())
{
GRenderer::BitmapDesc& bm = Bitmaps[NumBitmaps];
bm.Coords = slices[i];
sg.Transform(&bm.Coords.Left, &bm.Coords.Top);
sg.Transform(&bm.Coords.Right, &bm.Coords.Bottom);
bm.Coords.Left *= scaleMultiplier;
bm.Coords.Top *= scaleMultiplier;
bm.Coords.Right *= scaleMultiplier;
bm.Coords.Bottom *= scaleMultiplier;
const GRectF& slice = slices[i];
bm.TextureCoords.Left = (slice.Left - ox) * kw;
bm.TextureCoords.Top = (slice.Top - oy) * kh;
bm.TextureCoords.Right = (slice.Right - ox) * kw;
bm.TextureCoords.Bottom = (slice.Bottom - oy) * kh;
bm.Color = 0xFFFFFFFF;//0xFFB00000; // TO DO: Set proper color
++NumBitmaps;
}
}
}
//------------------------------------------------------------------------
void GFxTexture9Grid::Display(GFxDisplayContext &context,
const GFxFillStyle& style,
const GRenderer::Matrix& mtx)
{
#ifdef GFX_AMP_SERVER
ScopeFunctionTimer displayTimer(context.pStats, NativeCodeSwdHandle, Func_GFxTexture9Grid_Display, Amp_Profile_Level_Low);
#endif
if (NumBitmaps)
{
GImageInfoBase* pimageInfo = style.GetImageInfo(context);
if (pimageInfo)
{
GTexture* ptexture = pimageInfo->GetTexture(context.GetRenderer());
GRect<SInt> rect = pimageInfo->GetRect();
if (rect.Area() && pTexture != ptexture)
{
Float kw = rect.Width() / Float(pimageInfo->GetWidth());
Float kh = rect.Height() / Float(pimageInfo->GetHeight());
Float dx = rect.Left / Float(pimageInfo->GetWidth());
Float dy = rect.Top / Float(pimageInfo->GetHeight());
for (UInt i = 0; i < NumBitmaps; i++)
{
Bitmaps[i].TextureCoords.Left = Bitmaps[i].TextureCoords.Left * kw + dx;
Bitmaps[i].TextureCoords.Top = Bitmaps[i].TextureCoords.Top * kh + dy;
Bitmaps[i].TextureCoords.Right = Bitmaps[i].TextureCoords.Right * kw + dx;
Bitmaps[i].TextureCoords.Bottom = Bitmaps[i].TextureCoords.Bottom * kh + dy;
}
pTexture = ptexture;
}
context.GetRenderer()->DrawBitmaps(Bitmaps, NumBitmaps, 0, NumBitmaps, ptexture, mtx);
}
}
}