blob: 6049e7909b4b3b20f77826e565738d7db8c85177 [file] [log] [blame]
epoger@google.comec3ed6a2011-07-28 14:26:00 +00001
2/*
3 * Copyright 2006 The Android Open Source Project
4 *
5 * Use of this source code is governed by a BSD-style license that can be
6 * found in the LICENSE file.
7 */
8
reed@android.com8a1c16f2008-12-17 15:59:43 +00009
10#include "SkScanPriv.h"
11#include "SkPath.h"
12#include "SkMatrix.h"
13#include "SkBlitter.h"
14#include "SkRegion.h"
15#include "SkAntiRun.h"
16
17#define SHIFT 2
18#define SCALE (1 << SHIFT)
19#define MASK (SCALE - 1)
20
tomhudson@google.com05fffdc2011-12-01 20:41:24 +000021/** @file
reed@google.com2f3567c2011-06-09 15:54:38 +000022 We have two techniques for capturing the output of the supersampler:
23 - SUPERMASK, which records a large mask-bitmap
24 this is often faster for small, complex objects
25 - RLE, which records a rle-encoded scanline
26 this is often faster for large objects with big spans
27
tomhudson@google.com05fffdc2011-12-01 20:41:24 +000028 These blitters use two coordinate systems:
29 - destination coordinates, scale equal to the output - often
30 abbreviated with 'i' or 'I' in variable names
31 - supersampled coordinates, scale equal to the output * SCALE
32
reed@google.com5b7d6032012-05-09 14:07:34 +000033 Enabling SK_USE_LEGACY_AA_COVERAGE keeps the aa coverage calculations as
34 they were before the fix that unified the output of the RLE and MASK
35 supersamplers.
reed@google.com2f3567c2011-06-09 15:54:38 +000036 */
reed@google.com5b7d6032012-05-09 14:07:34 +000037
reed@google.com9f2f0a82011-04-11 19:43:58 +000038//#define FORCE_SUPERMASK
39//#define FORCE_RLE
reed@google.comcd4e9fb2012-05-09 14:33:02 +000040//#define SK_USE_LEGACY_AA_COVERAGE
reed@google.com9f2f0a82011-04-11 19:43:58 +000041
42///////////////////////////////////////////////////////////////////////////////
reed@android.com8a1c16f2008-12-17 15:59:43 +000043
tomhudson@google.com05fffdc2011-12-01 20:41:24 +000044/// Base class for a single-pass supersampled blitter.
reed@android.com8a1c16f2008-12-17 15:59:43 +000045class BaseSuperBlitter : public SkBlitter {
46public:
47 BaseSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir,
reedbcba2c92014-10-15 08:52:00 -070048 const SkRegion& clip, bool isInverse);
reed@android.com8a1c16f2008-12-17 15:59:43 +000049
tomhudson@google.com05fffdc2011-12-01 20:41:24 +000050 /// Must be explicitly defined on subclasses.
reed@android.com8a1c16f2008-12-17 15:59:43 +000051 virtual void blitAntiH(int x, int y, const SkAlpha antialias[],
reed@google.com967a35d2011-10-31 19:37:58 +000052 const int16_t runs[]) SK_OVERRIDE {
tomhudson@google.com0c00f212011-12-28 14:59:50 +000053 SkDEBUGFAIL("How did I get here?");
reed@android.com8a1c16f2008-12-17 15:59:43 +000054 }
tomhudson@google.com05fffdc2011-12-01 20:41:24 +000055 /// May not be called on BaseSuperBlitter because it blits out of order.
reed@google.com967a35d2011-10-31 19:37:58 +000056 virtual void blitV(int x, int y, int height, SkAlpha alpha) SK_OVERRIDE {
tomhudson@google.com0c00f212011-12-28 14:59:50 +000057 SkDEBUGFAIL("How did I get here?");
reed@android.com8a1c16f2008-12-17 15:59:43 +000058 }
reed@android.com8a1c16f2008-12-17 15:59:43 +000059
60protected:
61 SkBlitter* fRealBlitter;
tomhudson@google.com05fffdc2011-12-01 20:41:24 +000062 /// Current y coordinate, in destination coordinates.
reed@android.com8a1c16f2008-12-17 15:59:43 +000063 int fCurrIY;
tomhudson@google.com05fffdc2011-12-01 20:41:24 +000064 /// Widest row of region to be blitted, in destination coordinates.
65 int fWidth;
66 /// Leftmost x coordinate in any row, in destination coordinates.
67 int fLeft;
68 /// Leftmost x coordinate in any row, in supersampled coordinates.
69 int fSuperLeft;
reed@android.com8a1c16f2008-12-17 15:59:43 +000070
71 SkDEBUGCODE(int fCurrX;)
tomhudson@google.com05fffdc2011-12-01 20:41:24 +000072 /// Current y coordinate in supersampled coordinates.
reed@google.comfa57ae72011-05-31 19:18:02 +000073 int fCurrY;
tomhudson@google.com05fffdc2011-12-01 20:41:24 +000074 /// Initial y coordinate (top of bounds).
reed@google.com045e62d2011-10-24 12:19:46 +000075 int fTop;
reedbcba2c92014-10-15 08:52:00 -070076
77 SkIRect fSectBounds;
reed@android.com8a1c16f2008-12-17 15:59:43 +000078};
79
reedbcba2c92014-10-15 08:52:00 -070080BaseSuperBlitter::BaseSuperBlitter(SkBlitter* realBlit, const SkIRect& ir, const SkRegion& clip,
81 bool isInverse) {
82 fRealBlitter = realBlit;
83
84 SkIRect sectBounds;
85 if (isInverse) {
86 sectBounds = clip.getBounds();
87 } else {
88 if (!sectBounds.intersect(ir, clip.getBounds())) {
89 sectBounds.setEmpty();
90 }
91 }
reed@google.com55b6b582011-03-02 15:58:18 +000092
reed@google.comdceecc72012-02-23 19:20:19 +000093 /*
94 * We use the clip bounds instead of the ir, since we may be asked to
95 * draw outside of the rect if we're a inverse filltype
96 */
reedbcba2c92014-10-15 08:52:00 -070097 const int left = sectBounds.left();
98 const int right = sectBounds.right();
rmistry@google.comfbfcd562012-08-23 18:09:54 +000099
reed@android.com8a1c16f2008-12-17 15:59:43 +0000100 fLeft = left;
101 fSuperLeft = left << SHIFT;
102 fWidth = right - left;
reedbcba2c92014-10-15 08:52:00 -0700103 fTop = sectBounds.top();
104 fCurrIY = fTop - 1;
105 fCurrY = (fTop << SHIFT) - 1;
106
reed@google.com34e52a02011-06-01 17:32:11 +0000107 SkDEBUGCODE(fCurrX = -1;)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000108}
109
tomhudson@google.com05fffdc2011-12-01 20:41:24 +0000110/// Run-length-encoded supersampling antialiased blitter.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000111class SuperBlitter : public BaseSuperBlitter {
112public:
reedbcba2c92014-10-15 08:52:00 -0700113 SuperBlitter(SkBlitter* realBlitter, const SkIRect& ir, const SkRegion& clip, bool isInverse);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000114
115 virtual ~SuperBlitter() {
116 this->flush();
reed@android.com8a1c16f2008-12-17 15:59:43 +0000117 }
118
tomhudson@google.com05fffdc2011-12-01 20:41:24 +0000119 /// Once fRuns contains a complete supersampled row, flush() blits
120 /// it out through the wrapped blitter.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000121 void flush();
122
tomhudson@google.com05fffdc2011-12-01 20:41:24 +0000123 /// Blits a row of pixels, with location and width specified
124 /// in supersampled coordinates.
reed@google.com967a35d2011-10-31 19:37:58 +0000125 virtual void blitH(int x, int y, int width) SK_OVERRIDE;
tomhudson@google.com05fffdc2011-12-01 20:41:24 +0000126 /// Blits a rectangle of pixels, with location and size specified
127 /// in supersampled coordinates.
reed@google.com967a35d2011-10-31 19:37:58 +0000128 virtual void blitRect(int x, int y, int width, int height) SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000129
130private:
krajcevski2ec93fc2014-07-16 13:31:41 -0700131 // The next three variables are used to track a circular buffer that
132 // contains the values used in SkAlphaRuns. These variables should only
133 // ever be updated in advanceRuns(), and fRuns should always point to
134 // a valid SkAlphaRuns...
135 int fRunsToBuffer;
136 void* fRunsBuffer;
137 int fCurrentRun;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000138 SkAlphaRuns fRuns;
krajcevski2ec93fc2014-07-16 13:31:41 -0700139
140 // extra one to store the zero at the end
141 int getRunsSz() const { return (fWidth + 1 + (fWidth + 2)/2) * sizeof(int16_t); }
142
143 // This function updates the fRuns variable to point to the next buffer space
144 // with adequate storage for a SkAlphaRuns. It mostly just advances fCurrentRun
145 // and resets fRuns to point to an empty scanline.
146 void advanceRuns() {
147 const size_t kRunsSz = this->getRunsSz();
148 fCurrentRun = (fCurrentRun + 1) % fRunsToBuffer;
149 fRuns.fRuns = reinterpret_cast<int16_t*>(
150 reinterpret_cast<uint8_t*>(fRunsBuffer) + fCurrentRun * kRunsSz);
151 fRuns.fAlpha = reinterpret_cast<SkAlpha*>(fRuns.fRuns + fWidth + 1);
152 fRuns.reset(fWidth);
153 }
154
reed@google.comfa57ae72011-05-31 19:18:02 +0000155 int fOffsetX;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000156};
157
reedbcba2c92014-10-15 08:52:00 -0700158SuperBlitter::SuperBlitter(SkBlitter* realBlitter, const SkIRect& ir, const SkRegion& clip,
159 bool isInverse)
160 : BaseSuperBlitter(realBlitter, ir, clip, isInverse)
161{
krajcevski2ec93fc2014-07-16 13:31:41 -0700162 fRunsToBuffer = realBlitter->requestRowsPreserved();
krajcevski75f88512014-07-21 09:54:23 -0700163 fRunsBuffer = realBlitter->allocBlitMemory(fRunsToBuffer * this->getRunsSz());
krajcevski2ec93fc2014-07-16 13:31:41 -0700164 fCurrentRun = -1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000165
krajcevski2ec93fc2014-07-16 13:31:41 -0700166 this->advanceRuns();
reed@google.comfa57ae72011-05-31 19:18:02 +0000167
168 fOffsetX = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000169}
170
reed@google.com9f2f0a82011-04-11 19:43:58 +0000171void SuperBlitter::flush() {
reed@google.com045e62d2011-10-24 12:19:46 +0000172 if (fCurrIY >= fTop) {
krajcevski2ec93fc2014-07-16 13:31:41 -0700173
174 SkASSERT(fCurrentRun < fRunsToBuffer);
reed@google.com9f2f0a82011-04-11 19:43:58 +0000175 if (!fRuns.empty()) {
krajcevski2ec93fc2014-07-16 13:31:41 -0700176 // SkDEBUGCODE(fRuns.dump();)
reed@android.com8a1c16f2008-12-17 15:59:43 +0000177 fRealBlitter->blitAntiH(fLeft, fCurrIY, fRuns.fAlpha, fRuns.fRuns);
krajcevski2ec93fc2014-07-16 13:31:41 -0700178 this->advanceRuns();
reed@google.comfa57ae72011-05-31 19:18:02 +0000179 fOffsetX = 0;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000180 }
krajcevski2ec93fc2014-07-16 13:31:41 -0700181
reed@google.com045e62d2011-10-24 12:19:46 +0000182 fCurrIY = fTop - 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000183 SkDEBUGCODE(fCurrX = -1;)
184 }
185}
186
tomhudson@google.comffc92482012-02-23 19:34:34 +0000187/** coverage_to_partial_alpha() is being used by SkAlphaRuns, which
188 *accumulates* SCALE pixels worth of "alpha" in [0,(256/SCALE)]
189 to produce a final value in [0, 255] and handles clamping 256->255
190 itself, with the same (alpha - (alpha >> 8)) correction as
191 coverage_to_exact_alpha().
192*/
193static inline int coverage_to_partial_alpha(int aa) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000194 aa <<= 8 - 2*SHIFT;
reed@google.com5b7d6032012-05-09 14:07:34 +0000195#ifdef SK_USE_LEGACY_AA_COVERAGE
reed@android.com8a1c16f2008-12-17 15:59:43 +0000196 aa -= aa >> (8 - SHIFT - 1);
tomhudson@google.comffc92482012-02-23 19:34:34 +0000197#endif
reed@google.com5b7d6032012-05-09 14:07:34 +0000198 return aa;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000199}
200
tomhudson@google.comffc92482012-02-23 19:34:34 +0000201/** coverage_to_exact_alpha() is being used by our blitter, which wants
202 a final value in [0, 255].
203*/
tomhudson@google.com49eac192011-12-27 13:59:20 +0000204static inline int coverage_to_exact_alpha(int aa) {
tomhudson@google.com31bab392012-01-03 20:12:42 +0000205 int alpha = (256 >> SHIFT) * aa;
206 // clamp 256->255
207 return alpha - (alpha >> 8);
tomhudson@google.com49eac192011-12-27 13:59:20 +0000208}
209
reed@google.com9f2f0a82011-04-11 19:43:58 +0000210void SuperBlitter::blitH(int x, int y, int width) {
reed@google.com967a35d2011-10-31 19:37:58 +0000211 SkASSERT(width > 0);
212
reed@android.com8a1c16f2008-12-17 15:59:43 +0000213 int iy = y >> SHIFT;
214 SkASSERT(iy >= fCurrIY);
215
216 x -= fSuperLeft;
217 // hack, until I figure out why my cubics (I think) go beyond the bounds
reed@google.com9f2f0a82011-04-11 19:43:58 +0000218 if (x < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000219 width += x;
220 x = 0;
221 }
222
223#ifdef SK_DEBUG
reed@android.com8a1c16f2008-12-17 15:59:43 +0000224 SkASSERT(y != fCurrY || x >= fCurrX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000225#endif
reed@google.comfa57ae72011-05-31 19:18:02 +0000226 SkASSERT(y >= fCurrY);
227 if (fCurrY != y) {
228 fOffsetX = 0;
229 fCurrY = y;
230 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000231
reed@google.com9f2f0a82011-04-11 19:43:58 +0000232 if (iy != fCurrIY) { // new scanline
reed@android.com8a1c16f2008-12-17 15:59:43 +0000233 this->flush();
234 fCurrIY = iy;
235 }
236
reed@google.com5a1e7952011-05-31 14:36:21 +0000237 int start = x;
238 int stop = x + width;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000239
reed@google.com5a1e7952011-05-31 14:36:21 +0000240 SkASSERT(start >= 0 && stop > start);
tomhudson@google.com05fffdc2011-12-01 20:41:24 +0000241 // integer-pixel-aligned ends of blit, rounded out
242 int fb = start & MASK;
243 int fe = stop & MASK;
reed@google.com5a1e7952011-05-31 14:36:21 +0000244 int n = (stop >> SHIFT) - (start >> SHIFT) - 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000245
reed@google.com5a1e7952011-05-31 14:36:21 +0000246 if (n < 0) {
247 fb = fe - fb;
248 n = 0;
249 fe = 0;
250 } else {
251 if (fb == 0) {
252 n += 1;
reed@google.com9f2f0a82011-04-11 19:43:58 +0000253 } else {
tomhudson@google.com49eac192011-12-27 13:59:20 +0000254 fb = SCALE - fb;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000255 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000256 }
reed@google.comfa57ae72011-05-31 19:18:02 +0000257
tomhudson@google.comffc92482012-02-23 19:34:34 +0000258 fOffsetX = fRuns.add(x >> SHIFT, coverage_to_partial_alpha(fb),
259 n, coverage_to_partial_alpha(fe),
reed@google.comfa57ae72011-05-31 19:18:02 +0000260 (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT),
261 fOffsetX);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000262
263#ifdef SK_DEBUG
264 fRuns.assertValid(y & MASK, (1 << (8 - SHIFT)));
265 fCurrX = x + width;
266#endif
267}
268
caryclark@google.com803eceb2012-06-06 12:09:34 +0000269#if 0 // UNUSED
reed@google.coma89c77b2011-12-01 21:47:26 +0000270static void set_left_rite_runs(SkAlphaRuns& runs, int ileft, U8CPU leftA,
271 int n, U8CPU riteA) {
272 SkASSERT(leftA <= 0xFF);
273 SkASSERT(riteA <= 0xFF);
274
275 int16_t* run = runs.fRuns;
276 uint8_t* aa = runs.fAlpha;
277
278 if (ileft > 0) {
279 run[0] = ileft;
280 aa[0] = 0;
281 run += ileft;
282 aa += ileft;
283 }
284
285 SkASSERT(leftA < 0xFF);
286 if (leftA > 0) {
287 *run++ = 1;
288 *aa++ = leftA;
289 }
290
291 if (n > 0) {
292 run[0] = n;
293 aa[0] = 0xFF;
294 run += n;
295 aa += n;
296 }
297
298 SkASSERT(riteA < 0xFF);
299 if (riteA > 0) {
300 *run++ = 1;
301 *aa++ = riteA;
302 }
303 run[0] = 0;
304}
caryclark@google.com803eceb2012-06-06 12:09:34 +0000305#endif
reed@google.coma89c77b2011-12-01 21:47:26 +0000306
reed@google.com967a35d2011-10-31 19:37:58 +0000307void SuperBlitter::blitRect(int x, int y, int width, int height) {
308 SkASSERT(width > 0);
309 SkASSERT(height > 0);
310
tomhudson@google.com05fffdc2011-12-01 20:41:24 +0000311 // blit leading rows
312 while ((y & MASK)) {
reed@google.com967a35d2011-10-31 19:37:58 +0000313 this->blitH(x, y++, width);
314 if (--height <= 0) {
315 return;
316 }
317 }
318 SkASSERT(height > 0);
319
tomhudson@google.com05fffdc2011-12-01 20:41:24 +0000320 // Since this is a rect, instead of blitting supersampled rows one at a
321 // time and then resolving to the destination canvas, we can blit
322 // directly to the destintion canvas one row per SCALE supersampled rows.
reed@google.com967a35d2011-10-31 19:37:58 +0000323 int start_y = y >> SHIFT;
324 int stop_y = (y + height) >> SHIFT;
325 int count = stop_y - start_y;
326 if (count > 0) {
327 y += count << SHIFT;
328 height -= count << SHIFT;
329
330 // save original X for our tail blitH() loop at the bottom
331 int origX = x;
332
333 x -= fSuperLeft;
334 // hack, until I figure out why my cubics (I think) go beyond the bounds
335 if (x < 0) {
336 width += x;
337 x = 0;
338 }
339
tomhudson@google.com49eac192011-12-27 13:59:20 +0000340 // There is always a left column, a middle, and a right column.
341 // ileft is the destination x of the first pixel of the entire rect.
342 // xleft is (SCALE - # of covered supersampled pixels) in that
343 // destination pixel.
reed@google.com967a35d2011-10-31 19:37:58 +0000344 int ileft = x >> SHIFT;
tomhudson@google.com05fffdc2011-12-01 20:41:24 +0000345 int xleft = x & MASK;
tomhudson@google.com49eac192011-12-27 13:59:20 +0000346 // irite is the destination x of the last pixel of the OPAQUE section.
347 // xrite is the number of supersampled pixels extending beyond irite;
348 // xrite/SCALE should give us alpha.
reed@google.com967a35d2011-10-31 19:37:58 +0000349 int irite = (x + width) >> SHIFT;
tomhudson@google.com05fffdc2011-12-01 20:41:24 +0000350 int xrite = (x + width) & MASK;
tomhudson@google.com49eac192011-12-27 13:59:20 +0000351 if (!xrite) {
352 xrite = SCALE;
353 irite--;
354 }
355
tomhudson@google.com47143592011-12-28 17:58:07 +0000356 // Need to call flush() to clean up pending draws before we
357 // even consider blitV(), since otherwise it can look nonmonotonic.
358 SkASSERT(start_y > fCurrIY);
359 this->flush();
360
reed@google.com967a35d2011-10-31 19:37:58 +0000361 int n = irite - ileft - 1;
362 if (n < 0) {
tomhudson@google.com47143592011-12-28 17:58:07 +0000363 // If n < 0, we'll only have a single partially-transparent column
364 // of pixels to render.
reed@google.com967a35d2011-10-31 19:37:58 +0000365 xleft = xrite - xleft;
tomhudson@google.com49eac192011-12-27 13:59:20 +0000366 SkASSERT(xleft <= SCALE);
367 SkASSERT(xleft > 0);
reed@google.com967a35d2011-10-31 19:37:58 +0000368 xrite = 0;
tomhudson@google.com49eac192011-12-27 13:59:20 +0000369 fRealBlitter->blitV(ileft + fLeft, start_y, count,
370 coverage_to_exact_alpha(xleft));
reed@google.com967a35d2011-10-31 19:37:58 +0000371 } else {
tomhudson@google.com47143592011-12-28 17:58:07 +0000372 // With n = 0, we have two possibly-transparent columns of pixels
373 // to render; with n > 0, we have opaque columns between them.
374
tomhudson@google.com49eac192011-12-27 13:59:20 +0000375 xleft = SCALE - xleft;
tomhudson@google.com47143592011-12-28 17:58:07 +0000376
377 // Using coverage_to_exact_alpha is not consistent with blitH()
378 const int coverageL = coverage_to_exact_alpha(xleft);
379 const int coverageR = coverage_to_exact_alpha(xrite);
380
381 SkASSERT(coverageL > 0 || n > 0 || coverageR > 0);
382 SkASSERT((coverageL != 0) + n + (coverageR != 0) <= fWidth);
383
384 fRealBlitter->blitAntiRect(ileft + fLeft, start_y, n, count,
385 coverageL, coverageR);
reed@google.com967a35d2011-10-31 19:37:58 +0000386 }
387
reed@google.com967a35d2011-10-31 19:37:58 +0000388 // preamble for our next call to blitH()
389 fCurrIY = stop_y - 1;
390 fOffsetX = 0;
391 fCurrY = y - 1;
392 fRuns.reset(fWidth);
393 x = origX;
394 }
395
tomhudson@google.com49eac192011-12-27 13:59:20 +0000396 // catch any remaining few rows
tomhudson@google.com05fffdc2011-12-01 20:41:24 +0000397 SkASSERT(height <= MASK);
reed@google.com967a35d2011-10-31 19:37:58 +0000398 while (--height >= 0) {
399 this->blitH(x, y++, width);
400 }
401}
402
reed@android.com8a1c16f2008-12-17 15:59:43 +0000403///////////////////////////////////////////////////////////////////////////////
404
tomhudson@google.com05fffdc2011-12-01 20:41:24 +0000405/// Masked supersampling antialiased blitter.
reed@android.com8a1c16f2008-12-17 15:59:43 +0000406class MaskSuperBlitter : public BaseSuperBlitter {
407public:
reedbcba2c92014-10-15 08:52:00 -0700408 MaskSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir, const SkRegion&, bool isInverse);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000409 virtual ~MaskSuperBlitter() {
410 fRealBlitter->blitMask(fMask, fClipRect);
411 }
412
reed@google.com967a35d2011-10-31 19:37:58 +0000413 virtual void blitH(int x, int y, int width) SK_OVERRIDE;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000414
reed@google.com9f2f0a82011-04-11 19:43:58 +0000415 static bool CanHandleRect(const SkIRect& bounds) {
416#ifdef FORCE_RLE
417 return false;
418#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000419 int width = bounds.width();
reed@google.comf9f258c2012-03-12 16:09:06 +0000420 int64_t rb = SkAlign4(width);
421 // use 64bits to detect overflow
422 int64_t storage = rb * bounds.height();
reed@google.com55b6b582011-03-02 15:58:18 +0000423
reed@android.com8a1c16f2008-12-17 15:59:43 +0000424 return (width <= MaskSuperBlitter::kMAX_WIDTH) &&
reed@google.comf9f258c2012-03-12 16:09:06 +0000425 (storage <= MaskSuperBlitter::kMAX_STORAGE);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000426 }
reed@google.com55b6b582011-03-02 15:58:18 +0000427
reed@android.com8a1c16f2008-12-17 15:59:43 +0000428private:
429 enum {
reed@google.com9f2f0a82011-04-11 19:43:58 +0000430#ifdef FORCE_SUPERMASK
431 kMAX_WIDTH = 2048,
432 kMAX_STORAGE = 1024 * 1024 * 2
433#else
reed@android.com8a1c16f2008-12-17 15:59:43 +0000434 kMAX_WIDTH = 32, // so we don't try to do very wide things, where the RLE blitter would be faster
435 kMAX_STORAGE = 1024
reed@google.com9f2f0a82011-04-11 19:43:58 +0000436#endif
reed@android.com8a1c16f2008-12-17 15:59:43 +0000437 };
438
439 SkMask fMask;
440 SkIRect fClipRect;
441 // we add 1 because add_aa_span can write (unchanged) 1 extra byte at the end, rather than
442 // perform a test to see if stopAlpha != 0
443 uint32_t fStorage[(kMAX_STORAGE >> 2) + 1];
444};
445
reedbcba2c92014-10-15 08:52:00 -0700446MaskSuperBlitter::MaskSuperBlitter(SkBlitter* realBlitter, const SkIRect& ir, const SkRegion& clip,
447 bool isInverse)
448 : BaseSuperBlitter(realBlitter, ir, clip, isInverse)
449{
reed@android.com8a1c16f2008-12-17 15:59:43 +0000450 SkASSERT(CanHandleRect(ir));
reedbcba2c92014-10-15 08:52:00 -0700451 SkASSERT(!isInverse);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000452
453 fMask.fImage = (uint8_t*)fStorage;
454 fMask.fBounds = ir;
455 fMask.fRowBytes = ir.width();
456 fMask.fFormat = SkMask::kA8_Format;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000457
reed@android.com8a1c16f2008-12-17 15:59:43 +0000458 fClipRect = ir;
459 fClipRect.intersect(clip.getBounds());
reed@google.com55b6b582011-03-02 15:58:18 +0000460
reed@android.com8a1c16f2008-12-17 15:59:43 +0000461 // For valgrind, write 1 extra byte at the end so we don't read
462 // uninitialized memory. See comment in add_aa_span and fStorage[].
463 memset(fStorage, 0, fMask.fBounds.height() * fMask.fRowBytes + 1);
464}
465
reed@google.com9f2f0a82011-04-11 19:43:58 +0000466static void add_aa_span(uint8_t* alpha, U8CPU startAlpha) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000467 /* I should be able to just add alpha[x] + startAlpha.
468 However, if the trailing edge of the previous span and the leading
469 edge of the current span round to the same super-sampled x value,
470 I might overflow to 256 with this add, hence the funny subtract.
471 */
472 unsigned tmp = *alpha + startAlpha;
473 SkASSERT(tmp <= 256);
474 *alpha = SkToU8(tmp - (tmp >> 8));
475}
476
reed@google.com9f2f0a82011-04-11 19:43:58 +0000477static inline uint32_t quadplicate_byte(U8CPU value) {
478 uint32_t pair = (value << 8) | value;
479 return (pair << 16) | pair;
480}
481
reed@google.com6997e5d2012-05-09 13:48:50 +0000482// Perform this tricky subtract, to avoid overflowing to 256. Our caller should
483// only ever call us with at most enough to hit 256 (never larger), so it is
484// enough to just subtract the high-bit. Actually clamping with a branch would
485// be slower (e.g. if (tmp > 255) tmp = 255;)
486//
487static inline void saturated_add(uint8_t* ptr, U8CPU add) {
488 unsigned tmp = *ptr + add;
489 SkASSERT(tmp <= 256);
490 *ptr = SkToU8(tmp - (tmp >> 8));
491}
492
reed@google.com9f2f0a82011-04-11 19:43:58 +0000493// minimum count before we want to setup an inner loop, adding 4-at-a-time
494#define MIN_COUNT_FOR_QUAD_LOOP 16
495
496static void add_aa_span(uint8_t* alpha, U8CPU startAlpha, int middleCount,
497 U8CPU stopAlpha, U8CPU maxValue) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000498 SkASSERT(middleCount >= 0);
499
reed@google.com6997e5d2012-05-09 13:48:50 +0000500 saturated_add(alpha, startAlpha);
501 alpha += 1;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000502
reed@google.com9f2f0a82011-04-11 19:43:58 +0000503 if (middleCount >= MIN_COUNT_FOR_QUAD_LOOP) {
504 // loop until we're quad-byte aligned
505 while (SkTCast<intptr_t>(alpha) & 0x3) {
506 alpha[0] = SkToU8(alpha[0] + maxValue);
507 alpha += 1;
508 middleCount -= 1;
509 }
510
511 int bigCount = middleCount >> 2;
512 uint32_t* qptr = reinterpret_cast<uint32_t*>(alpha);
513 uint32_t qval = quadplicate_byte(maxValue);
514 do {
515 *qptr++ += qval;
516 } while (--bigCount > 0);
517
518 middleCount &= 3;
519 alpha = reinterpret_cast<uint8_t*> (qptr);
520 // fall through to the following while-loop
521 }
522
523 while (--middleCount >= 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000524 alpha[0] = SkToU8(alpha[0] + maxValue);
525 alpha += 1;
526 }
527
528 // potentially this can be off the end of our "legal" alpha values, but that
529 // only happens if stopAlpha is also 0. Rather than test for stopAlpha != 0
530 // every time (slow), we just do it, and ensure that we've allocated extra space
531 // (see the + 1 comment in fStorage[]
reed@google.com6997e5d2012-05-09 13:48:50 +0000532 saturated_add(alpha, stopAlpha);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000533}
534
reed@google.com9f2f0a82011-04-11 19:43:58 +0000535void MaskSuperBlitter::blitH(int x, int y, int width) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000536 int iy = (y >> SHIFT);
reed@google.com55b6b582011-03-02 15:58:18 +0000537
reed@android.com8a1c16f2008-12-17 15:59:43 +0000538 SkASSERT(iy >= fMask.fBounds.fTop && iy < fMask.fBounds.fBottom);
539 iy -= fMask.fBounds.fTop; // make it relative to 0
540
senorblanco@chromium.orgc41513c2009-08-21 22:00:12 +0000541 // This should never happen, but it does. Until the true cause is
542 // discovered, let's skip this span instead of crashing.
543 // See http://crbug.com/17569.
544 if (iy < 0) {
545 return;
546 }
547
reed@android.com8a1c16f2008-12-17 15:59:43 +0000548#ifdef SK_DEBUG
549 {
550 int ix = x >> SHIFT;
551 SkASSERT(ix >= fMask.fBounds.fLeft && ix < fMask.fBounds.fRight);
552 }
553#endif
reed@google.com55b6b582011-03-02 15:58:18 +0000554
reed@android.com8a1c16f2008-12-17 15:59:43 +0000555 x -= (fMask.fBounds.fLeft << SHIFT);
556
557 // hack, until I figure out why my cubics (I think) go beyond the bounds
reed@google.com9f2f0a82011-04-11 19:43:58 +0000558 if (x < 0) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000559 width += x;
560 x = 0;
561 }
562
reed@android.com8a1c16f2008-12-17 15:59:43 +0000563 uint8_t* row = fMask.fImage + iy * fMask.fRowBytes + (x >> SHIFT);
564
565 int start = x;
566 int stop = x + width;
567
568 SkASSERT(start >= 0 && stop > start);
tomhudson@google.com05fffdc2011-12-01 20:41:24 +0000569 int fb = start & MASK;
570 int fe = stop & MASK;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000571 int n = (stop >> SHIFT) - (start >> SHIFT) - 1;
572
573
reed@google.com9f2f0a82011-04-11 19:43:58 +0000574 if (n < 0) {
senorblanco@chromium.orgfeef49c2009-08-12 20:21:49 +0000575 SkASSERT(row >= fMask.fImage);
576 SkASSERT(row < fMask.fImage + kMAX_STORAGE + 1);
tomhudson@google.comffc92482012-02-23 19:34:34 +0000577 add_aa_span(row, coverage_to_partial_alpha(fe - fb));
reed@google.com9f2f0a82011-04-11 19:43:58 +0000578 } else {
tomhudson@google.com49eac192011-12-27 13:59:20 +0000579 fb = SCALE - fb;
senorblanco@chromium.orgfeef49c2009-08-12 20:21:49 +0000580 SkASSERT(row >= fMask.fImage);
581 SkASSERT(row + n + 1 < fMask.fImage + kMAX_STORAGE + 1);
tomhudson@google.comffc92482012-02-23 19:34:34 +0000582 add_aa_span(row, coverage_to_partial_alpha(fb),
583 n, coverage_to_partial_alpha(fe),
reed@android.com8a1c16f2008-12-17 15:59:43 +0000584 (1 << (8 - SHIFT)) - (((y & MASK) + 1) >> SHIFT));
585 }
586
587#ifdef SK_DEBUG
588 fCurrX = x + width;
589#endif
590}
591
592///////////////////////////////////////////////////////////////////////////////
593
reed@google.com07db8612012-02-28 15:32:42 +0000594static bool fitsInsideLimit(const SkRect& r, SkScalar max) {
595 const SkScalar min = -max;
596 return r.fLeft > min && r.fTop > min &&
597 r.fRight < max && r.fBottom < max;
598}
599
tomhudson@google.com17b6ba42012-04-12 14:23:58 +0000600static int overflows_short_shift(int value, int shift) {
601 const int s = 16 + shift;
602 return (value << s >> s) - value;
603}
604
605/**
606 Would any of the coordinates of this rectangle not fit in a short,
607 when left-shifted by shift?
608*/
609static int rect_overflows_short_shift(SkIRect rect, int shift) {
610 SkASSERT(!overflows_short_shift(8191, SHIFT));
611 SkASSERT(overflows_short_shift(8192, SHIFT));
612 SkASSERT(!overflows_short_shift(32767, 0));
613 SkASSERT(overflows_short_shift(32768, 0));
614
615 // Since we expect these to succeed, we bit-or together
616 // for a tiny extra bit of speed.
617 return overflows_short_shift(rect.fLeft, SHIFT) |
618 overflows_short_shift(rect.fRight, SHIFT) |
619 overflows_short_shift(rect.fTop, SHIFT) |
620 overflows_short_shift(rect.fBottom, SHIFT);
621}
622
reed@google.com07db8612012-02-28 15:32:42 +0000623static bool safeRoundOut(const SkRect& src, SkIRect* dst, int32_t maxInt) {
624 const SkScalar maxScalar = SkIntToScalar(maxInt);
reed@google.com8f4d2302013-12-17 16:44:46 +0000625
reed@google.com07db8612012-02-28 15:32:42 +0000626 if (fitsInsideLimit(src, maxScalar)) {
627 src.roundOut(dst);
628 return true;
629 }
630 return false;
reed@android.com8a1c16f2008-12-17 15:59:43 +0000631}
632
reed@google.com5546ef22012-01-30 17:09:45 +0000633void SkScan::AntiFillPath(const SkPath& path, const SkRegion& origClip,
reed@google.com2c508f22011-10-03 21:15:46 +0000634 SkBlitter* blitter, bool forceRLE) {
reed@google.com5546ef22012-01-30 17:09:45 +0000635 if (origClip.isEmpty()) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000636 return;
637 }
638
reedbcba2c92014-10-15 08:52:00 -0700639 const bool isInverse = path.isInverseFillType();
reed@android.comd252db02009-04-01 18:31:44 +0000640 SkIRect ir;
reed@google.com07db8612012-02-28 15:32:42 +0000641
642 if (!safeRoundOut(path.getBounds(), &ir, SK_MaxS32 >> SHIFT)) {
643#if 0
644 const SkRect& r = path.getBounds();
645 SkDebugf("--- bounds can't fit in SkIRect\n", r.fLeft, r.fTop, r.fRight, r.fBottom);
646#endif
647 return;
648 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000649 if (ir.isEmpty()) {
reedbcba2c92014-10-15 08:52:00 -0700650 if (isInverse) {
reed@google.com5546ef22012-01-30 17:09:45 +0000651 blitter->blitRegion(origClip);
reed@google.comee068aa2011-12-21 19:36:21 +0000652 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000653 return;
654 }
655
tomhudson@google.com17b6ba42012-04-12 14:23:58 +0000656 // If the intersection of the path bounds and the clip bounds
657 // will overflow 32767 when << by SHIFT, we can't supersample,
658 // so draw without antialiasing.
659 SkIRect clippedIR;
reedbcba2c92014-10-15 08:52:00 -0700660 if (isInverse) {
tomhudson@google.com17b6ba42012-04-12 14:23:58 +0000661 // If the path is an inverse fill, it's going to fill the entire
662 // clip, and we care whether the entire clip exceeds our limits.
663 clippedIR = origClip.getBounds();
664 } else {
665 if (!clippedIR.intersect(ir, origClip.getBounds())) {
666 return;
667 }
668 }
669 if (rect_overflows_short_shift(clippedIR, SHIFT)) {
670 SkScan::FillPath(path, origClip, blitter);
671 return;
672 }
673
reed@google.com5546ef22012-01-30 17:09:45 +0000674 // Our antialiasing can't handle a clip larger than 32767, so we restrict
675 // the clip to that limit here. (the runs[] uses int16_t for its index).
676 //
tomhudson@google.com17b6ba42012-04-12 14:23:58 +0000677 // A more general solution (one that could also eliminate the need to
678 // disable aa based on ir bounds (see overflows_short_shift) would be
679 // to tile the clip/target...
reed@google.com5546ef22012-01-30 17:09:45 +0000680 SkRegion tmpClipStorage;
681 const SkRegion* clipRgn = &origClip;
682 {
683 static const int32_t kMaxClipCoord = 32767;
684 const SkIRect& bounds = origClip.getBounds();
685 if (bounds.fRight > kMaxClipCoord || bounds.fBottom > kMaxClipCoord) {
686 SkIRect limit = { 0, 0, kMaxClipCoord, kMaxClipCoord };
687 tmpClipStorage.op(origClip, limit, SkRegion::kIntersect_Op);
688 clipRgn = &tmpClipStorage;
689 }
690 }
691 // for here down, use clipRgn, not origClip
692
693 SkScanClipper clipper(blitter, clipRgn, ir);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000694 const SkIRect* clipRect = clipper.getClipRect();
695
696 if (clipper.getBlitter() == NULL) { // clipped out
reedbcba2c92014-10-15 08:52:00 -0700697 if (isInverse) {
reed@google.com5546ef22012-01-30 17:09:45 +0000698 blitter->blitRegion(*clipRgn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000699 }
700 return;
701 }
reed@google.com55b6b582011-03-02 15:58:18 +0000702
reed@android.com8a1c16f2008-12-17 15:59:43 +0000703 // now use the (possibly wrapped) blitter
704 blitter = clipper.getBlitter();
705
reedbcba2c92014-10-15 08:52:00 -0700706 if (isInverse) {
reed@google.com5546ef22012-01-30 17:09:45 +0000707 sk_blit_above(blitter, ir, *clipRgn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000708 }
709
710 SkIRect superRect, *superClipRect = NULL;
711
reed@google.com9f2f0a82011-04-11 19:43:58 +0000712 if (clipRect) {
reed@android.com8a1c16f2008-12-17 15:59:43 +0000713 superRect.set( clipRect->fLeft << SHIFT, clipRect->fTop << SHIFT,
714 clipRect->fRight << SHIFT, clipRect->fBottom << SHIFT);
715 superClipRect = &superRect;
716 }
717
senorblanco@chromium.orgc41513c2009-08-21 22:00:12 +0000718 SkASSERT(SkIntToScalar(ir.fTop) <= path.getBounds().fTop);
719
reed@android.com8a1c16f2008-12-17 15:59:43 +0000720 // MaskSuperBlitter can't handle drawing outside of ir, so we can't use it
721 // if we're an inverse filltype
reedbcba2c92014-10-15 08:52:00 -0700722 if (!isInverse && MaskSuperBlitter::CanHandleRect(ir) && !forceRLE) {
723 MaskSuperBlitter superBlit(blitter, ir, *clipRgn, isInverse);
senorblanco@chromium.orgc41513c2009-08-21 22:00:12 +0000724 SkASSERT(SkIntToScalar(ir.fTop) <= path.getBounds().fTop);
reed@google.com5546ef22012-01-30 17:09:45 +0000725 sk_fill_path(path, superClipRect, &superBlit, ir.fTop, ir.fBottom, SHIFT, *clipRgn);
reed@google.com9f2f0a82011-04-11 19:43:58 +0000726 } else {
reedbcba2c92014-10-15 08:52:00 -0700727 SuperBlitter superBlit(blitter, ir, *clipRgn, isInverse);
reed@google.com5546ef22012-01-30 17:09:45 +0000728 sk_fill_path(path, superClipRect, &superBlit, ir.fTop, ir.fBottom, SHIFT, *clipRgn);
reed@android.com8a1c16f2008-12-17 15:59:43 +0000729 }
reed@google.com55b6b582011-03-02 15:58:18 +0000730
reedbcba2c92014-10-15 08:52:00 -0700731 if (isInverse) {
reed@google.com5546ef22012-01-30 17:09:45 +0000732 sk_blit_below(blitter, ir, *clipRgn);
reed@google.com55b6b582011-03-02 15:58:18 +0000733 }
reed@android.com8a1c16f2008-12-17 15:59:43 +0000734}
reed@google.com1ba71372011-10-12 20:42:05 +0000735
736///////////////////////////////////////////////////////////////////////////////
737
738#include "SkRasterClip.h"
739
740void SkScan::FillPath(const SkPath& path, const SkRasterClip& clip,
741 SkBlitter* blitter) {
742 if (clip.isEmpty()) {
743 return;
744 }
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000745
reed@google.com1ba71372011-10-12 20:42:05 +0000746 if (clip.isBW()) {
747 FillPath(path, clip.bwRgn(), blitter);
748 } else {
749 SkRegion tmp;
750 SkAAClipBlitter aaBlitter;
rmistry@google.comfbfcd562012-08-23 18:09:54 +0000751
reed@google.com1ba71372011-10-12 20:42:05 +0000752 tmp.setRect(clip.getBounds());
753 aaBlitter.init(blitter, &clip.aaRgn());
754 SkScan::FillPath(path, tmp, &aaBlitter);
755 }
756}
757
758void SkScan::AntiFillPath(const SkPath& path, const SkRasterClip& clip,
759 SkBlitter* blitter) {
760 if (clip.isEmpty()) {
761 return;
762 }
763
764 if (clip.isBW()) {
765 AntiFillPath(path, clip.bwRgn(), blitter);
766 } else {
767 SkRegion tmp;
768 SkAAClipBlitter aaBlitter;
769
770 tmp.setRect(clip.getBounds());
771 aaBlitter.init(blitter, &clip.aaRgn());
772 SkScan::AntiFillPath(path, tmp, &aaBlitter, true);
773 }
774}