/* * Copyright 2006 The Android Open Source Project * * Use of this source code is governed by a BSD-style license that can be * found in the LICENSE file.
*/
staticvoid insert_new_edges(SkEdge* newEdge, int curr_y) { if (newEdge->fFirstY != curr_y) { return;
}
SkEdge* prev = newEdge->fPrev; if (prev->fX <= newEdge->fX) { return;
} // find first x pos to insert
SkEdge* start = backward_insert_start(prev, newEdge->fX); // insert the lot, fixing up the links as we go do {
SkEdge* next = newEdge->fNext; do { if (start->fNext == newEdge) { goto nextEdge;
}
SkEdge* after = start->fNext; if (after->fX >= newEdge->fX) { break;
}
start = after;
} while (true);
remove_edge(newEdge);
insert_edge_after(newEdge, start);
nextEdge:
start = newEdge;
newEdge = next;
} while (newEdge->fFirstY == curr_y);
}
#ifdefined _WIN32 // disable warning : local variable used without having been initialized #pragma warning ( push ) #pragma warning ( disable : 4701 ) #endif
typedefvoid (*PrePostProc)(SkBlitter* blitter, int y, bool isStartOfScanline); #define PREPOST_START true #define PREPOST_END false
staticvoid walk_edges(SkEdge* prevHead, SkPathFillType fillType,
SkBlitter* blitter, int start_y, int stop_y,
PrePostProc proc, int rightClip) {
validate_sort(prevHead->fNext);
int curr_y = start_y; int windingMask = SkPathFillType_IsEvenOdd(fillType) ? 1 : -1;
for (;;) { int w = 0; int left SK_INIT_TO_AVOID_WARNING;
SkEdge* currE = prevHead->fNext;
SkFixed prevX = prevHead->fX;
validate_edges_for_y(currE, curr_y);
if (proc) {
proc(blitter, curr_y, PREPOST_START); // pre-proc
}
while (currE->fFirstY <= curr_y) {
SkASSERT(currE->fLastY >= curr_y);
int x = SkFixedRoundToInt(currE->fX);
if ((w & windingMask) == 0) { // we're starting interval
left = x;
}
w += currE->fWinding;
if ((w & windingMask) == 0) { // we finished an interval int width = x - left;
SkASSERT(width >= 0); if (width > 0) {
blitter->blitH(left, curr_y, width);
}
}
SkEdge* next = currE->fNext;
SkFixed newX;
if (currE->fLastY == curr_y) { // are we done with this edge? if (currE->fCurveCount > 0) { if (((SkQuadraticEdge*)currE)->updateQuadratic()) {
newX = currE->fX; goto NEXT_X;
}
} elseif (currE->fCurveCount < 0) { if (((SkCubicEdge*)currE)->updateCubic()) {
SkASSERT(currE->fFirstY == curr_y + 1);
if ((w & windingMask) != 0) { // was our right-edge culled away? int width = rightClip - left; if (width > 0) {
blitter->blitH(left, curr_y, width);
}
}
if (proc) {
proc(blitter, curr_y, PREPOST_END); // post-proc
}
curr_y += 1; if (curr_y >= stop_y) { break;
} // now currE points to the first edge with a Yint larger than curr_y
insert_new_edges(currE, curr_y);
}
}
// return true if we're NOT done with this edge staticbool update_edge(SkEdge* edge, int last_y) {
SkASSERT(edge->fLastY >= last_y); if (last_y == edge->fLastY) { if (edge->fCurveCount < 0) { if (((SkCubicEdge*)edge)->updateCubic()) {
SkASSERT(edge->fFirstY == last_y + 1); returntrue;
}
} elseif (edge->fCurveCount > 0) { if (((SkQuadraticEdge*)edge)->updateQuadratic()) {
SkASSERT(edge->fFirstY == last_y + 1); returntrue;
}
} returnfalse;
} returntrue;
}
// Unexpected conditions for which we need to return #define ASSERT_RETURN(cond) \ do { \ if (!(cond)) { \
SkDEBUGFAILF("assert(%s)", #cond); \ return; \
} \
} while (0)
// Needs Y to only change once (looser than convex in X) staticvoid walk_simple_edges(SkEdge* prevHead, SkBlitter* blitter, int start_y, int stop_y) {
validate_sort(prevHead->fNext);
// our edge choppers for curves can result in the initial edges // not lining up, so we take the max. int local_top = std::max(leftE->fFirstY, riteE->fFirstY);
ASSERT_RETURN(local_top >= start_y);
if (0 == (dLeft | dRite)) { int L = SkFixedRoundToInt(left); int R = SkFixedRoundToInt(rite); if (L > R) {
std::swap(L, R);
} if (L < R) {
count += 1;
blitter->blitRect(L, local_top, R - L, count);
}
local_top = local_bot + 1;
} else { do { int L = SkFixedRoundToInt(left); int R = SkFixedRoundToInt(rite); if (L > R) {
std::swap(L, R);
} if (L < R) {
blitter->blitH(L, local_top, R - L);
} // Either/both of these might overflow, since we perform this step even if // (later) we determine that we are done with the edge, and so the computed // left or rite edge will not be used (see update_edge). Use this helper to // silence UBSAN when we perform the add.
left = Sk32_can_overflow_add(left, dLeft);
rite = Sk32_can_overflow_add(rite, dRite);
local_top += 1;
} while (--count >= 0);
}
// this overrides blitH, and will call its proxy blitter with the inverse // of the spans it is given (clipped to the left/right of the cliprect) // // used to implement inverse filltypes on paths // class InverseBlitter : public SkBlitter { public: void setBlitter(SkBlitter* blitter, const SkIRect& clip, int shift) {
fBlitter = blitter;
fFirstX = clip.fLeft << shift;
fLastX = clip.fRight << shift;
} void prepost(int y, bool isStart) { if (isStart) {
fPrevX = fFirstX;
} else { int invWidth = fLastX - fPrevX; if (invWidth > 0) {
fBlitter->blitH(fPrevX, y, invWidth);
}
}
}
// overrides void blitH(int x, int y, int width) override { int invWidth = x - fPrevX; if (invWidth > 0) {
fBlitter->blitH(fPrevX, y, invWidth);
}
fPrevX = x + width;
}
// we do not expect to get called with these entrypoints void blitAntiH(int, int, const SkAlpha[], const int16_t runs[]) override {
SkDEBUGFAIL("blitAntiH unexpected");
} void blitV(int x, int y, int height, SkAlpha alpha) override {
SkDEBUGFAIL("blitV unexpected");
} void blitRect(int x, int y, int width, int height) override {
SkDEBUGFAIL("blitRect unexpected");
} void blitMask(const SkMask&, const SkIRect& clip) override {
SkDEBUGFAIL("blitMask unexpected");
}
private:
SkBlitter* fBlitter; int fFirstX, fLastX, fPrevX;
};
staticvoid PrePostInverseBlitterProc(SkBlitter* blitter, int y, bool isStart) {
((InverseBlitter*)blitter)->prepost(y, isStart);
}
static SkEdge* sort_edges(SkEdge* list[], int count, SkEdge** last) {
SkTQSort(list, list + count);
// now make the edges linked in sorted order for (int i = 1; i < count; i++) {
list[i - 1]->fNext = list[i];
list[i]->fPrev = list[i - 1];
}
*last = list[count - 1]; return list[0];
}
// clipRect has not been shifted up void sk_fill_path(const SkPath& path, const SkIRect& clipRect, SkBlitter* blitter, int start_y, int stop_y, int shiftEdgesUp, bool pathContainedInClip) {
SkASSERT(blitter);
SkBasicEdgeBuilder builder(shiftEdgesUp); int count = builder.buildEdges(path, pathContainedInClip ? nullptr : &shiftedClip);
SkEdge** list = builder.edgeList();
if (0 == count) { if (path.isInverseFillType()) { /* * Since we are in inverse-fill, our caller has already drawn above * our top (start_y) and will draw below our bottom (stop_y). Thus * we need to restrict our drawing to the intersection of the clip * and those two limits.
*/
SkIRect rect = clipRect; if (rect.fTop < start_y) {
rect.fTop = start_y;
} if (rect.fBottom > stop_y) {
rect.fBottom = stop_y;
} if (!rect.isEmpty()) {
blitter->blitRect(rect.fLeft << shiftEdgesUp,
rect.fTop << shiftEdgesUp,
rect.width() << shiftEdgesUp,
rect.height() << shiftEdgesUp);
}
} return;
}
SkEdge headEdge, tailEdge, *last; // this returns the first and last edge after they're sorted into a dlink list
SkEdge* edge = sort_edges(list, count, &last);
/** * If the caller is drawing an inverse-fill path, then it pass true for * skipRejectTest, so we don't abort drawing just because the src bounds (ir) * is outside of the clip.
*/
SkScanClipper::SkScanClipper(SkBlitter* blitter, const SkRegion* clip, const SkIRect& ir, bool skipRejectTest, bool irPreClipped) {
fBlitter = nullptr; // null means blit nothing
fClipRect = nullptr;
if (clip) {
fClipRect = &clip->getBounds(); if (!skipRejectTest && !SkIRect::Intersects(*fClipRect, ir)) { // completely clipped out return;
}
staticbool clip_to_limit(const SkRegion& orig, SkRegion* reduced) { // need to limit coordinates such that the width/height of our rect can be represented // in SkFixed (16.16). See skbug.com/7998 const int32_t limit = 32767 >> 1;
// Bias used for conservative rounding of float rects to int rects, to nudge the irects a little // larger, so we don't "think" a path's bounds are inside a clip, when (due to numeric drift in // the scan-converter) we might walk beyond the predicted limits. // // This value has been determined trial and error: pick the smallest value (after the 0.5) that // fixes any problematic cases (e.g. crbug.com/844457) // NOTE: cubics appear to be the main reason for needing this slop. If we could (perhaps) have a // more accurate walker for cubics, we may be able to reduce this fudge factor. staticconstdouble kConservativeRoundBias = 0.5 + 1.5 / SK_FDot6One;
/** * Round the value down. This is used to round the top and left of a rectangle, * and corresponds to the way the scan converter treats the top and left edges. * It has a slight bias to make the "rounded" int smaller than a normal round, to create a more * conservative int-bounds (larger) from a float rect.
*/ staticinlineint round_down_to_int(SkScalar x) { double xx = x;
xx -= kConservativeRoundBias; return sk_double_saturate2int(ceil(xx));
}
/** * Round the value up. This is used to round the right and bottom of a rectangle. * It has a slight bias to make the "rounded" int smaller than a normal round, to create a more * conservative int-bounds (larger) from a float rect.
*/ staticinlineint round_up_to_int(SkScalar x) { double xx = x;
xx += kConservativeRoundBias; return sk_double_saturate2int(floor(xx));
}
/* * Conservative rounding function, which effectively nudges the int-rect to be slightly larger * than SkRect::round() might have produced. This is a safety-net for the scan-converter, which * inspects the returned int-rect, and may disable clipping (for speed) if it thinks all of the * edges will fit inside the clip's bounds. The scan-converter introduces slight numeric errors * due to accumulated += of the slope, so this function is used to return a conservatively large * int-bounds, and thus we will only disable clipping if we're sure the edges will stay in-bounds.
*/ static SkIRect conservative_round_to_int(const SkRect& src) { return {
round_down_to_int(src.fLeft),
round_down_to_int(src.fTop),
round_up_to_int(src.fRight),
round_up_to_int(src.fBottom),
};
}
// Our edges are fixed-point, and don't like the bounds of the clip to // exceed that. Here we trim the clip just so we don't overflow later on const SkRegion* clipPtr = &origClip;
SkRegion finiteClip; if (clip_to_limit(origClip, &finiteClip)) { if (finiteClip.isEmpty()) { return;
}
clipPtr = &finiteClip;
} // don't reference "origClip" any more, just use clipPtr
SkRect bounds = path.getBounds(); bool irPreClipped = false; if (!SkRectPriv::MakeLargeS32().contains(bounds)) { if (!bounds.intersect(SkRectPriv::MakeLargeS32())) {
bounds.setEmpty();
}
irPreClipped = true;
}
SkIRect ir = conservative_round_to_int(bounds); if (ir.isEmpty()) { if (path.isInverseFillType()) {
blitter->blitRegion(*clipPtr);
} return;
}
SkScanClipper clipper(blitter, clipPtr, ir, path.isInverseFillType(), irPreClipped);
blitter = clipper.getBlitter(); if (blitter) { // we have to keep our calls to blitter in sorted order, so we // must blit the above section first, then the middle, then the bottom. if (path.isInverseFillType()) {
sk_blit_above(blitter, ir, *clipPtr);
}
SkASSERT(clipper.getClipRect() == nullptr ||
*clipper.getClipRect() == clipPtr->getBounds());
sk_fill_path(path, clipPtr->getBounds(), blitter, ir.fTop, ir.fBottom,
0, clipper.getClipRect() == nullptr); if (path.isInverseFillType()) {
sk_blit_below(blitter, ir, *clipPtr);
}
} else { // what does it mean to not have a blitter if path.isInverseFillType???
}
}
// now edge is the head of the sorted linklist int stop_y = ir.fBottom; if (clipRect && stop_y > clipRect->fBottom) {
stop_y = clipRect->fBottom;
} int start_y = ir.fTop; if (clipRect && start_y < clipRect->fTop) {
start_y = clipRect->fTop;
}
walk_simple_edges(&headEdge, blitter, start_y, stop_y);
}
SkRect r;
r.setBounds(pts, 3); // If r is too large (larger than can easily fit in SkFixed) then we need perform geometric // clipping. This is a bit of work, so we just call the general FillPath() to handle it. // Use FixedMax/2 as the limit so we can subtract two edges and still store that in Fixed. const SkScalar limit = SK_MaxS16 >> 1; if (!SkRect::MakeLTRB(-limit, -limit, limit, limit).contains(r)) {
SkPath path;
path.addPoly(pts, 3, false);
FillPath(path, clip, blitter); return;
}
SkIRect ir = conservative_round_to_int(r); if (ir.isEmpty() || !SkIRect::Intersects(ir, clip.getBounds())) { return;
}
¤ 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.0.13Angebot
(Wie Sie bei der Firma Beratungs- und Dienstleistungen beauftragen können 2026-04-28)
¤