1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009 |
- /*
- * Copyright 2011 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.
- */
- #include "src/core/SkScan.h"
- #include "include/private/SkColorData.h"
- #include "include/private/SkTo.h"
- #include "src/core/SkBlitter.h"
- #include "src/core/SkFDot6.h"
- #include "src/core/SkLineClipper.h"
- #include "src/core/SkRasterClip.h"
- #include <utility>
- /* Our attempt to compute the worst case "bounds" for the horizontal and
- vertical cases has some numerical bug in it, and we sometimes undervalue
- our extends. The bug is that when this happens, we will set the clip to
- nullptr (for speed), and thus draw outside of the clip by a pixel, which might
- only look bad, but it might also access memory outside of the valid range
- allcoated for the device bitmap.
- This define enables our fix to outset our "bounds" by 1, thus avoiding the
- chance of the bug, but at the cost of sometimes taking the rectblitter
- case (i.e. not setting the clip to nullptr) when we might not actually need
- to. If we can improve/fix the actual calculations, then we can remove this
- step.
- */
- #define OUTSET_BEFORE_CLIP_TEST true
- #define HLINE_STACK_BUFFER 100
- static inline int SmallDot6Scale(int value, int dot6) {
- SkASSERT((int16_t)value == value);
- SkASSERT((unsigned)dot6 <= 64);
- return (value * dot6) >> 6;
- }
- //#define TEST_GAMMA
- #ifdef TEST_GAMMA
- static uint8_t gGammaTable[256];
- #define ApplyGamma(table, alpha) (table)[alpha]
- static void build_gamma_table() {
- static bool gInit = false;
- if (gInit == false) {
- for (int i = 0; i < 256; i++) {
- SkFixed n = i * 257;
- n += n >> 15;
- SkASSERT(n >= 0 && n <= SK_Fixed1);
- n = SkFixedSqrt(n);
- n = n * 255 >> 16;
- // SkDebugf("morph %d -> %d\n", i, n);
- gGammaTable[i] = SkToU8(n);
- }
- gInit = true;
- }
- }
- #else
- #define ApplyGamma(table, alpha) SkToU8(alpha)
- #endif
- ///////////////////////////////////////////////////////////////////////////////
- static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
- U8CPU alpha) {
- SkASSERT(count > 0);
- int16_t runs[HLINE_STACK_BUFFER + 1];
- uint8_t aa[HLINE_STACK_BUFFER];
- aa[0] = ApplyGamma(gGammaTable, alpha);
- do {
- 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);
- }
- class SkAntiHairBlitter {
- public:
- SkAntiHairBlitter() : fBlitter(nullptr) {}
- virtual ~SkAntiHairBlitter() {}
- SkBlitter* getBlitter() const { return fBlitter; }
- void setup(SkBlitter* blitter) {
- fBlitter = blitter;
- }
- virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) = 0;
- virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed slope) = 0;
- private:
- SkBlitter* fBlitter;
- };
- class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
- public:
- SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) override {
- fy += SK_Fixed1/2;
- int y = fy >> 16;
- uint8_t a = (uint8_t)((fy >> 8) & 0xFF);
- // lower line
- unsigned ma = SmallDot6Scale(a, mod64);
- if (ma) {
- call_hline_blitter(this->getBlitter(), x, y, 1, ma);
- }
- // upper line
- ma = SmallDot6Scale(255 - a, mod64);
- if (ma) {
- call_hline_blitter(this->getBlitter(), x, y - 1, 1, ma);
- }
- return fy - SK_Fixed1/2;
- }
- virtual SkFixed drawLine(int x, int stopx, SkFixed fy,
- SkFixed slope) override {
- SkASSERT(x < stopx);
- int count = stopx - x;
- fy += SK_Fixed1/2;
- int y = fy >> 16;
- uint8_t a = (uint8_t)((fy >> 8) & 0xFF);
- // 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_Fixed1/2;
- }
- };
- class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
- public:
- SkFixed drawCap(int x, SkFixed fy, SkFixed dy, int mod64) override {
- fy += SK_Fixed1/2;
- int lower_y = fy >> 16;
- uint8_t a = (uint8_t)((fy >> 8) & 0xFF);
- unsigned a0 = SmallDot6Scale(255 - a, mod64);
- unsigned a1 = SmallDot6Scale(a, mod64);
- this->getBlitter()->blitAntiV2(x, lower_y - 1, a0, a1);
- return fy + dy - SK_Fixed1/2;
- }
- SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) override {
- SkASSERT(x < stopx);
- fy += SK_Fixed1/2;
- SkBlitter* blitter = this->getBlitter();
- do {
- int lower_y = fy >> 16;
- uint8_t a = (uint8_t)((fy >> 8) & 0xFF);
- blitter->blitAntiV2(x, lower_y - 1, 255 - a, a);
- fy += dy;
- } while (++x < stopx);
- return fy - SK_Fixed1/2;
- }
- };
- class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
- public:
- SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override {
- SkASSERT(0 == dx);
- fx += SK_Fixed1/2;
- int x = fx >> 16;
- int a = (uint8_t)((fx >> 8) & 0xFF);
- unsigned ma = SmallDot6Scale(a, mod64);
- if (ma) {
- this->getBlitter()->blitV(x, y, 1, ma);
- }
- ma = SmallDot6Scale(255 - a, mod64);
- if (ma) {
- this->getBlitter()->blitV(x - 1, y, 1, ma);
- }
- return fx - SK_Fixed1/2;
- }
- SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
- SkASSERT(y < stopy);
- SkASSERT(0 == dx);
- fx += SK_Fixed1/2;
- int x = fx >> 16;
- int a = (uint8_t)((fx >> 8) & 0xFF);
- 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_Fixed1/2;
- }
- };
- class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
- public:
- SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override {
- fx += SK_Fixed1/2;
- int x = fx >> 16;
- uint8_t a = (uint8_t)((fx >> 8) & 0xFF);
- this->getBlitter()->blitAntiH2(x - 1, y,
- SmallDot6Scale(255 - a, mod64), SmallDot6Scale(a, mod64));
- return fx + dx - SK_Fixed1/2;
- }
- SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
- SkASSERT(y < stopy);
- fx += SK_Fixed1/2;
- do {
- int x = fx >> 16;
- uint8_t a = (uint8_t)((fx >> 8) & 0xFF);
- this->getBlitter()->blitAntiH2(x - 1, y, 255 - a, a);
- fx += dx;
- } while (++y < stopy);
- return fx - SK_Fixed1/2;
- }
- };
- static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
- SkASSERT((SkLeftShift(a, 16) >> 16) == a);
- SkASSERT(b != 0);
- return SkLeftShift(a, 16) / b;
- }
- #define SkBITCOUNT(x) (sizeof(x) << 3)
- #if 1
- // returns high-bit set iff x==0x8000...
- static inline int bad_int(int x) {
- return x & -x;
- }
- static int any_bad_ints(int a, int b, int c, int d) {
- return (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> (SkBITCOUNT(int) - 1);
- }
- #else
- static inline int good_int(int x) {
- return x ^ (1 << (SkBITCOUNT(x) - 1));
- }
- static int any_bad_ints(int a, int b, int c, int d) {
- return !(good_int(a) & good_int(b) & good_int(c) & good_int(d));
- }
- #endif
- #ifdef SK_DEBUG
- static bool canConvertFDot6ToFixed(SkFDot6 x) {
- const int maxDot6 = SK_MaxS32 >> (16 - 6);
- return SkAbs32(x) <= maxDot6;
- }
- #endif
- /*
- * We want the fractional part of ordinate, but we want multiples of 64 to
- * return 64, not 0, so we can't just say (ordinate & 63).
- * We basically want to compute those bits, and if they're 0, return 64.
- * We can do that w/o a branch with an extra sub and add.
- */
- static int contribution_64(SkFDot6 ordinate) {
- #if 0
- int result = ordinate & 63;
- if (0 == result) {
- result = 64;
- }
- #else
- int result = ((ordinate - 1) & 63) + 1;
- #endif
- SkASSERT(result > 0 && result <= 64);
- return result;
- }
- static void 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, but avoids overflowing the intermediate result if the
- values are huge. A better fix might be to clip the original pts
- directly (i.e. do the divide), so we don't spend time subdividing
- huge lines at all.
- */
- 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 scaleStart, scaleStop;
- int istart, istop;
- SkFixed fstart, slope;
- HLine_SkAntiHairBlitter hline_blitter;
- Horish_SkAntiHairBlitter horish_blitter;
- VLine_SkAntiHairBlitter vline_blitter;
- Vertish_SkAntiHairBlitter vertish_blitter;
- SkAntiHairBlitter* hairBlitter = nullptr;
- 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);
- fstart = SkFDot6ToFixed(y0);
- if (y0 == y1) { // completely horizontal, take fast case
- slope = 0;
- hairBlitter = &hline_blitter;
- } else {
- slope = fastfixdiv(y1 - y0, x1 - x0);
- SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
- fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
- hairBlitter = &horish_blitter;
- }
- SkASSERT(istop > istart);
- if (istop - istart == 1) {
- // we are within a single pixel
- scaleStart = x1 - x0;
- SkASSERT(scaleStart >= 0 && scaleStart <= 64);
- scaleStop = 0;
- } else {
- scaleStart = 64 - (x0 & 63);
- scaleStop = x1 & 63;
- }
- if (clip){
- if (istart >= clip->fRight || istop <= clip->fLeft) {
- return;
- }
- if (istart < clip->fLeft) {
- fstart += slope * (clip->fLeft - istart);
- istart = clip->fLeft;
- scaleStart = 64;
- if (istop - istart == 1) {
- // we are within a single pixel
- scaleStart = contribution_64(x1);
- scaleStop = 0;
- }
- }
- if (istop > clip->fRight) {
- istop = clip->fRight;
- scaleStop = 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);
- }
- #ifdef OUTSET_BEFORE_CLIP_TEST
- top -= 1;
- bottom += 1;
- #endif
- 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);
- fstart = SkFDot6ToFixed(x0);
- if (x0 == x1) {
- if (y0 == y1) { // are we zero length?
- return; // nothing to do
- }
- slope = 0;
- hairBlitter = &vline_blitter;
- } else {
- slope = fastfixdiv(x1 - x0, y1 - y0);
- SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
- fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
- hairBlitter = &vertish_blitter;
- }
- SkASSERT(istop > istart);
- if (istop - istart == 1) {
- // we are within a single pixel
- scaleStart = y1 - y0;
- SkASSERT(scaleStart >= 0 && scaleStart <= 64);
- scaleStop = 0;
- } else {
- scaleStart = 64 - (y0 & 63);
- scaleStop = y1 & 63;
- }
- if (clip) {
- if (istart >= clip->fBottom || istop <= clip->fTop) {
- return;
- }
- if (istart < clip->fTop) {
- fstart += slope * (clip->fTop - istart);
- istart = clip->fTop;
- scaleStart = 64;
- if (istop - istart == 1) {
- // we are within a single pixel
- scaleStart = contribution_64(y1);
- scaleStop = 0;
- }
- }
- if (istop > clip->fBottom) {
- istop = clip->fBottom;
- scaleStop = 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);
- }
- #ifdef OUTSET_BEFORE_CLIP_TEST
- left -= 1;
- right += 1;
- #endif
- if (left >= clip->fRight || right <= clip->fLeft) {
- return;
- }
- if (clip->fLeft <= left && clip->fRight >= right) {
- clip = nullptr;
- }
- }
- }
- SkRectClipBlitter rectClipper;
- if (clip) {
- rectClipper.init(blitter, *clip);
- blitter = &rectClipper;
- }
- SkASSERT(hairBlitter);
- hairBlitter->setup(blitter);
- #ifdef SK_DEBUG
- if (scaleStart > 0 && scaleStop > 0) {
- // be sure we don't draw twice in the same pixel
- SkASSERT(istart < istop - 1);
- }
- #endif
- fstart = hairBlitter->drawCap(istart, fstart, slope, scaleStart);
- istart += 1;
- int fullSpans = istop - istart - (scaleStop > 0);
- if (fullSpans > 0) {
- fstart = hairBlitter->drawLine(istart, istart + fullSpans, fstart, slope);
- }
- if (scaleStop > 0) {
- hairBlitter->drawCap(istop - 1, fstart, slope, scaleStop);
- }
- }
- void SkScan::AntiHairLineRgn(const SkPoint array[], int arrayCount, const SkRegion* clip,
- SkBlitter* blitter) {
- if (clip && clip->isEmpty()) {
- return;
- }
- SkASSERT(clip == nullptr || !clip->getBounds().isEmpty());
- #ifdef TEST_GAMMA
- build_gamma_table();
- #endif
- const SkScalar max = SkIntToScalar(32767);
- const SkRect fixedBounds = SkRect::MakeLTRB(-max, -max, max, max);
- SkRect clipBounds;
- if (clip) {
- clipBounds.set(clip->getBounds());
- /* We perform integral clipping later on, but we do a scalar clip first
- to ensure that our coordinates are expressible in fixed/integers.
- antialiased hairlines can draw up to 1/2 of a pixel outside of
- their bounds, so we need to outset the clip before calling the
- clipper. To make the numerics safer, we outset by a whole pixel,
- since the 1/2 pixel boundary is important to the antihair blitter,
- we don't want to risk numerical fate by chopping on that edge.
- */
- clipBounds.outset(SK_Scalar1, SK_Scalar1);
- }
- for (int i = 0; i < arrayCount - 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(&array[i], fixedBounds, pts)) {
- continue;
- }
- if (clip && !SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
- continue;
- }
- SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
- SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
- SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
- SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
- if (clip) {
- SkFDot6 left = SkMin32(x0, x1);
- SkFDot6 top = SkMin32(y0, y1);
- SkFDot6 right = SkMax32(x0, x1);
- SkFDot6 bottom = SkMax32(y0, y1);
- SkIRect ir;
- ir.set( SkFDot6Floor(left) - 1,
- SkFDot6Floor(top) - 1,
- SkFDot6Ceil(right) + 1,
- SkFDot6Ceil(bottom) + 1);
- if (clip->quickReject(ir)) {
- continue;
- }
- if (!clip->quickContains(ir)) {
- SkRegion::Cliperator iter(*clip, ir);
- const SkIRect* r = &iter.rect();
- while (!iter.done()) {
- do_anti_hairline(x0, y0, x1, y1, r, blitter);
- iter.next();
- }
- continue;
- }
- // fall through to no-clip case
- }
- do_anti_hairline(x0, y0, x1, y1, nullptr, blitter);
- }
- }
- void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
- SkBlitter* blitter) {
- SkPoint pts[5];
- pts[0].set(rect.fLeft, rect.fTop);
- pts[1].set(rect.fRight, rect.fTop);
- pts[2].set(rect.fRight, rect.fBottom);
- pts[3].set(rect.fLeft, rect.fBottom);
- pts[4] = pts[0];
- SkScan::AntiHairLine(pts, 5, clip, blitter);
- }
- ///////////////////////////////////////////////////////////////////////////////
- typedef int FDot8; // 24.8 integer fixed point
- static inline FDot8 SkFixedToFDot8(SkFixed x) {
- return (x + 0x80) >> 8;
- }
- static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
- SkBlitter* blitter) {
- SkASSERT(L < R);
- if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
- blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
- return;
- }
- int left = L >> 8;
- 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));
- }
- }
- static void 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);
- }
- }
- static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
- antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
- SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
- blitter, true);
- }
- ///////////////////////////////////////////////////////////////////////////////
- void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
- SkBlitter* blitter) {
- if (nullptr == clip) {
- antifillrect(xr, blitter);
- } else {
- SkIRect outerBounds;
- XRect_roundOut(xr, &outerBounds);
- if (clip->isRect()) {
- const SkIRect& clipBounds = clip->getBounds();
- if (clipBounds.contains(outerBounds)) {
- antifillrect(xr, blitter);
- } else {
- SkXRect tmpR;
- // this keeps our original edges fractional
- XRect_set(&tmpR, clipBounds);
- if (tmpR.intersect(xr)) {
- antifillrect(tmpR, blitter);
- }
- }
- } else {
- SkRegion::Cliperator clipper(*clip, outerBounds);
- const SkIRect& rr = clipper.rect();
- while (!clipper.done()) {
- SkXRect tmpR;
- // this keeps our original edges fractional
- XRect_set(&tmpR, rr);
- if (tmpR.intersect(xr)) {
- antifillrect(tmpR, blitter);
- }
- clipper.next();
- }
- }
- }
- }
- void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
- SkBlitter* blitter) {
- if (clip.isBW()) {
- AntiFillXRect(xr, &clip.bwRgn(), blitter);
- } else {
- SkIRect outerBounds;
- XRect_roundOut(xr, &outerBounds);
- if (clip.quickContains(outerBounds)) {
- AntiFillXRect(xr, nullptr, blitter);
- } else {
- SkAAClipBlitterWrapper wrapper(clip, blitter);
- AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
- }
- }
- }
- /* This guy takes a float-rect, but with the key improvement that it has
- already been clipped, so we know that it is safe to convert it into a
- XRect (fixedpoint), as it won't overflow.
- */
- static void 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
- overflow if we blindly converted it to an XRect. This sucks that we have to
- repeat the clipping logic, but I don't see how to share the code/logic.
- We clip r (as needed) into one or more (smaller) float rects, and then pass
- those to our version of antifillrect, which converts it into an XRect and
- then calls the blit.
- */
- void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
- SkBlitter* blitter) {
- if (clip) {
- SkRect newR;
- newR.set(clip->getBounds());
- if (!newR.intersect(origR)) {
- return;
- }
- const SkIRect outerBounds = newR.roundOut();
- if (clip->isRect()) {
- antifillrect(newR, blitter);
- } else {
- SkRegion::Cliperator clipper(*clip, outerBounds);
- while (!clipper.done()) {
- newR.set(clipper.rect());
- if (newR.intersect(origR)) {
- antifillrect(newR, blitter);
- }
- clipper.next();
- }
- }
- } else {
- antifillrect(origR, blitter);
- }
- }
- void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
- SkBlitter* blitter) {
- if (clip.isBW()) {
- AntiFillRect(r, &clip.bwRgn(), blitter);
- } else {
- SkAAClipBlitterWrapper wrap(clip, blitter);
- AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
- }
- }
- ///////////////////////////////////////////////////////////////////////////////
- #define SkAlphaMulRound(a, b) SkMulDiv255Round(a, b)
- // calls blitRect() if the rectangle is non-empty
- static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
- if (L < R && T < B) {
- blitter->blitRect(L, T, R - L, B - T);
- }
- }
- static inline FDot8 SkScalarToFDot8(SkScalar x) {
- return (int)(x * 256);
- }
- static inline int FDot8Floor(FDot8 x) {
- return x >> 8;
- }
- static inline int FDot8Ceil(FDot8 x) {
- return (x + 0xFF) >> 8;
- }
- // 1 - (1 - a)*(1 - b)
- static inline 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));
- }
- static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
- SkBlitter* blitter) {
- SkASSERT(L < R);
- if ((L >> 8) == ((R - 1) >> 8)) { // 1x1 pixel
- FDot8 widClamp = R - L;
- // border case clamp 256 to 255 instead of going through call_hline_blitter
- // see skbug/4406
- widClamp = widClamp - (widClamp >> 8);
- blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, widClamp));
- return;
- }
- int left = L >> 8;
- if (L & 0xFF) {
- blitter->blitV(left, top, 1, InvAlphaMul(alpha, 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, InvAlphaMul(alpha, ~R & 0xFF));
- }
- }
- static void 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);
- }
- }
- if (B & 0xFF) {
- inner_scanline(L, bot, R, ~B & 0xFF, blitter);
- }
- }
- static inline void align_thin_stroke(FDot8& edge1, FDot8& edge2) {
- SkASSERT(edge1 <= edge2);
- if (FDot8Floor(edge1) == FDot8Floor(edge2)) {
- edge2 -= (edge1 & 0xFF);
- edge1 &= ~0xFF;
- }
- }
- void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
- const SkRegion* clip, SkBlitter* blitter) {
- SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
- SkScalar rx = SkScalarHalf(strokeSize.fX);
- SkScalar ry = SkScalarHalf(strokeSize.fY);
- // outset by the radius
- FDot8 outerL = SkScalarToFDot8(r.fLeft - rx);
- FDot8 outerT = SkScalarToFDot8(r.fTop - ry);
- FDot8 outerR = SkScalarToFDot8(r.fRight + rx);
- FDot8 outerB = SkScalarToFDot8(r.fBottom + ry);
- SkIRect outer;
- // set outer to the outer rect of the outer section
- outer.set(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;
- // inset by the radius
- FDot8 innerL = SkScalarToFDot8(r.fLeft + rx);
- FDot8 innerT = SkScalarToFDot8(r.fTop + ry);
- FDot8 innerR = SkScalarToFDot8(r.fRight - rx);
- FDot8 innerB = SkScalarToFDot8(r.fBottom - 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);
- }
- // stroke the outer hull
- antifilldot8(outerL, outerT, outerR, outerB, blitter, false);
- // set outer to the outer rect of the middle section
- outer.set(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.set(FDot8Floor(innerL), FDot8Floor(innerT), FDot8Ceil(innerR), FDot8Ceil(innerB));
- // draw the frame in 4 pieces
- fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
- blitter);
- fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
- blitter);
- fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
- blitter);
- fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
- blitter);
- // 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);
- }
- }
- void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
- const SkRasterClip& clip, SkBlitter* blitter) {
- if (clip.isBW()) {
- AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
- } else {
- SkAAClipBlitterWrapper wrap(clip, blitter);
- AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
- }
- }
|