xref: /aosp_15_r20/external/skia/src/core/SkAlphaRuns.h (revision c8dee2aa9b3f27cf6c858bd81872bdeb2c07ed17)
1 /*
2  * Copyright 2006 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 #ifndef SkAlphaRuns_DEFINED
9 #define SkAlphaRuns_DEFINED
10 
11 #include "include/core/SkColor.h"
12 #include "include/core/SkTypes.h"
13 #include "include/private/base/SkCPUTypes.h"
14 #include "include/private/base/SkDebug.h"
15 #include "include/private/base/SkTo.h"
16 
17 #include <cstdint>
18 
19 /** Sparse array of run-length-encoded alpha (supersampling coverage) values.
20     Sparseness allows us to independently compose several paths into the
21     same SkAlphaRuns buffer.
22 */
23 
24 class SkAlphaRuns {
25 public:
26     int16_t*    fRuns;
27     uint8_t*     fAlpha;
28 
29     // Return 0-255 given 0-256
CatchOverflow(int alpha)30     static inline SkAlpha CatchOverflow(int alpha) {
31         SkASSERT(alpha >= 0 && alpha <= 256);
32         return alpha - (alpha >> 8);
33     }
34 
35     /// Returns true if the scanline contains only a single run,
36     /// of alpha value 0.
empty()37     bool empty() const {
38         SkASSERT(fRuns[0] > 0);
39         return fAlpha[0] == 0 && fRuns[fRuns[0]] == 0;
40     }
41 
42     /// Reinitialize for a new scanline.
43     void    reset(int width);
44 
45     /**
46      *  Insert into the buffer a run starting at (x-offsetX):
47      *      if startAlpha > 0
48      *          one pixel with value += startAlpha,
49      *              max 255
50      *      if middleCount > 0
51      *          middleCount pixels with value += maxValue
52      *      if stopAlpha > 0
53      *          one pixel with value += stopAlpha
54      *  Returns the offsetX value that should be passed on the next call,
55      *  assuming we're on the same scanline. If the caller is switching
56      *  scanlines, then offsetX should be 0 when this is called.
57      */
add(int x,U8CPU startAlpha,int middleCount,U8CPU stopAlpha,U8CPU maxValue,int offsetX)58     SK_ALWAYS_INLINE int add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha,
59                              U8CPU maxValue, int offsetX) {
60         SkASSERT(middleCount >= 0);
61         SkASSERT(x >= 0 && x + (startAlpha != 0) + middleCount + (stopAlpha != 0) <= fWidth);
62 
63         SkASSERT(fRuns[offsetX] >= 0);
64 
65         int16_t*    runs = fRuns + offsetX;
66         uint8_t*    alpha = fAlpha + offsetX;
67         uint8_t*    lastAlpha = alpha;
68         x -= offsetX;
69 
70         if (startAlpha) {
71             SkAlphaRuns::Break(runs, alpha, x, 1);
72             /*  I should be able to just add alpha[x] + startAlpha.
73                 However, if the trailing edge of the previous span and the leading
74                 edge of the current span round to the same super-sampled x value,
75                 I might overflow to 256 with this add, hence the funny subtract (crud).
76             */
77             unsigned tmp = alpha[x] + startAlpha;
78             SkASSERT(tmp <= 256);
79             alpha[x] = SkToU8(tmp - (tmp >> 8));    // was (tmp >> 7), but that seems wrong if we're trying to catch 256
80 
81             runs += x + 1;
82             alpha += x + 1;
83             x = 0;
84             SkDEBUGCODE(this->validate();)
85         }
86 
87         if (middleCount) {
88             SkAlphaRuns::Break(runs, alpha, x, middleCount);
89             alpha += x;
90             runs += x;
91             x = 0;
92             do {
93                 alpha[0] = SkToU8(CatchOverflow(alpha[0] + maxValue));
94                 int n = runs[0];
95                 SkASSERT(n <= middleCount);
96                 alpha += n;
97                 runs += n;
98                 middleCount -= n;
99             } while (middleCount > 0);
100             SkDEBUGCODE(this->validate();)
101             lastAlpha = alpha;
102         }
103 
104         if (stopAlpha) {
105             SkAlphaRuns::Break(runs, alpha, x, 1);
106             alpha += x;
107             alpha[0] = SkToU8(alpha[0] + stopAlpha);
108             SkDEBUGCODE(this->validate();)
109             lastAlpha = alpha;
110         }
111 
112         return SkToS32(lastAlpha - fAlpha);  // new offsetX
113     }
114 
115     SkDEBUGCODE(void assertValid(int y, int maxStep) const;)
SkDEBUGCODE(void dump ()const;)116     SkDEBUGCODE(void dump() const;)
117 
118     /**
119      * Break the runs in the buffer at offsets x and x+count, properly
120      * updating the runs to the right and left.
121      *   i.e. from the state AAAABBBB, run-length encoded as A4B4,
122      *   Break(..., 2, 5) would produce AAAABBBB rle as A2A2B3B1.
123      * Allows add() to sum another run to some of the new sub-runs.
124      *   i.e. adding ..CCCCC. would produce AADDEEEB, rle as A2D2E3B1.
125      */
126     static void Break(int16_t runs[], uint8_t alpha[], int x, int count) {
127         SkASSERT(count > 0 && x >= 0);
128 
129         //  SkAlphaRuns::BreakAt(runs, alpha, x);
130         //  SkAlphaRuns::BreakAt(&runs[x], &alpha[x], count);
131 
132         int16_t* next_runs = runs + x;
133         uint8_t*  next_alpha = alpha + x;
134 
135         while (x > 0) {
136             int n = runs[0];
137             SkASSERT(n > 0);
138 
139             if (x < n) {
140                 alpha[x] = alpha[0];
141                 runs[0] = SkToS16(x);
142                 runs[x] = SkToS16(n - x);
143                 break;
144             }
145             runs += n;
146             alpha += n;
147             x -= n;
148         }
149 
150         runs = next_runs;
151         alpha = next_alpha;
152         x = count;
153 
154         for (;;) {
155             int n = runs[0];
156             SkASSERT(n > 0);
157 
158             if (x < n) {
159                 alpha[x] = alpha[0];
160                 runs[0] = SkToS16(x);
161                 runs[x] = SkToS16(n - x);
162                 break;
163             }
164             x -= n;
165             if (x <= 0) {
166                 break;
167             }
168             runs += n;
169             alpha += n;
170         }
171     }
172 
173     /**
174      * Cut (at offset x in the buffer) a run into two shorter runs with
175      * matching alpha values.
176      * Used by the RectClipBlitter to trim a RLE encoding to match the
177      * clipping rectangle.
178      */
BreakAt(int16_t runs[],uint8_t alpha[],int x)179     static void BreakAt(int16_t runs[], uint8_t alpha[], int x) {
180         while (x > 0) {
181             int n = runs[0];
182             SkASSERT(n > 0);
183 
184             if (x < n) {
185                 alpha[x] = alpha[0];
186                 runs[0] = SkToS16(x);
187                 runs[x] = SkToS16(n - x);
188                 break;
189             }
190             runs += n;
191             alpha += n;
192             x -= n;
193         }
194     }
195 
196 private:
197     SkDEBUGCODE(int fWidth;)
198     SkDEBUGCODE(void validate() const;)
199 };
200 
201 #endif
202