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