Leaked source code of windows server 2003
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

840 lines
16 KiB

/**************************************************************************\
*
* Copyright (c) 1999 Microsoft Corporation
*
* Module Name:
*
* mesh.cxx
*
* Abstract:
*
* Implementation of spline meshes
*
* Revision History:
*
* 01/18/1999 davidx
* Created it.
*
\**************************************************************************/
#include "precomp.hxx"
//
// Mesh constructor
//
Mesh::Mesh(
INT gridRows,
INT gridCols
)
{
this->gridRows = gridRows;
this->gridCols = gridCols;
ptTemp = NULL;
ptxTemp1 = ptxTemp2 = NULL;
dstSize.cx = dstSize.cy = 2;
mesh = new PointX[gridRows*gridCols];
if (mesh == NULL)
Error("Couldn't allocate memory to hold mesh grids\n");
initMesh();
}
//
// Mesh destructor
//
Mesh::~Mesh()
{
delete[] mesh;
delete[] ptTemp;
delete[] ptxTemp1;
delete[] ptxTemp2;
}
//
// Initialize mesh configuration
//
VOID
Mesh::initMesh()
{
PointX* p = mesh;
double sx = 1.0 / (gridCols-1);
double sy = 1.0 / (gridRows-1);
for (INT row=0; row < gridRows; row++)
{
double y = row * sy;
for (INT col=0; col < gridCols; col++)
{
p->x = col * sx;
p->y = y;
p++;
}
}
}
//
// Allocate temporary memory to hold
//
VOID
Mesh::allocTempPoints()
{
// Check to see if we have already allocated
// temporary memory for holding spline and bezier points
if (ptTemp)
return;
INT count = max(gridRows, gridCols);
ptxTemp1 = new PointX[count];
ptxTemp2 = new PointX[3*count+1];
ptTemp = new POINT[3*count+1];
if (!ptxTemp1 || !ptxTemp2 || !ptTemp)
Error("Out of memory\n");
}
//
// Convert a spline curve to Bezier segments
//
VOID
Mesh::spline2Bezier(
const PointX* srcPts,
PointX* dstPts,
INT count
)
{
const PointX* p;
PointX tempPts[4];
// We use the default tension of 0.5
double a3 = 0.5 / 3;
*dstPts = *srcPts;
for (INT i=0; i < count; i++)
{
if (i > 1 && i < count-1)
p = srcPts + (i-1);
else
{
tempPts[0] = srcPts[(i > 0) ? (i-1) : 0];
tempPts[1] = srcPts[i];
tempPts[2] = srcPts[(i+1 < count) ? (i+1) : count];
tempPts[3] = srcPts[(i+2 < count) ? (i+2) : count];
p = tempPts;
}
dstPts[1].x = -a3*p[0].x + p[1].x + a3*p[2].x;
dstPts[1].y = -a3*p[0].y + p[1].y + a3*p[2].y;
dstPts[2].x = a3*p[1].x + p[2].x - a3*p[3].x;
dstPts[2].y = a3*p[1].y + p[2].y - a3*p[3].y;
dstPts[3] = p[2];
dstPts += 3;
}
}
//
// Return the beizer control points corresponding to
// the specified row of the mesh.
//
PointX*
Mesh::getMeshRowBeziers(
INT row,
INT* pointCount,
double sx,
double sy
)
{
allocTempPoints();
PointX* ptx = mesh + indexOf(row);
for (INT i=0; i < gridCols; i++)
{
ptxTemp1[i].x = ptx[i].x * sx;
ptxTemp1[i].y = ptx[i].y * sy;
}
INT segments = gridCols-1;
spline2Bezier(ptxTemp1, ptxTemp2, segments);
*pointCount = 3*segments + 1;
return ptxTemp2;
}
POINT*
Mesh::getMeshRowBeziers(
INT row,
INT* pointCount
)
{
PointX* ptx;
ptx = getMeshRowBeziers(row, pointCount, dstSize.cx - 1, dstSize.cy - 1);
PointX::convertToPOINT(ptx, ptTemp, *pointCount);
return ptTemp;
}
//
// Return the beizer control points corresponding to
// the specified column of the mesh.
//
PointX*
Mesh::getMeshColumnBeziers(
INT col,
INT* pointCount,
double sx,
double sy
)
{
allocTempPoints();
INT i, j;
for (i=0, j=col; i < gridRows; i++, j+=gridCols)
{
ptxTemp1[i].x = mesh[j].x * sx;
ptxTemp1[i].y = mesh[j].y * sy;
}
INT segments = gridRows-1;
spline2Bezier(ptxTemp1, ptxTemp2, segments);
*pointCount = 3*segments + 1;
return ptxTemp2;
}
POINT*
Mesh::getMeshColumnBeziers(
INT col,
INT* pointCount
)
{
PointX* ptx;
ptx = getMeshColumnBeziers(col, pointCount, dstSize.cx - 1, dstSize.cy - 1);
PointX::convertToPOINT(ptxTemp2, ptTemp, *pointCount);
return ptTemp;
}
//
// Return the mesh control points for the specified row
//
POINT*
Mesh::getMeshRowPoints(
INT row,
INT* pointCount
)
{
allocTempPoints();
POINT* pt = ptTemp;
PointX* ptx = mesh + indexOf(row);
double sx = dstSize.cx - 1;
double sy = dstSize.cy - 1;
for (INT j=0; j < gridCols; j++)
{
pt[j].x = ROUND2INT(ptx[j].x * sx);
pt[j].y = ROUND2INT(ptx[j].y * sy);
}
return pt;
}
//
// Return the specified mesh control point (given row & column)
//
VOID
Mesh::getMeshPoint(
INT row,
INT col,
POINT* point
)
{
INT index = indexOf(row, col);
point->x = ROUND2INT(mesh[index].x * (dstSize.cx - 1));
point->y = ROUND2INT(mesh[index].y * (dstSize.cy - 1));
}
//
// Set a mesh control point to the specified values
//
BOOL
Mesh::setMeshPoint(
INT row,
INT col,
INT x,
INT y
)
{
// special case for mesh control points along the border
if ((row == 0 && y != 0) ||
(row == gridRows-1 && y != dstSize.cy-1) ||
(col == 0 && x != 0) ||
(col == gridCols-1 && x != dstSize.cx-1))
{
return FALSE;
}
double tx, ty;
tx = (double) x / (dstSize.cx - 1);
ty = (double) y / (dstSize.cy - 1);
// quick test to ensure the mesh control point
// is well-ordered relative to its four neighbors
if (col > 0 && tx <= mesh[indexOf(row, col-1)].x ||
col < gridCols-1 && tx >= mesh[indexOf(row, col+1)].x ||
row > 0 && ty <= mesh[indexOf(row-1, col)].y ||
row < gridRows-1 && ty >= mesh[indexOf(row+1, col)].y)
{
return FALSE;
}
INT index = indexOf(row, col);
PointX ptx = mesh[index];
mesh[index].x = tx;
mesh[index].y = ty;
// verify the mesh row and mesh column is single-valued
if (verifyRow(row) && verifyColumn(col))
return TRUE;
// if not, reject the mesh control point
mesh[index] = ptx;
return FALSE;
}
//
// Verify that the specified mesh row is well-ordered
//
BOOL
Mesh::verifyRow(INT row)
{
INT count;
PointX* points = getMeshRowBeziers(row, &count, dstSize.cx, dstSize.cy);
while (count > 3)
{
if (!verifyBezierX(points))
return FALSE;
points += 3;
count -= 3;
}
return TRUE;
}
//
// Verify that the specified mesh column is well-ordered
//
BOOL
Mesh::verifyColumn(INT col)
{
INT count;
PointX* points = getMeshColumnBeziers(col, &count, dstSize.cx, dstSize.cy);
while (count > 3)
{
if (!verifyBezierY(points))
return FALSE;
points += 3;
count -= 3;
}
return TRUE;
}
//
// Check if a Bezier segment is well-ordered in the x-direction
//
BOOL
Mesh::verifyBezierX(
PointX* pts
)
{
double a, b, c, d;
// get the quadratic equation for x'(t)
a = 3.0 * (3*pts[1].x + pts[3].x - pts[0].x - 3*pts[2].x);
b = 6.0 * (pts[0].x - 2*pts[1].x + pts[2].x);
c = 3.0 * (pts[1].x - pts[0].x);
// solve t for x'(t) = 0
d = b*b - 4*a*c;
if (d <= 0 || a == 0)
return TRUE;
// if both solution are between 0 <= t <= 1
// then the Bezier curve is not well-ordered in x-direction
double t1, t2;
d = sqrt(d);
a = 0.5 / a;
t1 = (d - b) * a;
t2 = -(d + b) * a;
return t1 < 0 || t1 > 1 ||
t2 < 0 || t2 > 1;
}
//
// Check if a Bezier segment is well-ordered in the y-direction
//
BOOL
Mesh::verifyBezierY(
PointX* pts
)
{
double a, b, c, d;
// get the quadratic equation for y'(t)
a = 3.0 * (3*pts[1].y + pts[3].y - pts[0].y - 3*pts[2].y);
b = 6.0 * (pts[0].y - 2*pts[1].y + pts[2].y);
c = 3.0 * (pts[1].y - pts[0].y);
// solve t for y'(t) = 0
d = b*b - 4*a*c;
if (d <= 0 || a == 0)
return TRUE;
// if both solution are between 0 <= t <= 1
// then the Bezier curve is not well-ordered in y-direction
double t1, t2;
d = sqrt(d);
a = 0.5 / a;
t1 = (d - b) * a;
t2 = -(d + b) * a;
return t1 < 0 || t1 > 1 ||
t2 < 0 || t2 > 1;
}
//
// Return a new MeshIterator object so that we can
// step along the y-direction and get the scale factors
// for each scanline.
//
MeshIterator*
Mesh::getYIterator(
INT srcWidth,
INT dstWidth,
INT ySteps
)
{
MeshIterator* iterator;
iterator = new MeshIterator(srcWidth, dstWidth, ySteps, gridCols);
if (iterator == NULL)
Error("Out of memory\n");
PointX* ptx;
INT count;
for (INT col=0; col < gridCols; col++)
{
ptx = getMeshColumnBeziers(col, &count, dstWidth-1, ySteps-1);
// swap x and y coordinates
for (INT i=0; i < count; i++)
{
double t = ptx[i].x;
ptx[i].x = ptx[i].y;
ptx[i].y = t;
}
iterator->addCurve(ptx, count);
}
return iterator;
}
//
// Return a new MeshIterator object so that we can
// step along the y-direction and get the scale factors
// for each vertical column of pixels.
//
MeshIterator*
Mesh::getXIterator(
INT srcHeight,
INT dstHeight,
INT xSteps
)
{
MeshIterator* iterator;
iterator = new MeshIterator(srcHeight, dstHeight, xSteps, gridRows);
if (iterator == NULL)
Error("Out of memory\n");
PointX* ptx;
INT count;
for (INT row=0; row < gridRows; row++)
{
ptx = getMeshRowBeziers(row, &count, xSteps-1, dstHeight-1);
iterator->addCurve(ptx, count);
}
return iterator;
}
//
// MeshIterator constructor
//
MeshIterator::MeshIterator(
INT srcLen,
INT dstLen,
INT steps,
INT maxCurves
)
{
if (maxCurves > MAXCURVES)
Error("Too many curves in MeshIterator constructor\n");
this->srcLen = srcLen;
this->dstLen = dstLen;
this->steps = steps;
this->maxCurves = maxCurves;
curveCount = 0;
for (INT i=0; i < MAXCURVES; i++)
curves[i] = NULL;
}
//
// MeshIterator - destructor
//
MeshIterator::~MeshIterator()
{
for (INT i=0; i < MAXCURVES; i++)
delete curves[i];
}
//
// MeshIterator - add another curve
//
VOID
MeshIterator::addCurve(
const PointX* pts,
INT count
)
{
if (curveCount == maxCurves)
Error("Program error in MeshIterator::addCurve\n");
FlatCurve* curve = new FlatCurve(pts, count);
if (curve == NULL)
Error("Out of memory\n");
curves[curveCount++] = curve;
}
//
// MeshIterator - get out positions
//
VOID
MeshIterator::getOutPos(
INT index,
double* outpos
)
{
if (curveCount != maxCurves ||
index < 0 || index >= steps)
{
Error("Program error in MeshIterator::getOutPos\n");
}
INT i, j;
double scale;
double x = index;
for (i=0; i < maxCurves; i++)
stops[i] = curves[i]->getPos(x);
scale = 1.0 / (srcLen-1);
for (i=0; i < srcLen; i++)
{
j = i * (maxCurves-1) / (srcLen-1);
INT i0 = (srcLen-1) * j / (maxCurves-1);
if (i == i0)
outpos[i] = stops[j];
else
{
INT i1 = (srcLen-1) * (j+1) / (maxCurves-1);
outpos[i] = stops[j] + (stops[j+1] - stops[j]) * (i-i0) / (i1-i0);
}
}
outpos[srcLen] = dstLen;
}
//
// FlatCurve constructor
//
FlatCurve::FlatCurve(
const PointX* pts,
INT count
)
{
capacity = elementCount = 0;
allocIncr = count * 3;
allocIncr = (allocIncr + 31) & ~31;
pointArray = NULL;
lastIndex = 0;
while (count > 3)
{
addBezierFlatten(pts);
count -= 3;
pts += 3;
}
}
//
// FlatCurve destructor
//
FlatCurve::~FlatCurve()
{
free(pointArray);
}
//
// FlatCurve - add a flattened bezier segment
//
VOID
FlatCurve::addBezierFlatten(
const PointX* pts
)
{
BOOL flatEnough;
double dx, dy, distSq;
PointX tempPts[4];
static const double epsilon = 0.00001;
static const double flatness = 1;
static const double flatness2 = flatness*flatness;
//
// Determine if the Bezier curve is flat enough
//
// Algorithm
// Given 3 points (Ax, Ay), (Bx, By), and (Cx, Cy),
// the distance from C to line AB is:
// dx = Bx - Ax
// dy = By - Ay
// L = sqrt(dx*dx + dy*dy)
// dist = (dy * (Cx - Ax) - dx * (Cy - Ay))/ L
//
dx = pts[3].x - pts[0].x;
dy = pts[3].y - pts[0].y;
distSq = dx*dx + dy*dy;
if (distSq < epsilon)
{
// if P0 and P3 are too close
flatEnough = PointX::getSquareDist(pts[0], pts[1]) <= flatness2 &&
PointX::getSquareDist(pts[2], pts[3]) <= flatness2;
}
else
{
// check if P1 is close enough to line P0-P3
double s;
s = dy*(pts[1].x - pts[0].x) - dx*(pts[1].y - pts[0].y);
s *= s;
distSq *= flatness2;
if (s > distSq)
flatEnough = FALSE;
else
{
// check if P2 is close enough to line P0-P3
s = dy*(pts[2].x - pts[0].x) - dx*(pts[2].y - pts[0].y);
flatEnough = (s*s <= distSq);
}
}
//
// If Bezier segment is already flat enough, we're done
//
if (flatEnough)
{
addLine(pts[0], pts[3]);
return;
}
//
// Otherwise, we need to subdivide
//
tempPts[0] = pts[0];
tempPts[1].x = (pts[0].x + pts[1].x) * 0.5;
tempPts[1].y = (pts[0].y + pts[1].y) * 0.5;
tempPts[2].x = (pts[0].x + pts[2].x) * 0.25 + pts[1].x * 0.5;
tempPts[2].y = (pts[0].y + pts[2].y) * 0.25 + pts[1].y * 0.5;
tempPts[3].x = (pts[0].x + pts[3].x) * 0.125 +
(pts[1].x + pts[2].x) * 0.375;
tempPts[3].y = (pts[0].y + pts[3].y) * 0.125 +
(pts[1].y + pts[2].y) * 0.375;
addBezierFlatten(tempPts);
tempPts[0] = tempPts[3];
tempPts[1].x = (pts[1].x + pts[3].x) * 0.25 + pts[2].x * 0.5;
tempPts[1].y = (pts[1].y + pts[3].y) * 0.25 + pts[2].y * 0.5;
tempPts[2].x = (pts[2].x + pts[3].x) * 0.5;
tempPts[2].y = (pts[2].y + pts[3].y) * 0.5;
tempPts[3] = pts[3];
addBezierFlatten(tempPts);
}
//
// FlatCurve - add a line segment
//
VOID
FlatCurve::addLine(
const PointX& p1,
const PointX& p2
)
{
// make sure we have enough space
if (capacity < elementCount+2)
{
capacity += allocIncr;
pointArray = (PointX*) realloc(pointArray, capacity*sizeof(PointX));
if (pointArray == NULL)
Error("Out of memory\n");
}
// add the first end point of the line, if necessary
if (elementCount == 0 ||
p1.x != pointArray[elementCount-1].x ||
p1.y != pointArray[elementCount-1].y)
{
pointArray[elementCount++] = p1;
}
// add the second end point
pointArray[elementCount++] = p2;
}
//
// FlatCurve - calculate the value of the curve at a given position
//
double
FlatCurve::getPos(
double x
)
{
while (lastIndex < elementCount && pointArray[lastIndex].x < x)
lastIndex++;
if (lastIndex == elementCount)
return pointArray[elementCount-1].y;
if (pointArray[lastIndex].x == x)
return pointArray[lastIndex].y;
double x0 = pointArray[lastIndex-1].x;
double y0 = pointArray[lastIndex-1].y;
return y0 + (x - x0) * (pointArray[lastIndex].y - y0) /
(pointArray[lastIndex].x - x0);
}