WindowsXP-SP1/windows/advcore/gdiplus/engine/entry/quadtransforms.cpp
2020-09-30 16:53:49 +02:00

1139 lines
28 KiB
C++

/**************************************************************************\
*
* 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);
}