// 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.
// We the inactive-array separate from the edge allocations so that // we can more easily do in-place sorts on it: #[derive(Clone)] pubstruct CInactiveEdge<'a> {
Edge: Ref<'a, CEdge<'a>>, // Associated edge
Yx: LONGLONG, // Sorting key, StartY and X packed into an lword
}
// 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 _;
};
}
// 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:
// 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)asUINT)<cNewTotalCount){ returnWINCODEC_ERR_VALUEOVERFLOW; }
// We have to grow our data structure by adding a new buffer // and adding it to the list:
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.
pubfn 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;
pubstruct 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,
}
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);
// 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.
letmut point = &mut *pointArray; letmut 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
} {}
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);
// 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:
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':
// 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);
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
letmut 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));
}
// 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);
// 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);
}
// 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;
}
}
}
}
/**************************************************************************\ * *FunctionDescription: * *Somedebugcodeforverifyingthepath. * *Created: * *03/25/2000andrewgo *
\**************************************************************************/
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. //
pubfn 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; letmut bufferStart: [POINT; ENUMERATE_BUFFER_NUMBER!()] = [(); ENUMERATE_BUFFER_NUMBER!()].map(|_| Default::default()); letmut bezierBuffer: [POINT; 4] = Default::default(); letmut buffer: &mut [POINT]; letmut bufferSize: usize; letmut startFigure: [POINT; 1] = Default::default(); // The current point offset in rgpt letmut iPoint: usize; // The current type offset in rgTypes letmut iType: usize; letmut runSize: usize; letmut thisCount: usize; letmut isMore: bool = false; letmut xLast: INT; letmut yLast: INT;
ASSERTPATH!(rgTypes, cPoints);
// Every valid subpath has at least two vertices in it, hence the // check of 'cPoints - 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
));
}
}
/*#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);
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);
pubfn 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>] {
previous = current;
current = next;
next = (*next).Next.get();
nextX = (*next).X.get();
nextX != INT::MAX
} {}
swapOccurred
} {}
}
Messung V0.5 in Prozent
¤ Diese beiden folgenden Angebotsgruppen bietet das Unternehmen0.25Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-06-18)
¤
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.