// Licensed to the .NET Foundation under one or more agreements. // The .NET Foundation licenses this file to you under the MIT license. // See the LICENSE file in the project root for more information. #![allow(unused_parens)] use std::cell::Cell; use crate::aacoverage::c_nShift; use crate::bezier::CMILBezier; use crate::helpers::Int32x32To64; use crate::matrix::CMILMatrix; use crate::nullable_ref::Ref; use crate::real::CFloatFPU; //use crate::types::PathPointType::*; use crate::types::*; use typed_arena_nomut::Arena; const S_OK: HRESULT = 0; #[cfg(debug_assertions)] macro_rules! EDGE_STORE_STACK_NUMBER { () => { 10 }; } #[cfg(debug_assertions)] macro_rules! EDGE_STORE_ALLOCATION_NUMBER { () => { 11 }; } #[cfg(debug_assertions)] macro_rules! INACTIVE_LIST_NUMBER { () => { 12 }; } #[cfg(debug_assertions)] macro_rules! ENUMERATE_BUFFER_NUMBER { () => { 15 }; } #[cfg(not(debug_assertions))] macro_rules! EDGE_STORE_STACK_NUMBER { () => { (1600 / std::mem::size_of::()) }; } #[cfg(not(debug_assertions))] macro_rules! EDGE_STORE_ALLOCATION_NUMBER { () => { (4032 / std::mem::size_of::()) as u32 }; } #[cfg(not(debug_assertions))] macro_rules! INACTIVE_LIST_NUMBER { () => { EDGE_STORE_STACK_NUMBER!() }; } #[cfg(not(debug_assertions))] macro_rules! ENUMERATE_BUFFER_NUMBER { () => { 32 }; } macro_rules! ASSERTACTIVELIST { ($list: expr, $y: expr) => { // make sure we use y even in non debug builds _ = $y; #[cfg(debug_assertions)] AssertActiveList($list, $y); }; } pub struct CEdge<'a> { pub Next: Cell>>, // Next active edge (don't check for NULL, // look for tail sentinel instead) pub X: Cell, // Current X location pub Dx: INT, // X increment pub Error: Cell, // Current DDA error pub ErrorUp: INT, // Error increment pub ErrorDown: INT, // Error decrement when the error rolls over pub StartY: INT, // Y-row start pub EndY: INT, // Y-row end pub WindingDirection: INT, // -1 or 1 } impl<'a> std::default::Default for CEdge<'a> { fn default() -> Self { Self { Next: Cell::new(unsafe { Ref::null() }), X: Default::default(), Dx: Default::default(), Error: Default::default(), ErrorUp: Default::default(), ErrorDown: Default::default(), StartY: Default::default(), EndY: Default::default(), WindingDirection: Default::default(), } } } // We the inactive-array separate from the edge allocations so that // we can more easily do in-place sorts on it: #[derive(Clone)] pub struct CInactiveEdge<'a> { Edge: Ref<'a, CEdge<'a>>, // Associated edge Yx: LONGLONG, // Sorting key, StartY and X packed into an lword } impl<'a> Default for CInactiveEdge<'a> { fn default() -> Self { Self { Edge: unsafe { Ref::null() }, Yx: Default::default(), } } } macro_rules! ASSERTACTIVELISTORDER { ($list: expr) => { #[cfg(debug_assertions)] AssertActiveListOrder($list) }; } /**************************************************************************\ * * Function Description: * * Advance DDA and update active edge list * * Created: * * 06/20/2003 ashrafm * \**************************************************************************/ pub fn AdvanceDDAAndUpdateActiveEdgeList(nSubpixelYCurrent: INT, pEdgeActiveList: Ref) { let mut outOfOrder = false; let mut pEdgePrevious: Ref = pEdgeActiveList; let mut pEdgeCurrent: Ref = pEdgeActiveList.Next.get(); let mut prevX = pEdgePrevious.X.get(); // Advance DDA and update edge list loop { if (pEdgeCurrent.EndY <= nSubpixelYCurrent) { // If we've hit the sentinel, our work here is done: if (pEdgeCurrent.EndY == INT::MIN) { break; // ============> } // This edge is stale, remove it from the list: pEdgeCurrent = pEdgeCurrent.Next.get(); pEdgePrevious.Next.set(pEdgeCurrent); continue; // ============> } // Advance the DDA: let mut x = pEdgeCurrent.X.get() + pEdgeCurrent.Dx; let mut error = pEdgeCurrent.Error.get() + pEdgeCurrent.ErrorUp; if (error >= 0) { error -= pEdgeCurrent.ErrorDown; x += 1; } pEdgeCurrent.X.set(x); pEdgeCurrent.Error.set(error); // Is this entry out-of-order with respect to the previous one? outOfOrder |= (prevX > x); // Advance: pEdgePrevious = pEdgeCurrent; pEdgeCurrent = pEdgeCurrent.Next.get(); prevX = x; } // It turns out that having any out-of-order edges at this point // is extremely rare in practice, so only call the bubble-sort // if it's truly needed. // // NOTE: If you're looking at this code trying to fix a bug where // the edges are out of order when the filler is called, do // NOT simply change the code to always do the bubble-sort! // Instead, figure out what caused our 'outOfOrder' logic // above to get messed up. if (outOfOrder) { SortActiveEdges(pEdgeActiveList); } ASSERTACTIVELISTORDER!(pEdgeActiveList); } //+---------------------------------------------------------------------------- // // // Description: Code for rasterizing the fill of a path. // // >>>> Note that some of this code is duplicated in hw\hwrasterizer.cpp, // >>>> so changes to this file may need to propagate. // // pursue reduced code duplication // // This option may potentially increase performance for many // paths that have edges adjacent at their top point and cover // more than one span. The code has been tested, but performance // has not been thoroughly investigated. const SORT_EDGES_INCLUDING_SLOPE: bool = false; ///////////////////////////////////////////////////////////////////////// // The x86 C compiler insists on making a divide and modulus operation // into two DIVs, when it can in fact be done in one. So we use this // macro. // // Note: QUOTIENT_REMAINDER implicitly takes unsigned arguments. // // QUOTIENT_REMAINDER_64_32 takes a 64-bit numerator and produces 32-bit // results. macro_rules! QUOTIENT_REMAINDER { ($ulNumerator: ident, $ulDenominator: ident, $ulQuotient: ident, $ulRemainder: ident) => { $ulQuotient = (($ulNumerator as ULONG) / ($ulDenominator as ULONG)) as _; $ulRemainder = (($ulNumerator as ULONG) % ($ulDenominator as ULONG)) as _; }; } macro_rules! QUOTIENT_REMAINDER_64_32 { ($ulNumerator: ident, $ulDenominator: ident, $ulQuotient: ident, $ulRemainder: ident) => { $ulQuotient = (($ulNumerator as ULONGLONG) / (($ulDenominator as ULONG) as ULONGLONG)) as _; $ulRemainder = (($ulNumerator as ULONGLONG) % (($ulDenominator as ULONG) as ULONGLONG)) as _; }; } // SWAP macro: macro_rules! SWAP { ($temp: ident, $a: expr, $b: expr) => { $temp = $a; $a = $b; $b = $temp; }; } struct CEdgeAllocation { Next: *mut CEdgeAllocation, // Next allocation batch (may be NULL) /*__field_range(<=, EDGE_STORE_ALLOCATION_NUMBER)*/ Count: UINT, EdgeArray: [CEdge<'static>; EDGE_STORE_STACK_NUMBER!()], } impl Default for CEdgeAllocation { fn default() -> Self { Self { Next: NULL(), Count: Default::default(), EdgeArray: [(); EDGE_STORE_STACK_NUMBER!()].map(|_| Default::default()) } } } /* pub struct CEdgeStore { /* __field_range(<=, UINT_MAX - 2) */ TotalCount: UINT, // Total edge count in store /* __field_range(<=, CurrentBuffer->Count) */ CurrentRemaining: UINT, // How much room remains in current buffer CurrentBuffer: *mut CEdgeAllocation, // Current buffer CurrentEdge: *mut CEdge<'static>, // Current edge in current buffer Enumerator: *mut CEdgeAllocation, // For enumerating all the edges EdgeHead: CEdgeAllocation, // Our built-in allocation } impl Default for CEdgeStore { fn default() -> Self { Self { TotalCount: Default::default(), CurrentRemaining: Default::default(), CurrentBuffer: NULL(), CurrentEdge: NULL(), Enumerator: NULL(), EdgeHead: Default::default() } } } impl CEdgeStore { pub fn init(&mut self) { self.TotalCount = 0; self.CurrentBuffer = NULL(); self.CurrentEdge = NULL(); self.Enumerator = NULL(); self.CurrentRemaining = EDGE_STORE_STACK_NUMBER!() as u32; self.EdgeHead = CEdgeAllocation { Count: EDGE_STORE_STACK_NUMBER!() as u32, // hack to work around limited Default implementation for arrays EdgeArray: [(); EDGE_STORE_STACK_NUMBER!()].map(|_| Default::default()), Next: NULL(), }; self.CurrentBuffer = &mut self.EdgeHead; self.CurrentEdge = &mut self.EdgeHead.EdgeArray[0]; } } impl Drop for CEdgeStore { fn drop(&mut self) { // Free our allocation list, skipping the head, which is not // dynamically allocated: let mut allocation: *mut CEdgeAllocation = self.EdgeHead.Next; while (allocation != NULL()) { let next = unsafe { (*allocation).Next }; drop(unsafe { Box::from_raw(allocation) }); allocation = next; } } } impl CEdgeStore { pub fn StartEnumeration(&mut self) -> UINT { unsafe { self.Enumerator = &mut self.EdgeHead; // Update the count and make sure nothing more gets added (in // part because this Count would have to be re-computed): (*self.CurrentBuffer).Count -= self.CurrentRemaining; // This will never overflow because NextAddBuffer always ensures that TotalCount has // space remaining to describe the capacity of all new buffers added to the edge list. self.TotalCount += (*self.CurrentBuffer).Count; // Prevent this from being called again, because bad things would // happen: self.CurrentBuffer = NULL(); return self.TotalCount; } } fn Enumerate( &mut self, /*__deref_out_ecount(*ppEndEdge - *ppStartEdge)*/ ppStartEdge: &mut *mut CEdge, /* __deref_out_ecount(0) */ ppEndEdge: &mut *mut CEdge, ) -> bool { /* unsafe { let enumerator: *mut CEdgeAllocation = self.Enumerator; // Might return startEdge == endEdge: *ppStartEdge = &mut (*enumerator).EdgeArray[0]; *ppEndEdge = (*ppStartEdge).offset((*enumerator).Count as isize); self.Enumerator = (*enumerator).Next; return (self.Enumerator != NULL()); }*/ return true; } fn StartAddBuffer( &self, /*__deref_out_ecount(*puRemaining)*/ ppCurrentEdge: &mut *mut CEdge, /* __deref_out_range(==, (this->CurrentRemaining)) */ puRemaining: &mut UINT, ) { panic!() // *ppCurrentEdge = self.CurrentEdge; // *puRemaining = self.CurrentRemaining; } fn EndAddBuffer( &mut self, /*__in_ecount(remaining) */ pCurrentEdge: *mut CEdge, /* __range(0, (this->CurrentBuffer->Count)) */ remaining: UINT, ) { panic!(); //self.CurrentEdge = pCurrentEdge; //self.CurrentRemaining = remaining; } // Disable instrumentation checks within all methods of this class //SET_MILINSTRUMENTATION_FLAGS(MILINSTRUMENTATIONFLAGS_DONOTHING); } /**************************************************************************\ * * Function Description: * * The edge initializer is out of room in its current 'store' buffer; * get it a new one. * * Created: * * 03/25/2000 andrewgo * \**************************************************************************/ impl CEdgeStore { fn NextAddBuffer( &mut self, /*__deref_out_ecount(*puRemaining)*/ ppCurrentEdge: &mut *mut CEdge, puRemaining: &mut UINT, ) -> HRESULT { panic!() /* unsafe { let hr = S_OK; let mut cNewTotalCount: u32 = 0; // The caller has completely filled up this chunk: assert!(*puRemaining == 0); // Check to make sure that "TotalCount" will be able to represent the current capacity cNewTotalCount = self.TotalCount + (*self.CurrentBuffer).Count; if (cNewTotalCount < self.TotalCount) { return WINCODEC_ERR_VALUEOVERFLOW; } // And that it can represent the new capacity as well, with at least 2 to spare. // This "magic" 2 comes from the fact that the usage pattern of this class has callers // needing to allocate space for TotalCount + 2 edges. if (cNewTotalCount + ((EDGE_STORE_ALLOCATION_NUMBER!() + 2) as UINT) < cNewTotalCount) { return WINCODEC_ERR_VALUEOVERFLOW; } // We have to grow our data structure by adding a new buffer // and adding it to the list: let newBuffer: *mut CEdgeAllocation = Box::into_raw(Box::::new(Default::default()));/*static_cast (GpMalloc(Mt(MAARasterizerEdge), sizeof(CEdgeAllocation) + sizeof(CEdge) * (EDGE_STORE_ALLOCATION_NUMBER - EDGE_STORE_STACK_NUMBER)));*/ IFCOOM!(newBuffer); (*newBuffer).Next = NULL(); (*newBuffer).Count = EDGE_STORE_STACK_NUMBER!() as u32;//EDGE_STORE_ALLOCATION_NUMBER!() as u32; self.TotalCount = cNewTotalCount; (*self.CurrentBuffer).Next = newBuffer; self.CurrentBuffer = newBuffer; self.CurrentEdge = &mut (*newBuffer).EdgeArray[0]; *ppCurrentEdge = panic!();//self.CurrentEdge; self.CurrentRemaining = EDGE_STORE_STACK_NUMBER!() as u32;//EDGE_STORE_ALLOCATION_NUMBER!(); *puRemaining = EDGE_STORE_STACK_NUMBER!() as u32; //EDGE_STORE_ALLOCATION_NUMBER!(); return hr; }*/ } } */ /**************************************************************************\ * * Function Description: * * Some debug code for verifying the state of the active edge list. * * Created: * * 03/25/2000 andrewgo * \**************************************************************************/ pub fn AssertActiveList(mut list: Ref, yCurrent: INT) -> bool { let mut b = true; let mut activeCount = 0; assert!((*list).X.get() == INT::MIN); b &= ((*list).X.get() == INT::MIN); // Skip the head sentinel: list = (*list).Next.get(); while ((*list).X.get() != INT::MAX) { assert!((*list).X.get() != INT::MIN); b &= ((*list).X.get() != INT::MIN); assert!((*list).X <= (*(*list).Next.get()).X); b &= ((*list).X <= (*(*list).Next.get()).X); assert!(((*list).StartY <= yCurrent) && (yCurrent < (*list).EndY)); b &= (((*list).StartY <= yCurrent) && (yCurrent < (*list).EndY)); activeCount += 1; list = (*list).Next.get(); } assert!((*list).X.get() == INT::MAX); b &= ((*list).X.get() == INT::MAX); // There should always be a multiple of 2 edges in the active list. // // NOTE: If you hit this assert, do NOT simply comment it out! // It usually means that all the edges didn't get initialized // properly. For every scan-line, there has to be a left edge // and a right edge (or a multiple thereof). So if you give // even a single bad edge to the edge initializer (or you miss // one), you'll probably hit this assert. assert!((activeCount & 1) == 0); b &= ((activeCount & 1) == 0); return (b); } /**************************************************************************\ * * Function Description: * * Some debug code for verifying the state of the active edge list. * * Created: * * 03/25/2000 andrewgo * \**************************************************************************/ fn AssertActiveListOrder(mut list: Ref) { assert!((*list).X.get() == INT::MIN); // Skip the head sentinel: list = (*list).Next.get(); while ((*list).X.get() != INT::MAX) { assert!((*list).X.get() != INT::MIN); assert!((*list).X <= (*(*list).Next.get()).X); list = (*list).Next.get(); } assert!((*list).X.get() == INT::MAX); } /**************************************************************************\ * * Function Description: * * Clip the edge vertically. * * We've pulled this routine out-of-line from InitializeEdges mainly * because it needs to call inline Asm, and when there is in-line * Asm in a routine the compiler generally does a much less efficient * job optimizing the whole routine. InitializeEdges is rather * performance critical, so we avoid polluting the whole routine * by having this functionality out-of-line. * * Created: * * 03/25/2000 andrewgo * \**************************************************************************/ fn ClipEdge(edgeBuffer: &mut CEdge, yClipTopInteger: INT, dMOriginal: INT) { let mut xDelta: INT; let mut error: INT; // Cases where bigNumerator will exceed 32-bits in precision // will be rare, but could happen, and we can't fall over // in those cases. let dN: INT = edgeBuffer.ErrorDown; let mut bigNumerator: LONGLONG = Int32x32To64(dMOriginal, yClipTopInteger - edgeBuffer.StartY) + (edgeBuffer.Error.get() + dN) as LONGLONG; if (bigNumerator >= 0) { QUOTIENT_REMAINDER_64_32!(bigNumerator, dN, xDelta, error); } else { bigNumerator = -bigNumerator; QUOTIENT_REMAINDER_64_32!(bigNumerator, dN, xDelta, error); xDelta = -xDelta; if (error != 0) { xDelta -= 1; error = dN - error; } } // Update the edge data structure with the results: edgeBuffer.StartY = yClipTopInteger; edgeBuffer.X.set(edgeBuffer.X.get() + xDelta); edgeBuffer.Error.set(error - dN); // Renormalize error } pub fn CheckValidRange28_4(x: f32, y: f32) -> bool { // // We want coordinates in the 28.4 range in the end. The matrix we get // as input includes the scale by 16 to get to 28.4, so we want to // ensure that we are in integer range. Assuming a sign bit and // five bits for the rasterizer working range, we want coordinates in the // -2^26 to 2^26. // // Note that the 5-bit requirement comes from the // implementation of InitializeEdges. // (See line with "error -= dN * (16 - (xStart & 15))") // // Anti-aliasing uses another c_nShift bits, so we get a // desired range of -2^(26-c_nShift) to 2^(26-c_nShift) // let rPixelCoordinateMax = (1 << (26 - c_nShift)) as f32; let rPixelCoordinateMin = -rPixelCoordinateMax; return x <= rPixelCoordinateMax && x >= rPixelCoordinateMin && y <= rPixelCoordinateMax && y >= rPixelCoordinateMin; } //+----------------------------------------------------------------------------- // // Function: TransformRasterizerPointsTo28_4 // // Synopsis: // Transform rasterizer points to 28.4. If overflow occurs, return that // information. // //------------------------------------------------------------------------------ fn TransformRasterizerPointsTo28_4( pmat: &CMILMatrix, // Transform to take us to 28.4 mut pPtsSource: &[MilPoint2F], // Source points mut cPoints: UINT, // Count of points mut pPtsDest: &mut [POINT], // Destination points ) -> HRESULT { let hr = S_OK; debug_assert!(cPoints > 0); while { // // Transform coordinates // let rPixelX = (pmat.GetM11() * pPtsSource[0].X) + (pmat.GetM21() * pPtsSource[0].Y) + pmat.GetDx(); let rPixelY = (pmat.GetM12() * pPtsSource[0].X) + (pmat.GetM22() * pPtsSource[0].Y) + pmat.GetDy(); // // Check for NaNs or overflow // if !CheckValidRange28_4(rPixelX, rPixelY) { return WGXERR_BADNUMBER; } // // Assign coordinates // pPtsDest[0].x = CFloatFPU::Round(rPixelX); pPtsDest[0].y = CFloatFPU::Round(rPixelY); pPtsDest = &mut pPtsDest[1..]; pPtsSource = &pPtsSource[1..]; cPoints -= 1; cPoints != 0 } {} return hr; } pub fn AppendScaleToMatrix(pmat: &mut CMILMatrix, scaleX: REAL, scaleY: REAL) { pmat.SetM11(pmat.GetM11() * scaleX); pmat.SetM21(pmat.GetM21() * scaleX); pmat.SetM12(pmat.GetM12() * scaleY); pmat.SetM22(pmat.GetM22() * scaleY); pmat.SetDx(pmat.GetDx() * scaleX); pmat.SetDy(pmat.GetDy() * scaleY); } /**************************************************************************\ * * Function Description: * * Add edges to the edge list. * * Created: * * 03/25/2000 andrewgo * \**************************************************************************/ pub struct CInitializeEdgesContext<'a> { pub MaxY: INT, // Maximum 'y' found, should be INT_MIN on // first call to 'InitializeEdges' pub ClipRect: Option<&'a RECT>, // Bounding clip rectangle in 28.4 format pub Store: &'a Arena>, // Where to stick the edges pub AntiAliasMode: MilAntiAliasMode, } impl<'a> CInitializeEdgesContext<'a> { pub fn new(store: &'a Arena>) -> Self { CInitializeEdgesContext { MaxY: Default::default(), ClipRect: Default::default(), Store: store, AntiAliasMode: MilAntiAliasMode::None } } } fn InitializeEdges( pEdgeContext: &mut CInitializeEdgesContext, /*__inout_ecount(vertexCount)*/ mut pointArray: &mut [POINT], // Points to a 28.4 array of size 'vertexCount' // Note that we may modify the contents! /*__in_range(>=, 2)*/ vertexCount: UINT, ) -> HRESULT { // Disable instrumentation checks for this function //SET_MILINSTRUMENTATION_FLAGS(MILINSTRUMENTATIONFLAGS_DONOTHING); let hr = S_OK; let mut xStart; let mut yStart; let mut yStartInteger; let mut yEndInteger; let mut dMOriginal; let mut dM: i32; let mut dN: i32; let mut dX: i32; let mut errorUp: i32; let mut quotient: i32; let mut remainder: i32; let mut error: i32; let mut windingDirection; //let mut edgeBuffer: *mut CEdge = NULL(); let bufferCount: UINT = 0; let mut yClipTopInteger; let mut yClipTop; let mut yClipBottom; let mut xClipLeft; let mut xClipRight; let mut yMax = pEdgeContext.MaxY; let _store = &mut pEdgeContext.Store; let clipRect = pEdgeContext.ClipRect; let mut edgeCount = vertexCount - 1; assert!(edgeCount >= 1); if let Some(clipRect) = clipRect { yClipTopInteger = clipRect.top >> 4; yClipTop = clipRect.top; yClipBottom = clipRect.bottom; xClipLeft = clipRect.left; xClipRight = clipRect.right; assert!(yClipBottom > 0); assert!(yClipTop <= yClipBottom); } else { yClipBottom = 0; yClipTopInteger = INT::MIN >> c_nShift; // These 3 values are only used when clipRect is non-NULL yClipTop = 0; xClipLeft = 0; xClipRight = 0; } if (pEdgeContext.AntiAliasMode != MilAntiAliasMode::None) { // If antialiasing, apply the supersampling scaling here before we // calculate the DDAs. We do this here and not in the Matrix // transform we give to FixedPointPathEnumerate mainly so that the // Bezier flattener can continue to operate in its optimal 28.4 // format. // // PS#856364-2003/07/01-JasonHa Remove pixel center fixup // // We also apply a half-pixel offset here so that the antialiasing // code can assume that the pixel centers are at half-pixel // coordinates, not on the integer coordinates. let mut point = &mut *pointArray; let mut i = vertexCount; while { point[0].x = (point[0].x + 8) << c_nShift; point[0].y = (point[0].y + 8) << c_nShift; point = &mut point[1..]; i -= 1; i != 0 } {} yClipTopInteger <<= c_nShift; yClipTop <<= c_nShift; yClipBottom <<= c_nShift; xClipLeft <<= c_nShift; xClipRight <<= c_nShift; } // Make 'yClipBottom' inclusive by subtracting off one pixel // (keeping in mind that we're in 28.4 device space): yClipBottom -= 16; // Warm up the store where we keep the edge data: //store.StartAddBuffer(&mut edgeBuffer, &mut bufferCount); 'outer: loop { loop { // Handle trivial rejection: if (yClipBottom >= 0) { // Throw out any edges that are above or below the clipping. // This has to be a precise check, because we assume later // on that every edge intersects in the vertical dimension // with the clip rectangle. That asssumption is made in two // places: // // 1. When we sort the edges, we assume either zero edges, // or two or more. // 2. When we start the DDAs, we assume either zero edges, // or that there's at least one scan of DDAs to output. // // Plus, of course, it's less efficient if we let things // through. // // Note that 'yClipBottom' is inclusive: let clipHigh = ((pointArray[0]).y <= yClipTop) && ((pointArray[1]).y <= yClipTop); let clipLow = ((pointArray[0]).y > yClipBottom) && ((pointArray[1]).y > yClipBottom); #[cfg(debug_assertions)] { let (mut yRectTop, mut yRectBottom, y0, y1, yTop, yBottom); // Getting the trivial rejection code right is tricky. // So on checked builds let's verify that we're doing it // correctly, using a different approach: let mut clipped = false; if let Some(clipRect) = clipRect { yRectTop = clipRect.top >> 4; yRectBottom = clipRect.bottom >> 4; if (pEdgeContext.AntiAliasMode != MilAntiAliasMode::None) { yRectTop <<= c_nShift; yRectBottom <<= c_nShift; } y0 = ((pointArray[0]).y + 15) >> 4; y1 = ((pointArray[1]).y + 15) >> 4; yTop = y0.min(y1); yBottom = y0.max(y1); clipped = ((yTop >= yRectBottom) || (yBottom <= yRectTop)); } assert!(clipped == (clipHigh || clipLow)); } if (clipHigh || clipLow) { break; // ======================> } if (edgeCount > 1) { // Here we'll collapse two edges down to one if both are // to the left or to the right of the clipping rectangle. if (((pointArray[0]).x < xClipLeft) && ((pointArray[1]).x < xClipLeft) && ((pointArray[2]).x < xClipLeft)) { // Note this is one reason why 'pointArray' can't be 'const': pointArray[1] = pointArray[0]; break; // ======================> } if (((pointArray[0]).x > xClipRight) && ((pointArray[1]).x > xClipRight) && ((pointArray[2]).x > xClipRight)) { // Note this is one reason why 'pointArray' can't be 'const': pointArray[1] = pointArray[0]; break; // ======================> } } } dM = (pointArray[1]).x - (pointArray[0]).x; dN = (pointArray[1]).y - (pointArray[0]).y; if (dN >= 0) { // The vector points downward: xStart = (pointArray[0]).x; yStart = (pointArray[0]).y; yStartInteger = (yStart + 15) >> 4; yEndInteger = ((pointArray[1]).y + 15) >> 4; windingDirection = 1; } else { // The vector points upward, so we have to essentially // 'swap' the end points: dN = -dN; dM = -dM; xStart = (pointArray[1]).x; yStart = (pointArray[1]).y; yStartInteger = (yStart + 15) >> 4; yEndInteger = ((pointArray[0]).y + 15) >> 4; windingDirection = -1; } // The edgeBuffer must span an integer y-value in order to be // added to the edgeBuffer list. This serves to get rid of // horizontal edges, which cause trouble for our divides. if (yEndInteger > yStartInteger) { yMax = yMax.max(yEndInteger); dMOriginal = dM; if (dM < 0) { dM = -dM; if (dM < dN) // Can't be '<=' { dX = -1; errorUp = dN - dM; } else { QUOTIENT_REMAINDER!(dM, dN, quotient, remainder); dX = -quotient; errorUp = remainder; if (remainder > 0) { dX = -quotient - 1; errorUp = dN - remainder; } } } else { if (dM < dN) { dX = 0; errorUp = dM; } else { QUOTIENT_REMAINDER!(dM, dN, quotient, remainder); dX = quotient; errorUp = remainder; } } error = -1; // Error is initially zero (add dN - 1 for // the ceiling, but subtract off dN so that // we can check the sign instead of comparing // to dN) if ((yStart & 15) != 0) { // Advance to the next integer y coordinate let mut i = 16 - (yStart & 15); while i != 0 { xStart += dX; error += errorUp; if (error >= 0) { error -= dN; xStart += 1; } i -= 1; } } if ((xStart & 15) != 0) { error -= dN * (16 - (xStart & 15)); xStart += 15; // We'll want the ceiling in just a bit... } xStart >>= 4; error >>= 4; if (bufferCount == 0) { //IFC!(store.NextAddBuffer(&mut edgeBuffer, &mut bufferCount)); } let mut edge = CEdge { Next: Cell::new(unsafe { Ref::null() } ), X: Cell::new(xStart), Dx: dX, Error: Cell::new(error), ErrorUp: errorUp, ErrorDown: dN, WindingDirection: windingDirection, StartY: yStartInteger, EndY: yEndInteger,// Exclusive of end }; assert!(error < 0); // Here we handle the case where the edge starts above the // clipping rectangle, and we need to jump down in the 'y' // direction to the first unclipped scan-line. // // Consequently, we advance the DDA here: if (yClipTopInteger > yStartInteger) { assert!(edge.EndY > yClipTopInteger); ClipEdge(&mut edge, yClipTopInteger, dMOriginal); } // Advance to handle the next edge: //edgeBuffer = unsafe { edgeBuffer.offset(1) }; pEdgeContext.Store.alloc(edge); //bufferCount -= 1; } break; } pointArray = &mut pointArray[1..]; edgeCount -= 1; if edgeCount == 0 { break 'outer; } } // We're done with this batch. Let the store know how many edges // we ended up with: //store.EndAddBuffer(edgeBuffer, bufferCount); pEdgeContext.MaxY = yMax; return hr; } /**************************************************************************\ * * Function Description: * * Does complete parameter checking on the 'types' array of a path. * * Created: * * 03/25/2000 andrewgo * \**************************************************************************/ fn ValidatePathTypes(typesArray: &[BYTE], mut count: INT) -> bool { let mut types = typesArray; if (count == 0) { return (true); } loop { // The first point in every subpath has to be an unadorned // 'start' point: if ((types[0] & PathPointTypePathTypeMask) != PathPointTypeStart) { TraceTag!((tagMILWarning, "Bad subpath start")); return (false); } // Advance to the first point after the 'start' point: count -= 1; if (count == 0) { TraceTag!((tagMILWarning, "Path ended after start-path")); return (false); } if ((types[1] & PathPointTypePathTypeMask) == PathPointTypeStart) { TraceTag!((tagMILWarning, "Can't have a start followed by a start!")); return (false); } // Process runs of lines and Bezier curves: loop { match (types[1] & PathPointTypePathTypeMask) { PathPointTypeLine => { types = &types[1..]; count -= 1; if (count == 0) { return (true); } } PathPointTypeBezier => { if (count < 3) { TraceTag!(( tagMILWarning, "Path ended before multiple of 3 Bezier points" )); return (false); } if ((types[1] & PathPointTypePathTypeMask) != PathPointTypeBezier) { TraceTag!((tagMILWarning, "Bad subpath start")); return (false); } types = &types[1..]; count -= 3; if (count == 0) { return (true); } } _ => { TraceTag!((tagMILWarning, "Illegal type")); return (false); } } // A close-subpath marker or a start-subpath marker marks the // end of a subpath: if !(!((types[0] & PathPointTypeCloseSubpath) != 0) && ((types[1] & PathPointTypePathTypeMask) != PathPointTypeStart)) { types = &types[1..]; break; } } } } /**************************************************************************\ * * Function Description: * * Some debug code for verifying the path. * * Created: * * 03/25/2000 andrewgo * \**************************************************************************/ macro_rules! ASSERTPATH { ($types: expr, $points: expr) => { #[cfg(debug_assertions)] AssertPath($types, $points) }; } fn AssertPath(rgTypes: &[BYTE], cPoints: UINT) { // Make sure that the 'types' array is well-formed, otherwise we // may fall over in the FixedPointPathEnumerate function. // // NOTE: If you hit this assert, DO NOT SIMPLY COMMENT THIS Assert OUT! // // Instead, fix the ValidatePathTypes code if it's letting through // valid paths, or (more likely) fix the code that's letting bogus // paths through. The FixedPointPathEnumerate routine has some // subtle assumptions that require the path to be perfectly valid! // // No internal code should be producing invalid paths, and all // paths created by the application must be parameter checked! assert!(ValidatePathTypes(rgTypes, cPoints as INT)); } //+---------------------------------------------------------------------------- // // Member: // FixedPointPathEnumerate // // Synopsis: // // Enumerate the path. // // NOTE: The 'enumerateFunction' function is allowed to modify the // contents of our call-back buffer! (This is mainly done to allow // 'InitializeEdges' to be simpler for some clipping trivial // rejection cases.) // // NOTICE-2006/03/22-milesc This function was initially built to be a // general path enumeration function. However, we were only using it for // one specific purpose... for Initializing edges of a path to be filled. // In doing security work, I simplified this function to just do edge // initialization. The name is therefore now overly general. I have kept // the name to be a reminder that this function has been written to be // more general than would otherwise be evident. // pub fn FixedPointPathEnumerate( rgpt: &[POINT], rgTypes: &[BYTE], cPoints: UINT, _matrix: &CMILMatrix, clipRect: Option<&RECT>, // In scaled 28.4 format enumerateContext: &mut CInitializeEdgesContext, ) -> HRESULT { let hr = S_OK; let mut bufferStart: [POINT; ENUMERATE_BUFFER_NUMBER!()] = [(); ENUMERATE_BUFFER_NUMBER!()].map(|_| Default::default()); let mut bezierBuffer: [POINT; 4] = Default::default(); let mut buffer: &mut [POINT]; let mut bufferSize: usize; let mut startFigure: [POINT; 1] = Default::default(); // The current point offset in rgpt let mut iPoint: usize; // The current type offset in rgTypes let mut iType: usize; let mut runSize: usize; let mut thisCount: usize; let mut isMore: bool = false; let mut xLast: INT; let mut yLast: INT; ASSERTPATH!(rgTypes, cPoints); // Every valid subpath has at least two vertices in it, hence the // check of 'cPoints - 1': iPoint = 0; iType = 0; assert!(cPoints > 1); while (iPoint < cPoints as usize - 1) { assert!((rgTypes[iType] & PathPointTypePathTypeMask) == PathPointTypeStart); assert!((rgTypes[iType + 1] & PathPointTypePathTypeMask) != PathPointTypeStart); // Add the start point to the beginning of the batch, and // remember it for handling the close figure: startFigure[0] = rgpt[iPoint]; bufferStart[0].x = startFigure[0].x; bufferStart[0].y = startFigure[0].y; let bufferStartPtr = bufferStart.as_ptr(); buffer = &mut bufferStart[1..]; bufferSize = ENUMERATE_BUFFER_NUMBER!() - 1; // We need to enter our loop with 'iType' pointing one past // the start figure: iPoint += 1; iType += 1; while { // Try finding a run of lines: if ((rgTypes[iType] & PathPointTypePathTypeMask) == PathPointTypeLine) { runSize = 1; while ((iPoint + runSize < cPoints as usize) && ((rgTypes[iType + runSize] & PathPointTypePathTypeMask) == PathPointTypeLine)) { runSize += 1; } // Okay, we've found a run of lines. Break it up into our // buffer size: loop { thisCount = bufferSize.min(runSize); buffer[0 .. thisCount].copy_from_slice(&rgpt[iPoint .. iPoint + thisCount]); __analysis_assume!( buffer + bufferSize == bufferStart + ENUMERATE_BUFFER_NUMBER ); assert!(buffer.as_ptr().wrapping_offset(bufferSize as isize) == bufferStartPtr.wrapping_offset(ENUMERATE_BUFFER_NUMBER!()) ); iPoint += thisCount; iType += thisCount; buffer = &mut buffer[thisCount..]; runSize -= thisCount; bufferSize -= thisCount; if (bufferSize > 0) { break; } xLast = bufferStart[ENUMERATE_BUFFER_NUMBER!() - 1].x; yLast = bufferStart[ENUMERATE_BUFFER_NUMBER!() - 1].y; IFR!(InitializeEdges( enumerateContext, &mut bufferStart, ENUMERATE_BUFFER_NUMBER!() )); // Continue the last vertex as the first in the new batch: bufferStart[0].x = xLast; bufferStart[0].y = yLast; buffer = &mut bufferStart[1..]; bufferSize = ENUMERATE_BUFFER_NUMBER!() - 1; if !(runSize != 0) { break; } } } else { assert!(iPoint + 3 <= cPoints as usize); assert!((rgTypes[iType] & PathPointTypePathTypeMask) == PathPointTypeBezier); bezierBuffer.copy_from_slice(&rgpt[(iPoint - 1) .. iPoint + 3]); // Prepare for the next iteration: iPoint += 3; iType += 1; // Process the Bezier: let mut bezier = CMILBezier::new(&bezierBuffer, clipRect); loop { thisCount = bezier.Flatten(buffer, &mut isMore) as usize; __analysis_assume!( buffer + bufferSize == bufferStart + ENUMERATE_BUFFER_NUMBER!() ); assert!(buffer.as_ptr().wrapping_offset(bufferSize as isize) == bufferStartPtr.wrapping_offset(ENUMERATE_BUFFER_NUMBER!())); buffer = &mut buffer[thisCount..]; bufferSize -= thisCount; if (bufferSize > 0) { break; } xLast = bufferStart[ENUMERATE_BUFFER_NUMBER!() - 1].x; yLast = bufferStart[ENUMERATE_BUFFER_NUMBER!() - 1].y; IFR!(InitializeEdges( enumerateContext, &mut bufferStart, ENUMERATE_BUFFER_NUMBER!() )); // Continue the last vertex as the first in the new batch: bufferStart[0].x = xLast; bufferStart[0].y = yLast; buffer = &mut bufferStart[1..]; bufferSize = ENUMERATE_BUFFER_NUMBER!() - 1; if !isMore { break; } } } ((iPoint < cPoints as usize) && ((rgTypes[iType] & PathPointTypePathTypeMask) != PathPointTypeStart)) } {} // Okay, the subpath is done. But we still have to handle the // 'close figure' (which is implicit for a fill): // Add the close-figure point: buffer[0].x = startFigure[0].x; buffer[0].y = startFigure[0].y; bufferSize -= 1; // We have to flush anything we might have in the batch, unless // there's only one vertex in there! (The latter case may happen // for the stroke case with no close figure if we just flushed a // batch.) // If we're flattening, we must call the one additional time to // correctly handle closing the subpath, even if there is only // one entry in the batch. The flattening callback handles the // one point case and closes the subpath properly without adding // extraneous points. let verticesInBatch = ENUMERATE_BUFFER_NUMBER!() - bufferSize; if (verticesInBatch > 1) { IFR!(InitializeEdges( enumerateContext, &mut bufferStart, (verticesInBatch) as UINT )); } } return hr; } /**************************************************************************\ * * Function Description: * * We want to sort in the inactive list; the primary key is 'y', and * the secondary key is 'x'. This routine creates a single LONGLONG * key that represents both. * * Created: * * 03/25/2000 andrewgo * \**************************************************************************/ fn YX(x: INT, y: INT, p: &mut LONGLONG) { // Bias 'x' by INT_MAX so that it's effectively unsigned: /* reinterpret_cast(p)->HighPart = y; reinterpret_cast(p)->LowPart = x + INT_MAX; */ *p = (((y as u64) << 32) | (((x as i64 + i32::MAX as i64) as u64) & 0xffffffff)) as i64; } /**************************************************************************\ * * Function Description: * * Recursive function to quick-sort our inactive edge list. Note that * for performance, the results are not completely sorted; an insertion * sort has to be run after the quicksort in order to do a lighter-weight * sort of the subtables. * * Created: * * 03/25/2000 andrewgo * \**************************************************************************/ const QUICKSORT_THRESHOLD: isize = 8; fn QuickSortEdges(inactive: &mut [CInactiveEdge], /*__inout_xcount(f - l + 1 elements)*/ f: usize, /*__inout_xcount(array starts at f)*/ l: usize, ) { let mut e: Ref; let mut y: LONGLONG; let mut first: LONGLONG; let mut second: LONGLONG; let mut last: LONGLONG; // Find the median of the first, middle, and last elements: let m = f + ((l - f) >> 1); SWAP!(y, inactive[f + 1].Yx, inactive[m].Yx); SWAP!(e, inactive[f + 1].Edge, inactive[m].Edge); if {second = inactive[f + 1].Yx; second > {last = inactive[l].Yx; last}} { inactive[f + 1].Yx = last; inactive[l].Yx = second; SWAP!(e, inactive[f + 1].Edge, inactive[l].Edge); } if {first = inactive[f].Yx; first} > {last = inactive[l].Yx; last} { inactive[f].Yx = last; inactive[l].Yx = first; SWAP!(e, inactive[f].Edge, inactive[l].Edge); } if {second = inactive[f + 1].Yx; second} > {first = inactive[f].Yx; first} { inactive[f + 1].Yx = first; inactive[f].Yx = second; SWAP!(e, inactive[f + 1].Edge, inactive[f].Edge); } // f->Yx is now the desired median, and (f + 1)->Yx <= f->Yx <= l->Yx debug_assert!((inactive[f + 1].Yx <= inactive[f].Yx) && (inactive[f].Yx <= inactive[l].Yx)); let median = inactive[f].Yx; let mut i = f + 2; while (inactive[i].Yx < median) { i += 1; } let mut j = l - 1; while (inactive[j].Yx > median) { j -= 1; } while (i < j) { SWAP!(y, inactive[i].Yx, inactive[j].Yx); SWAP!(e, inactive[i].Edge, inactive[j].Edge); while { i = i + 1; inactive[i].Yx < median } {} while { j = j - 1 ; inactive[j].Yx > median } {} } SWAP!(y, inactive[f].Yx, inactive[j].Yx); SWAP!(e, inactive[f].Edge, inactive[j].Edge); let a = j - f; let b = l - j; // Use less stack space by recursing on the shorter subtable. Also, // have the less-overhead insertion-sort handle small subtables. if (a <= b) { if (a > QUICKSORT_THRESHOLD as usize) { // 'a' is the smallest, so do it first: QuickSortEdges(inactive, f, j - 1); QuickSortEdges(inactive, j + 1, l); } else if (b > QUICKSORT_THRESHOLD as usize) { QuickSortEdges(inactive, j + 1, l); } } else { if (b > QUICKSORT_THRESHOLD as usize) { // 'b' is the smallest, so do it first: QuickSortEdges(inactive, j + 1 , l); QuickSortEdges(inactive, f, j + 1); } else if (a > QUICKSORT_THRESHOLD as usize) { QuickSortEdges(inactive, f, j -1); } } } /**************************************************************************\ * * Function Description: * * Do a sort of the inactive table using an insertion-sort. Expects * large tables to have already been sorted via quick-sort. * * Created: * * 03/25/2000 andrewgo * \**************************************************************************/ fn InsertionSortEdges( /* __inout_xcount(count forward & -1 back)*/ mut inactive: &mut [CInactiveEdge], mut count: INT, ) { let mut e: Ref; let mut y: LONGLONG; let mut yPrevious: LONGLONG; assert!(inactive[0].Yx == i64::MIN); assert!(count >= 2); //inactive = &mut inactive[1..]; let mut indx = 2; // Skip first entry (by definition it's already in order!) count -= 1; while { let mut p = indx; // Copy the current stuff to temporary variables to make a hole: e = (inactive[indx]).Edge; y = (inactive[indx]).Yx; // Shift everything one slot to the right (effectively moving // the hole one position to the left): while (y < {yPrevious = inactive[p-1].Yx; yPrevious}) { inactive[p].Yx = yPrevious; inactive[p].Edge = inactive[p-1].Edge; p -= 1; } // Drop the temporary stuff into the final hole: inactive[p].Yx = y; inactive[p].Edge = e; // The quicksort should have ensured that we don't have to move // any entry terribly far: assert!((indx - p) <= QUICKSORT_THRESHOLD as usize); indx += 1; count -= 1; count != 0 } {} } /**************************************************************************\ * * Function Description: * * Assert the state of the inactive array. * * Created: * * 03/25/2000 andrewgo * \**************************************************************************/ macro_rules! ASSERTINACTIVEARRAY { ($inactive: expr, $count: expr) => { #[cfg(debug_assertions)] AssertInactiveArray($inactive, $count); }; } fn AssertInactiveArray( /*__in_ecount(count)*/ mut inactive: &[CInactiveEdge], // Annotation should allow the -1 element mut count: INT, ) { // Verify the head: /*#if !ANALYSIS*/ // #if needed because prefast don't know that the -1 element is avaliable assert!(inactive[0].Yx == i64::MIN); /*#endif*/ assert!(inactive[1].Yx != i64::MIN); while { let mut yx: LONGLONG = 0; YX((*inactive[1].Edge).X.get(), (*inactive[1].Edge).StartY, &mut yx); assert!(inactive[1].Yx == yx); /*#if !ANALYSIS*/ // #if needed because tools don't know that the -1 element is avaliable assert!(inactive[1].Yx >= inactive[0].Yx); /*#endif*/ inactive = &inactive[1..]; count -= 1; count != 0 } {} // Verify that the tail is setup appropriately: assert!((*inactive[1].Edge).StartY == INT::MAX); } /**************************************************************************\ * * Function Description: * * Initialize and sort the inactive array. * * Returns: * * 'y' value of topmost edge. * * Created: * * 03/25/2000 andrewgo * \**************************************************************************/ pub fn InitializeInactiveArray<'a>( pEdgeStore: &'a Arena>, /*__in_ecount(count+2)*/ rgInactiveArray: &mut [CInactiveEdge<'a>], count: UINT, tailEdge: Ref<'a, CEdge<'a>> // Tail sentinel for inactive list ) -> INT { let rgInactiveArrayPtr = rgInactiveArray.as_mut_ptr(); // First initialize the inactive array. Skip the first entry, // which we reserve as a head sentinel for the insertion sort: let mut pInactiveEdge = &mut rgInactiveArray[1..]; for e in pEdgeStore.iter() { pInactiveEdge[0].Edge = Ref::new(e); YX(e.X.get(), e.StartY, &mut pInactiveEdge[0].Yx); pInactiveEdge = &mut pInactiveEdge[1..]; } assert!(unsafe { pInactiveEdge.as_mut_ptr().offset_from(rgInactiveArrayPtr) } as UINT == count + 1); // Add the tail, which is used when reading back the array. This // is why we had to allocate the array as 'count + 1': pInactiveEdge[0].Edge = tailEdge; // Add the head, which is used for the insertion sort. This is why // we had to allocate the array as 'count + 2': rgInactiveArray[0].Yx = i64::MIN; // Only invoke the quicksort routine if it's worth the overhead: if (count as isize > QUICKSORT_THRESHOLD) { // Quick-sort this, skipping the first and last elements, // which are sentinels. // // We do 'inactiveArray + count' to be inclusive of the last // element: QuickSortEdges(rgInactiveArray, 1, count as usize); } // Do a quick sort to handle the mostly sorted result: InsertionSortEdges(rgInactiveArray, count as i32); ASSERTINACTIVEARRAY!(rgInactiveArray, count as i32); // Return the 'y' value of the topmost edge: return (*rgInactiveArray[1].Edge).StartY; } /**************************************************************************\ * * Function Description: * * Insert edges into the active edge list. * * Created: * * 03/25/2000 andrewgo * \**************************************************************************/ pub fn InsertNewEdges<'a>( mut pActiveList: Ref<'a, CEdge<'a>>, iCurrentY: INT, /*__deref_inout_xcount(array terminated by an edge with StartY != iCurrentY)*/ ppInactiveEdge: &'a mut [CInactiveEdge<'a>], pYNextInactive: &mut INT, // will be INT_MAX when no more ) -> &'a mut [CInactiveEdge<'a>] { let mut inactive: &mut [CInactiveEdge] = ppInactiveEdge; assert!((*inactive[0].Edge).StartY == iCurrentY); while { let newActive: Ref = inactive[0].Edge; // The activeList edge list sentinel has X = INT_MAX, so this always // terminates: while ((*(*pActiveList).Next.get()).X < (*newActive).X) { pActiveList = (*pActiveList).Next.get(); } if SORT_EDGES_INCLUDING_SLOPE { // The activeList edge list sentinel has Dx = INT_MAX, so this always // terminates: while (((*(*pActiveList).Next.get()).X == (*newActive).X) && ((*(*pActiveList).Next.get()).Dx < (*newActive).Dx)) { pActiveList = (*pActiveList).Next.get(); } } (*newActive).Next.set((*pActiveList).Next.get()); (*pActiveList).Next.set(newActive); inactive = &mut inactive[1..]; (*(inactive[0]).Edge).StartY == iCurrentY } {} *pYNextInactive = (*(inactive[0]).Edge).StartY; return inactive; } /**************************************************************************\ * * Function Description: * * Sort the edges so that they're in ascending 'x' order. * * We use a bubble-sort for this stage, because edges maintain good * locality and don't often switch ordering positions. * * Created: * * 03/25/2000 andrewgo * \**************************************************************************/ fn SortActiveEdges(list: Ref) { let mut swapOccurred: bool; let mut tmp: Ref; // We should never be called with an empty active edge list: assert!((*(*list).Next.get()).X.get() != INT::MAX); while { swapOccurred = false; let mut previous = list; let mut current = (*list).Next.get(); let mut next = (*current).Next.get(); let mut nextX = (*next).X.get(); while { if (nextX < (*current).X.get()) { swapOccurred = true; (*previous).Next.set(next); (*current).Next.set((*next).Next.get()); (*next).Next.set(current); SWAP!(tmp, next, current); } previous = current; current = next; next = (*next).Next.get(); nextX = (*next).X.get(); nextX != INT::MAX } {} swapOccurred } {} }