/**************************************************************************\ * * Copyright (c) 1999 Microsoft Corporation * * Abstract: * * Implementation of XBezier class and its DDA. * * History: * * 11/08/1999 ikkof * Created it. * \**************************************************************************/ #include "precomp.hpp" // Path types used for advanced path. // !!! [asecchia] // this is a very clumsy hack to enable the XPath stuff to work. // we should have an internal set of enum values that are different // from the external ones. #define PathPointTypeBezier2 2 // quadratic Beizer #define PathPointTypeBezier4 4 // quartic (4th order) Beizer #define PathPointTypeBezier5 5 // quintic (5th order) Bezier #define PathPointTypeBezier6 6 // hexaic (6th order) Bezier GpXPath::GpXPath(const GpPath* path) { InitDefaultState(); if(!path || !path->IsValid()) return; INT count = path->GetPointCount(); const GpPointF *pts = ((GpPath*) path)->GetPathPoints(); const BYTE *types = ((GpPath*) path)->GetPathTypes(); if(pts && types && count > 0) { Types = (BYTE*) GpMalloc(count); XPoints.Count = count; XPoints.Dimension = 2; XPoints.Data = (REALD*) GpMalloc(2*count*sizeof(REALD)); XPoints.IsDataAllocated = TRUE; if(Types && XPoints.Data) { GpMemcpy(Types, types, count); REALD* data = XPoints.Data; for(INT i = 0; i < count; i++) { *data++ = pts->X; *data++ = pts->Y; pts++; } SetValid(TRUE); } FillMode = ((GpPath*) path)->GetFillMode(); } } GpXPath::GpXPath( const GpPath* path, const GpRectF& rect, const GpPointF* points, INT count, WarpMode warpMode ) { InitDefaultState(); if(warpMode == WarpModePerspective) ConvertToPerspectivePath(path, rect, points, count); else if(warpMode == WarpModeBilinear) ConvertToBilinearPath(path, rect, points, count); } GpStatus GpXPath::ConvertToPerspectivePath( const GpPath* path, const GpRectF& rect, const GpPointF* points, INT count ) { ASSERT(path && path->IsValid()); if(!path || !path->IsValid()) return InvalidParameter; // Obtain the path points. const GpPointF* pathPts = ((GpPath*) path)->GetPathPoints(); const BYTE* pathTypes = ((GpPath*) path)->GetPathTypes(); INT pathCount = path->GetPointCount(); BYTE* types = (BYTE*) GpMalloc(pathCount); if(!types) return OutOfMemory; GpMemcpy(types, pathTypes, pathCount); // Set the perspective transform. GpPerspectiveTransform trans(rect, points, count); // Convert the path points to 3D perspective points. REALD* data = (REALD*) GpMalloc(3*pathCount*sizeof(REALD)); if(!data) return OutOfMemory; // Use this data for xpoints. XPoints.Count = pathCount; XPoints.Dimension = 3; XPoints.Data = data; XPoints.IsDataAllocated = TRUE; Types = types; GpStatus status = trans.ConvertPoints(pathPts, pathCount, &XPoints); if(status == Ok) SetValid(TRUE); return status; } GpStatus GpXPath::ConvertToBilinearPath( const GpPath* path, const GpRectF& rect, const GpPointF* points, INT count ) { ASSERT(path && path->IsValid()); if(!path || !path->IsValid()) return InvalidParameter; // Obtain the path points. const GpPointF* pathPts = ((GpPath*) path)->GetPathPoints(); const BYTE* pathTypes = ((GpPath*) path)->GetPathTypes(); INT pathCount = path->GetPointCount(); // The maximum data size of the bilinear transform is // 2*pathCount - 1. Here set it as 2*pathCount. INT dimension = 2; INT maxCount = 2*pathCount; REALD* data = (REALD*) GpMalloc(dimension*maxCount*sizeof(REALD)); BYTE* types = (BYTE*) GpMalloc(maxCount); if(!data || !types) { GpFree(data); GpFree(types); return OutOfMemory; } GpMemset(types, 0, maxCount); // Set the bilinear transform. GpBilinearTransform trans(rect, points, count); DpPathIterator iter(pathPts, pathTypes, pathCount); INT startIndex, endIndex; BOOL isClosed; GpStatus status = Ok; REALD* dataPtr = data; INT totalCount = 0; // Number of control points. while( iter.NextSubpath( &startIndex, &endIndex, &isClosed) && status == Ok ) { INT typeStartIndex, typeEndIndex; BYTE pathType; BOOL isFirstPoint = TRUE; while( iter.NextPathType(&pathType, &typeStartIndex, &typeEndIndex) && status == Ok ) { // Starting point of the current suptype // and the number of points. const GpPointF* pts = pathPts + typeStartIndex; INT typeCount = typeEndIndex - typeStartIndex + 1; switch(pathType) { case PathPointTypeBezier3: trans.ConvertCubicBeziers(pts, typeCount, dataPtr); pts += typeCount - 1; dataPtr += dimension*2*(typeCount - 1); if(isFirstPoint) { *(types + totalCount) = PathPointTypeStart; totalCount++; } GpMemset(types + totalCount, PathPointTypeBezier6, 2*(typeCount - 1)); totalCount += 2*(typeCount - 1); isFirstPoint = FALSE; break; case PathPointTypeLine: trans.ConvertLines(pts, typeCount, dataPtr); pts += typeCount - 1; dataPtr += dimension*2*(typeCount - 1); if(isFirstPoint) { *(types + totalCount) = PathPointTypeStart; totalCount++; } GpMemset(types + totalCount, PathPointTypeBezier2, 2*(typeCount - 1)); totalCount += 2*(typeCount - 1); isFirstPoint = FALSE; break; case PathPointTypeStart: case PathPointTypeBezier2: case PathPointTypeBezier4: case PathPointTypeBezier5: case PathPointTypeBezier6: default: // Should not have any of those types in GpPath. ASSERT(0); break; } } } Types = types; XPoints.Count = totalCount; XPoints.Dimension = dimension; XPoints.Data = data; XPoints.IsDataAllocated = TRUE; SetValid(TRUE); return Ok; } GpStatus GpXPath::Flatten( DynByteArray* flattenTypes, DynPointFArray* flattenPoints, const GpMatrix *matrix ) { GpStatus status = Ok; ASSERT(matrix); FPUStateSaver fpuState; // Setup the FPU state. flattenPoints->Reset(FALSE); flattenTypes->Reset(FALSE); INT dimension = XPoints.Dimension; REALD* data = XPoints.Data; GpXPathIterator iter(this); INT startIndex, endIndex; BOOL isClosed; GpPointF* ptsBuffer = NULL; while( iter.NextSubpath( &startIndex, &endIndex, &isClosed) && status == Ok ) { INT typeStartIndex, typeEndIndex; BYTE pathType; BOOL isFirstPoint = TRUE; while( iter.NextPathType(&pathType, &typeStartIndex, &typeEndIndex) && status == Ok ) { INT count, index; BYTE* types; GpPointF* pts; switch(pathType) { case PathPointTypeStart: break; case PathPointTypeBezier2: case PathPointTypeBezier3: case PathPointTypeBezier4: case PathPointTypeBezier5: case PathPointTypeBezier6: { BOOL dontCopy = FALSE; GpXBezier bezier; GpXPoints xpoints; INT order; xpoints.SetData( data + typeStartIndex*dimension, dimension, typeEndIndex - typeStartIndex + 1, dontCopy // Don't copy the data. ); order = (INT) pathType; if(bezier.SetBeziers(order, xpoints) == Ok) { // Flatten() flattens Bezier. // The flattened points are already transformed. DynPointFArray bezierFlattenPts; bezier.Flatten(&bezierFlattenPts, matrix); // count = bezier.GetFlattenCount(); count = bezierFlattenPts.GetCount(); // Check if there is already the first point. if(!isFirstPoint) count--; // Don't add the first point. if (count > 0) { if((types = flattenTypes->AddMultiple(count)) != NULL) { // pts = bezier.GetFlattenData(); pts = bezierFlattenPts.GetDataBuffer(); if(!isFirstPoint) pts++; // Skip the first point. flattenPoints->AddMultiple(pts, count); GpMemset(types, PathPointTypeLine, count); if(isFirstPoint) types[0] = PathPointTypeStart; isFirstPoint = FALSE; } else status = OutOfMemory; } } else status =InvalidParameter; } break; case PathPointTypeLine: default: count = typeEndIndex - typeStartIndex + 1; if(!isFirstPoint) count--; if((types = flattenTypes->AddMultiple(count)) != NULL) { // Set the type. GpMemset(types, PathPointTypeLine, count); if(isFirstPoint) types[0] = PathPointTypeStart; // Get the first data. REALD* dataPtr = data + typeStartIndex*dimension; if(!isFirstPoint) dataPtr += dimension; // Skip the first point. // Allocate the point buffer to save // for the flatten points. pts = (GpPointF*) GpRealloc(ptsBuffer, count*sizeof(GpPointF)); if(!pts) { status = OutOfMemory; break; } else ptsBuffer = pts; // Copy the data GpPointF* ptsPtr = pts; for(INT k = 0; k < count; k++) { // Simply copy the first 2 elments // of the data as the x and y component. REALD x, y, w; x = *dataPtr++; y = *dataPtr++; // Do the perspective projection if // dimension is higher than 2. if(dimension > 2) { w = *dataPtr; x /= w; y /= w; } ptsPtr->X = TOREAL(x); ptsPtr->Y = TOREAL(y); ptsPtr++; // Skip the rest. if(dimension > 2) dataPtr += (dimension - 2); } // Add to the flatten points. index = flattenPoints->GetCount(); flattenPoints->AddMultiple(pts, count); // Get the data biffer of the flatten points. pts = flattenPoints->GetDataBuffer(); // Transform the newly added points. matrix->Transform(pts + index, count); isFirstPoint = FALSE; } break; } } // This is the end of the current subpath. Close subpath // if necessary. if(isClosed) { BYTE* typeBuffer = flattenTypes->GetDataBuffer(); INT lastCount = flattenTypes->GetCount(); typeBuffer[lastCount - 1] |= PathPointTypeCloseSubpath; } } if(ptsBuffer) GpFree(ptsBuffer); return status; } /**************************************************************************\ * * GpXPathIterator class * * 11/08/1999 ikkof * Created it. * \**************************************************************************/ GpXPathIterator::GpXPathIterator(GpXPath* xpath) { Initialize(); if(xpath && xpath->IsValid()) { TotalCount = xpath->GetPointCount(); XPoints.SetData( xpath->GetPathPoints(), xpath->GetPointDimension(), TotalCount, FALSE // Don't copy data. ); Types = xpath->GetPathTypes(); } // Check if this path is valid. if( XPoints.Data && XPoints.Dimension > 0 && XPoints.Count > 0 && Types && TotalCount > 0 ) { SetValid(TRUE); } } VOID GpXPathIterator::Initialize() { // XPath = NULL; Types = NULL; TotalCount = 0; Index = 0; SubpathStartIndex = 0; SubpathEndIndex = 0; TypeStartIndex = 0; TypeEndIndex = 0; SetValid(FALSE); } INT GpXPathIterator::Enumerate(GpXPoints* xpoints, BYTE* types) { if(!IsValid()) return 0; ASSERT(xpoints && types); if(xpoints == NULL || types == NULL) return 0; INT inputSize = xpoints->Dimension*xpoints->Count; ASSERT(xpoints->Data && inputSize > 0); if(xpoints->Data == NULL || inputSize <= 0) return 0; if(Index >= TotalCount) return 0; // Make sure the resultant dimension is the same as // the internal dimension. INT dimension = XPoints.Dimension; if(xpoints->Dimension != dimension) { xpoints->Dimension = dimension; xpoints->Count = inputSize/dimension; } INT number = min(TotalCount - Index, xpoints->Count); GpMemcpy( xpoints->Data, XPoints.Data + Index*dimension, number*dimension*sizeof(REALD)); GpMemcpy(types, Types + Index, number*sizeof(BYTE)); Index += number; return number; } INT GpXPathIterator::NextSubpath(INT* startIndex, INT* endIndex, BOOL *isClosed) { if(!IsValid()) return 0; INT count = TotalCount; if(SubpathEndIndex >= count - 1) return 0; const BYTE* types = Types; INT i; // Set the starting index of the current subpath. if(SubpathEndIndex == 0) { SubpathStartIndex = 0; i = 1; } else { SubpathStartIndex = SubpathEndIndex + 1; SubpathEndIndex = SubpathStartIndex; i = SubpathStartIndex + 1; } BOOL hasData = FALSE; INT segmentCount = 0; while(i < count) { // Do the move segments. segmentCount = 0; while(i < count && (types[i] & PathPointTypePathTypeMask) == PathPointTypeStart) { segmentCount++; if(hasData) { break; } else { SubpathStartIndex = i; SubpathEndIndex = SubpathStartIndex; i++; } } if(segmentCount > 0 && hasData) { SubpathEndIndex = i - 1; break; } // Do the non-move segments. segmentCount = 0; BYTE nextType = types[i] & PathPointTypePathTypeMask; while(i < count && (types[i] & PathPointTypePathTypeMask) == nextType) { i++; segmentCount++; } if(segmentCount > 0) { hasData = TRUE; } } *startIndex = SubpathStartIndex; if(i >= count) SubpathEndIndex = count - 1; // The last subpath. *endIndex = SubpathEndIndex; segmentCount = SubpathEndIndex - SubpathStartIndex + 1; if(segmentCount <= 1) // Start and end point is the same. segmentCount = 0; if(segmentCount > 1) { // If there is the close flag or the start and end points match, // this subpath is closed. if( (types[SubpathEndIndex] & PathPointTypeCloseSubpath) || XPoints.AreEqualPoints(SubpathStartIndex, SubpathEndIndex) ) { *isClosed = TRUE; } else *isClosed = FALSE; } else *isClosed = FALSE; // Set the current index to the starting index of the current subpath. Index = SubpathStartIndex; // Set the start and end index of type to be the starting index of // the current subpath. NextPathType() will start from the // beginning of the current subpath. TypeStartIndex = TypeEndIndex = SubpathStartIndex; return segmentCount; } INT GpXPathIterator::EnumerateSubpath( GpXPoints* xpoints, BYTE* types ) { if(!IsValid()) return 0; ASSERT(xpoints && types); if(xpoints == NULL || types == NULL) return 0; INT inputSize = xpoints->Dimension*xpoints->Count; ASSERT(xpoints->Data && inputSize > 0); if(xpoints->Data == NULL || inputSize <= 0) return 0; if(Index > SubpathEndIndex) return 0; // Make sure the resultant dimension is the same as // the internal dimension. INT dimension = XPoints.Dimension; if(xpoints->Dimension != dimension) { xpoints->Dimension = dimension; xpoints->Count = inputSize/dimension; } INT number = min(SubpathEndIndex - Index + 1, xpoints->Count); GpMemcpy( xpoints->Data, XPoints.Data + Index*dimension, number*dimension*sizeof(REALD)); GpMemcpy(types, Types + Index, number*sizeof(BYTE)); Index += number; return number; } INT GpXPathIterator::NextPathType( BYTE* pathType, INT* startIndex, INT* endIndex ) { if(!IsValid()) return 0; if(TypeEndIndex >= SubpathEndIndex) return 0; // There is no more segment in the current subpath. INT count = SubpathEndIndex + 1; // Limit for the ending index. const BYTE* types = Types; TypeStartIndex = TypeEndIndex; INT i = TypeStartIndex; INT segmentCount = 0; i++; // Go to the next point. while(i < count) { // Do the move segments. segmentCount = 0; while(i < count && (types[i] & PathPointTypePathTypeMask) == PathPointTypeStart) { // Move the start and end index. TypeStartIndex = i; TypeEndIndex = TypeStartIndex; i++; segmentCount++; } // Do the non-move segments. segmentCount = 0; BYTE nextType = types[i] & PathPointTypePathTypeMask; while(i < count && (types[i] & PathPointTypePathTypeMask) == nextType) { i++; segmentCount++; } if(segmentCount > 0) { TypeEndIndex = TypeStartIndex + segmentCount; *pathType = nextType; break; } } *startIndex = TypeStartIndex; *endIndex = TypeEndIndex; segmentCount = TypeEndIndex - TypeStartIndex + 1; if(segmentCount <= 1) { // Start and End type index is the same. This means there is // no more segment left. segmentCount = 0; } // Set the current index to the starting index of the current subpath. Index = TypeStartIndex; return segmentCount; } INT GpXPathIterator::EnumeratePathType( GpXPoints* xpoints, BYTE* types ) { if(!IsValid()) return 0; ASSERT(xpoints && types); if(xpoints == NULL || types == NULL) return 0; INT inputSize = xpoints->Dimension*xpoints->Count; ASSERT(xpoints->Data && inputSize > 0); if(xpoints->Data == NULL || inputSize <= 0) return 0; if(Index > TypeEndIndex) return 0; // Make sure the resultant dimension is the same as // the internal dimension. INT dimension = XPoints.Dimension; if(xpoints->Dimension != dimension) { xpoints->Dimension = dimension; xpoints->Count = inputSize/dimension; } INT number = min(TypeEndIndex - Index + 1, xpoints->Count); GpMemcpy( xpoints->Data, XPoints.Data + Index*dimension, number*dimension*sizeof(REALD)); GpMemcpy(types, Types + Index, number*sizeof(BYTE)); Index += number; return number; }