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.
1138 lines
28 KiB
1138 lines
28 KiB
/**************************************************************************\
|
|
*
|
|
* Copyright (c) 1999 - 2000 Microsoft Corporation
|
|
*
|
|
* Abstract:
|
|
*
|
|
* Quad Transforms
|
|
*
|
|
* History:
|
|
*
|
|
* 03/17/1999 ikkof
|
|
* Created it.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
#include "precomp.hpp"
|
|
|
|
INT solveQuadraticEquationForQuadTransform(REAL a, REAL b, REAL c, REAL* x);
|
|
|
|
// Constants used in GpQuadAnalyzer
|
|
|
|
#define EdgeHorizontal 0
|
|
#define EdgeDown 1
|
|
#define EdgeUp 2
|
|
|
|
VOID
|
|
GpQuadAnalyzer::SetQuadAnalyzer(const GpPointF* points)
|
|
{
|
|
Left = Right = points[0].X;
|
|
Top = Bottom = points[0].Y;
|
|
|
|
for(INT i = 0; i < 4; i++)
|
|
{
|
|
INT j = i + 1;
|
|
if(j == 4)
|
|
j = 0;
|
|
|
|
if(points[i].Y < points[j].Y)
|
|
{
|
|
Directions[i] = EdgeDown;
|
|
Y1[i] = points[i].Y;
|
|
Y2[i] = points[j].Y;
|
|
X1[i] = points[i].X;
|
|
DxDy[i] = (points[j].X - points[i].X)/(points[j].Y - points[i].Y);
|
|
}
|
|
else if(points[i].Y > points[j].Y)
|
|
{
|
|
Directions[i] = EdgeUp;
|
|
Y1[i] = points[j].Y;
|
|
Y2[i] = points[i].Y;
|
|
X1[i] = points[j].X;
|
|
DxDy[i] = (points[j].X - points[i].X)/(points[j].Y - points[i].Y);
|
|
}
|
|
else // Horizontal
|
|
{
|
|
Directions[i] = EdgeHorizontal;
|
|
Y1[i] = points[i].Y;
|
|
Y2[i] = points[i].Y;
|
|
X1[i] = points[i].X;
|
|
DxDy[i] = 0; // It is not used.
|
|
}
|
|
|
|
if(points[i].X < Left)
|
|
Left = points[i].X;
|
|
else if(points[i].X > Right)
|
|
Right = points[i].X;
|
|
|
|
if(points[i].Y < Top)
|
|
Top = points[i].Y;
|
|
else if(points[i].Y > Bottom)
|
|
Bottom = points[i].Y;
|
|
}
|
|
}
|
|
|
|
// Get the x-spans of a quad and returns the number of
|
|
// pairs of x-spans.
|
|
|
|
INT
|
|
GpQuadAnalyzer::GetXSpans(REAL* xSpans, REAL y)
|
|
{
|
|
if(y < Top || y >= Bottom)
|
|
return 0;
|
|
|
|
INT count = 0;
|
|
|
|
for(INT i = 0; i < 4; i++)
|
|
{
|
|
if(Directions[i] != EdgeHorizontal && y >= Y1[i] && y < Y2[i])
|
|
xSpans[count++] = X1[i] + DxDy[i]*(y - Y1[i]);
|
|
}
|
|
|
|
return (count >> 1);
|
|
}
|
|
|
|
VOID
|
|
GpBilinearTransform::Initialize()
|
|
{
|
|
GpMemset(&SrcRect, 0, sizeof(GpRectF));
|
|
GpMemset(&DstBounds, 0, sizeof(GpRectF));
|
|
GpMemset(&A, 0, sizeof(GpPointF));
|
|
GpMemset(&B, 0, sizeof(GpPointF));
|
|
GpMemset(&C, 0, sizeof(GpPointF));
|
|
GpMemset(&D, 0, sizeof(GpPointF));
|
|
|
|
C_VV = C_V = 0;
|
|
|
|
FixedValue = -1.0f;
|
|
|
|
#ifdef TEST_QUADTRANSFORMS
|
|
// For testing purpose only.
|
|
|
|
GpMemset(&Verteces[0], 0, 4*sizeof(GpPointF));
|
|
|
|
#endif
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* P0 P1
|
|
* ------------
|
|
* | \
|
|
* | \
|
|
* | \
|
|
* | \
|
|
* ----------------\
|
|
* P2 P3
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
GpBilinearTransform::SetBilinearTransform(
|
|
const GpRectF& rect,
|
|
const GpPointF* points,
|
|
INT count,
|
|
REAL fixed
|
|
)
|
|
{
|
|
BOOL test = ((points != NULL) && (count == 3 || count == 4));
|
|
ASSERT(test);
|
|
|
|
if(!test)
|
|
return InvalidParameter;
|
|
|
|
SrcRect = rect;
|
|
|
|
REAL left, right, top, bottom;
|
|
|
|
left = right = points[0].X;
|
|
top = bottom = points[0].Y;
|
|
|
|
for(INT i = 1; i < count; i++)
|
|
{
|
|
if(points[i].X < left)
|
|
left = points[i].X;
|
|
else if(points[i].X > right)
|
|
right = points[i].X;
|
|
|
|
if(points[i].Y < top)
|
|
top = points[i].Y;
|
|
else if(points[i].Y > bottom)
|
|
bottom = points[i].Y;
|
|
}
|
|
|
|
GpPointF quad[4];
|
|
|
|
quad[0] = points[0];
|
|
quad[1] = points[1];
|
|
quad[3] = points[2];
|
|
|
|
if(count == 4)
|
|
{
|
|
A.X = points[0].X - points[1].X - points[2].X + points[3].X;
|
|
A.Y = points[0].Y - points[1].Y - points[2].Y + points[3].Y;
|
|
|
|
quad[2] = points[3];
|
|
}
|
|
else
|
|
{
|
|
// This is a palallelogram.
|
|
|
|
A.X = 0;
|
|
A.Y = 0;
|
|
|
|
// Obtain the fourth vertex.
|
|
|
|
REAL x3 = points[1].X + points[2].X - points[0].X;
|
|
REAL y3 = points[1].Y + points[2].Y - points[0].Y;
|
|
|
|
if(x3 < left)
|
|
left = x3;
|
|
else if(x3 > right)
|
|
right = x3;
|
|
|
|
if(y3 < top)
|
|
top = y3;
|
|
else if(y3 > bottom)
|
|
bottom = y3;
|
|
|
|
quad[2].X = x3;
|
|
quad[2].Y = y3;
|
|
}
|
|
|
|
B.X = points[1].X - points[0].X;
|
|
B.Y = points[1].Y - points[0].Y;
|
|
C.X = points[2].X - points[0].X;
|
|
C.Y = points[2].Y - points[0].Y;
|
|
D = points[0];
|
|
|
|
if(A.X != C.X || A.Y != C.Y)
|
|
C_VV = A.X*C.Y - A.Y*C.X;
|
|
else
|
|
C_VV = 0;
|
|
C_V = B.X*C.Y - B.Y*C.X;
|
|
|
|
DstBounds.X = left;
|
|
DstBounds.Y = top;
|
|
DstBounds.Width = right - left;
|
|
DstBounds.Height = bottom - top;
|
|
|
|
QAnalyzer.SetQuadAnalyzer(&quad[0]);
|
|
|
|
FixedValue = fixed;
|
|
|
|
#ifdef TEST_QUADTRANSFORMS
|
|
// For testing purpose only.
|
|
|
|
GpMemcpy(&Verteces[0], points, count*sizeof(GpPointF));
|
|
if(count == 3)
|
|
{
|
|
// Set the fourth vertex.
|
|
|
|
Verteces[3].X = points[1].X + points[2].X - points[0].X;
|
|
Verteces[3].Y = points[1].Y + points[2].Y - points[0].Y;
|
|
}
|
|
|
|
#endif
|
|
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* Solve the quadratic equation of a branch which reduces
|
|
* to x = - c/b when a is very small. This returns the number of
|
|
* appropriate solution which is either 0 or 1.
|
|
*
|
|
* a x^2 + b x + c = 0.
|
|
*
|
|
* 12/22/1999 ikkof
|
|
* Created it.
|
|
\**************************************************************************/
|
|
|
|
INT solveQuadraticEquationForQuadTransform(REAL a, REAL b, REAL c, REAL* x)
|
|
{
|
|
INT n = 0;
|
|
REAL x1 = 0, x2 = 0;
|
|
|
|
if(a != 0)
|
|
{
|
|
REAL D = b*b - 4*a*c;
|
|
|
|
if(D > 0)
|
|
{
|
|
n = 2;
|
|
D = REALSQRT(D);
|
|
|
|
if(b >= 0)
|
|
{
|
|
x1 = (2*c)/(-b - D);
|
|
x2 = (-b - D)/(2*a);
|
|
}
|
|
else
|
|
{
|
|
x1 = (2*c)/(-b + D);
|
|
x2 = (-b + D)/(2*a);
|
|
}
|
|
|
|
if(x1 < 0 || x1 > 1)
|
|
{
|
|
if(x2 >= 0 && x2 <= 1)
|
|
{
|
|
REAL temp = x1;
|
|
x1 = x2;
|
|
x2 = temp;
|
|
}
|
|
}
|
|
|
|
}
|
|
else if(D == 0)
|
|
{
|
|
n = 1;
|
|
x1 = - b/(2*a);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// This is a linear equation.
|
|
|
|
if(b != 0)
|
|
{
|
|
n = 1;
|
|
x1 = - c/b;
|
|
}
|
|
}
|
|
|
|
x[0] = x1;
|
|
x[1] = x2;
|
|
|
|
return n;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* This returns the x-spans of the current quad at given y
|
|
* between xmin and xmax.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [OUT] xSpans - the x-spans
|
|
* [IN] y - y-coordinate to evaluate.
|
|
* [IN] xmin - the minimum x (inclusive)
|
|
* [IN] xmax - the maximum x (exclusive)
|
|
*
|
|
* Return Value:
|
|
*
|
|
* INT - Retuns the number of x-span pairs (0, 1, or 2).
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/04/2000 ikkof
|
|
*
|
|
\**************************************************************************/
|
|
|
|
INT
|
|
GpBilinearTransform::GetXSpans(
|
|
INT* xSpans,
|
|
INT y,
|
|
INT xmin,
|
|
INT xmax
|
|
)
|
|
{
|
|
REAL realY = TOREAL(y);
|
|
|
|
if(
|
|
realY < DstBounds.Y
|
|
|| realY >= DstBounds.Y + DstBounds.Height
|
|
|| TOREAL(xmax) < DstBounds.X
|
|
|| TOREAL(xmin) >= DstBounds.X + DstBounds.Width
|
|
)
|
|
return 0; // Do the quick rejection.
|
|
|
|
REAL x[4];
|
|
INT index = (QAnalyzer.GetXSpans(&x[0], realY) << 1);
|
|
|
|
// Sort x in ascending order.
|
|
|
|
if(index >= 2)
|
|
{
|
|
for(INT i = 0; i < index - 1; i++)
|
|
{
|
|
for(INT j = i + 1; j < index; j++)
|
|
{
|
|
if(x[j] < x[i])
|
|
{
|
|
REAL temp = x[i];
|
|
x[i] = x[j];
|
|
x[j] = temp;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
return 0; // No x-span in the given y.
|
|
|
|
// Check for the first span.
|
|
|
|
if(x[0] >= xmax || x[1] <= xmin)
|
|
{
|
|
x[0] = x[2];
|
|
x[1] = x[3];
|
|
index -= 2;
|
|
}
|
|
else
|
|
{
|
|
x[0] = max(x[0], xmin);
|
|
x[1] = min(x[1], xmax);
|
|
}
|
|
|
|
if(index >= 4)
|
|
{
|
|
// Check for the second span
|
|
|
|
if(x[2] >= xmax || x[3] <= xmin)
|
|
index -= 2;
|
|
else
|
|
{
|
|
x[2] = max(x[2], xmin);
|
|
x[3] = min(x[3], xmax);
|
|
}
|
|
}
|
|
|
|
INT j = 0;
|
|
|
|
for(INT i = 0; i < index; i += 2)
|
|
{
|
|
// Use Ceiling for both since xmin is inclusive
|
|
// and xmax is exclusive (hence the real inclusive
|
|
// span is being bounded by Celing and Floor).
|
|
|
|
xSpans[j] = GpCeiling(x[i]);
|
|
xSpans[j + 1] = GpCeiling(x[i+1]);
|
|
if(xSpans[j + 1] > xSpans[j])
|
|
j += 2;
|
|
}
|
|
|
|
return j/2;
|
|
}
|
|
|
|
BOOL
|
|
GpBilinearTransform::GetSourceParameter(
|
|
REAL* u,
|
|
REAL* v,
|
|
const GpPointF& point
|
|
)
|
|
{
|
|
if (FixedValue >= 0)
|
|
{
|
|
*u = FixedValue;
|
|
*v = FixedValue;
|
|
return TRUE;
|
|
}
|
|
|
|
REAL b, c, vv[2];
|
|
GpPointF dD;
|
|
|
|
dD.X = D.X - point.X;
|
|
dD.Y = D.Y - point.Y;
|
|
|
|
b = C_V + A.X*dD.Y - A.Y*dD.X;
|
|
c = B.X*dD.Y - B.Y*dD.X;
|
|
|
|
INT num = solveQuadraticEquationForQuadTransform(C_VV, b, c, &vv[0]);
|
|
|
|
if(num == 0)
|
|
return FALSE;
|
|
|
|
REAL u1 = 0, v1 = 0, u2 = 0, v2 = 0;
|
|
|
|
BOOL firstSolutionOk = FALSE;
|
|
BOOL secondSolutionOk = FALSE;
|
|
|
|
firstSolutionOk = TRUE;
|
|
|
|
v1 = vv[0];
|
|
REAL denomX = A.X*v1 + B.X;
|
|
REAL denomY = A.Y*v1 + B.Y;
|
|
|
|
if(REALABS(denomX) > REALABS(denomY))
|
|
{
|
|
u1 = - (C.X*v1 + dD.X)/denomX;
|
|
}
|
|
else if(REALABS(denomY) > 0)
|
|
{
|
|
u1 = - (C.Y*v1 + dD.Y)/denomY;
|
|
}
|
|
else // Both denomX and denomY = 0.
|
|
firstSolutionOk = FALSE;
|
|
|
|
if(num == 2)
|
|
{
|
|
// Allow 1 % error between 0 and 1.
|
|
|
|
if(u1 < -0.02f || u1 > 1.02f || v1 < -0.02f || v1 > 1.02f || !firstSolutionOk)
|
|
{
|
|
// We may be picking a wrong solution. Evaluate the other.
|
|
|
|
secondSolutionOk = TRUE;
|
|
|
|
v2 = vv[1];
|
|
denomX = A.X*v2 + B.X;
|
|
denomY = A.Y*v2 + B.Y;
|
|
if(REALABS(denomX) > REALABS(denomY))
|
|
{
|
|
u2 = - (C.X*v2 + dD.X)/denomX;
|
|
}
|
|
else if(REALABS(denomY) > 0)
|
|
{
|
|
u2 = - (C.Y*v2 + dD.Y)/denomY;
|
|
}
|
|
else // Both denomX and denomY = 0.
|
|
secondSolutionOk = FALSE;
|
|
|
|
// Allow 1 % error between 0 and 1.
|
|
|
|
if(secondSolutionOk
|
|
&& u2 >= - 0.02f && u2 <= 1.02f && v2 >= -0.02f && v2 <= 1.02f)
|
|
{
|
|
REAL temp = u1;
|
|
u1 = u2;
|
|
u2 = temp;
|
|
|
|
temp = v1;
|
|
v1 = v2;
|
|
v2 = temp;
|
|
}
|
|
else secondSolutionOk = FALSE;
|
|
}
|
|
}
|
|
|
|
if(firstSolutionOk || secondSolutionOk > 0)
|
|
{
|
|
u[0] = u1;
|
|
v[0] = v1;
|
|
u[1] = u2;
|
|
v[1] = v2;
|
|
|
|
return TRUE; // success
|
|
}
|
|
else
|
|
return FALSE; // no valid parameter.
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* Function Description:
|
|
*
|
|
* This returns the x-spans and uv arrays of the current quad at given y
|
|
* between xmin and xmax.
|
|
*
|
|
* Arguments:
|
|
*
|
|
* [OUT] u - u-array
|
|
* [OUT] v - v-array
|
|
* [OUT] xSpans - the x-spans
|
|
* [IN] y - y-coordinate to evaluate.
|
|
* [IN] xmin - the minimum x (inclusive)
|
|
* [IN] xmax - the maximum x (exclusive)
|
|
*
|
|
* Return Value:
|
|
*
|
|
* INT - Retuns the number of x-span pairs (0, 1, or 2).
|
|
*
|
|
* Created:
|
|
*
|
|
* 01/04/2000 ikkof
|
|
*
|
|
\**************************************************************************/
|
|
|
|
INT
|
|
GpBilinearTransform::GetSourceParameterArrays(
|
|
REAL* u,
|
|
REAL* v,
|
|
INT* xSpans,
|
|
INT y,
|
|
INT xmin,
|
|
INT xmax
|
|
)
|
|
{
|
|
ASSERT(u && v && xSpans);
|
|
|
|
INT pairCount = GetXSpans(xSpans, y, xmin, xmax);
|
|
|
|
INT count = 0;
|
|
|
|
REAL u1[2], v1[2];
|
|
|
|
GpMemset(&u1[0], 0, 2*sizeof(REAL));
|
|
GpMemset(&v1[0], 0, 2*sizeof(REAL));
|
|
|
|
for(INT k = 0; k < pairCount; k++)
|
|
{
|
|
GpPointF destPt;
|
|
destPt.Y = TOREAL(y);
|
|
destPt.X = TOREAL(xSpans[2*k]);
|
|
|
|
BOOL firstPoint = TRUE;
|
|
INT width = xSpans[2*k + 1] - xSpans[2*k];
|
|
|
|
for(INT i = 0; i < width; i++)
|
|
{
|
|
BOOL success = GetSourceParameter(&u1[0], &v1[0], destPt);
|
|
if(!success)
|
|
WARNING(("There is no solution for quadratic equation."));
|
|
|
|
*(u + count) = u1[0];
|
|
*(v + count) = v1[0];
|
|
|
|
count++;
|
|
|
|
destPt.X += 1;
|
|
}
|
|
}
|
|
|
|
return pairCount;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* This converts lines to quadratic Beziers.
|
|
*
|
|
* [IN] points: the line points
|
|
* [IN] count: the number of line points.
|
|
* [OUT] q: the quadratic Bezier control points.
|
|
*
|
|
* The array size of q must be larger than or equal to 2*count - 1.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
GpBilinearTransform::ConvertLines(
|
|
const GpPointF* points,
|
|
INT count,
|
|
GpPointF* q)
|
|
{
|
|
ASSERT(points && q);
|
|
ASSERT(count >= 2);
|
|
|
|
REAL mx, my, nx, ny;
|
|
|
|
GpPointF pt1, pt2;
|
|
|
|
pt1 = points[0];
|
|
|
|
INT j = 0;
|
|
for(INT i = 1; i < count; i++)
|
|
{
|
|
pt2 = points[i];
|
|
mx = (pt2.X - pt1.X)/SrcRect.Width;
|
|
my = (pt2.Y - pt1.Y)/SrcRect.Height;
|
|
nx = (pt1.X - SrcRect.X)/SrcRect.Width;
|
|
ny = (pt1.Y - SrcRect.Y)/SrcRect.Height;
|
|
|
|
GpPointF c0, c1, c2;
|
|
REAL temp;
|
|
|
|
temp = mx*my;
|
|
c2.X = temp*A.X;
|
|
c2.Y = temp*A.Y;
|
|
temp = mx*ny + my*nx;
|
|
c1.X = temp*A.X + mx*B.X + my*C.X;
|
|
c1.Y = temp*A.Y + mx*B.Y + my*C.Y;
|
|
temp = nx*ny;
|
|
c0.X = temp*A.X + nx*B.X + ny*C.X + D.X;
|
|
c0.Y = temp*A.Y + nx*B.Y + ny*C.Y + D.Y;
|
|
|
|
if(j == 0)
|
|
q[j++] = c0;
|
|
q[j].X = c0.X + c1.X/2;
|
|
q[j++].Y = c0.Y + c1.Y/2;
|
|
q[j].X = c0.X + c1.X + c2.X;
|
|
q[j++].Y = c0.Y + c1.Y + c2.Y;
|
|
|
|
pt1 = pt2;
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* This converts lines to quadratic Beziers.
|
|
*
|
|
* [IN] points: the line points
|
|
* [IN] count: the number of line points.
|
|
* [OUT] data: the quadratic Bezier control points.
|
|
*
|
|
* The array size of q must be larger than or equal to 2*(2*count - 1).
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
GpBilinearTransform::ConvertLines(
|
|
const GpPointF* points,
|
|
INT count,
|
|
REALD* data)
|
|
{
|
|
ASSERT(points && data);
|
|
ASSERT(count >= 2);
|
|
|
|
REALD mx, my, nx, ny;
|
|
|
|
GpPointF pt1, pt2;
|
|
|
|
pt1 = points[0];
|
|
|
|
INT j = 0;
|
|
for(INT i = 1; i < count; i++)
|
|
{
|
|
pt2 = points[i];
|
|
mx = (pt2.X - pt1.X)/SrcRect.Width;
|
|
my = (pt2.Y - pt1.Y)/SrcRect.Height;
|
|
nx = (pt1.X - SrcRect.X)/SrcRect.Width;
|
|
ny = (pt1.Y - SrcRect.Y)/SrcRect.Height;
|
|
|
|
GpPointD c0, c1, c2;
|
|
REALD temp;
|
|
|
|
temp = mx*my;
|
|
c2.X = temp*A.X;
|
|
c2.Y = temp*A.Y;
|
|
temp = mx*ny + my*nx;
|
|
c1.X = temp*A.X + mx*B.X + my*C.X;
|
|
c1.Y = temp*A.Y + mx*B.Y + my*C.Y;
|
|
temp = nx*ny;
|
|
c0.X = temp*A.X + nx*B.X + ny*C.X + D.X;
|
|
c0.Y = temp*A.Y + nx*B.Y + ny*C.Y + D.Y;
|
|
|
|
if(j == 0)
|
|
{
|
|
*data++ = c0.X;
|
|
*data++ = c0.Y;
|
|
j++;
|
|
}
|
|
|
|
*data++ = c0.X + c1.X/2;
|
|
*data++ = c0.Y + c1.Y/2;
|
|
|
|
*data++ = c0.X + c1.X + c2.X;
|
|
*data++ = c0.Y + c1.Y + c2.Y;
|
|
|
|
j += 2;
|
|
|
|
pt1 = pt2;
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* This converts cubic Beziers to 6-th order Beziers.
|
|
*
|
|
* [IN] points: the cubic Bezier control points
|
|
* [IN] count: the number of the control points.
|
|
* [OUT] q: the 6-th order Bezier control points.
|
|
*
|
|
* The array size of q must be larger than or equal to 2*count - 1.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
GpBilinearTransform::ConvertCubicBeziers(
|
|
const GpPointF* srcQ,
|
|
INT count,
|
|
GpPointF* q
|
|
)
|
|
{
|
|
ASSERT(srcQ && q);
|
|
ASSERT(count > 3 && (count % 3 == 1));
|
|
|
|
GpPointF a0, a1, a2, a3;
|
|
|
|
INT j = 0;
|
|
for(INT i = 1; i < count; i += 3)
|
|
{
|
|
a0.X = (srcQ[i - 1].X - SrcRect.X)/SrcRect.Width;
|
|
a0.Y = (srcQ[i - 1].Y - SrcRect.Y)/SrcRect.Height;
|
|
a1.X = 3*(srcQ[i].X - srcQ[i - 1].X)/SrcRect.Width;
|
|
a1.Y = 3*(srcQ[i].Y - srcQ[i - 1].Y)/SrcRect.Height;
|
|
a2.X = 3*(srcQ[i - 1].X - srcQ[i].X - srcQ[i].X + srcQ[i + 1].X)/SrcRect.Width;
|
|
a2.Y = 3*(srcQ[i - 1].Y - srcQ[i].Y - srcQ[i].Y + srcQ[i + 1].Y)/SrcRect.Height;
|
|
a3.X = (srcQ[i + 2].X - srcQ[i - 1].X + 3*(srcQ[i].X - srcQ[i + 1].X))/SrcRect.Width;
|
|
a3.Y = (srcQ[i + 2].Y - srcQ[i - 1].Y + 3*(srcQ[i].Y - srcQ[i + 1].Y))/SrcRect.Height;
|
|
|
|
REAL temp;
|
|
GpPointF c[7];
|
|
|
|
temp = a3.X*a3.Y;
|
|
c[6].X = temp*A.X;
|
|
c[6].Y = temp*A.Y;
|
|
|
|
temp = a3.X*a2.Y + a2.X*a3.Y;
|
|
c[5].X = temp*A.X;
|
|
c[5].Y = temp*A.Y;
|
|
|
|
temp = a3.X*a1.Y + a2.X*a2.Y + a1.X*a3.Y;
|
|
c[4].X = temp*A.X;
|
|
c[4].Y = temp*A.Y;
|
|
|
|
temp = a3.X*a0.Y + a2.X*a1.Y + a1.X*a2.Y + a0.X*a3.Y;
|
|
c[3].X = temp*A.X + a3.X*B.X + a3.Y*C.X;
|
|
c[3].Y = temp*A.Y + a3.X*B.Y + a3.Y*C.Y;
|
|
|
|
temp = a2.X*a0.Y + a1.X*a1.Y + a0.X*a2.Y;
|
|
c[2].X = temp*A.X + a2.X*B.X + a2.Y*C.X;
|
|
c[2].Y = temp*A.Y + a2.X*B.Y + a2.Y*C.Y;
|
|
|
|
temp = a1.X*a0.Y + a0.X*a1.Y;
|
|
c[1].X = temp*A.X + a1.X*B.X + a1.Y*C.X;
|
|
c[1].Y = temp*A.Y + a1.X*B.Y + a1.Y*C.Y;
|
|
|
|
temp = a0.X*a0.Y;
|
|
c[0].X = temp*A.X + a0.X*B.X + a0.Y*C.X + D.X;
|
|
c[0].Y = temp*A.Y + a0.X*B.Y + a0.Y*C.Y + D.Y;
|
|
|
|
if(j == 0)
|
|
q[j++] = c[0];
|
|
q[j].X = c[0].X + c[1].X/6;
|
|
q[j++].Y = c[0].Y + c[1].Y/6;
|
|
q[j].X = c[0].X + c[1].X/3 + c[2].X/15;
|
|
q[j++].Y = c[0].Y + c[1].Y/3 + c[2].Y/15;
|
|
q[j].X = c[0].X + c[1].X/2 + c[2].X/5 + c[3].X/20;
|
|
q[j++].Y = c[0].Y + c[1].Y/2 + c[2].Y/5 + c[3].Y/20;
|
|
q[j].X = c[0].X + 2*c[1].X/3 + 2*c[2].X/5 + c[3].X/5 + c[4].X/15;
|
|
q[j++].Y = c[0].Y + 2*c[1].Y/3 + 2*c[2].Y/5 + c[3].Y/5 + c[4].Y/15;
|
|
q[j].X = c[0].X + 5*c[1].X/6 + 2*c[2].X/3 + c[3].X/2 + c[4].X/3 + c[5].X/6;
|
|
q[j++].Y = c[0].Y + 5*c[1].Y/6 + 2*c[2].Y/3 + c[3].Y/2 + c[4].Y/3 + c[5].Y/6;
|
|
q[j].X = c[0].X + c[1].X + c[2].X + c[3].X + c[4].X + c[5].X + c[6].X;
|
|
q[j++].Y = c[0].Y + c[1].Y + c[2].Y + c[3].Y + c[4].Y + c[5].Y + c[6].Y;
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* This converts cubic Beziers to 6-th order Beziers.
|
|
*
|
|
* [IN] points: the cubic Bezier control points
|
|
* [IN] count: the number of the control points.
|
|
* [OUT] q: the 6-th order Bezier control points.
|
|
*
|
|
* The array size of q must be larger than or equal to 2*count - 1.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
GpBilinearTransform::ConvertCubicBeziers(
|
|
const GpPointF* srcQ,
|
|
INT count,
|
|
REALD* data
|
|
)
|
|
{
|
|
ASSERT(srcQ && data);
|
|
ASSERT(count > 3 && (count % 3 == 1));
|
|
|
|
GpPointD a0, a1, a2, a3;
|
|
|
|
INT j = 0;
|
|
for(INT i = 1; i < count; i += 3)
|
|
{
|
|
a0.X = (srcQ[i - 1].X - SrcRect.X)/SrcRect.Width;
|
|
a0.Y = (srcQ[i - 1].Y - SrcRect.Y)/SrcRect.Height;
|
|
a1.X = 3*(srcQ[i].X - srcQ[i - 1].X)/SrcRect.Width;
|
|
a1.Y = 3*(srcQ[i].Y - srcQ[i - 1].Y)/SrcRect.Height;
|
|
a2.X = 3*(srcQ[i - 1].X - srcQ[i].X - srcQ[i].X + srcQ[i + 1].X)/SrcRect.Width;
|
|
a2.Y = 3*(srcQ[i - 1].Y - srcQ[i].Y - srcQ[i].Y + srcQ[i + 1].Y)/SrcRect.Height;
|
|
a3.X = (srcQ[i + 2].X - srcQ[i - 1].X + 3*(srcQ[i].X - srcQ[i + 1].X))/SrcRect.Width;
|
|
a3.Y = (srcQ[i + 2].Y - srcQ[i - 1].Y + 3*(srcQ[i].Y - srcQ[i + 1].Y))/SrcRect.Height;
|
|
|
|
REALD temp;
|
|
GpPointD c[7];
|
|
|
|
temp = a3.X*a3.Y;
|
|
c[6].X = temp*A.X;
|
|
c[6].Y = temp*A.Y;
|
|
|
|
temp = a3.X*a2.Y + a2.X*a3.Y;
|
|
c[5].X = temp*A.X;
|
|
c[5].Y = temp*A.Y;
|
|
|
|
temp = a3.X*a1.Y + a2.X*a2.Y + a1.X*a3.Y;
|
|
c[4].X = temp*A.X;
|
|
c[4].Y = temp*A.Y;
|
|
|
|
temp = a3.X*a0.Y + a2.X*a1.Y + a1.X*a2.Y + a0.X*a3.Y;
|
|
c[3].X = temp*A.X + a3.X*B.X + a3.Y*C.X;
|
|
c[3].Y = temp*A.Y + a3.X*B.Y + a3.Y*C.Y;
|
|
|
|
temp = a2.X*a0.Y + a1.X*a1.Y + a0.X*a2.Y;
|
|
c[2].X = temp*A.X + a2.X*B.X + a2.Y*C.X;
|
|
c[2].Y = temp*A.Y + a2.X*B.Y + a2.Y*C.Y;
|
|
|
|
temp = a1.X*a0.Y + a0.X*a1.Y;
|
|
c[1].X = temp*A.X + a1.X*B.X + a1.Y*C.X;
|
|
c[1].Y = temp*A.Y + a1.X*B.Y + a1.Y*C.Y;
|
|
|
|
temp = a0.X*a0.Y;
|
|
c[0].X = temp*A.X + a0.X*B.X + a0.Y*C.X + D.X;
|
|
c[0].Y = temp*A.Y + a0.X*B.Y + a0.Y*C.Y + D.Y;
|
|
|
|
if(j == 0)
|
|
{
|
|
*data++ = c[0].X;
|
|
*data++ = c[0].Y;
|
|
j++;
|
|
}
|
|
|
|
*data++ = c[0].X + c[1].X/6;
|
|
*data++ = c[0].Y + c[1].Y/6;
|
|
|
|
*data++ = c[0].X + c[1].X/3 + c[2].X/15;
|
|
*data++ = c[0].Y + c[1].Y/3 + c[2].Y/15;
|
|
|
|
*data++ = c[0].X + c[1].X/2 + c[2].X/5 + c[3].X/20;
|
|
*data++ = c[0].Y + c[1].Y/2 + c[2].Y/5 + c[3].Y/20;
|
|
|
|
*data++ = c[0].X + 2*c[1].X/3 + 2*c[2].X/5 + c[3].X/5 + c[4].X/15;
|
|
*data++ = c[0].Y + 2*c[1].Y/3 + 2*c[2].Y/5 + c[3].Y/5 + c[4].Y/15;
|
|
|
|
*data++ = c[0].X + 5*c[1].X/6 + 2*c[2].X/3 + c[3].X/2 + c[4].X/3 + c[5].X/6;
|
|
*data++ = c[0].Y + 5*c[1].Y/6 + 2*c[2].Y/3 + c[3].Y/2 + c[4].Y/3 + c[5].Y/6;
|
|
|
|
*data++ = c[0].X + c[1].X + c[2].X + c[3].X + c[4].X + c[5].X + c[6].X;
|
|
*data++ = c[0].Y + c[1].Y + c[2].Y + c[3].Y + c[4].Y + c[5].Y + c[6].Y;
|
|
|
|
j += 6;
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* P0 P1
|
|
* ------------
|
|
* | \
|
|
* | \
|
|
* | \
|
|
* | \
|
|
* ----------------\
|
|
* P2 P3
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpPerspectiveTransform::GpPerspectiveTransform(
|
|
const GpRectF& rect,
|
|
const GpPointF* pts,
|
|
INT count
|
|
)
|
|
{
|
|
ASSERT(count == 3 || count == 4)
|
|
|
|
SrcRect = rect;
|
|
|
|
REAL left, right, top, bottom;
|
|
|
|
left = right = pts[0].X;
|
|
top = bottom = pts[0].Y;
|
|
|
|
for(INT i = 1; i < count; i++)
|
|
{
|
|
if(pts[i].X < left)
|
|
left = pts[i].X;
|
|
else if(pts[i].X > right)
|
|
right = pts[i].X;
|
|
|
|
if(pts[i].Y < top)
|
|
top = pts[i].Y;
|
|
else if(pts[i].Y > bottom)
|
|
bottom = pts[i].Y;
|
|
}
|
|
|
|
if(count == 4)
|
|
{
|
|
REAL dx1, dx2, dy1, dy2;
|
|
REAL sx, sy, det;
|
|
|
|
dx1 = pts[1].X - pts[3].X;
|
|
dy1 = pts[1].Y - pts[3].Y;
|
|
dx2 = pts[2].X - pts[3].X;
|
|
dy2 = pts[2].Y - pts[3].Y;
|
|
sx = pts[0].X - pts[1].X - pts[2].X + pts[3].X;
|
|
sy = pts[0].Y - pts[1].Y - pts[2].Y + pts[3].Y;
|
|
det = dx1*dy2 - dy1*dx2;
|
|
M02 = (sx*dy2 - sy*dx2)/det;
|
|
M12 = (dx1*sy - dy1*sx)/det;
|
|
}
|
|
else
|
|
{
|
|
// This is a palallelogram.
|
|
|
|
M02 = 0;
|
|
M12 = 0;
|
|
|
|
// Obtain the fourth vertex.
|
|
|
|
REAL x3 = pts[1].X + pts[2].X - pts[0].X;
|
|
REAL y3 = pts[1].Y + pts[2].Y - pts[0].Y;
|
|
|
|
if(x3 < left)
|
|
left = x3;
|
|
else if(x3 > right)
|
|
right = x3;
|
|
|
|
if(y3 < top)
|
|
top = y3;
|
|
else if(y3 > bottom)
|
|
bottom = y3;
|
|
}
|
|
|
|
M00 = pts[1].X - pts[0].X + M02*pts[1].X;
|
|
M01 = pts[1].Y - pts[0].Y + M02*pts[1].Y;
|
|
M10 = pts[2].X - pts[0].X + M12*pts[2].X;
|
|
M11 = pts[2].Y - pts[0].Y + M12*pts[2].Y;
|
|
M20 = pts[0].X;
|
|
M21 = pts[0].Y;
|
|
M22 = 1;
|
|
|
|
DstBounds.X = left;
|
|
DstBounds.Y = top;
|
|
DstBounds.Width = right - left;
|
|
DstBounds.Height = bottom - top;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* This converts the points to the perspective points
|
|
*
|
|
* [IN] points: the point data
|
|
* [IN] count: the number of points.
|
|
* [OUT] q: the perspective point data.
|
|
*
|
|
* The array size of q must be larger than or equal to count.
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
GpPerspectiveTransform::ConvertPoints(
|
|
const GpPointF* points,
|
|
INT count,
|
|
GpPoint3F* q
|
|
)
|
|
{
|
|
ASSERT(points && q);
|
|
ASSERT(count > 0);
|
|
|
|
const GpPointF* pts = points;
|
|
GpPoint3F* qPts = q;
|
|
|
|
while(count > 0)
|
|
{
|
|
REAL u, v;
|
|
|
|
u = (pts->X - SrcRect.X)/SrcRect.Width;
|
|
v = (pts->Y - SrcRect.Y)/SrcRect.Height;
|
|
|
|
qPts->X = u*M00 + v*M10 + M20;
|
|
qPts->Y = u*M01 + v*M11 + M21;
|
|
qPts->Z = u*M02 + v*M12 + 1;
|
|
|
|
pts++;
|
|
qPts++;
|
|
count--;
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
|
|
/**************************************************************************\
|
|
*
|
|
* This converts the points to the perspective points
|
|
*
|
|
* [IN] points: the point data
|
|
* [IN] count: the number of points.
|
|
* [OUT] xpoints: the perspective point data.
|
|
*
|
|
*
|
|
\**************************************************************************/
|
|
|
|
GpStatus
|
|
GpPerspectiveTransform::ConvertPoints(
|
|
const GpPointF* points,
|
|
INT count,
|
|
GpXPoints* xpoints
|
|
)
|
|
{
|
|
|
|
ASSERT(points && xpoints && count > 0);
|
|
|
|
if(!points || !xpoints || count <= 0)
|
|
return InvalidParameter;
|
|
|
|
REALD* data = xpoints->Data;
|
|
/*
|
|
REALD* data = (REALD*) GpMalloc(3*count*sizeof(REALD));
|
|
|
|
|
|
if(!data)
|
|
return OutOfMemory;
|
|
|
|
// Use this data for xpoints.
|
|
|
|
xpoints->SetData(data, 3, count, FALSE);
|
|
*/
|
|
|
|
const GpPointF* pts = points;
|
|
|
|
while(count > 0)
|
|
{
|
|
REAL u, v;
|
|
|
|
u = (pts->X - SrcRect.X)/SrcRect.Width;
|
|
v = (pts->Y - SrcRect.Y)/SrcRect.Height;
|
|
|
|
*data++ = u*M00 + v*M10 + M20;
|
|
*data++ = u*M01 + v*M11 + M21;
|
|
*data++ = u*M02 + v*M12 + 1;
|
|
|
|
pts++;
|
|
count--;
|
|
}
|
|
|
|
return Ok;
|
|
}
|
|
|
|
|
|
class GpQuadData
|
|
{
|
|
GpBilinearTransform BLTransform;
|
|
GpQuadData();
|
|
GpStatus SetQuad(
|
|
const GpRectF& rect,
|
|
const GpPointF* points
|
|
);
|
|
|
|
GpStatus OutputSpan(ARGB* buffer, INT compositingMode,
|
|
INT y, INT &xMin, INT &xMax);
|
|
};
|
|
|
|
GpStatus
|
|
GpQuadData::SetQuad(
|
|
const GpRectF& rect,
|
|
const GpPointF* points
|
|
)
|
|
{
|
|
return BLTransform.SetBilinearTransform(rect, points, 4);
|
|
}
|