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

600 lines
15 KiB
C++

/**********************************************************************
Filename : GRasterizer.cpp
Content :
Created : 2005
Authors : Maxim Shemanarev
Copyright : (c) 2001-2006 Scaleform Corp. All Rights Reserved.
Notes : Scanline rasterizer with anti-aliasing
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.
----------------------------------------------------------------------
The code of these classes 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.
See http://antigtain.com for details.
The author of Anti-Grain Geometry gratefully acknowleges the support
of David Turner, Robert Wilhelm, and Werner Lemberg - the authors of
the FreeType libray - in producing this work.
See http://www.freetype.org for details.
**********************************************************************/
#include "GRasterizer.h"
//------------------------------------------------------------------------
GRasterizer::GRasterizer() :
FillRule(FillNonZero),
Gamma(1.0f),
Cells(),
SortedCells(),
SortedYs(),
CurrCell(),
MinX(0x7FFFFFFF),
MinY(0x7FFFFFFF),
MaxX(-0x7FFFFFFF),
MaxY(-0x7FFFFFFF),
StartX(0),
StartY(0),
LastX(0),
LastY(0)
{
CurrCell.x = 0x7FFFFFFF;
CurrCell.y = 0x7FFFFFFF;
CurrCell.Cover = 0;
CurrCell.Area = 0;
}
//------------------------------------------------------------------------
UPInt GRasterizer::GetNumBytes() const
{
return GammaLut.GetNumBytes() +
Cells.GetNumBytes() +
SortedCells.GetNumBytes() +
SortedYs.GetNumBytes();
}
//------------------------------------------------------------------------
void GRasterizer::Clear()
{
Cells.Clear();
SortedCells.Clear();
SortedYs.Clear();
CurrCell.x = 0x7FFFFFFF;
CurrCell.y = 0x7FFFFFFF;
CurrCell.Cover = 0;
CurrCell.Area = 0;
MinX = 0x7FFFFFFF;
MinY = 0x7FFFFFFF;
MaxX = -0x7FFFFFFF;
MaxY = -0x7FFFFFFF;
StartX = 0;
StartY = 0;
LastX = 0;
LastY = 0;
}
//------------------------------------------------------------------------
void GRasterizer::ClearAndRelease()
{
Clear();
Cells.ClearAndRelease();
SortedCells.ClearAndRelease();
SortedYs.ClearAndRelease();
}
//------------------------------------------------------------------------
void GRasterizer::SetGamma(GCoordType g)
{
if(g == 1)
{
GammaLut.Clear();
}
else
{
int i;
GammaLut.Resize(AntiAliasScale);
for(i = 0; i < AntiAliasScale; i++)
{
GammaLut[i] =
unsigned(pow(GCoordType(i) / AntiAliasMask, g) *
AntiAliasMask + GCoordType(0.5));
}
}
Gamma = g;
}
//------------------------------------------------------------------------
void GRasterizer::MoveTo(GCoordType x, GCoordType y)
{
StartX = LastX = int(x * SubpixelScale);
StartY = LastY = int(y * SubpixelScale);
}
//------------------------------------------------------------------------
void GRasterizer::LineTo(GCoordType x, GCoordType y)
{
int xi = int(x * SubpixelScale);
int yi = int(y * SubpixelScale);
line(LastX, LastY, xi, yi);
LastX = xi;
LastY = yi;
}
//------------------------------------------------------------------------
void GRasterizer::ClosePolygon()
{
line(LastX, LastY, StartX, StartY);
LastX = StartX;
LastY = StartY;
}
//------------------------------------------------------------------------
void GRasterizer::AddShapeScaled(const GCompoundShape& shape,
GCoordType scaleX, GCoordType scaleY,
GCoordType transX, GCoordType transY,
int activeStyle)
{
unsigned i, j;
for(i = 0; i < shape.GetNumPaths(); i++)
{
const GCompoundShape::SPath& path = shape.GetPath(i);
if(path.GetLeftStyle() != path.GetRightStyle())
{
if((activeStyle >= 0 &&
path.GetLeftStyle() == activeStyle) ||
(activeStyle < 0 &&
path.GetLeftStyle() >= 0))
{
MoveTo(path.GetVertex(0).x * scaleX + transX,
path.GetVertex(0).y * scaleY + transY);
for(j = 1; j < path.GetNumVertices(); j++)
{
LineTo(path.GetVertex(j).x * scaleX + transX,
path.GetVertex(j).y * scaleY + transY);
}
}
if((activeStyle >= 0 &&
path.GetRightStyle() == activeStyle) ||
(activeStyle < 0 &&
path.GetRightStyle() >= 0))
{
MoveTo(path.GetVertex(path.GetNumVertices() - 1).x * scaleX + transX,
path.GetVertex(path.GetNumVertices() - 1).y * scaleY + transY);
for(j = path.GetNumVertices(); j > 1; j--)
{
LineTo(path.GetVertex(j-2).x * scaleX + transX,
path.GetVertex(j-2).y * scaleY + transY);
}
}
}
}
}
//------------------------------------------------------------------------
void GRasterizer::AddShape(const GCompoundShape& shape,
GCoordType ratio,
int activeStyle)
{
AddShapeScaled(shape, ratio, ratio, 0, 0, activeStyle);
}
//------------------------------------------------------------------------
void GRasterizer::horLine(int ey, int x1, int y1, int x2, int y2)
{
int ex1 = x1 >> SubpixelShift;
int ex2 = x2 >> SubpixelShift;
int fx1 = x1 & SubpixelMask;
int fx2 = x2 & SubpixelMask;
int delta, p, first, dx;
int incr, lift, mod, rem;
// Trivial case. Happens often
if(y1 == y2)
{
setCurrCell(ex2, ey);
return;
}
// Everything is located in a single cell. That is easy!
if(ex1 == ex2)
{
delta = y2 - y1;
CurrCell.Cover += delta;
CurrCell.Area += (fx1 + fx2) * delta;
return;
}
// OK, we'll have to render a run of adjacent cells
// on the same HorLine...
p = (SubpixelScale - fx1) * (y2 - y1);
first = SubpixelScale;
incr = 1;
dx = x2 - x1;
if(dx < 0)
{
p = fx1 * (y2 - y1);
first = 0;
incr = -1;
dx = -dx;
}
delta = p / dx;
mod = p % dx;
if(mod < 0)
{
delta--;
mod += dx;
}
CurrCell.Cover += delta;
CurrCell.Area += (fx1 + first) * delta;
ex1 += incr;
setCurrCell(ex1, ey);
y1 += delta;
if(ex1 != ex2)
{
p = SubpixelScale * (y2 - y1 + delta);
lift = p / dx;
rem = p % dx;
if (rem < 0)
{
lift--;
rem += dx;
}
mod -= dx;
while(ex1 != ex2)
{
delta = lift;
mod += rem;
if(mod >= 0)
{
mod -= dx;
delta++;
}
CurrCell.Cover += delta;
CurrCell.Area += SubpixelScale * delta;
y1 += delta;
ex1 += incr;
setCurrCell(ex1, ey);
}
}
delta = y2 - y1;
CurrCell.Cover += delta;
CurrCell.Area += (fx2 + SubpixelScale - first) * delta;
}
//------------------------------------------------------------------------
void GRasterizer::line(int x1, int y1, int x2, int y2)
{
int dx = x2 - x1;
int dy = y2 - y1;
int ex1 = x1 >> SubpixelShift;
int ey1 = y1 >> SubpixelShift;
int ex2 = x2 >> SubpixelShift;
int ey2 = y2 >> SubpixelShift;
int fy1 = y1 & SubpixelMask;
int fy2 = y2 & SubpixelMask;
int xFrom, xTo;
int p, rem, mod, lift, delta, first, incr;
if(ex1 < MinX) MinX = ex1;
if(ex1 > MaxX) MaxX = ex1;
if(ey1 < MinY) MinY = ey1;
if(ey1 > MaxY) MaxY = ey1;
if(ex2 < MinX) MinX = ex2;
if(ex2 > MaxX) MaxX = ex2;
if(ey2 < MinY) MinY = ey2;
if(ey2 > MaxY) MaxY = ey2;
setCurrCell(ex1, ey1);
// Everything is on a single HorLine
if(ey1 == ey2)
{
horLine(ey1, x1, fy1, x2, fy2);
return;
}
// Vertical line - we have to calculate start and end cells,
// and then - the common values of the area and coverage for
// all cells of the line. We know exactly there's only one
// cell, so, we don't have to call render_hline().
incr = 1;
if(dx == 0)
{
int ex = x1 >> SubpixelShift;
int twoFx = (x1 - (ex << SubpixelShift)) << 1;
int area;
first = SubpixelScale;
if(dy < 0)
{
first = 0;
incr = -1;
}
delta = first - fy1;
CurrCell.Cover += delta;
CurrCell.Area += twoFx * delta;
ey1 += incr;
setCurrCell(ex, ey1);
delta = first + first - SubpixelScale;
area = twoFx * delta;
while(ey1 != ey2)
{
CurrCell.Cover = delta;
CurrCell.Area = area;
ey1 += incr;
setCurrCell(ex, ey1);
}
delta = fy2 - SubpixelScale + first;
CurrCell.Cover += delta;
CurrCell.Area += twoFx * delta;
return;
}
// OK, we have to render several HorLines
p = (SubpixelScale - fy1) * dx;
first = SubpixelScale;
if(dy < 0)
{
p = fy1 * dx;
first = 0;
incr = -1;
dy = -dy;
}
delta = p / dy;
mod = p % dy;
if(mod < 0)
{
delta--;
mod += dy;
}
xFrom = x1 + delta;
horLine(ey1, x1, fy1, xFrom, first);
ey1 += incr;
setCurrCell(xFrom >> SubpixelShift, ey1);
if(ey1 != ey2)
{
p = SubpixelScale * dx;
lift = p / dy;
rem = p % dy;
if(rem < 0)
{
lift--;
rem += dy;
}
mod -= dy;
while(ey1 != ey2)
{
delta = lift;
mod += rem;
if (mod >= 0)
{
mod -= dy;
delta++;
}
xTo = xFrom + delta;
horLine(ey1, xFrom, SubpixelScale - first, xTo, first);
xFrom = xTo;
ey1 += incr;
setCurrCell(xFrom >> SubpixelShift, ey1);
}
}
horLine(ey1, xFrom, SubpixelScale - first, x2, fy2);
}
//------------------------------------------------------------------------
bool GRasterizer::SortCells()
{
if(CurrCell.Area | CurrCell.Cover)
{
Cells.PushBack(CurrCell);
}
CurrCell.x = 0x7FFFFFFF;
CurrCell.y = 0x7FFFFFFF;
CurrCell.Cover = 0;
CurrCell.Area = 0;
if(Cells.GetSize() == 0)
return false; // Empty
if(SortedYs.GetSize())
return true; // Already sorted.
// *** Perform sort.
// Allocate the array of cell pointers
SortedCells.Resize(Cells.GetSize(), 256);
// Allocate and zero the Y array
SortedYs.Resize(MaxY - MinY + 1, 16);
SortedYs.Zero();
unsigned i;
// Create the Y-histogram (count the numbers of cells for each Y)
for(i = 0; i < Cells.GetSize(); i++)
{
SortedYs[Cells[i].y - MinY].Start++;
}
// Convert the Y-histogram into the array of starting indexes
unsigned start = 0;
for(i = 0; i < SortedYs.GetSize(); i++)
{
unsigned v = SortedYs[i].Start;
SortedYs[i].Start = start;
start += v;
}
// Fill the cell pointer array sorted by Y
for(i = 0; i < Cells.GetSize(); i++)
{
Cell* pcell = &Cells[i];
SortedY& currY = SortedYs[pcell->y - MinY];
SortedCells[currY.Start + currY.Count] = pcell;
++currY.Count;
}
// Finally arrange the X-arrays
for(i = 0; i < SortedYs.GetSize(); i++)
{
const SortedY& currY = SortedYs[i];
if(currY.Count)
{
GArrayAdaptor<Cell*> sortedCells(
SortedCells.GetDataPtr() + currY.Start,
currY.Count);
G_QuickSort(sortedCells, cellXLess);
}
}
return true;
}
//------------------------------------------------------------------------
void GRasterizer::SweepScanline(unsigned scanline, unsigned char* pRaster,
unsigned numChannels) const
{
if(scanline >= SortedYs.GetSize()) return;
unsigned numCells = SortedYs[scanline].Count;
const Cell* const* cells = &SortedCells[SortedYs[scanline].Start];
int cover = 0;
while(numCells)
{
const Cell* currCell = *cells;
int x = currCell->x;
int area = currCell->Area;
unsigned alpha;
cover += currCell->Cover;
//accumulate all cells with the same X
while(--numCells)
{
currCell = *++cells;
if(currCell->x != x)
break;
area += currCell->Area;
cover += currCell->Cover;
}
if(area)
{
alpha = calcAlpha((cover << (SubpixelShift + 1)) - area);
UByte* p = pRaster + (x - MinX) * numChannels;
for (unsigned j = 0; j < numChannels; j++)
*p++ = UByte(alpha);
x++;
}
if(numCells && currCell->x > x)
{
alpha = calcAlpha(cover << (SubpixelShift + 1));
if (alpha)
{
memset(pRaster + (x - MinX) * numChannels,
alpha,
(currCell->x - x) * numChannels);
}
}
}
}
//------------------------------------------------------------------------
void GRasterizer::SweepScanlineThreshold(unsigned scanline, unsigned char* pRaster,
unsigned numChannels, unsigned threshold) const
{
if(scanline >= SortedYs.GetSize()) return;
unsigned numCells = SortedYs[scanline].Count;
const Cell* const* cells = &SortedCells[SortedYs[scanline].Start];
int cover = 0;
while(numCells)
{
const Cell* currCell = *cells;
int x = currCell->x;
int area = currCell->Area;
unsigned alpha;
cover += currCell->Cover;
//accumulate all cells with the same X
while(--numCells)
{
currCell = *++cells;
if(currCell->x != x)
break;
area += currCell->Area;
cover += currCell->Cover;
}
if(area)
{
alpha = calcAlpha((cover << (SubpixelShift + 1)) - area, threshold);
if(alpha)
{
UByte* p = pRaster + (x - MinX) * numChannels;
for (unsigned j = 0; j < numChannels; j++)
*p++ = UByte(alpha);
}
x++;
}
if(numCells && currCell->x > x)
{
alpha = calcAlpha(cover << (SubpixelShift + 1), threshold);
if (alpha)
{
memset(pRaster + (x - MinX) * numChannels,
alpha,
(currCell->x - x) * numChannels);
}
}
}
}