Revision control
Copy as Markdown
Other Tools
// 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<Ref<'a, CCoverageInterval<'a>>>, // m_pNext interval (look for sentinel, not NULL)
pub m_nPixelX: Cell<INT>, // Interval's left edge (m_pNext->X is the right edge)
pub m_nCoverage: Cell<INT>, // 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<Option<& 'a CCoverageIntervalBuffer<'a>>>,
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<Ref<'a, CCoverageInterval<'a>>>, // Points to list head entry
//private:
m_pIntervalNew: Cell<Ref<'a, CCoverageInterval<'a>>>,
interval_new_index: Cell<usize>,
// 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<Ref<'a, CCoverageInterval<'a>>>,
// Cache the next-to-last added interval to accelerate insertion.
m_pIntervalLast: Cell<Ref<'a, CCoverageInterval<'a>>>,
m_pIntervalBufferBuiltin: CCoverageIntervalBuffer<'a>,
m_pIntervalBufferCurrent: Cell<Ref<'a, CCoverageIntervalBuffer<'a>>>,
arena: Arena<CCoverageIntervalBuffer<'a>>
// 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<CEdge>,
nSubpixelYCurrent: INT
) -> HRESULT
{
let hr: HRESULT = S_OK;
let mut pEdgeStart: Ref<CEdge> = (*pEdgeActiveList).Next.get();
let mut pEdgeEnd: Ref<CEdge>;
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<CEdge>,
nSubpixelYCurrent: INT
) -> HRESULT
{
let hr: HRESULT = S_OK;
let mut pEdgeStart: Ref<CEdge> = pEdgeActiveList.Next.get();
let mut pEdgeEnd: Ref<CEdge>;
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();
}
}*/