/*
|
* Copyright 2006 The Android Open Source Project
|
*
|
* Use of this source code is governed by a BSD-style license that can be
|
* found in the LICENSE file.
|
*/
|
|
#ifndef SkAntiRun_DEFINED
|
#define SkAntiRun_DEFINED
|
|
#include "SkBlitter.h"
|
#include "SkTo.h"
|
|
/** Sparse array of run-length-encoded alpha (supersampling coverage) values.
|
Sparseness allows us to independently compose several paths into the
|
same SkAlphaRuns buffer.
|
*/
|
|
class SkAlphaRuns {
|
public:
|
int16_t* fRuns;
|
uint8_t* fAlpha;
|
|
// Return 0-255 given 0-256
|
static inline SkAlpha CatchOverflow(int alpha) {
|
SkASSERT(alpha >= 0 && alpha <= 256);
|
return alpha - (alpha >> 8);
|
}
|
|
/// Returns true if the scanline contains only a single run,
|
/// of alpha value 0.
|
bool empty() const {
|
SkASSERT(fRuns[0] > 0);
|
return fAlpha[0] == 0 && fRuns[fRuns[0]] == 0;
|
}
|
|
/// Reinitialize for a new scanline.
|
void reset(int width);
|
|
/**
|
* Insert into the buffer a run starting at (x-offsetX):
|
* if startAlpha > 0
|
* one pixel with value += startAlpha,
|
* max 255
|
* if middleCount > 0
|
* middleCount pixels with value += maxValue
|
* if stopAlpha > 0
|
* one pixel with value += stopAlpha
|
* Returns the offsetX value that should be passed on the next call,
|
* assuming we're on the same scanline. If the caller is switching
|
* scanlines, then offsetX should be 0 when this is called.
|
*/
|
SK_ALWAYS_INLINE int add(int x, U8CPU startAlpha, int middleCount, U8CPU stopAlpha,
|
U8CPU maxValue, int offsetX) {
|
SkASSERT(middleCount >= 0);
|
SkASSERT(x >= 0 && x + (startAlpha != 0) + middleCount + (stopAlpha != 0) <= fWidth);
|
|
SkASSERT(fRuns[offsetX] >= 0);
|
|
int16_t* runs = fRuns + offsetX;
|
uint8_t* alpha = fAlpha + offsetX;
|
uint8_t* lastAlpha = alpha;
|
x -= offsetX;
|
|
if (startAlpha) {
|
SkAlphaRuns::Break(runs, alpha, x, 1);
|
/* I should be able to just add alpha[x] + startAlpha.
|
However, if the trailing edge of the previous span and the leading
|
edge of the current span round to the same super-sampled x value,
|
I might overflow to 256 with this add, hence the funny subtract (crud).
|
*/
|
unsigned tmp = alpha[x] + startAlpha;
|
SkASSERT(tmp <= 256);
|
alpha[x] = SkToU8(tmp - (tmp >> 8)); // was (tmp >> 7), but that seems wrong if we're trying to catch 256
|
|
runs += x + 1;
|
alpha += x + 1;
|
x = 0;
|
SkDEBUGCODE(this->validate();)
|
}
|
|
if (middleCount) {
|
SkAlphaRuns::Break(runs, alpha, x, middleCount);
|
alpha += x;
|
runs += x;
|
x = 0;
|
do {
|
alpha[0] = SkToU8(CatchOverflow(alpha[0] + maxValue));
|
int n = runs[0];
|
SkASSERT(n <= middleCount);
|
alpha += n;
|
runs += n;
|
middleCount -= n;
|
} while (middleCount > 0);
|
SkDEBUGCODE(this->validate();)
|
lastAlpha = alpha;
|
}
|
|
if (stopAlpha) {
|
SkAlphaRuns::Break(runs, alpha, x, 1);
|
alpha += x;
|
alpha[0] = SkToU8(alpha[0] + stopAlpha);
|
SkDEBUGCODE(this->validate();)
|
lastAlpha = alpha;
|
}
|
|
return SkToS32(lastAlpha - fAlpha); // new offsetX
|
}
|
|
SkDEBUGCODE(void assertValid(int y, int maxStep) const;)
|
SkDEBUGCODE(void dump() const;)
|
|
/**
|
* Break the runs in the buffer at offsets x and x+count, properly
|
* updating the runs to the right and left.
|
* i.e. from the state AAAABBBB, run-length encoded as A4B4,
|
* Break(..., 2, 5) would produce AAAABBBB rle as A2A2B3B1.
|
* Allows add() to sum another run to some of the new sub-runs.
|
* i.e. adding ..CCCCC. would produce AADDEEEB, rle as A2D2E3B1.
|
*/
|
static void Break(int16_t runs[], uint8_t alpha[], int x, int count) {
|
SkASSERT(count > 0 && x >= 0);
|
|
// SkAlphaRuns::BreakAt(runs, alpha, x);
|
// SkAlphaRuns::BreakAt(&runs[x], &alpha[x], count);
|
|
int16_t* next_runs = runs + x;
|
uint8_t* next_alpha = alpha + x;
|
|
while (x > 0) {
|
int n = runs[0];
|
SkASSERT(n > 0);
|
|
if (x < n) {
|
alpha[x] = alpha[0];
|
runs[0] = SkToS16(x);
|
runs[x] = SkToS16(n - x);
|
break;
|
}
|
runs += n;
|
alpha += n;
|
x -= n;
|
}
|
|
runs = next_runs;
|
alpha = next_alpha;
|
x = count;
|
|
for (;;) {
|
int n = runs[0];
|
SkASSERT(n > 0);
|
|
if (x < n) {
|
alpha[x] = alpha[0];
|
runs[0] = SkToS16(x);
|
runs[x] = SkToS16(n - x);
|
break;
|
}
|
x -= n;
|
if (x <= 0) {
|
break;
|
}
|
runs += n;
|
alpha += n;
|
}
|
}
|
|
/**
|
* Cut (at offset x in the buffer) a run into two shorter runs with
|
* matching alpha values.
|
* Used by the RectClipBlitter to trim a RLE encoding to match the
|
* clipping rectangle.
|
*/
|
static void BreakAt(int16_t runs[], uint8_t alpha[], int x) {
|
while (x > 0) {
|
int n = runs[0];
|
SkASSERT(n > 0);
|
|
if (x < n) {
|
alpha[x] = alpha[0];
|
runs[0] = SkToS16(x);
|
runs[x] = SkToS16(n - x);
|
break;
|
}
|
runs += n;
|
alpha += n;
|
x -= n;
|
}
|
}
|
|
private:
|
SkDEBUGCODE(int fWidth;)
|
SkDEBUGCODE(void validate() const;)
|
};
|
|
#endif
|