// Extracts the high 8 bits of the fractional part of a 16.16 fixed-point // number, returning an 8-bit alpha value. staticinline U8CPU fixed_to_alpha(SkFixed f) { return (f >> 8) & 0xFF;
}
staticvoid call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
U8CPU alpha) {
SkASSERT(count > 0);
do { // In theory, we should be able to just do this once (outside of the loop), // since aa[] and runs[] are "supposed" to be const when we call the blitter. // In reality, some wrapper-blitters (e.g. SkRgnClipBlitter) cast away that // constness, and modify the buffers in-place. Hence the need to be defensive // here and reseed the aa value.
aa[0] = SkToU8(alpha);
int n = count; if (n > HLINE_STACK_BUFFER) {
n = HLINE_STACK_BUFFER;
}
runs[0] = SkToS16(n);
runs[n] = 0;
blitter->blitAntiH(x, y, aa, runs);
x += n;
count -= n;
} while (count > 0);
}
// This is an abstract class that defines the blitter interface for drawing // anti-aliased hairlines. There are concrete implementations for different // line orientations. The do_anti_hairline function chooses the appropriate // implementation based on the line's slope. class SkAntiHairBlitter {
public:
SkAntiHairBlitter() : fBlitter(nullptr) {}
virtual ~SkAntiHairBlitter() {}
// This class is responsible for drawing perfectly horizontal hairlines. // Such hairlines will be over two rows. One row may be 100% coverage // if the line was exactly on a pixel row boundary. class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
public:
SkFixed drawCap(int x, SkFixed fy, SkFixed, SkFDot6 coverage) override {
fy += SK_FixedHalf;
int y = SkFixedFloorToInt(fy); // Compute an alpha va lue based on the fractional part of fy // 0 means fy was at NN.5 and we'll only be drawing the upper line. // 128 means fy was at NN.0 and we'll be coloring both lines approximately // the same opacity.
U8CPU a = fixed_to_alpha(fy);
// lower line
U8CPU ma = scale_alpha_by_coverage(a, coverage); if (ma) {
call_hline_blitter(this->getBlitter(), x, y, 1, ma);
}
// upper line
ma = scale_alpha_by_coverage(255 - a, coverage); if (ma) {
call_hline_blitter(this->getBlitter(), x, y - 1, 1, ma);
}
int y = SkFixedFloorToInt(fy);
U8CPU a = fixed_to_alpha(fy);
// lower line if (a) {
call_hline_blitter(this->getBlitter(), x, y, count, a);
}
// upper line
a = 255 - a; if (a) {
call_hline_blitter(this->getBlitter(), x, y - 1, count, a);
}
return fy - SK_FixedHalf;
}
};
// This class handles lines that are mostly horizontal (i.e., their slope is // between -1 and 1). class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
public:
SkFixed drawCap(int x, SkFixed fy, SkFixed dy, SkFDot6 coverage) override {
fy += SK_FixedHalf;
int lower_y = SkFixedFloorToInt(fy);
U8CPU a = fixed_to_alpha(fy);
U8CPU a0 = scale_alpha_by_coverage(255 - a, coverage);
U8CPU a1 = scale_alpha_by_coverage(a, coverage);
this->getBlitter()->blitAntiV2(x, lower_y - 1, a0, a1);
fy += SK_FixedHalf;
SkBlitter* blitter = this->getBlitter(); do { int lower_y = SkFixedFloorToInt(fy);
U8CPU a = fixed_to_alpha(fy);
blitter->blitAntiV2(x, lower_y - 1, 255 - a, a);
fy += dy;
} while (++x < stopx);
return fy - SK_FixedHalf;
}
};
// This class is responsible for drawing perfectly vertical hairlines. class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
public:
SkFixed drawCap(int y, SkFixed fx, SkFixed dx, SkFDot6 coverage) override {
SkASSERT(0 == dx);
fx += SK_FixedHalf;
int x = SkFixedFloorToInt(fx);
U8CPU a = fixed_to_alpha(fx);
U8CPU ma = scale_alpha_by_coverage(a, coverage); if (ma) {
this->getBlitter()->blitV(x, y, 1, ma);
}
ma = scale_alpha_by_coverage(255 - a, coverage); if (ma) {
this->getBlitter()->blitV(x - 1, y, 1, ma);
}
return fx - SK_FixedHalf;
}
SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
SkASSERT(y < stopy);
SkASSERT(0 == dx);
fx += SK_FixedHalf;
int x = SkFixedFloorToInt(fx);
U8CPU a = fixed_to_alpha(fx);
if (a) {
this->getBlitter()->blitV(x, y, stopy - y, a);
}
a = 255 - a; if (a) {
this->getBlitter()->blitV(x - 1, y, stopy - y, a);
}
return fx - SK_FixedHalf;
}
};
// This class handles lines that are mostly vertical (i.e., their slope is // greater than 1 or less than -1). class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
public:
SkFixed drawCap(int y, SkFixed fx, SkFixed dx, SkFDot6 coverage) override {
fx += SK_FixedHalf;
int x = SkFixedFloorToInt(fx);
U8CPU a = fixed_to_alpha(fx);
this->getBlitter()->blitAntiH2(x - 1, y,
scale_alpha_by_coverage(255 - a, coverage), scale_alpha_by_coverage(a, coverage));
return fx + dx - SK_FixedHalf;
}
SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
SkASSERT(y < stopy);
fx += SK_FixedHalf; do { int x = SkFixedFloorToInt(fx);
U8CPU a = fixed_to_alpha(fx);
this->getBlitter()->blitAntiH2(x - 1, y, 255 - a, a);
fx += dx;
} while (++y < stopy);
// Returns the fractional part of the passed in number. // e.g. 2.75 -> 0.75 staticinline SkFDot6 fd6_frac(SkFDot6 x) { return x & (SK_FDot6One - 1);
}
/* *Wewantthefractionalpartofxory,butwewantmultiplesof64to *return64,not0,sowecan'tjustsaytakethefractionalcomponent. *Webasicallywanttocomputethosebits,andifthey're0,return64. *Wecandothatw/oabranchwithanextrasubandadd.
*/ static SkFDot6 partial_pixel_coverage(SkFDot6 pos) { #if0 int result = fd6_frac(pos); if (0 == result) {
result = SK_FDot6One;
} #else int result = fd6_frac(pos - 1) + 1; #endif
SkASSERT(result > 0 && result <= SK_FDot6One); return result;
}
staticvoid do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1, const SkIRect* clip, SkBlitter* blitter) { // check for integer NaN (0x80000000) which we can't handle (can't negate it) // It appears typically from a huge float (inf or nan) being converted to int. // If we see it, just don't draw. if (any_bad_ints(x0, y0, x1, y1)) { return;
}
// The caller must clip the line to [-32767.0 ... 32767.0] ahead of time // (in dot6 format)
SkASSERT(canConvertFDot6ToFixed(x0));
SkASSERT(canConvertFDot6ToFixed(y0));
SkASSERT(canConvertFDot6ToFixed(x1));
SkASSERT(canConvertFDot6ToFixed(y1));
if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) { /* instead of (x0 + x1) >> 1, we shift each separately. This is less precise,butavoidsoverflowingtheintermediateresultifthe valuesarehuge.Abetterfixmightbetocliptheoriginalpts directly(i.e.dothedivide),sowedon'tspendtimesubdividing hugelinesatall.
*/ int hx = (x0 >> 1) + (x1 >> 1); int hy = (y0 >> 1) + (y1 >> 1);
do_anti_hairline(x0, y0, hx, hy, clip, blitter);
do_anti_hairline(hx, hy, x1, y1, clip, blitter); return;
}
int startCoverage, stopCoverage; int istart, istop;
SkFixed fstart, slope;
if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) { // mostly horizontal if (x0 > x1) { // we want to go left-to-right
using std::swap;
swap(x0, x1);
swap(y0, y1);
}
istart = SkFDot6Floor(x0);
istop = SkFDot6Ceil(x1); if (y0 == y1) { // completely horizontal, take fast case
slope = 0;
hairBlitter = &hline_blitter;
fstart = SkFDot6ToFixed(y0);
} else {
slope = fastfixdiv(y1 - y0, x1 - x0);
SkASSERTF(slope >= -SK_Fixed1 && slope <= SK_Fixed1, "should be vertical or mostly vertical"); // Adjust fstart to be the Y-intercept at the center of the first pixel.
SkFDot6 dx_to_center = SK_FDot6Half - fd6_frac(x0);
fstart = SkFDot6ToFixed(y0) + ((slope * dx_to_center + SK_FDot6Half) >> 6);
hairBlitter = &horish_blitter;
}
SkASSERT(istop > istart); if (istop - istart == 1) { // we are within a single pixel
startCoverage = x1 - x0;
SkASSERT(startCoverage >= 0 && startCoverage <= SK_FDot6One);
stopCoverage = 0;
} else {
startCoverage = SK_FDot6One - fd6_frac(x0);
stopCoverage = fd6_frac(x1);
}
if (clip){ if (istart >= clip->fRight || istop <= clip->fLeft) { return;
} if (istart < clip->fLeft) {
fstart += slope * (clip->fLeft - istart);
istart = clip->fLeft;
startCoverage = SK_FDot6One; if (istop - istart == 1) { // we are within a single pixel
startCoverage = partial_pixel_coverage(x1);
stopCoverage = 0;
}
} if (istop > clip->fRight) {
istop = clip->fRight;
stopCoverage = 0; // so we don't draw this last column
}
SkASSERT(istart <= istop); if (istart == istop) { return;
} // now test if our Y values are completely inside the clip int top, bottom; if (slope >= 0) { // T2B
top = SkFixedFloorToInt(fstart - SK_FixedHalf);
bottom = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
} else { // B2T
bottom = SkFixedCeilToInt(fstart + SK_FixedHalf);
top = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
} // Expand outset to work around possible numerical calculation bug that lead to overflow
top -= 1;
bottom += 1;
if (top >= clip->fBottom || bottom <= clip->fTop) { return;
} if (clip->fTop <= top && clip->fBottom >= bottom) {
clip = nullptr;
}
}
} else { // mostly vertical if (y0 > y1) { // we want to go top-to-bottom
using std::swap;
swap(x0, x1);
swap(y0, y1);
}
istart = SkFDot6Floor(y0);
istop = SkFDot6Ceil(y1); if (x0 == x1) { if (y0 == y1) { // are we zero length? return; // nothing to do
}
slope = 0;
hairBlitter = &vline_blitter;
fstart = SkFDot6ToFixed(x0);
} else {
slope = fastfixdiv(x1 - x0, y1 - y0);
SkASSERTF(slope <= SK_Fixed1 && slope >= -SK_Fixed1, "should be horizontal or mostly horizontal"); // Adjust fstart to be the X-intercept at the center of the first pixel row.
SkFDot6 dy_to_center = SK_FDot6Half - fd6_frac(y0);
fstart = SkFDot6ToFixed(x0) + ((slope * dy_to_center + SK_FDot6Half) >> 6);
hairBlitter = &vertish_blitter;
}
SkASSERT(istop > istart); if (istop - istart == 1) { // we are within a single pixel
startCoverage = y1 - y0;
SkASSERT(startCoverage >= 0 && startCoverage <= SK_FDot6One);
stopCoverage = 0;
} else {
startCoverage = SK_FDot6One - fd6_frac(y0);
stopCoverage = fd6_frac(y1);
}
if (clip) { if (istart >= clip->fBottom || istop <= clip->fTop) { return;
} if (istart < clip->fTop) {
fstart += slope * (clip->fTop - istart);
istart = clip->fTop;
startCoverage = SK_FDot6One; if (istop - istart == 1) { // we are within a single pixel
startCoverage = partial_pixel_coverage(y1);
stopCoverage = 0;
}
} if (istop > clip->fBottom) {
istop = clip->fBottom;
stopCoverage = 0; // so we don't draw this last row
}
SkASSERT(istart <= istop); if (istart == istop) return;
// now test if our X values are completely inside the clip int left, right; if (slope >= 0) { // L2R
left = SkFixedFloorToInt(fstart - SK_FixedHalf);
right = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
} else { // R2L
right = SkFixedCeilToInt(fstart + SK_FixedHalf);
left = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
} // Expand outset to work around possible numerical calculation bug that lead to overflow
left -= 1;
right += 1;
if (left >= clip->fRight || right <= clip->fLeft) { return;
} if (clip->fLeft <= left && clip->fRight >= right) {
clip = nullptr;
}
}
}
SkRect clipBounds; if (clip) {
clipBounds.set(clip->getBounds()); /* We perform integral clipping later on, but we do a scalar clip first toensurethatourcoordinatesareexpressibleinfixed/integers.
for (size_t i = 0; i < src.size() - 1; ++i) {
SkPoint pts[2];
// We have to pre-clip the line to fit in a SkFixed, so we just chop // the line. TODO find a way to actually draw beyond that range. if (!SkLineClipper::IntersectLine(&src[i], fixedBounds, pts)) { continue;
}
if (clip && !SkLineClipper::IntersectLine(pts, clipBounds, pts)) { continue;
}
if (L & 0xFF) {
blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
left += 1;
}
int rite = R >> 8; int width = rite - left; if (width > 0) {
call_hline_blitter(blitter, left, top, width, alpha);
} if (R & 0xFF) {
blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
}
}
staticvoid antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter, bool fillInner) { // check for empty now that we're in our reduced precision space if (L >= R || T >= B) { return;
} int top = T >> 8; if (top == ((B - 1) >> 8)) { // just one scanline high
do_scanline(L, top, R, B - T - 1, blitter); return;
}
if (T & 0xFF) {
do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
top += 1;
}
int bot = B >> 8; int height = bot - top; if (height > 0) { int left = L >> 8; if (left == ((R - 1) >> 8)) { // just 1-pixel wide
blitter->blitV(left, top, height, R - L - 1);
} else { if (L & 0xFF) {
blitter->blitV(left, top, height, 256 - (L & 0xFF));
left += 1;
} int rite = R >> 8; int width = rite - left; if (width > 0 && fillInner) {
blitter->blitRect(left, top, width, height);
} if (R & 0xFF) {
blitter->blitV(rite, top, height, R & 0xFF);
}
}
}
if (B & 0xFF) {
do_scanline(L, bot, R, B & 0xFF, blitter);
}
}
/* This takes a float-rect, but with the key improvement that it has alreadybeenclipped,soweknowthatitissafetoconvertitintoa XRect(fixedpoint),asitwon'toverflow.
*/ staticvoid antifillrect(const SkRect& r, SkBlitter* blitter) {
SkXRect xr;
XRect_set(&xr, r);
antifillrect(xr, blitter);
}
/* We repeat the clipping logic of AntiFillXRect because the float rect might overflowifweblindlyconvertedittoanXRect.Thissucksthatwehaveto repeattheclippinglogic,butIdon'tseehowtosharethecode/logic.
#define SkAlphaMulRound(a, b) SkMulDiv255Round(a, b)
// calls blitRect() if the rectangle is non-empty staticvoid fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) { if (L < R && T < B) {
blitter->blitRect(L, T, R - L, B - T);
}
}
// 1 - (1 - a)*(1 - b) staticinline U8CPU InvAlphaMul(U8CPU a, U8CPU b) { // need precise rounding (not just SkAlphaMul) so that values like // a=228, b=252 don't overflow the result return SkToU8(a + b - SkAlphaMulRound(a, b));
}
staticvoid innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
SkBlitter* blitter) {
SkASSERT(L < R && T < B);
int top = T >> 8; if (top == ((B - 1) >> 8)) { // just one scanline high // We want the inverse of B-T, since we're the inner-stroke int alpha = 256 - (B - T); if (alpha) {
inner_scanline(L, top, R, alpha, blitter);
} return;
}
if (T & 0xFF) {
inner_scanline(L, top, R, T & 0xFF, blitter);
top += 1;
}
int bot = B >> 8; int height = bot - top; if (height > 0) { if (L & 0xFF) {
blitter->blitV(L >> 8, top, height, L & 0xFF);
} if (R & 0xFF) {
blitter->blitV(R >> 8, top, height, ~R & 0xFF);
}
}
SkScalar rx = SkScalarHalf(strokeSize.fX);
SkScalar ry = SkScalarHalf(strokeSize.fY);
// If we're empty on either axis, we remove the outset amount, to be sure // we stroke the same way a polygon would (i.e. it would just see a "line" // and not extend it for the miter join). if (r.width() == 0) {
ry = 0;
} if (r.height() == 0) {
rx = 0;
}
SkIRect outer; // set outer to the outer rect of the outer section
outer.setLTRB(FDot8Floor(outerL), FDot8Floor(outerT), FDot8Ceil(outerR), FDot8Ceil(outerB));
SkBlitterClipper clipper; if (clip) { if (clip->quickReject(outer)) { return;
} if (!clip->contains(outer)) {
blitter = clipper.apply(blitter, clip, &outer);
} // now we can ignore clip for the rest of the function
}
// in case we lost a bit with diameter/2
rx = strokeSize.fX - rx;
ry = strokeSize.fY - ry;
// For sub-unit strokes, tweak the hulls such that one of the edges coincides with the pixel // edge. This ensures that the general rect stroking logic below // a) doesn't blit the same scanline twice // b) computes the correct coverage when both edges fall within the same pixel if (strokeSize.fX < 1 || strokeSize.fY < 1) {
align_thin_stroke(outerL, innerL);
align_thin_stroke(outerT, innerT);
align_thin_stroke(innerR, outerR);
align_thin_stroke(innerB, outerB);
}
// set outer to the outer rect of the middle section
outer.setLTRB(FDot8Ceil(outerL), FDot8Ceil(outerT), FDot8Floor(outerR), FDot8Floor(outerB));
if (innerL >= innerR || innerT >= innerB) {
fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
blitter);
} else {
SkIRect inner; // set inner to the inner rect of the middle section
inner.setLTRB(FDot8Floor(innerL), FDot8Floor(innerT), FDot8Ceil(innerR), FDot8Ceil(innerB));
// now stroke the inner rect, which is similar to antifilldot8() except that // it treats the fractional coordinates with the inverse bias (since its // inner).
innerstrokedot8(innerL, innerT, innerR, innerB, blitter);
}
}
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.