Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 


SSL aarasterizer.rs   Sprache: unbekannt

 
// 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::<CEdge>()) }; }
#[cfg(not(debug_assertions))]
macro_rules! EDGE_STORE_ALLOCATION_NUMBER { () => { (4032 / std::mem::size_of::<CEdge>()) 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<Ref<'a, CEdge<'a>>>, // Next active edge (don't check for NULL,
    //   look for tail sentinel instead)
    pub X: Cell<INT>,                // Current X location
    pub Dx: INT,               // X increment
    pub Error: Cell<INT>,            // 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<CEdge>) {
        let mut outOfOrder = false;
        let mut pEdgePrevious: Ref<CEdge> = pEdgeActiveList;
        let mut pEdgeCurrent: Ref<CEdge> = 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::<CEdgeAllocation>::new(Default::default()));/*static_cast<CEdgeAllocation*>
                                                            (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<CEdge>, 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<CEdge>) {

    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<CEdge<'a>>,  // Where to stick the edges
    pub AntiAliasMode: MilAntiAliasMode,
}

impl<'a> CInitializeEdgesContext<'a> {
    pub fn new(store: &'a Arena<CEdge<'a>>) -> 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<LARGE_INTEGER*>(p)->HighPart = y;
    reinterpret_cast<LARGE_INTEGER*>(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<CEdge>;
    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<CEdge>;
    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<CEdge<'a>>,
    /*__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<CEdge> = 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<CEdge>) {

    let mut swapOccurred: bool;
    let mut tmp: Ref<CEdge>;

    // 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
    } {}

}

[ Verzeichnis aufwärts0.49unsichere Verbindung  Übersetzung europäischer Sprachen durch Browser  ]

                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Software

     Produkte
     Quellcodebibliothek

Aktivitäten

     Artikel über Sicherheit
     Anleitung zur Aktivierung von SSL

Muße

     Gedichte
     Musik
     Bilder

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Monitoring

Montastic status badge