Quellcodebibliothek Statistik Leitseite products/Sources/formale Sprachen/C/Firefox/third_party/rust/wpf-gpu-raster/src/   (Browser von der Mozilla Stiftung Version 136.0.1©)  Datei vom 10.2.2025 mit Größe 55 kB image not shown  

SSL aarasterizer.rs   Interaktion und
Portierbarkeitunbekannt

 
// 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.54unsichere Verbindung  Übersetzung europäischer Sprachen durch Browser  ]