/**************************************************************************\ * * Copyright (c) 1999 Microsoft Corporation * * Module Name: * * PathIterator.cpp * * Abstract: * * Implementation of the DpPathTypeIterator and DpPathIterator classes * * Revision History: * * 11/13/1999 ikkof * Created it. * \**************************************************************************/ #include "precomp.hpp" VOID DpPathTypeIterator::SetTypes(const BYTE* types, INT count) { if(types && count > 0) { Types = types; Count = count; // Set Valid flag to TRUE so that CheckValid() // can call NextSubpath() and NextPathType(). SetValid(TRUE); SetValid(CheckValid()); } else Initialize(); } // !!! bhouse CheckValid should not be required // // When we create an iterator, we should be able to provide // data that is consistent and that does not need to be // checked. // Also, CheckValid actually has side effects like determining // whether the path has beziers and calculating sub-path count. // These should be provided at iterator creation time or taken // out of the source path for which the iterator is created. // The reasoning is that this data should persist beyond the // lifespan of the iterator so that we do not have to keep // re-calculating information that can be calculated once and // stored with the path. BOOL DpPathTypeIterator::CheckValid() { if(Count < 0) return FALSE; else if(Count == 0) // Allow an empty path. return TRUE; else { // If Count > 0 and Types = NULL, the path is not valid. if(Types == NULL) return FALSE; } INT startIndex, endIndex; BOOL isClosed; BOOL isValid = TRUE; Rewind(); INT subpathCount = 0; HasBezier = FALSE; ExtendedPath = FALSE; while( NextSubpath(&startIndex, &endIndex, &isClosed) && isValid ) { INT typeStartIndex, typeEndIndex; BYTE pathType = PathPointTypeStart; if(endIndex == startIndex && Count > 1) { // This is a case when the next subpath has only one point. isValid = FALSE; } while( NextPathType(&pathType, &typeStartIndex, &typeEndIndex) && isValid ) { INT count = typeEndIndex - typeStartIndex + 1; INT order = (INT) pathType; switch(pathType) { case PathPointTypeStart: // This happens when there is only one point // in a path. if(count == 1) isValid; else { ASSERT(("Invalid path type.")); isValid = FALSE; } break; // The number of the Bezier points must be in the form of // n*order + 1. case PathPointTypeBezier3: if(count % order != 1) isValid = FALSE; if(!HasBezier) HasBezier = TRUE; if(order != 3 && !ExtendedPath) ExtendedPath = TRUE; break; case PathPointTypeLine: break; default: // Undefined path type. WARNING(("Undefined path type.")); isValid = FALSE; break; } } subpathCount++; } Rewind(); SubpathCount = subpathCount; return isValid; } /**************************************************************************\ * * Function Description: * * Returns the index range of the next subpah. * * Arguments: * * [OUT] startIndex - the starting index of the next subpath. * [OUT] endIndex - the ending index of the next subpath. * [OUT] isClosed - TRUE is the next subpath is closed. * * Return Value: * * Retuns the sumber of points in the next subpath. * Returns 0 if there is no more subpath. * * History: * * 11/01/1999 ikkof * Created it. * \**************************************************************************/ INT DpPathTypeIterator::NextSubpath( INT* startIndex, INT* endIndex, BOOL *isClosed ) { if(!IsValid() || Count == 0) return 0; INT count = Count; if(SubpathEndIndex >= count - 1) return 0; 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; const BYTE* types = Types + i; while(i < count) { // Do the move segments. segmentCount = 0; while(i < count && (*types & PathPointTypePathTypeMask) == PathPointTypeStart) { segmentCount++; if(hasData) { break; } else { // Skip the consequtive move segments in the beginning of a subpath. SubpathStartIndex = i; SubpathEndIndex = SubpathStartIndex; i++; types++; } } if(segmentCount > 0 && hasData) { SubpathEndIndex = i - 1; break; } // Do the non-move segments. segmentCount = 0; while(i < count && (*types & PathPointTypePathTypeMask) != PathPointTypeStart) { i++; types++; 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) { // If there is the close flag this subpath is closed. if((Types[SubpathEndIndex] & PathPointTypeCloseSubpath)) { *isClosed = TRUE; } else *isClosed = FALSE; } else *isClosed = FALSE; // 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 = SubpathStartIndex; TypeEndIndex = -1; // Indicate that this is the first type // in the current subpath. // Set the current index to the start index of the subpath. Index = SubpathStartIndex; return segmentCount; } /**************************************************************************\ * * Function Description: * * Returns the path type, and the index range of the next segment. * This must be used after NextSubpath() is called. This returns only * the next segment within the current subpath. * * Arguments: * * [OUT] pathType - the type of the next segment ignoring the closed * and other flags. * [OUT] startIndex - the starting index of the next subpath. * [OUT] endIndex - the ending index of the next subpath. * * Return Value: * * Retuns the sumber of points in the next segment. * Returns 0 if there is no more segment in the current subpath. * * History: * * 11/01/1999 ikkof * Created it. * \**************************************************************************/ INT DpPathTypeIterator::NextPathType( BYTE* pathType, INT* startIndex, INT* endIndex ) { if(!IsValid() || Count == 0) 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. if(TypeEndIndex <= 0) TypeEndIndex = SubpathStartIndex; // This is the first type. TypeStartIndex = TypeEndIndex; INT i = TypeStartIndex; INT segmentCount = 0; i++; // Go to the next point. const BYTE* types = Types + i; while(i < count) { // Do the move segments. segmentCount = 0; while(i < count && (*types & PathPointTypePathTypeMask) == PathPointTypeStart) { // Move the start and end index. TypeStartIndex = i; TypeEndIndex = TypeStartIndex; i++; types++; segmentCount++; } // Do the non-move segments. segmentCount = 0; BYTE nextType = *types & PathPointTypePathTypeMask; while(i < count && (*types & PathPointTypePathTypeMask) == nextType) { i++; types++; segmentCount++; } if(segmentCount > 0) { TypeEndIndex = TypeStartIndex + segmentCount; *pathType = nextType; break; } } *startIndex = TypeStartIndex; *endIndex = TypeEndIndex; segmentCount = TypeEndIndex - TypeStartIndex + 1; return segmentCount; } /**************************************************************************\ * * Function Description: * * Returns the index range of the next subpah. * * Arguments: * * [OUT] startIndex - the starting index of the next subpath. * [OUT] endIndex - the ending index of the next subpath. * [OUT] isClosed - TRUE is the next subpath is closed. * * Return Value: * * Retuns the sumber of points in the next subpath. * Returns 0 if there is no more subpath. * * History: * * 11/01/1999 ikkof * Created it. * \**************************************************************************/ INT DpPathTypeIterator::NextMarker( INT* startIndex, INT* endIndex ) { if(!IsValid() || Count == 0) return 0; INT count = Count; if(MarkerEndIndex >= count - 1) return 0; INT i; // Set the starting index of the current subpath. if(MarkerEndIndex <= 0) { MarkerStartIndex = 0; i = 1; } else { MarkerStartIndex = MarkerEndIndex + 1; MarkerEndIndex = MarkerStartIndex; i = MarkerStartIndex + 1; } const BYTE* types = Types + i; BOOL hasData = FALSE; while(i < count && (*types & PathPointTypePathMarker) == 0) { i++; types++; } if(i < count) MarkerEndIndex = i; else MarkerEndIndex = count - 1; *startIndex = MarkerStartIndex; *endIndex = MarkerEndIndex; INT segmentCount = MarkerEndIndex - MarkerStartIndex + 1; // Set the start and end index of type to be the starting index of // the current marker. NextSubpath() and NextPathType() will // start from the beginning of the current current marked path. SubpathStartIndex = SubpathEndIndex = MarkerStartIndex; TypeStartIndex = TypeEndIndex = MarkerStartIndex; // Set the current index to the start index of the subpath. Index = MarkerStartIndex; return segmentCount; } /**************************************************************************\ * * Function Description: * * Returns TRUE if the control type in the given index is in dash mode. * Otherwise this returns FALSE. If the given index is outside of * the range this returns FALSE. A user must know the range of the * index beforehand to find the correct information. * \**************************************************************************/ BOOL DpPathTypeIterator::IsDashMode(INT index) { if(!IsValid() || Count == 0 || index < 0 || index >= Count) return FALSE; return (Types[index] & PathPointTypeDashMode); } DpPathIterator::DpPathIterator( const DpPath* path ) { Initialize(); if(path) { const BYTE* types = path->GetPathTypes(); const GpPointF* points = path->GetPathPoints(); INT count = path->GetPointCount(); SetData(points, types, count); } } VOID DpPathIterator::SetData( const GpPointF* points, const BYTE* types, INT count ) { if(points && types && count > 0) { Points = points; Types = types; Count = count; // Set Valid flag to TRUE so that CheckValid() // can call NextSubpath() and NextPathType(). SetValid(TRUE); SetValid(CheckValid()); } else Initialize(); } VOID DpPathIterator::SetData( const DpPath* path ) { if(path) { const BYTE* types = path->GetPathTypes(); const GpPointF* points = path->GetPathPoints(); INT count = path->GetPointCount(); SetData(points, types, count); } else Initialize(); } INT DpPathIterator::NextSubpath( INT* startIndex, INT* endIndex, BOOL *isClosed ) { if(!IsValid() || Count == 0) { return 0; } BOOL closed = TRUE; INT start = 0, end = 0; INT count = DpPathTypeIterator::NextSubpath(&start, &end, &closed); *startIndex = start; *endIndex = end; *isClosed = closed; return count; } INT DpPathIterator::NextSubpath( DpPath* path, BOOL *isClosed ) { if(!IsValid() || Count == 0 || !path) { return 0; } BOOL closed = TRUE; INT start = 0, end = 0; INT count = DpPathTypeIterator::NextSubpath(&start, &end, &closed); GpPathData pathData; pathData.Count = count; pathData.Points = (GpPointF*) &Points[start]; pathData.Types = (BYTE*) &Types[start]; path->SetPathData(&pathData); *isClosed = closed; return count; } INT DpPathIterator::NextMarker( INT* startIndex, INT* endIndex ) { if(!IsValid() || Count == 0) return 0; INT count = DpPathTypeIterator::NextMarker(startIndex, endIndex); return count; } INT DpPathIterator::NextMarker( DpPath* path ) { if(!IsValid() || Count == 0 || !path) return 0; BOOL closed; INT start = 0, end = 0; INT count = DpPathTypeIterator::NextMarker(&start, &end); GpPathData pathData; pathData.Count = count; pathData.Points = (GpPointF*) &Points[start]; pathData.Types = (BYTE*) &Types[start]; path->SetPathData(&pathData); return count; } /**************************************************************************\ * * Function Description: * * This retrieves the next points and types up to the number count. * This does not copy the unnecessary (consequetive) move points. * This fills up the data until the count number is reached. This fills * data beyond the current subpath. * * Arguments: * * [OUT] points - point array to copy the retrieved point data. * [OUT] types - type array to copy the retrieved type data. * [IN] count - the number of points to be copied (request). * * Retrun Value: * Returns the total number of the retrieved points. * \**************************************************************************/ INT DpPathIterator::Enumerate( GpPointF* points, BYTE* types, INT count ) { if(!IsValid() || Count == 0) return 0; INT totalNumber = 0; INT number = EnumerateWithinSubpath(points, types, count); while(number > 0) { totalNumber += number; count -= number; if(count > 0) { points += number; types += number; number = EnumerateWithinSubpath(points, types, count); } else number = 0; } return totalNumber; } /**************************************************************************\ * * Function Description: * * This retrieves the next points and types up to the number count. * This does not copy the unnecessary (consequetive) move points. * This fills up the data until the count number is reached or the * end of the current subopath is reached. * * Arguments: * * [OUT] points - point array to copy the retrieved point data. * [OUT] types - type array to copy the retrieved type data. * [IN] count - the number of points to be copied (request). * * Retrun Value: * Returns the total number of the retrieved points. * \**************************************************************************/ INT DpPathIterator::EnumerateWithinSubpath( GpPointF* points, BYTE* types, INT count ) { if(!IsValid() || Count == 0 || count <= 0 || !points || !types) return 0; INT startIndex, endIndex; BOOL isClosed; INT segmentCount; if(Index == 0) segmentCount = NextSubpath(&startIndex, &endIndex, &isClosed); if(Index > SubpathEndIndex) segmentCount = NextSubpath(&startIndex, &endIndex, &isClosed); else segmentCount = SubpathEndIndex - SubpathStartIndex + 1; if(segmentCount == 0) return 0; // No more segment. count = min(count, SubpathEndIndex - Index + 1); if(count > 0) { GpMemcpy(points, Points + Index, count*sizeof(GpPointF)); GpMemcpy(types, Types + Index, count); Index += count; } return count; } /**************************************************************************\ * * Function Description: * * This copies the data stored in Points and Types arrays * in the index range between startIndex and endIndex. * This may copy unnecessary (consequetive) move points. * startIndex and endIndex must be within the index range of * the original data. Otherwise, this does not copy the data * and returns 0. * * Arguments: * * [OUT] points - point array to copy the retrieved point data. * [OUT] types - type array to copy the retrieved type data. * [IN] startIndex - start index of the origianl data * [IN] endIndex - end index of the origianl data. * * Retrun Value: * Returns the total number of the copied points. * \**************************************************************************/ INT DpPathIterator::CopyData( GpPointF* points, BYTE* types, INT startIndex, INT endIndex ) { if(!IsValid() || Count == 0 || startIndex < 0 || endIndex >= Count || startIndex > endIndex || !points || !types) return 0; INT count = endIndex - startIndex + 1; ASSERT(count > 0); GpMemcpy(points, Points + startIndex, count*sizeof(GpPointF)); GpMemcpy(types, Types + startIndex, count); Index += count; return count; }