/* * Copyright 2009 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.
*/
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 it to be increasing in Y. If it had to reverse the order of the points, it returns true, otherwise it returns false
*/ 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];
} returntrue;
} 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 * We solve for t, using quadratic equation, hence we have to rearrange * our cooefficents to look like At^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]; returntrue;
} 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);
}
}
static SkScalar mono_cubic_closestT(const SkScalar src[], SkScalar x) {
SkScalar t = 0.5f;
SkScalar lastT;
SkScalar bestT SK_INIT_TO_AVOID_WARNING;
SkScalar step = 0.25f;
SkScalar D = src[0];
SkScalar A = src[6] + 3*(src[2] - src[4]) - D;
SkScalar B = 3*(src[4] - src[2] - src[2] + D);
SkScalar C = 3*(src[2] - D);
x -= D;
SkScalar closest = SK_ScalarMax; do {
SkScalar loc = ((A * t + B) * t + C) * t;
SkScalar dist = SkScalarAbs(loc - x); if (closest > dist) {
closest = dist;
bestT = t;
}
lastT = t;
t += loc < x ? step : -step;
step *= 0.5f;
} while (closest > 0.25f && lastT != t); return bestT;
}
staticvoid chop_mono_cubic_at_y(SkPoint src[4], SkScalar y, SkPoint dst[7]) { if (SkChopMonoCubicAtY(src, y, dst)) { return;
}
SkChopCubicAt(src, dst, mono_cubic_closestT(&src->fY, y));
}
// Modify pts[] in place so that it is clipped in Y to the clip rect staticvoid chop_cubic_in_Y(SkPoint pts[4], const SkRect& clip) {
// are we partially above if (pts[0].fY < clip.fTop) {
SkPoint tmp[7];
chop_mono_cubic_at_y(pts, clip.fTop, tmp);
/* * For a large range in the points, we can do a poor job of chopping, such that the t * we computed resulted in the lower cubic still being partly above the clip. * * If just the first or first 2 Y values are above the fTop, we can just smash them * down. If the first 3 Ys are above fTop, we can't smash all 3, as that can really * distort the cubic. In this case, we take the first output (tmp[3..6] and treat it as * a guess, and re-chop against fTop. Then we fall through to checking if we need to * smash the first 1 or 2 Y values.
*/ if (tmp[3].fY < clip.fTop && tmp[4].fY < clip.fTop && tmp[5].fY < clip.fTop) {
SkPoint tmp2[4];
memcpy(tmp2, &tmp[3].fX, 4 * sizeof(SkPoint));
chop_mono_cubic_at_y(tmp2, clip.fTop, tmp);
}
// 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.