// 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. //------------------------------------------------------------------------------ // use std::cell::Cell; use typed_arena_nomut::Arena; // // Description: // Coverage buffer implementation #[cfg(debug_assertions)] use crate::aarasterizer::AssertActiveList; use crate::aarasterizer::CEdge; use crate::nullable_ref::Ref; use crate::types::*; //struct CEdge; //struct CInactiveEdge; //------------------------------------------------------------------------- // // TrapezoidalAA only supports 8x8 mode, so the shifts/masks are all // constants. Also, since we must be symmetrical, x and y shifts are // merged into one shift unlike the implementation in aarasterizer. // //------------------------------------------------------------------------- pub const c_nShift: INT = 3; pub const c_nShiftSize: INT = 8; pub const c_nShiftSizeSquared: INT = c_nShiftSize * c_nShiftSize; pub const c_nHalfShiftSize: INT = 4; pub const c_nShiftMask: INT = 7; //pub const c_rShiftSize: f32 = 8.0; //pub const c_rHalfShiftSize: f32 = 4.0; pub const c_rInvShiftSize: f32 = 1.0/8.0; pub const c_antiAliasMode: MilAntiAliasMode = MilAntiAliasMode::EightByEight; // // Interval coverage descriptor for our antialiased filler // pub struct CCoverageInterval<'a> { pub m_pNext: Cell>>, // m_pNext interval (look for sentinel, not NULL) pub m_nPixelX: Cell, // Interval's left edge (m_pNext->X is the right edge) pub m_nCoverage: Cell, // Pixel coverage for interval } impl<'a> Default for CCoverageInterval<'a> { fn default() -> Self { Self { m_pNext: Cell::new(unsafe { Ref::null() } ), m_nPixelX: Default::default(), m_nCoverage: Default::default() } } } // Define our on-stack storage use. The 'free' versions are nicely tuned // to avoid allocations in most common scenarios, while at the same time // not chewing up toooo much stack space. // // We make the debug versions small so that we hit the 'grow' cases more // frequently, for better testing: #[cfg(debug_assertions)] // Must be at least 6 now: 4 for the "minus4" logic in hwrasterizer.*, and then // 1 each for the head and tail sentinels (since their allocation doesn't use Grow). const INTERVAL_BUFFER_NUMBER: usize = 8; #[cfg(not(debug_assertions))] const INTERVAL_BUFFER_NUMBER: usize = 32; // // Allocator structure for the antialiased fill interval data // struct CCoverageIntervalBuffer<'a> { m_pNext: Cell>>, m_interval: [CCoverageInterval<'a>; INTERVAL_BUFFER_NUMBER], } impl<'a> Default for CCoverageIntervalBuffer<'a> { fn default() -> Self { Self { m_pNext: Cell::new(None), m_interval: Default::default() } } } //------------------------------------------------------------------------------ // // Class: CCoverageBuffer // // Description: // Coverage buffer implementation that maintains coverage information // for one scanline. // // This implementation will maintain a linked list of intervals consisting // of x value in pixel space and a coverage value that applies for all pixels // between pInterval->X and pInterval->Next->X. // // For example, if we add the following interval (assuming 8x8 anti-aliasing) // to the coverage buffer: // _____ _____ _____ _____ // | | | | | // | ------------------- | // |_____|_____|_____|_____| // (0,0) (1,0) (2,0) (3,0) (4,0) // // Then we will get the following coverage buffer: // // m_nPixelX: INT_MIN | 0 | 1 | 3 | 4 | INT_MAX // m_nCoverage: 0 | 4 | 8 | 4 | 0 | 0xdeadbeef // m_pNext: -------->|---->|---->|---->|---->| NULL // //------------------------------------------------------------------------------ pub struct CCoverageBuffer<'a> { /* public: // // Init/Destroy methods // VOID Initialize(); VOID Destroy(); // // Setup the buffer so that it can accept another scanline // VOID Reset(); // // Add a subpixel interval to the coverage buffer // HRESULT FillEdgesAlternating( __in_ecount(1) const CEdge *pEdgeActiveList, INT nSubpixelYCurrent ); HRESULT FillEdgesWinding( __in_ecount(1) const CEdge *pEdgeActiveList, INT nSubpixelYCurrent ); HRESULT AddInterval(INT nSubpixelXLeft, INT nSubpixelXRight); private: HRESULT Grow( __deref_out_ecount(1) CCoverageInterval **ppIntervalNew, __deref_out_ecount(1) CCoverageInterval **ppIntervalEndMinus4 ); public:*/ pub m_pIntervalStart: Cell>>, // Points to list head entry //private: m_pIntervalNew: Cell>>, interval_new_index: Cell, // The Minus4 in the below variable refers to the position at which // we need to Grow the buffer. The buffer is grown once before an // AddInterval, so the Grow has to ensure that there are enough // intervals for the AddInterval worst case which is the following: // // 1 2 3 4 // *_____*_____ _____*_____* // | | | | | // | ---|-----------|--- | // |_____|_____|_____|_____| // // Note that the *'s above mark potentional insert points in the list, // so we need to ensure that at least 4 intervals can be allocated. // m_pIntervalEndMinus4: Cell>>, // Cache the next-to-last added interval to accelerate insertion. m_pIntervalLast: Cell>>, m_pIntervalBufferBuiltin: CCoverageIntervalBuffer<'a>, m_pIntervalBufferCurrent: Cell>>, arena: Arena> // Disable instrumentation checks within all methods of this class //SET_MILINSTRUMENTATION_FLAGS(MILINSTRUMENTATIONFLAGS_DONOTHING); } impl<'a> Default for CCoverageBuffer<'a> { fn default() -> Self { Self { m_pIntervalStart: Cell::new(unsafe { Ref::null() }), m_pIntervalNew: Cell::new(unsafe { Ref::null() }), m_pIntervalEndMinus4: Cell::new(unsafe { Ref::null() }), m_pIntervalLast: Cell::new(unsafe { Ref::null() }), m_pIntervalBufferBuiltin: Default::default(), m_pIntervalBufferCurrent: unsafe { Cell::new(Ref::null()) }, arena: Arena::new(), interval_new_index: Cell::new(0), } } } // // Inlines // impl<'a> CCoverageBuffer<'a> { //------------------------------------------------------------------------- // // Function: CCoverageBuffer::AddInterval // // Synopsis: Add a subpixel resolution interval to the coverage buffer // //------------------------------------------------------------------------- pub fn AddInterval(&'a self, nSubpixelXLeft: INT, nSubpixelXRight: INT) -> HRESULT { let hr: HRESULT = S_OK; let mut nPixelXNext: INT; let nPixelXLeft: INT; let nPixelXRight: INT; let nCoverageLeft: INT; // coverage from right edge of pixel for interval start let nCoverageRight: INT; // coverage from left edge of pixel for interval end let mut pInterval = self.m_pIntervalStart.get(); let mut pIntervalNew = self.m_pIntervalNew.get(); let mut interval_new_index = self.interval_new_index.get(); let mut pIntervalEndMinus4 = self.m_pIntervalEndMinus4.get(); // Make sure we have enough room to add two intervals if // necessary: if (pIntervalNew >= pIntervalEndMinus4) { IFC!(self.Grow(&mut pIntervalNew, &mut pIntervalEndMinus4, &mut interval_new_index)); } // Convert interval to pixel space so that we can insert it // into the coverage buffer debug_assert!(nSubpixelXLeft < nSubpixelXRight); nPixelXLeft = nSubpixelXLeft >> c_nShift; nPixelXRight = nSubpixelXRight >> c_nShift; // Try to resume searching from the last searched interval. if self.m_pIntervalLast.get().m_nPixelX.get() < nPixelXLeft { pInterval = self.m_pIntervalLast.get(); } // Skip any intervals less than 'nPixelLeft': loop { let nextInterval = pInterval.m_pNext.get(); nPixelXNext = nextInterval.m_nPixelX.get(); if !(nPixelXNext < nPixelXLeft) { break } pInterval = nextInterval; } // Remember the found interval. self.m_pIntervalLast.set(pInterval); // Insert a new interval if necessary: if (nPixelXNext != nPixelXLeft) { pIntervalNew.m_nPixelX.set(nPixelXLeft); pIntervalNew.m_nCoverage.set(pInterval.m_nCoverage.get()); pIntervalNew.m_pNext.set(pInterval.m_pNext.get()); pInterval.m_pNext.set(pIntervalNew); pInterval = pIntervalNew; interval_new_index += 1; pIntervalNew = Ref::new(&Ref::get_ref(self.m_pIntervalBufferCurrent.get()).m_interval[interval_new_index]) } else { pInterval = (*pInterval).m_pNext.get(); } // // Compute coverage for left segment as shown by the *'s below // // |_____|_____|_____|_ // | | | | // | ***---------- | // |_____|_____|_____| // nCoverageLeft = c_nShiftSize - (nSubpixelXLeft & c_nShiftMask); // If nCoverageLeft == 0, then the value of nPixelXLeft is wrong // and should have been equal to nPixelXLeft+1. debug_assert!(nCoverageLeft > 0); // If we have partial coverage, then ensure that we have a position // for the end of the pixel if ((nCoverageLeft < c_nShiftSize || (nPixelXLeft == nPixelXRight)) && nPixelXLeft + 1 != pInterval.m_pNext.get().m_nPixelX.get()) { pIntervalNew.m_nPixelX.set(nPixelXLeft + 1); pIntervalNew.m_nCoverage.set(pInterval.m_nCoverage.get()); pIntervalNew.m_pNext.set(pInterval.m_pNext.get()); pInterval.m_pNext.set(pIntervalNew); interval_new_index += 1; pIntervalNew = Ref::new(&Ref::get_ref(self.m_pIntervalBufferCurrent.get()).m_interval[interval_new_index]) } // // If the interval only includes one pixel, then the coverage is // nSubpixelXRight - nSubpixelXLeft // if (nPixelXLeft == nPixelXRight) { pInterval.m_nCoverage.set(pInterval.m_nCoverage.get() + nSubpixelXRight - nSubpixelXLeft); debug_assert!(pInterval.m_nCoverage.get() <= c_nShiftSize*c_nShiftSize); //goto Cleanup; //Cleanup: // Update the coverage buffer new interval self.interval_new_index.set(interval_new_index); self.m_pIntervalNew.set(pIntervalNew); return hr; } // Update coverage of current interval pInterval.m_nCoverage.set(pInterval.m_nCoverage.get() + nCoverageLeft); debug_assert!(pInterval.m_nCoverage.get() <= c_nShiftSize*c_nShiftSize); // Increase the coverage for any intervals between 'nPixelXLeft' // and 'nPixelXRight': loop { let nextInterval = pInterval.m_pNext.get(); (nPixelXNext = nextInterval.m_nPixelX.get()); if !(nPixelXNext < nPixelXRight) { break; } pInterval = nextInterval; pInterval.m_nCoverage.set(pInterval.m_nCoverage.get() + c_nShiftSize); debug_assert!(pInterval.m_nCoverage.get() <= c_nShiftSize*c_nShiftSize); } // Remember the found interval. self.m_pIntervalLast.set(pInterval); // Insert another new interval if necessary: if (nPixelXNext != nPixelXRight) { pIntervalNew.m_nPixelX.set(nPixelXRight); pIntervalNew.m_nCoverage.set(pInterval.m_nCoverage.get() - c_nShiftSize); pIntervalNew.m_pNext.set(pInterval.m_pNext.get()); pInterval.m_pNext.set(pIntervalNew); pInterval = pIntervalNew; interval_new_index += 1; pIntervalNew = Ref::new(&Ref::get_ref(self.m_pIntervalBufferCurrent.get()).m_interval[interval_new_index]) } else { pInterval = pInterval.m_pNext.get(); } // // Compute coverage for right segment as shown by the *'s below // // |_____|_____|_____|_ // | | | | // | ---------**** | // |_____|_____|_____| // nCoverageRight = nSubpixelXRight & c_nShiftMask; if (nCoverageRight > 0) { if (nPixelXRight + 1 != (*(*pInterval).m_pNext.get()).m_nPixelX.get()) { pIntervalNew.m_nPixelX.set(nPixelXRight + 1); pIntervalNew.m_nCoverage.set(pInterval.m_nCoverage.get()); pIntervalNew.m_pNext.set(pInterval.m_pNext.get()); pInterval.m_pNext.set(pIntervalNew); interval_new_index += 1; pIntervalNew = Ref::new(&Ref::get_ref(self.m_pIntervalBufferCurrent.get()).m_interval[interval_new_index]) } pInterval.m_nCoverage.set((*pInterval).m_nCoverage.get() + nCoverageRight); debug_assert!(pInterval.m_nCoverage.get() <= c_nShiftSize*c_nShiftSize); } //Cleanup: // Update the coverage buffer new interval self.interval_new_index.set(interval_new_index); self.m_pIntervalNew.set(pIntervalNew); return hr; } //------------------------------------------------------------------------- // // Function: CCoverageBuffer::FillEdgesAlternating // // Synopsis: // Given the active edge list for the current scan, do an alternate-mode // antialiased fill. // //------------------------------------------------------------------------- pub fn FillEdgesAlternating(&'a self, pEdgeActiveList: Ref, nSubpixelYCurrent: INT ) -> HRESULT { let hr: HRESULT = S_OK; let mut pEdgeStart: Ref = (*pEdgeActiveList).Next.get(); let mut pEdgeEnd: Ref; let mut nSubpixelXLeft: INT; let mut nSubpixelXRight: INT; ASSERTACTIVELIST!(pEdgeActiveList, nSubpixelYCurrent); while (pEdgeStart.X.get() != INT::MAX) { pEdgeEnd = pEdgeStart.Next.get(); // We skip empty pairs: (nSubpixelXLeft = pEdgeStart.X.get()); if (nSubpixelXLeft != pEdgeEnd.X.get()) { // We now know we have a non-empty interval. Skip any // empty interior pairs: while ({(nSubpixelXRight = pEdgeEnd.X.get()); pEdgeEnd.X == pEdgeEnd.Next.get().X}) { pEdgeEnd = pEdgeEnd.Next.get().Next.get(); } debug_assert!((nSubpixelXLeft < nSubpixelXRight) && (nSubpixelXRight < INT::MAX)); IFC!(self.AddInterval(nSubpixelXLeft, nSubpixelXRight)); } // Prepare for the next iteration: pEdgeStart = pEdgeEnd.Next.get(); } //Cleanup: return hr } //------------------------------------------------------------------------- // // Function: CCoverageBuffer::FillEdgesWinding // // Synopsis: // Given the active edge list for the current scan, do an alternate-mode // antialiased fill. // //------------------------------------------------------------------------- pub fn FillEdgesWinding(&'a self, pEdgeActiveList: Ref, nSubpixelYCurrent: INT ) -> HRESULT { let hr: HRESULT = S_OK; let mut pEdgeStart: Ref = pEdgeActiveList.Next.get(); let mut pEdgeEnd: Ref; let mut nSubpixelXLeft: INT; let mut nSubpixelXRight: INT; let mut nWindingValue: INT; ASSERTACTIVELIST!(pEdgeActiveList, nSubpixelYCurrent); while (pEdgeStart.X.get() != INT::MAX) { pEdgeEnd = pEdgeStart.Next.get(); nWindingValue = pEdgeStart.WindingDirection; while ({nWindingValue += pEdgeEnd.WindingDirection; nWindingValue != 0}) { pEdgeEnd = pEdgeEnd.Next.get(); } debug_assert!(pEdgeEnd.X.get() != INT::MAX); // We skip empty pairs: if ({nSubpixelXLeft = pEdgeStart.X.get(); nSubpixelXLeft != pEdgeEnd.X.get()}) { // We now know we have a non-empty interval. Skip any // empty interior pairs: while ({nSubpixelXRight = pEdgeEnd.X.get(); nSubpixelXRight == pEdgeEnd.Next.get().X.get()}) { pEdgeStart = pEdgeEnd.Next.get(); pEdgeEnd = pEdgeStart.Next.get(); nWindingValue = pEdgeStart.WindingDirection; while ({nWindingValue += pEdgeEnd.WindingDirection; nWindingValue != 0}) { pEdgeEnd = pEdgeEnd.Next.get(); } } debug_assert!((nSubpixelXLeft < nSubpixelXRight) && (nSubpixelXRight < INT::MAX)); IFC!(self.AddInterval(nSubpixelXLeft, nSubpixelXRight)); } // Prepare for the next iteration: pEdgeStart = pEdgeEnd.Next.get(); } //Cleanup: return hr;//RRETURN(hr); } //------------------------------------------------------------------------- // // Function: CCoverageBuffer::Initialize // // Synopsis: Set the coverage buffer to a valid initial state // //------------------------------------------------------------------------- pub fn Initialize(&'a self) { self.m_pIntervalBufferBuiltin.m_interval[0].m_nPixelX.set(INT::MIN); self.m_pIntervalBufferBuiltin.m_interval[0].m_nCoverage.set(0); self.m_pIntervalBufferBuiltin.m_interval[0].m_pNext.set(Ref::new(&self.m_pIntervalBufferBuiltin.m_interval[1])); self.m_pIntervalBufferBuiltin.m_interval[1].m_nPixelX.set(INT::MAX); self.m_pIntervalBufferBuiltin.m_interval[1].m_nCoverage.set(0xdeadbeef); self.m_pIntervalBufferBuiltin.m_interval[1].m_pNext.set(unsafe { Ref::null() }); self.m_pIntervalBufferBuiltin.m_pNext.set(None); self.m_pIntervalBufferCurrent.set(Ref::new(&self.m_pIntervalBufferBuiltin)); self.m_pIntervalStart.set(Ref::new(&self.m_pIntervalBufferBuiltin.m_interval[0])); self.m_pIntervalNew.set(Ref::new(&self.m_pIntervalBufferBuiltin.m_interval[2])); self.interval_new_index.set(2); self.m_pIntervalEndMinus4.set(Ref::new(&self.m_pIntervalBufferBuiltin.m_interval[INTERVAL_BUFFER_NUMBER - 4])); self.m_pIntervalLast.set(Ref::new(&self.m_pIntervalBufferBuiltin.m_interval[1])); } //------------------------------------------------------------------------- // // Function: CCoverageBuffer::Destroy // // Synopsis: Free all allocated buffers // //------------------------------------------------------------------------- pub fn Destroy(&mut self) { // Free the linked-list of allocations (skipping 'm_pIntervalBufferBuiltin', // which is built into the class): } //------------------------------------------------------------------------- // // Function: CCoverageBuffer::Reset // // Synopsis: Reset the coverage buffer // //------------------------------------------------------------------------- pub fn Reset(&'a self) { // Reset our coverage structure. Point the head back to the tail, // and reset where the next new entry will be placed: self.m_pIntervalBufferBuiltin.m_interval[0].m_pNext.set(Ref::new(&self.m_pIntervalBufferBuiltin.m_interval[1])); self.m_pIntervalBufferCurrent.set(Ref::new(&self.m_pIntervalBufferBuiltin)); self.m_pIntervalNew.set(Ref::new(&self.m_pIntervalBufferBuiltin.m_interval[2])); self.interval_new_index.set(2); self.m_pIntervalEndMinus4.set(Ref::new(&self.m_pIntervalBufferBuiltin.m_interval[INTERVAL_BUFFER_NUMBER - 4])); self.m_pIntervalLast.set(Ref::new(&self.m_pIntervalBufferBuiltin.m_interval[1])); } //------------------------------------------------------------------------- // // Function: CCoverageBuffer::Grow // // Synopsis: // Grow our interval buffer. // //------------------------------------------------------------------------- fn Grow(&'a self, ppIntervalNew: &mut Ref<'a, CCoverageInterval<'a>>, ppIntervalEndMinus4: &mut Ref<'a, CCoverageInterval<'a>>, interval_new_index: &mut usize ) -> HRESULT { let hr: HRESULT = S_OK; let pIntervalBufferNew = (*self.m_pIntervalBufferCurrent.get()).m_pNext.get(); let pIntervalBufferNew = pIntervalBufferNew.unwrap_or_else(|| { let pIntervalBufferNew = self.arena.alloc(Default::default()); (*pIntervalBufferNew).m_pNext.set(None); (*self.m_pIntervalBufferCurrent.get()).m_pNext.set(Some(pIntervalBufferNew)); pIntervalBufferNew }); self.m_pIntervalBufferCurrent.set(Ref::new(pIntervalBufferNew)); self.m_pIntervalNew.set(Ref::new(&(*pIntervalBufferNew).m_interval[2])); self.interval_new_index.set(2); self.m_pIntervalEndMinus4.set(Ref::new(&(*pIntervalBufferNew).m_interval[INTERVAL_BUFFER_NUMBER - 4])); *ppIntervalNew = self.m_pIntervalNew.get(); *ppIntervalEndMinus4 = self.m_pIntervalEndMinus4.get(); *interval_new_index = 2; return hr; } } /* impl<'a> Drop for CCoverageBuffer<'a> { fn drop(&mut self) { self.Destroy(); } }*/