staticinlinevoid clamp_le(SkScalar& value, SkScalar max) { if (value > max) {
value = max;
}
}
staticinlinevoid clamp_ge(SkScalar& value, SkScalar min) { if (value < min) {
value = min;
}
}
/* src[] must be monotonic in Y. This routine copies src into dst, and sorts ittobeincreasinginY.Ifithadtoreversetheorderofthepoints, itreturnstrue,otherwiseitreturnsfalse
*/ staticbool sort_increasing_Y(SkPoint dst[], const SkPoint src[], int count) { // we need the data to be monotonically increasing in Y if (src[0].fY > src[count - 1].fY) { for (int i = 0; i < count; i++) {
dst[i] = src[count - i - 1];
} return true;
} else {
memcpy(dst, src, count * sizeof(SkPoint)); returnfalse;
}
}
staticbool chopMonoQuadAt(SkScalar c0, SkScalar c1, SkScalar c2,
SkScalar target, SkScalar* t) { /* Solve F(t) = y where F(t) := [0](1-t)^2 + 2[1]t(1-t) + [2]t^2 *Wesolvefort,usingquadraticequation,hencewehavetorearrange *ourcooefficentstolooklikeAt^2+Bt+C
*/
SkScalar A = c0 - c1 - c1 + c2;
SkScalar B = 2*(c1 - c0);
SkScalar C = c0 - target;
SkScalar roots[2]; // we only expect one, but make room for 2 for safety int count = SkFindUnitQuadRoots(A, B, C, roots); if (count) {
*t = roots[0]; return true;
} returnfalse;
}
staticbool chopMonoQuadAtY(SkPoint pts[3], SkScalar y, SkScalar* t) { return chopMonoQuadAt(pts[0].fY, pts[1].fY, pts[2].fY, y, t);
}
// Modify pts[] in place so that it is clipped in Y to the clip rect staticvoid chop_quad_in_Y(SkPoint pts[3], const SkRect& clip) {
SkScalar t;
SkPoint tmp[5]; // for SkChopQuadAt
// are we partially above if (pts[0].fY < clip.fTop) { if (chopMonoQuadAtY(pts, clip.fTop, &t)) { // take the 2nd chopped quad
SkChopQuadAt(pts, tmp, t); // clamp to clean up imprecise numerics in the chop
tmp[2].fY = clip.fTop;
clamp_ge(tmp[3].fY, clip.fTop);
pts[0] = tmp[2];
pts[1] = tmp[3];
} else { // if chopMonoQuadAtY failed, then we may have hit inexact numerics // so we just clamp against the top for (int i = 0; i < 3; i++) { if (pts[i].fY < clip.fTop) {
pts[i].fY = clip.fTop;
}
}
}
}
// are we partially below if (pts[2].fY > clip.fBottom) { if (chopMonoQuadAtY(pts, clip.fBottom, &t)) {
SkChopQuadAt(pts, tmp, t); // clamp to clean up imprecise numerics in the chop
clamp_le(tmp[1].fY, clip.fBottom);
tmp[2].fY = clip.fBottom;
pts[1] = tmp[1];
pts[2] = tmp[2];
} else { // if chopMonoQuadAtY failed, then we may have hit inexact numerics // so we just clamp against the bottom for (int i = 0; i < 3; i++) { if (pts[i].fY > clip.fBottom) {
pts[i].fY = clip.fBottom;
}
}
}
}
}
// srcPts[] must be monotonic in X and Y void SkEdgeClipper::clipMonoQuad(const SkPoint srcPts[3], const SkRect& clip) {
SkPoint pts[3]; bool reverse = sort_increasing_Y(pts, srcPts, 3);
// are we completely above or below if (pts[2].fY <= clip.fTop || pts[0].fY >= clip.fBottom) { return;
}
// Now chop so that pts is contained within clip in Y
chop_quad_in_Y(pts, clip);
if (pts[0].fX > pts[2].fX) {
using std::swap;
swap(pts[0], pts[2]);
reverse = !reverse;
}
SkASSERT(pts[0].fX <= pts[1].fX);
SkASSERT(pts[1].fX <= pts[2].fX);
// Now chop in X has needed, and record the segments
if (pts[2].fX <= clip.fLeft) { // wholly to the left
this->appendVLine(clip.fLeft, pts[0].fY, pts[2].fY, reverse); return;
} if (pts[0].fX >= clip.fRight) { // wholly to the right if (!this->canCullToTheRight()) {
this->appendVLine(clip.fRight, pts[0].fY, pts[2].fY, reverse);
} return;
}
SkScalar t;
SkPoint tmp[5]; // for SkChopQuadAt
// are we partially to the left if (pts[0].fX < clip.fLeft) { if (chopMonoQuadAtX(pts, clip.fLeft, &t)) {
SkChopQuadAt(pts, tmp, t);
this->appendVLine(clip.fLeft, tmp[0].fY, tmp[2].fY, reverse); // clamp to clean up imprecise numerics in the chop
tmp[2].fX = clip.fLeft;
clamp_ge(tmp[3].fX, clip.fLeft);
pts[0] = tmp[2];
pts[1] = tmp[3];
} else { // if chopMonoQuadAtY failed, then we may have hit inexact numerics // so we just clamp against the left
this->appendVLine(clip.fLeft, pts[0].fY, pts[2].fY, reverse); return;
}
}
// are we partially to the right if (pts[2].fX > clip.fRight) { if (chopMonoQuadAtX(pts, clip.fRight, &t)) {
SkChopQuadAt(pts, tmp, t); // clamp to clean up imprecise numerics in the chop
clamp_le(tmp[1].fX, clip.fRight);
tmp[2].fX = clip.fRight;
this->appendQuad(tmp, reverse);
this->appendVLine(clip.fRight, tmp[2].fY, tmp[4].fY, reverse);
} else { // if chopMonoQuadAtY failed, then we may have hit inexact numerics // so we just clamp against the right
pts[1].fX = std::min(pts[1].fX, clip.fRight);
pts[2].fX = std::min(pts[2].fX, clip.fRight);
this->appendQuad(pts, reverse);
}
} else { // wholly inside the clip
this->appendQuad(pts, reverse);
}
}
// tmp[3, 4].fY should all be to the below clip.fTop. // Since we can't trust the numerics of the chopper, we force those conditions now
tmp[3].fY = clip.fTop;
clamp_ge(tmp[4].fY, clip.fTop);
// srcPts[] must be monotonic in X and Y void SkEdgeClipper::clipMonoCubic(const SkPoint src[4], const SkRect& clip) {
SkPoint pts[4]; bool reverse = sort_increasing_Y(pts, src, 4);
// are we completely above or below if (pts[3].fY <= clip.fTop || pts[0].fY >= clip.fBottom) { return;
}
// Now chop so that pts is contained within clip in Y
chop_cubic_in_Y(pts, clip);
if (pts[0].fX > pts[3].fX) {
using std::swap;
swap(pts[0], pts[3]);
swap(pts[1], pts[2]);
reverse = !reverse;
}
// Now chop in X has needed, and record the segments
if (pts[3].fX <= clip.fLeft) { // wholly to the left
this->appendVLine(clip.fLeft, pts[0].fY, pts[3].fY, reverse); return;
} if (pts[0].fX >= clip.fRight) { // wholly to the right if (!this->canCullToTheRight()) {
this->appendVLine(clip.fRight, pts[0].fY, pts[3].fY, reverse);
} return;
}
// are we partially to the left if (pts[0].fX < clip.fLeft) {
SkPoint tmp[7];
chop_mono_cubic_at_x(pts, clip.fLeft, tmp);
this->appendVLine(clip.fLeft, tmp[0].fY, tmp[3].fY, reverse);
// tmp[3, 4].fX should all be to the right of clip.fLeft. // Since we can't trust the numerics of // the chopper, we force those conditions now
tmp[3].fX = clip.fLeft;
clamp_ge(tmp[4].fX, clip.fLeft);
// are we partially to the right if (pts[3].fX > clip.fRight) {
SkPoint tmp[7];
chop_mono_cubic_at_x(pts, clip.fRight, tmp);
tmp[3].fX = clip.fRight;
clamp_le(tmp[2].fX, clip.fRight);
staticbool too_big_for_reliable_float_math(const SkRect& r) { // limit set as the largest float value for which we can still reliably compute things like // - chopping at XY extrema // - chopping at Y or X values for clipping // // Current value chosen just by experiment. Larger (and still succeeds) is always better. // const SkScalar limit = 1 << 22; return r.fLeft < -limit || r.fTop < -limit || r.fRight > limit || r.fBottom > limit;
}
const SkRect bounds = compute_cubic_bounds(srcPts); // check if we're clipped out vertically if (bounds.fBottom > clip.fTop && bounds.fTop < clip.fBottom) { if (too_big_for_reliable_float_math(bounds)) { // can't safely clip the cubic, so we give up and draw a line (which we can safely clip) // // If we rewrote chopcubicat*extrema and chopmonocubic using doubles, we could very // likely always handle the cubic safely, but (it seems) at a big loss in speed, so // we'd only want to take that alternate impl if needed. Perhaps a TODO to try it. // return this->clipLine(srcPts[0], srcPts[3], clip);
} else {
SkPoint monoY[10]; int countY = SkChopCubicAtYExtrema(srcPts, monoY); for (int y = 0; y <= countY; y++) {
SkPoint monoX[10]; int countX = SkChopCubicAtXExtrema(&monoY[y * 3], monoX); for (int x = 0; x <= countX; x++) {
this->clipMonoCubic(&monoX[x * 3], clip);
SkASSERT(fCurrVerb - fVerbs < kMaxVerbs);
SkASSERT(fCurrPoint - fPoints <= kMaxPoints);
}
}
}
}
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.