LCOV - code coverage report
Current view: top level - gfx/skia/skia/src/core - SkScan_Antihair.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 73 503 14.5 %
Date: 2017-07-14 16:53:18 Functions: 8 54 14.8 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright 2011 The Android Open Source Project
       3             :  *
       4             :  * Use of this source code is governed by a BSD-style license that can be
       5             :  * found in the LICENSE file.
       6             :  */
       7             : 
       8             : 
       9             : #include "SkScan.h"
      10             : #include "SkBlitter.h"
      11             : #include "SkColorPriv.h"
      12             : #include "SkLineClipper.h"
      13             : #include "SkRasterClip.h"
      14             : #include "SkFDot6.h"
      15             : 
      16             : /*  Our attempt to compute the worst case "bounds" for the horizontal and
      17             :     vertical cases has some numerical bug in it, and we sometimes undervalue
      18             :     our extends. The bug is that when this happens, we will set the clip to
      19             :     nullptr (for speed), and thus draw outside of the clip by a pixel, which might
      20             :     only look bad, but it might also access memory outside of the valid range
      21             :     allcoated for the device bitmap.
      22             : 
      23             :     This define enables our fix to outset our "bounds" by 1, thus avoiding the
      24             :     chance of the bug, but at the cost of sometimes taking the rectblitter
      25             :     case (i.e. not setting the clip to nullptr) when we might not actually need
      26             :     to. If we can improve/fix the actual calculations, then we can remove this
      27             :     step.
      28             :  */
      29             : #define OUTSET_BEFORE_CLIP_TEST     true
      30             : 
      31             : #define HLINE_STACK_BUFFER      100
      32             : 
      33           0 : static inline int SmallDot6Scale(int value, int dot6) {
      34           0 :     SkASSERT((int16_t)value == value);
      35           0 :     SkASSERT((unsigned)dot6 <= 64);
      36           0 :     return (value * dot6) >> 6;
      37             : }
      38             : 
      39             : //#define TEST_GAMMA
      40             : 
      41             : #ifdef TEST_GAMMA
      42             :     static uint8_t gGammaTable[256];
      43             :     #define ApplyGamma(table, alpha)    (table)[alpha]
      44             : 
      45             :     static void build_gamma_table() {
      46             :         static bool gInit = false;
      47             : 
      48             :         if (gInit == false) {
      49             :             for (int i = 0; i < 256; i++) {
      50             :                 SkFixed n = i * 257;
      51             :                 n += n >> 15;
      52             :                 SkASSERT(n >= 0 && n <= SK_Fixed1);
      53             :                 n = SkFixedSqrt(n);
      54             :                 n = n * 255 >> 16;
      55             :             //  SkDebugf("morph %d -> %d\n", i, n);
      56             :                 gGammaTable[i] = SkToU8(n);
      57             :             }
      58             :             gInit = true;
      59             :         }
      60             :     }
      61             : #else
      62             :     #define ApplyGamma(table, alpha)    SkToU8(alpha)
      63             : #endif
      64             : 
      65             : ///////////////////////////////////////////////////////////////////////////////
      66             : 
      67          10 : static void call_hline_blitter(SkBlitter* blitter, int x, int y, int count,
      68             :                                U8CPU alpha) {
      69          10 :     SkASSERT(count > 0);
      70             : 
      71             :     int16_t runs[HLINE_STACK_BUFFER + 1];
      72             :     uint8_t  aa[HLINE_STACK_BUFFER];
      73             : 
      74          10 :     aa[0] = ApplyGamma(gGammaTable, alpha);
      75           2 :     do {
      76          12 :         int n = count;
      77          12 :         if (n > HLINE_STACK_BUFFER) {
      78           2 :             n = HLINE_STACK_BUFFER;
      79             :         }
      80          12 :         runs[0] = SkToS16(n);
      81          12 :         runs[n] = 0;
      82          12 :         blitter->blitAntiH(x, y, aa, runs);
      83          12 :         x += n;
      84          12 :         count -= n;
      85          12 :     } while (count > 0);
      86          10 : }
      87             : 
      88             : class SkAntiHairBlitter {
      89             : public:
      90           0 :     SkAntiHairBlitter() : fBlitter(nullptr) {}
      91           0 :     virtual ~SkAntiHairBlitter() {}
      92             : 
      93           0 :     SkBlitter* getBlitter() const { return fBlitter; }
      94             : 
      95           0 :     void setup(SkBlitter* blitter) {
      96           0 :         fBlitter = blitter;
      97           0 :     }
      98             : 
      99             :     virtual SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) = 0;
     100             :     virtual SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed slope) = 0;
     101             : 
     102             : private:
     103             :     SkBlitter*  fBlitter;
     104             : };
     105             : 
     106           0 : class HLine_SkAntiHairBlitter : public SkAntiHairBlitter {
     107             : public:
     108           0 :     SkFixed drawCap(int x, SkFixed fy, SkFixed slope, int mod64) override {
     109           0 :         fy += SK_Fixed1/2;
     110             : 
     111           0 :         int y = fy >> 16;
     112           0 :         uint8_t  a = (uint8_t)(fy >> 8);
     113             : 
     114             :         // lower line
     115           0 :         unsigned ma = SmallDot6Scale(a, mod64);
     116           0 :         if (ma) {
     117           0 :             call_hline_blitter(this->getBlitter(), x, y, 1, ma);
     118             :         }
     119             : 
     120             :         // upper line
     121           0 :         ma = SmallDot6Scale(255 - a, mod64);
     122           0 :         if (ma) {
     123           0 :             call_hline_blitter(this->getBlitter(), x, y - 1, 1, ma);
     124             :         }
     125             : 
     126           0 :         return fy - SK_Fixed1/2;
     127             :     }
     128             : 
     129           0 :     virtual SkFixed drawLine(int x, int stopx, SkFixed fy,
     130             :                              SkFixed slope) override {
     131           0 :         SkASSERT(x < stopx);
     132           0 :         int count = stopx - x;
     133           0 :         fy += SK_Fixed1/2;
     134             : 
     135           0 :         int y = fy >> 16;
     136           0 :         uint8_t  a = (uint8_t)(fy >> 8);
     137             : 
     138             :         // lower line
     139           0 :         if (a) {
     140           0 :             call_hline_blitter(this->getBlitter(), x, y, count, a);
     141             :         }
     142             : 
     143             :         // upper line
     144           0 :         a = 255 - a;
     145           0 :         if (a) {
     146           0 :             call_hline_blitter(this->getBlitter(), x, y - 1, count, a);
     147             :         }
     148             : 
     149           0 :         return fy - SK_Fixed1/2;
     150             :     }
     151             : };
     152             : 
     153           0 : class Horish_SkAntiHairBlitter : public SkAntiHairBlitter {
     154             : public:
     155           0 :     SkFixed drawCap(int x, SkFixed fy, SkFixed dy, int mod64) override {
     156           0 :         fy += SK_Fixed1/2;
     157             : 
     158           0 :         int lower_y = fy >> 16;
     159           0 :         uint8_t  a = (uint8_t)(fy >> 8);
     160           0 :         unsigned a0 = SmallDot6Scale(255 - a, mod64);
     161           0 :         unsigned a1 = SmallDot6Scale(a, mod64);
     162           0 :         this->getBlitter()->blitAntiV2(x, lower_y - 1, a0, a1);
     163             : 
     164           0 :         return fy + dy - SK_Fixed1/2;
     165             :     }
     166             : 
     167           0 :     SkFixed drawLine(int x, int stopx, SkFixed fy, SkFixed dy) override {
     168           0 :         SkASSERT(x < stopx);
     169             : 
     170           0 :         fy += SK_Fixed1/2;
     171           0 :         SkBlitter* blitter = this->getBlitter();
     172           0 :         do {
     173           0 :             int lower_y = fy >> 16;
     174           0 :             uint8_t  a = (uint8_t)(fy >> 8);
     175           0 :             blitter->blitAntiV2(x, lower_y - 1, 255 - a, a);
     176           0 :             fy += dy;
     177             :         } while (++x < stopx);
     178             : 
     179           0 :         return fy - SK_Fixed1/2;
     180             :     }
     181             : };
     182             : 
     183           0 : class VLine_SkAntiHairBlitter : public SkAntiHairBlitter {
     184             : public:
     185           0 :     SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override {
     186           0 :         SkASSERT(0 == dx);
     187           0 :         fx += SK_Fixed1/2;
     188             : 
     189           0 :         int x = fx >> 16;
     190           0 :         int a = (uint8_t)(fx >> 8);
     191             : 
     192           0 :         unsigned ma = SmallDot6Scale(a, mod64);
     193           0 :         if (ma) {
     194           0 :             this->getBlitter()->blitV(x, y, 1, ma);
     195             :         }
     196           0 :         ma = SmallDot6Scale(255 - a, mod64);
     197           0 :         if (ma) {
     198           0 :             this->getBlitter()->blitV(x - 1, y, 1, ma);
     199             :         }
     200             : 
     201           0 :         return fx - SK_Fixed1/2;
     202             :     }
     203             : 
     204           0 :     SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
     205           0 :         SkASSERT(y < stopy);
     206           0 :         SkASSERT(0 == dx);
     207           0 :         fx += SK_Fixed1/2;
     208             : 
     209           0 :         int x = fx >> 16;
     210           0 :         int a = (uint8_t)(fx >> 8);
     211             : 
     212           0 :         if (a) {
     213           0 :             this->getBlitter()->blitV(x, y, stopy - y, a);
     214             :         }
     215           0 :         a = 255 - a;
     216           0 :         if (a) {
     217           0 :             this->getBlitter()->blitV(x - 1, y, stopy - y, a);
     218             :         }
     219             : 
     220           0 :         return fx - SK_Fixed1/2;
     221             :     }
     222             : };
     223             : 
     224           0 : class Vertish_SkAntiHairBlitter : public SkAntiHairBlitter {
     225             : public:
     226           0 :     SkFixed drawCap(int y, SkFixed fx, SkFixed dx, int mod64) override {
     227           0 :         fx += SK_Fixed1/2;
     228             : 
     229           0 :         int x = fx >> 16;
     230           0 :         uint8_t a = (uint8_t)(fx >> 8);
     231           0 :         this->getBlitter()->blitAntiH2(x - 1, y,
     232           0 :                                        SmallDot6Scale(255 - a, mod64), SmallDot6Scale(a, mod64));
     233             : 
     234           0 :         return fx + dx - SK_Fixed1/2;
     235             :     }
     236             : 
     237           0 :     SkFixed drawLine(int y, int stopy, SkFixed fx, SkFixed dx) override {
     238           0 :         SkASSERT(y < stopy);
     239           0 :         fx += SK_Fixed1/2;
     240           0 :         do {
     241           0 :             int x = fx >> 16;
     242           0 :             uint8_t a = (uint8_t)(fx >> 8);
     243           0 :             this->getBlitter()->blitAntiH2(x - 1, y, 255 - a, a);
     244           0 :             fx += dx;
     245             :         } while (++y < stopy);
     246             : 
     247           0 :         return fx - SK_Fixed1/2;
     248             :     }
     249             : };
     250             : 
     251           0 : static inline SkFixed fastfixdiv(SkFDot6 a, SkFDot6 b) {
     252           0 :     SkASSERT((SkLeftShift(a, 16) >> 16) == a);
     253           0 :     SkASSERT(b != 0);
     254           0 :     return SkLeftShift(a, 16) / b;
     255             : }
     256             : 
     257             : #define SkBITCOUNT(x)   (sizeof(x) << 3)
     258             : 
     259             : #if 1
     260             : // returns high-bit set iff x==0x8000...
     261           0 : static inline int bad_int(int x) {
     262           0 :     return x & -x;
     263             : }
     264             : 
     265           0 : static int any_bad_ints(int a, int b, int c, int d) {
     266           0 :     return (bad_int(a) | bad_int(b) | bad_int(c) | bad_int(d)) >> (SkBITCOUNT(int) - 1);
     267             : }
     268             : #else
     269             : static inline int good_int(int x) {
     270             :     return x ^ (1 << (SkBITCOUNT(x) - 1));
     271             : }
     272             : 
     273             : static int any_bad_ints(int a, int b, int c, int d) {
     274             :     return !(good_int(a) & good_int(b) & good_int(c) & good_int(d));
     275             : }
     276             : #endif
     277             : 
     278             : #ifdef SK_DEBUG
     279           0 : static bool canConvertFDot6ToFixed(SkFDot6 x) {
     280           0 :     const int maxDot6 = SK_MaxS32 >> (16 - 6);
     281           0 :     return SkAbs32(x) <= maxDot6;
     282             : }
     283             : #endif
     284             : 
     285             : /*
     286             :  *  We want the fractional part of ordinate, but we want multiples of 64 to
     287             :  *  return 64, not 0, so we can't just say (ordinate & 63).
     288             :  *  We basically want to compute those bits, and if they're 0, return 64.
     289             :  *  We can do that w/o a branch with an extra sub and add.
     290             :  */
     291           0 : static int contribution_64(SkFDot6 ordinate) {
     292             : #if 0
     293             :     int result = ordinate & 63;
     294             :     if (0 == result) {
     295             :         result = 64;
     296             :     }
     297             : #else
     298           0 :     int result = ((ordinate - 1) & 63) + 1;
     299             : #endif
     300           0 :     SkASSERT(result > 0 && result <= 64);
     301           0 :     return result;
     302             : }
     303             : 
     304           0 : static void do_anti_hairline(SkFDot6 x0, SkFDot6 y0, SkFDot6 x1, SkFDot6 y1,
     305             :                              const SkIRect* clip, SkBlitter* blitter) {
     306             :     // check for integer NaN (0x80000000) which we can't handle (can't negate it)
     307             :     // It appears typically from a huge float (inf or nan) being converted to int.
     308             :     // If we see it, just don't draw.
     309           0 :     if (any_bad_ints(x0, y0, x1, y1)) {
     310           0 :         return;
     311             :     }
     312             : 
     313             :     // The caller must clip the line to [-32767.0 ... 32767.0] ahead of time
     314             :     // (in dot6 format)
     315           0 :     SkASSERT(canConvertFDot6ToFixed(x0));
     316           0 :     SkASSERT(canConvertFDot6ToFixed(y0));
     317           0 :     SkASSERT(canConvertFDot6ToFixed(x1));
     318           0 :     SkASSERT(canConvertFDot6ToFixed(y1));
     319             : 
     320           0 :     if (SkAbs32(x1 - x0) > SkIntToFDot6(511) || SkAbs32(y1 - y0) > SkIntToFDot6(511)) {
     321             :         /*  instead of (x0 + x1) >> 1, we shift each separately. This is less
     322             :             precise, but avoids overflowing the intermediate result if the
     323             :             values are huge. A better fix might be to clip the original pts
     324             :             directly (i.e. do the divide), so we don't spend time subdividing
     325             :             huge lines at all.
     326             :          */
     327           0 :         int hx = (x0 >> 1) + (x1 >> 1);
     328           0 :         int hy = (y0 >> 1) + (y1 >> 1);
     329           0 :         do_anti_hairline(x0, y0, hx, hy, clip, blitter);
     330           0 :         do_anti_hairline(hx, hy, x1, y1, clip, blitter);
     331           0 :         return;
     332             :     }
     333             : 
     334             :     int         scaleStart, scaleStop;
     335             :     int         istart, istop;
     336             :     SkFixed     fstart, slope;
     337             : 
     338           0 :     HLine_SkAntiHairBlitter     hline_blitter;
     339           0 :     Horish_SkAntiHairBlitter    horish_blitter;
     340           0 :     VLine_SkAntiHairBlitter     vline_blitter;
     341           0 :     Vertish_SkAntiHairBlitter   vertish_blitter;
     342           0 :     SkAntiHairBlitter*          hairBlitter = nullptr;
     343             : 
     344           0 :     if (SkAbs32(x1 - x0) > SkAbs32(y1 - y0)) {   // mostly horizontal
     345           0 :         if (x0 > x1) {    // we want to go left-to-right
     346           0 :             SkTSwap<SkFDot6>(x0, x1);
     347           0 :             SkTSwap<SkFDot6>(y0, y1);
     348             :         }
     349             : 
     350           0 :         istart = SkFDot6Floor(x0);
     351           0 :         istop = SkFDot6Ceil(x1);
     352           0 :         fstart = SkFDot6ToFixed(y0);
     353           0 :         if (y0 == y1) {   // completely horizontal, take fast case
     354           0 :             slope = 0;
     355           0 :             hairBlitter = &hline_blitter;
     356             :         } else {
     357           0 :             slope = fastfixdiv(y1 - y0, x1 - x0);
     358           0 :             SkASSERT(slope >= -SK_Fixed1 && slope <= SK_Fixed1);
     359           0 :             fstart += (slope * (32 - (x0 & 63)) + 32) >> 6;
     360           0 :             hairBlitter = &horish_blitter;
     361             :         }
     362             : 
     363           0 :         SkASSERT(istop > istart);
     364           0 :         if (istop - istart == 1) {
     365             :             // we are within a single pixel
     366           0 :             scaleStart = x1 - x0;
     367           0 :             SkASSERT(scaleStart >= 0 && scaleStart <= 64);
     368           0 :             scaleStop = 0;
     369             :         } else {
     370           0 :             scaleStart = 64 - (x0 & 63);
     371           0 :             scaleStop = x1 & 63;
     372             :         }
     373             : 
     374           0 :         if (clip){
     375           0 :             if (istart >= clip->fRight || istop <= clip->fLeft) {
     376           0 :                 return;
     377             :             }
     378           0 :             if (istart < clip->fLeft) {
     379           0 :                 fstart += slope * (clip->fLeft - istart);
     380           0 :                 istart = clip->fLeft;
     381           0 :                 scaleStart = 64;
     382           0 :                 if (istop - istart == 1) {
     383             :                     // we are within a single pixel
     384           0 :                     scaleStart = contribution_64(x1);
     385           0 :                     scaleStop = 0;
     386             :                 }
     387             :             }
     388           0 :             if (istop > clip->fRight) {
     389           0 :                 istop = clip->fRight;
     390           0 :                 scaleStop = 0;  // so we don't draw this last column
     391             :             }
     392             : 
     393           0 :             SkASSERT(istart <= istop);
     394           0 :             if (istart == istop) {
     395           0 :                 return;
     396             :             }
     397             :             // now test if our Y values are completely inside the clip
     398             :             int top, bottom;
     399           0 :             if (slope >= 0) { // T2B
     400           0 :                 top = SkFixedFloorToInt(fstart - SK_FixedHalf);
     401           0 :                 bottom = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
     402             :             } else {           // B2T
     403           0 :                 bottom = SkFixedCeilToInt(fstart + SK_FixedHalf);
     404           0 :                 top = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
     405             :             }
     406             : #ifdef OUTSET_BEFORE_CLIP_TEST
     407           0 :             top -= 1;
     408           0 :             bottom += 1;
     409             : #endif
     410           0 :             if (top >= clip->fBottom || bottom <= clip->fTop) {
     411           0 :                 return;
     412             :             }
     413           0 :             if (clip->fTop <= top && clip->fBottom >= bottom) {
     414           0 :                 clip = nullptr;
     415             :             }
     416             :         }
     417             :     } else {   // mostly vertical
     418           0 :         if (y0 > y1) {  // we want to go top-to-bottom
     419           0 :             SkTSwap<SkFDot6>(x0, x1);
     420           0 :             SkTSwap<SkFDot6>(y0, y1);
     421             :         }
     422             : 
     423           0 :         istart = SkFDot6Floor(y0);
     424           0 :         istop = SkFDot6Ceil(y1);
     425           0 :         fstart = SkFDot6ToFixed(x0);
     426           0 :         if (x0 == x1) {
     427           0 :             if (y0 == y1) { // are we zero length?
     428           0 :                 return;     // nothing to do
     429             :             }
     430           0 :             slope = 0;
     431           0 :             hairBlitter = &vline_blitter;
     432             :         } else {
     433           0 :             slope = fastfixdiv(x1 - x0, y1 - y0);
     434           0 :             SkASSERT(slope <= SK_Fixed1 && slope >= -SK_Fixed1);
     435           0 :             fstart += (slope * (32 - (y0 & 63)) + 32) >> 6;
     436           0 :             hairBlitter = &vertish_blitter;
     437             :         }
     438             : 
     439           0 :         SkASSERT(istop > istart);
     440           0 :         if (istop - istart == 1) {
     441             :             // we are within a single pixel
     442           0 :             scaleStart = y1 - y0;
     443           0 :             SkASSERT(scaleStart >= 0 && scaleStart <= 64);
     444           0 :             scaleStop = 0;
     445             :         } else {
     446           0 :             scaleStart = 64 - (y0 & 63);
     447           0 :             scaleStop = y1 & 63;
     448             :         }
     449             : 
     450           0 :         if (clip) {
     451           0 :             if (istart >= clip->fBottom || istop <= clip->fTop) {
     452           0 :                 return;
     453             :             }
     454           0 :             if (istart < clip->fTop) {
     455           0 :                 fstart += slope * (clip->fTop - istart);
     456           0 :                 istart = clip->fTop;
     457           0 :                 scaleStart = 64;
     458           0 :                 if (istop - istart == 1) {
     459             :                     // we are within a single pixel
     460           0 :                     scaleStart = contribution_64(y1);
     461           0 :                     scaleStop = 0;
     462             :                 }
     463             :             }
     464           0 :             if (istop > clip->fBottom) {
     465           0 :                 istop = clip->fBottom;
     466           0 :                 scaleStop = 0;  // so we don't draw this last row
     467             :             }
     468             : 
     469           0 :             SkASSERT(istart <= istop);
     470           0 :             if (istart == istop)
     471           0 :                 return;
     472             : 
     473             :             // now test if our X values are completely inside the clip
     474             :             int left, right;
     475           0 :             if (slope >= 0) { // L2R
     476           0 :                 left = SkFixedFloorToInt(fstart - SK_FixedHalf);
     477           0 :                 right = SkFixedCeilToInt(fstart + (istop - istart - 1) * slope + SK_FixedHalf);
     478             :             } else {           // R2L
     479           0 :                 right = SkFixedCeilToInt(fstart + SK_FixedHalf);
     480           0 :                 left = SkFixedFloorToInt(fstart + (istop - istart - 1) * slope - SK_FixedHalf);
     481             :             }
     482             : #ifdef OUTSET_BEFORE_CLIP_TEST
     483           0 :             left -= 1;
     484           0 :             right += 1;
     485             : #endif
     486           0 :             if (left >= clip->fRight || right <= clip->fLeft) {
     487           0 :                 return;
     488             :             }
     489           0 :             if (clip->fLeft <= left && clip->fRight >= right) {
     490           0 :                 clip = nullptr;
     491             :             }
     492             :         }
     493             :     }
     494             : 
     495           0 :     SkRectClipBlitter   rectClipper;
     496           0 :     if (clip) {
     497           0 :         rectClipper.init(blitter, *clip);
     498           0 :         blitter = &rectClipper;
     499             :     }
     500             : 
     501           0 :     SkASSERT(hairBlitter);
     502           0 :     hairBlitter->setup(blitter);
     503             : 
     504             : #ifdef SK_DEBUG
     505           0 :     if (scaleStart > 0 && scaleStop > 0) {
     506             :         // be sure we don't draw twice in the same pixel
     507           0 :         SkASSERT(istart < istop - 1);
     508             :     }
     509             : #endif
     510             : 
     511           0 :     fstart = hairBlitter->drawCap(istart, fstart, slope, scaleStart);
     512           0 :     istart += 1;
     513           0 :     int fullSpans = istop - istart - (scaleStop > 0);
     514           0 :     if (fullSpans > 0) {
     515           0 :         fstart = hairBlitter->drawLine(istart, istart + fullSpans, fstart, slope);
     516             :     }
     517           0 :     if (scaleStop > 0) {
     518           0 :         hairBlitter->drawCap(istop - 1, fstart, slope, scaleStop);
     519             :     }
     520             : }
     521             : 
     522           0 : void SkScan::AntiHairLineRgn(const SkPoint array[], int arrayCount, const SkRegion* clip,
     523             :                              SkBlitter* blitter) {
     524           0 :     if (clip && clip->isEmpty()) {
     525           0 :         return;
     526             :     }
     527             : 
     528           0 :     SkASSERT(clip == nullptr || !clip->getBounds().isEmpty());
     529             : 
     530             : #ifdef TEST_GAMMA
     531             :     build_gamma_table();
     532             : #endif
     533             : 
     534           0 :     const SkScalar max = SkIntToScalar(32767);
     535           0 :     const SkRect fixedBounds = SkRect::MakeLTRB(-max, -max, max, max);
     536             : 
     537             :     SkRect clipBounds;
     538           0 :     if (clip) {
     539           0 :         clipBounds.set(clip->getBounds());
     540             :         /*  We perform integral clipping later on, but we do a scalar clip first
     541             :          to ensure that our coordinates are expressible in fixed/integers.
     542             : 
     543             :          antialiased hairlines can draw up to 1/2 of a pixel outside of
     544             :          their bounds, so we need to outset the clip before calling the
     545             :          clipper. To make the numerics safer, we outset by a whole pixel,
     546             :          since the 1/2 pixel boundary is important to the antihair blitter,
     547             :          we don't want to risk numerical fate by chopping on that edge.
     548             :          */
     549           0 :         clipBounds.outset(SK_Scalar1, SK_Scalar1);
     550             :     }
     551             : 
     552           0 :     for (int i = 0; i < arrayCount - 1; ++i) {
     553             :         SkPoint pts[2];
     554             : 
     555             :         // We have to pre-clip the line to fit in a SkFixed, so we just chop
     556             :         // the line. TODO find a way to actually draw beyond that range.
     557           0 :         if (!SkLineClipper::IntersectLine(&array[i], fixedBounds, pts)) {
     558           0 :             continue;
     559             :         }
     560             : 
     561           0 :         if (clip && !SkLineClipper::IntersectLine(pts, clipBounds, pts)) {
     562           0 :             continue;
     563             :         }
     564             : 
     565           0 :         SkFDot6 x0 = SkScalarToFDot6(pts[0].fX);
     566           0 :         SkFDot6 y0 = SkScalarToFDot6(pts[0].fY);
     567           0 :         SkFDot6 x1 = SkScalarToFDot6(pts[1].fX);
     568           0 :         SkFDot6 y1 = SkScalarToFDot6(pts[1].fY);
     569             : 
     570           0 :         if (clip) {
     571           0 :             SkFDot6 left = SkMin32(x0, x1);
     572           0 :             SkFDot6 top = SkMin32(y0, y1);
     573           0 :             SkFDot6 right = SkMax32(x0, x1);
     574           0 :             SkFDot6 bottom = SkMax32(y0, y1);
     575             :             SkIRect ir;
     576             : 
     577           0 :             ir.set( SkFDot6Floor(left) - 1,
     578           0 :                     SkFDot6Floor(top) - 1,
     579           0 :                     SkFDot6Ceil(right) + 1,
     580           0 :                     SkFDot6Ceil(bottom) + 1);
     581             : 
     582           0 :             if (clip->quickReject(ir)) {
     583           0 :                 continue;
     584             :             }
     585           0 :             if (!clip->quickContains(ir)) {
     586           0 :                 SkRegion::Cliperator iter(*clip, ir);
     587           0 :                 const SkIRect*       r = &iter.rect();
     588             : 
     589           0 :                 while (!iter.done()) {
     590           0 :                     do_anti_hairline(x0, y0, x1, y1, r, blitter);
     591           0 :                     iter.next();
     592             :                 }
     593           0 :                 continue;
     594             :             }
     595             :             // fall through to no-clip case
     596             :         }
     597           0 :         do_anti_hairline(x0, y0, x1, y1, nullptr, blitter);
     598             :     }
     599             : }
     600             : 
     601           0 : void SkScan::AntiHairRect(const SkRect& rect, const SkRasterClip& clip,
     602             :                           SkBlitter* blitter) {
     603             :     SkPoint pts[5];
     604             : 
     605           0 :     pts[0].set(rect.fLeft, rect.fTop);
     606           0 :     pts[1].set(rect.fRight, rect.fTop);
     607           0 :     pts[2].set(rect.fRight, rect.fBottom);
     608           0 :     pts[3].set(rect.fLeft, rect.fBottom);
     609           0 :     pts[4] = pts[0];
     610           0 :     SkScan::AntiHairLine(pts, 5, clip, blitter);
     611           0 : }
     612             : 
     613             : ///////////////////////////////////////////////////////////////////////////////
     614             : 
     615             : typedef int FDot8;  // 24.8 integer fixed point
     616             : 
     617         944 : static inline FDot8 SkFixedToFDot8(SkFixed x) {
     618         944 :     return (x + 0x80) >> 8;
     619             : }
     620             : 
     621          10 : static void do_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
     622             :                         SkBlitter* blitter) {
     623          10 :     SkASSERT(L < R);
     624             : 
     625          10 :     if ((L >> 8) == ((R - 1) >> 8)) {  // 1x1 pixel
     626           0 :         blitter->blitV(L >> 8, top, 1, SkAlphaMul(alpha, R - L));
     627           0 :         return;
     628             :     }
     629             : 
     630          10 :     int left = L >> 8;
     631             : 
     632          10 :     if (L & 0xFF) {
     633           0 :         blitter->blitV(left, top, 1, SkAlphaMul(alpha, 256 - (L & 0xFF)));
     634           0 :         left += 1;
     635             :     }
     636             : 
     637          10 :     int rite = R >> 8;
     638          10 :     int width = rite - left;
     639          10 :     if (width > 0) {
     640          10 :         call_hline_blitter(blitter, left, top, width, alpha);
     641             :     }
     642          10 :     if (R & 0xFF) {
     643           0 :         blitter->blitV(rite, top, 1, SkAlphaMul(alpha, R & 0xFF));
     644             :     }
     645             : }
     646             : 
     647         236 : static void antifilldot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B, SkBlitter* blitter,
     648             :                          bool fillInner) {
     649             :     // check for empty now that we're in our reduced precision space
     650         236 :     if (L >= R || T >= B) {
     651           0 :         return;
     652             :     }
     653         236 :     int top = T >> 8;
     654         236 :     if (top == ((B - 1) >> 8)) {   // just one scanline high
     655          10 :         do_scanline(L, top, R, B - T - 1, blitter);
     656          10 :         return;
     657             :     }
     658             : 
     659         226 :     if (T & 0xFF) {
     660           0 :         do_scanline(L, top, R, 256 - (T & 0xFF), blitter);
     661           0 :         top += 1;
     662             :     }
     663             : 
     664         226 :     int bot = B >> 8;
     665         226 :     int height = bot - top;
     666         226 :     if (height > 0) {
     667         226 :         int left = L >> 8;
     668         226 :         if (left == ((R - 1) >> 8)) {   // just 1-pixel wide
     669           0 :             blitter->blitV(left, top, height, R - L - 1);
     670             :         } else {
     671         226 :             if (L & 0xFF) {
     672           0 :                 blitter->blitV(left, top, height, 256 - (L & 0xFF));
     673           0 :                 left += 1;
     674             :             }
     675         226 :             int rite = R >> 8;
     676         226 :             int width = rite - left;
     677         226 :             if (width > 0 && fillInner) {
     678         226 :                 blitter->blitRect(left, top, width, height);
     679             :             }
     680         226 :             if (R & 0xFF) {
     681           0 :                 blitter->blitV(rite, top, height, R & 0xFF);
     682             :             }
     683             :         }
     684             :     }
     685             : 
     686         226 :     if (B & 0xFF) {
     687           0 :         do_scanline(L, bot, R, B & 0xFF, blitter);
     688             :     }
     689             : }
     690             : 
     691         236 : static void antifillrect(const SkXRect& xr, SkBlitter* blitter) {
     692         472 :     antifilldot8(SkFixedToFDot8(xr.fLeft), SkFixedToFDot8(xr.fTop),
     693         472 :                  SkFixedToFDot8(xr.fRight), SkFixedToFDot8(xr.fBottom),
     694         236 :                  blitter, true);
     695         236 : }
     696             : 
     697             : ///////////////////////////////////////////////////////////////////////////////
     698             : 
     699           0 : void SkScan::AntiFillXRect(const SkXRect& xr, const SkRegion* clip,
     700             :                           SkBlitter* blitter) {
     701           0 :     if (nullptr == clip) {
     702           0 :         antifillrect(xr, blitter);
     703             :     } else {
     704             :         SkIRect outerBounds;
     705           0 :         XRect_roundOut(xr, &outerBounds);
     706             : 
     707           0 :         if (clip->isRect()) {
     708           0 :             const SkIRect& clipBounds = clip->getBounds();
     709             : 
     710           0 :             if (clipBounds.contains(outerBounds)) {
     711           0 :                 antifillrect(xr, blitter);
     712             :             } else {
     713             :                 SkXRect tmpR;
     714             :                 // this keeps our original edges fractional
     715           0 :                 XRect_set(&tmpR, clipBounds);
     716           0 :                 if (tmpR.intersect(xr)) {
     717           0 :                     antifillrect(tmpR, blitter);
     718             :                 }
     719             :             }
     720             :         } else {
     721           0 :             SkRegion::Cliperator clipper(*clip, outerBounds);
     722           0 :             const SkIRect&       rr = clipper.rect();
     723             : 
     724           0 :             while (!clipper.done()) {
     725             :                 SkXRect  tmpR;
     726             : 
     727             :                 // this keeps our original edges fractional
     728           0 :                 XRect_set(&tmpR, rr);
     729           0 :                 if (tmpR.intersect(xr)) {
     730           0 :                     antifillrect(tmpR, blitter);
     731             :                 }
     732           0 :                 clipper.next();
     733             :             }
     734             :         }
     735             :     }
     736           0 : }
     737             : 
     738           0 : void SkScan::AntiFillXRect(const SkXRect& xr, const SkRasterClip& clip,
     739             :                            SkBlitter* blitter) {
     740           0 :     if (clip.isBW()) {
     741           0 :         AntiFillXRect(xr, &clip.bwRgn(), blitter);
     742             :     } else {
     743             :         SkIRect outerBounds;
     744           0 :         XRect_roundOut(xr, &outerBounds);
     745             : 
     746           0 :         if (clip.quickContains(outerBounds)) {
     747           0 :             AntiFillXRect(xr, nullptr, blitter);
     748             :         } else {
     749           0 :             SkAAClipBlitterWrapper wrapper(clip, blitter);
     750           0 :             AntiFillXRect(xr, &wrapper.getRgn(), wrapper.getBlitter());
     751             :         }
     752             :     }
     753           0 : }
     754             : 
     755             : /*  This guy takes a float-rect, but with the key improvement that it has
     756             :     already been clipped, so we know that it is safe to convert it into a
     757             :     XRect (fixedpoint), as it won't overflow.
     758             : */
     759         236 : static void antifillrect(const SkRect& r, SkBlitter* blitter) {
     760             :     SkXRect xr;
     761             : 
     762         236 :     XRect_set(&xr, r);
     763         236 :     antifillrect(xr, blitter);
     764         236 : }
     765             : 
     766             : /*  We repeat the clipping logic of AntiFillXRect because the float rect might
     767             :     overflow if we blindly converted it to an XRect. This sucks that we have to
     768             :     repeat the clipping logic, but I don't see how to share the code/logic.
     769             : 
     770             :     We clip r (as needed) into one or more (smaller) float rects, and then pass
     771             :     those to our version of antifillrect, which converts it into an XRect and
     772             :     then calls the blit.
     773             : */
     774         205 : void SkScan::AntiFillRect(const SkRect& origR, const SkRegion* clip,
     775             :                           SkBlitter* blitter) {
     776         205 :     if (clip) {
     777             :         SkRect newR;
     778         205 :         newR.set(clip->getBounds());
     779         205 :         if (!newR.intersect(origR)) {
     780           0 :             return;
     781             :         }
     782             : 
     783         205 :         const SkIRect outerBounds = newR.roundOut();
     784             : 
     785         205 :         if (clip->isRect()) {
     786         193 :             antifillrect(newR, blitter);
     787             :         } else {
     788          12 :             SkRegion::Cliperator clipper(*clip, outerBounds);
     789          98 :             while (!clipper.done()) {
     790          43 :                 newR.set(clipper.rect());
     791          43 :                 if (newR.intersect(origR)) {
     792          43 :                     antifillrect(newR, blitter);
     793             :                 }
     794          43 :                 clipper.next();
     795             :             }
     796             :         }
     797             :     } else {
     798           0 :         antifillrect(origR, blitter);
     799             :     }
     800             : }
     801             : 
     802         205 : void SkScan::AntiFillRect(const SkRect& r, const SkRasterClip& clip,
     803             :                           SkBlitter* blitter) {
     804         205 :     if (clip.isBW()) {
     805          78 :         AntiFillRect(r, &clip.bwRgn(), blitter);
     806             :     } else {
     807         254 :         SkAAClipBlitterWrapper wrap(clip, blitter);
     808         127 :         AntiFillRect(r, &wrap.getRgn(), wrap.getBlitter());
     809             :     }
     810         205 : }
     811             : 
     812             : ///////////////////////////////////////////////////////////////////////////////
     813             : 
     814             : #define SkAlphaMulRound(a, b)   SkMulDiv255Round(a, b)
     815             : 
     816             : // calls blitRect() if the rectangle is non-empty
     817           0 : static void fillcheckrect(int L, int T, int R, int B, SkBlitter* blitter) {
     818           0 :     if (L < R && T < B) {
     819           0 :         blitter->blitRect(L, T, R - L, B - T);
     820             :     }
     821           0 : }
     822             : 
     823           0 : static inline FDot8 SkScalarToFDot8(SkScalar x) {
     824           0 :     return (int)(x * 256);
     825             : }
     826             : 
     827           0 : static inline int FDot8Floor(FDot8 x) {
     828           0 :     return x >> 8;
     829             : }
     830             : 
     831           0 : static inline int FDot8Ceil(FDot8 x) {
     832           0 :     return (x + 0xFF) >> 8;
     833             : }
     834             : 
     835             : // 1 - (1 - a)*(1 - b)
     836           0 : static inline U8CPU InvAlphaMul(U8CPU a, U8CPU b) {
     837             :     // need precise rounding (not just SkAlphaMul) so that values like
     838             :     // a=228, b=252 don't overflow the result
     839           0 :     return SkToU8(a + b - SkAlphaMulRound(a, b));
     840             : }
     841             : 
     842           0 : static void inner_scanline(FDot8 L, int top, FDot8 R, U8CPU alpha,
     843             :                            SkBlitter* blitter) {
     844           0 :     SkASSERT(L < R);
     845             : 
     846           0 :     if ((L >> 8) == ((R - 1) >> 8)) {  // 1x1 pixel
     847           0 :         FDot8 widClamp = R - L;
     848             :         // border case clamp 256 to 255 instead of going through call_hline_blitter
     849             :         // see skbug/4406
     850           0 :         widClamp = widClamp - (widClamp >> 8);
     851           0 :         blitter->blitV(L >> 8, top, 1, InvAlphaMul(alpha, widClamp));
     852           0 :         return;
     853             :     }
     854             : 
     855           0 :     int left = L >> 8;
     856           0 :     if (L & 0xFF) {
     857           0 :         blitter->blitV(left, top, 1, InvAlphaMul(alpha, L & 0xFF));
     858           0 :         left += 1;
     859             :     }
     860             : 
     861           0 :     int rite = R >> 8;
     862           0 :     int width = rite - left;
     863           0 :     if (width > 0) {
     864           0 :         call_hline_blitter(blitter, left, top, width, alpha);
     865             :     }
     866             : 
     867           0 :     if (R & 0xFF) {
     868           0 :         blitter->blitV(rite, top, 1, InvAlphaMul(alpha, ~R & 0xFF));
     869             :     }
     870             : }
     871             : 
     872           0 : static void innerstrokedot8(FDot8 L, FDot8 T, FDot8 R, FDot8 B,
     873             :                             SkBlitter* blitter) {
     874           0 :     SkASSERT(L < R && T < B);
     875             : 
     876           0 :     int top = T >> 8;
     877           0 :     if (top == ((B - 1) >> 8)) {   // just one scanline high
     878             :         // We want the inverse of B-T, since we're the inner-stroke
     879           0 :         int alpha = 256 - (B - T);
     880           0 :         if (alpha) {
     881           0 :             inner_scanline(L, top, R, alpha, blitter);
     882             :         }
     883           0 :         return;
     884             :     }
     885             : 
     886           0 :     if (T & 0xFF) {
     887           0 :         inner_scanline(L, top, R, T & 0xFF, blitter);
     888           0 :         top += 1;
     889             :     }
     890             : 
     891           0 :     int bot = B >> 8;
     892           0 :     int height = bot - top;
     893           0 :     if (height > 0) {
     894           0 :         if (L & 0xFF) {
     895           0 :             blitter->blitV(L >> 8, top, height, L & 0xFF);
     896             :         }
     897           0 :         if (R & 0xFF) {
     898           0 :             blitter->blitV(R >> 8, top, height, ~R & 0xFF);
     899             :         }
     900             :     }
     901             : 
     902           0 :     if (B & 0xFF) {
     903           0 :         inner_scanline(L, bot, R, ~B & 0xFF, blitter);
     904             :     }
     905             : }
     906             : 
     907           0 : static inline void align_thin_stroke(FDot8& edge1, FDot8& edge2) {
     908           0 :     SkASSERT(edge1 <= edge2);
     909             : 
     910           0 :     if (FDot8Floor(edge1) == FDot8Floor(edge2)) {
     911           0 :         edge2 -= (edge1 & 0xFF);
     912           0 :         edge1 &= ~0xFF;
     913             :     }
     914           0 : }
     915             : 
     916           0 : void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
     917             :                            const SkRegion* clip, SkBlitter* blitter) {
     918           0 :     SkASSERT(strokeSize.fX >= 0 && strokeSize.fY >= 0);
     919             : 
     920           0 :     SkScalar rx = SkScalarHalf(strokeSize.fX);
     921           0 :     SkScalar ry = SkScalarHalf(strokeSize.fY);
     922             : 
     923             :     // outset by the radius
     924           0 :     FDot8 outerL = SkScalarToFDot8(r.fLeft - rx);
     925           0 :     FDot8 outerT = SkScalarToFDot8(r.fTop - ry);
     926           0 :     FDot8 outerR = SkScalarToFDot8(r.fRight + rx);
     927           0 :     FDot8 outerB = SkScalarToFDot8(r.fBottom + ry);
     928             : 
     929             :     SkIRect outer;
     930             :     // set outer to the outer rect of the outer section
     931           0 :     outer.set(FDot8Floor(outerL), FDot8Floor(outerT), FDot8Ceil(outerR), FDot8Ceil(outerB));
     932             : 
     933           0 :     SkBlitterClipper clipper;
     934           0 :     if (clip) {
     935           0 :         if (clip->quickReject(outer)) {
     936           0 :             return;
     937             :         }
     938           0 :         if (!clip->contains(outer)) {
     939           0 :             blitter = clipper.apply(blitter, clip, &outer);
     940             :         }
     941             :         // now we can ignore clip for the rest of the function
     942             :     }
     943             : 
     944             :     // in case we lost a bit with diameter/2
     945           0 :     rx = strokeSize.fX - rx;
     946           0 :     ry = strokeSize.fY - ry;
     947             : 
     948             :     // inset by the radius
     949           0 :     FDot8 innerL = SkScalarToFDot8(r.fLeft + rx);
     950           0 :     FDot8 innerT = SkScalarToFDot8(r.fTop + ry);
     951           0 :     FDot8 innerR = SkScalarToFDot8(r.fRight - rx);
     952           0 :     FDot8 innerB = SkScalarToFDot8(r.fBottom - ry);
     953             : 
     954             :     // For sub-unit strokes, tweak the hulls such that one of the edges coincides with the pixel
     955             :     // edge. This ensures that the general rect stroking logic below
     956             :     //   a) doesn't blit the same scanline twice
     957             :     //   b) computes the correct coverage when both edges fall within the same pixel
     958           0 :     if (strokeSize.fX < 1 || strokeSize.fY < 1) {
     959           0 :         align_thin_stroke(outerL, innerL);
     960           0 :         align_thin_stroke(outerT, innerT);
     961           0 :         align_thin_stroke(innerR, outerR);
     962           0 :         align_thin_stroke(innerB, outerB);
     963             :     }
     964             : 
     965             :     // stroke the outer hull
     966           0 :     antifilldot8(outerL, outerT, outerR, outerB, blitter, false);
     967             : 
     968             :     // set outer to the outer rect of the middle section
     969           0 :     outer.set(FDot8Ceil(outerL), FDot8Ceil(outerT), FDot8Floor(outerR), FDot8Floor(outerB));
     970             : 
     971           0 :     if (innerL >= innerR || innerT >= innerB) {
     972           0 :         fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, outer.fBottom,
     973           0 :                       blitter);
     974             :     } else {
     975             :         SkIRect inner;
     976             :         // set inner to the inner rect of the middle section
     977           0 :         inner.set(FDot8Floor(innerL), FDot8Floor(innerT), FDot8Ceil(innerR), FDot8Ceil(innerB));
     978             : 
     979             :         // draw the frame in 4 pieces
     980           0 :         fillcheckrect(outer.fLeft, outer.fTop, outer.fRight, inner.fTop,
     981           0 :                       blitter);
     982           0 :         fillcheckrect(outer.fLeft, inner.fTop, inner.fLeft, inner.fBottom,
     983           0 :                       blitter);
     984           0 :         fillcheckrect(inner.fRight, inner.fTop, outer.fRight, inner.fBottom,
     985           0 :                       blitter);
     986           0 :         fillcheckrect(outer.fLeft, inner.fBottom, outer.fRight, outer.fBottom,
     987           0 :                       blitter);
     988             : 
     989             :         // now stroke the inner rect, which is similar to antifilldot8() except that
     990             :         // it treats the fractional coordinates with the inverse bias (since its
     991             :         // inner).
     992           0 :         innerstrokedot8(innerL, innerT, innerR, innerB, blitter);
     993             :     }
     994             : }
     995             : 
     996           0 : void SkScan::AntiFrameRect(const SkRect& r, const SkPoint& strokeSize,
     997             :                            const SkRasterClip& clip, SkBlitter* blitter) {
     998           0 :     if (clip.isBW()) {
     999           0 :         AntiFrameRect(r, strokeSize, &clip.bwRgn(), blitter);
    1000             :     } else {
    1001           0 :         SkAAClipBlitterWrapper wrap(clip, blitter);
    1002           0 :         AntiFrameRect(r, strokeSize, &wrap.getRgn(), wrap.getBlitter());
    1003             :     }
    1004           0 : }

Generated by: LCOV version 1.13