/*
|
* Copyright 2017 Google Inc.
|
*
|
* Use of this source code is governed by a BSD-style license that can be
|
* found in the LICENSE file.
|
*/
|
|
#ifndef SkCoverageDelta_DEFINED
|
#define SkCoverageDelta_DEFINED
|
|
#include "SkArenaAlloc.h"
|
#include "SkFixed.h"
|
#include "SkMask.h"
|
#include "SkTSort.h"
|
#include "SkUtils.h"
|
|
// Future todo: maybe we can make fX and fDelta 16-bit long to speed it up a little bit.
|
struct SkCoverageDelta {
|
int fX; // the y coordinate will be implied in SkCoverageDeltaList
|
SkFixed fDelta; // the amount that the alpha changed
|
|
// Sort according to fX
|
bool operator<(const SkCoverageDelta& other) const {
|
return fX < other.fX;
|
}
|
};
|
|
// All the arguments needed for SkBlitter::blitAntiRect
|
struct SkAntiRect {
|
int fX;
|
int fY;
|
int fWidth;
|
int fHeight;
|
SkAlpha fLeftAlpha;
|
SkAlpha fRightAlpha;
|
};
|
|
// A list of SkCoverageDelta with y from top() to bottom().
|
// For each row y, there are count(y) number of deltas.
|
// You can ask whether they are sorted or not by sorted(y), and you can sort them by sort(y).
|
// Once sorted, getDelta(y, i) should return the i-th leftmost delta on row y.
|
class SkCoverageDeltaList {
|
public:
|
// We can store INIT_ROW_SIZE deltas per row (i.e., per y-scanline) initially.
|
#ifdef SK_BUILD_FOR_GOOGLE3
|
static constexpr int INIT_ROW_SIZE = 8; // google3 has 16k stack limit; so we make it small
|
#else
|
static constexpr int INIT_ROW_SIZE = 32;
|
#endif
|
|
SkCoverageDeltaList(SkArenaAlloc* alloc, const SkIRect& bounds, bool forceRLE);
|
|
int top() const { return fBounds.fTop; }
|
int bottom() const { return fBounds.fBottom; }
|
int left() const { return fBounds.fLeft; }
|
int right() const { return fBounds.fRight; }
|
bool forceRLE() const { return fForceRLE; }
|
int count(int y) const { this->checkY(y); return fCounts[y]; }
|
bool sorted(int y) const { this->checkY(y); return fSorted[y]; }
|
|
SK_ALWAYS_INLINE void addDelta(int x, int y, SkFixed delta) { this->push_back(y, {x, delta}); }
|
SK_ALWAYS_INLINE const SkCoverageDelta& getDelta(int y, int i) const {
|
this->checkY(y);
|
SkASSERT(i < fCounts[y]);
|
return fRows[y][i];
|
}
|
|
// It might be better to sort right before blitting to make the memory hot
|
void sort(int y) {
|
this->checkY(y);
|
if (!fSorted[y]) {
|
SkTQSort(fRows[y], fRows[y] + fCounts[y] - 1);
|
fSorted[y] = true;
|
}
|
}
|
|
const SkAntiRect& getAntiRect() const { return fAntiRect; }
|
void setAntiRect(int x, int y, int width, int height,
|
SkAlpha leftAlpha, SkAlpha rightAlpha) {
|
fAntiRect = {x, y, width, height, leftAlpha, rightAlpha};
|
}
|
|
private:
|
SkArenaAlloc* fAlloc;
|
SkCoverageDelta** fRows;
|
bool* fSorted;
|
int* fCounts;
|
int* fMaxCounts;
|
SkIRect fBounds;
|
SkAntiRect fAntiRect;
|
bool fForceRLE;
|
|
void checkY(int y) const { SkASSERT(y >= fBounds.fTop && y < fBounds.fBottom); }
|
|
SK_ALWAYS_INLINE void push_back(int y, const SkCoverageDelta& delta) {
|
this->checkY(y);
|
if (fCounts[y] == fMaxCounts[y]) {
|
fMaxCounts[y] *= 4;
|
SkCoverageDelta* newRow = fAlloc->makeArrayDefault<SkCoverageDelta>(fMaxCounts[y]);
|
memcpy(newRow, fRows[y], sizeof(SkCoverageDelta) * fCounts[y]);
|
fRows[y] = newRow;
|
}
|
SkASSERT(fCounts[y] < fMaxCounts[y]);
|
fRows[y][fCounts[y]++] = delta;
|
fSorted[y] = fSorted[y] && (fCounts[y] == 1 || delta.fX >= fRows[y][fCounts[y] - 2].fX);
|
}
|
};
|
|
class SkCoverageDeltaMask {
|
public:
|
// 3 for precision error, 1 for boundary delta (e.g., -SK_Fixed1 at fBounds.fRight + 1)
|
static constexpr int PADDING = 4;
|
|
static constexpr int SIMD_WIDTH = 8;
|
static constexpr int SUITABLE_WIDTH = 32;
|
#ifdef SK_BUILD_FOR_GOOGLE3
|
static constexpr int MAX_MASK_SIZE = 1024; // G3 has 16k stack limit based on -fstack-usage
|
#else
|
static constexpr int MAX_MASK_SIZE = 2048;
|
#endif
|
static constexpr int MAX_SIZE = MAX_MASK_SIZE * (sizeof(SkFixed) + sizeof(SkAlpha));
|
|
// Expand PADDING on both sides, and make it a multiple of SIMD_WIDTH
|
static int ExpandWidth(int width);
|
static bool CanHandle(const SkIRect& bounds); // whether bounds fits into MAX_MASK_SIZE
|
static bool Suitable(const SkIRect& bounds); // CanHandle(bounds) && width <= SUITABLE_WIDTH
|
|
SkCoverageDeltaMask(SkArenaAlloc* alloc, const SkIRect& bounds);
|
|
int top() const { return fBounds.fTop; }
|
int bottom() const { return fBounds.fBottom; }
|
SkAlpha* getMask() { return fMask; }
|
const SkIRect& getBounds() const { return fBounds; }
|
|
SK_ALWAYS_INLINE void addDelta (int x, int y, SkFixed delta) { this->delta(x, y) += delta; }
|
SK_ALWAYS_INLINE SkFixed& delta (int x, int y) {
|
this->checkX(x);
|
this->checkY(y);
|
return fDeltas[this->index(x, y)];
|
}
|
|
void setAntiRect(int x, int y, int width, int height,
|
SkAlpha leftAlpha, SkAlpha rightAlpha) {
|
fAntiRect = {x, y, width, height, leftAlpha, rightAlpha};
|
}
|
|
SkMask prepareSkMask() {
|
SkMask mask;
|
mask.fImage = fMask;
|
mask.fBounds = fBounds;
|
mask.fRowBytes = fBounds.width();
|
mask.fFormat = SkMask::kA8_Format;
|
return mask;
|
}
|
|
void convertCoverageToAlpha(bool isEvenOdd, bool isInverse, bool isConvex);
|
|
private:
|
SkIRect fBounds;
|
SkFixed* fDeltaStorage;
|
SkFixed* fDeltas;
|
SkAlpha* fMask;
|
int fExpandedWidth;
|
SkAntiRect fAntiRect;
|
|
SK_ALWAYS_INLINE int index(int x, int y) const { return y * fExpandedWidth + x; }
|
|
void checkY(int y) const { SkASSERT(y >= fBounds.fTop && y < fBounds.fBottom); }
|
void checkX(int x) const {
|
SkASSERT(x >= fBounds.fLeft - PADDING && x < fBounds.fRight + PADDING);
|
}
|
};
|
|
static SK_ALWAYS_INLINE SkAlpha CoverageToAlpha(SkFixed coverage, bool isEvenOdd, bool isInverse) {
|
SkAlpha result;
|
if (isEvenOdd) {
|
SkFixed mod17 = coverage & 0x1ffff;
|
SkFixed mod16 = coverage & 0xffff;
|
result = SkTPin(SkAbs32((mod16 << 1) - mod17) >> 8, 0, 255);
|
} else {
|
result = SkTPin(SkAbs32(coverage) >> 8, 0, 255);
|
}
|
return isInverse ? 255 - result : result;
|
}
|
|
struct SkDAARecord {
|
enum class Type {
|
kToBeComputed,
|
kMask,
|
kList,
|
kEmpty
|
} fType;
|
|
SkMask fMask;
|
SkCoverageDeltaList* fList;
|
SkArenaAlloc* fAlloc;
|
|
SkDAARecord(SkArenaAlloc* alloc) : fType(Type::kToBeComputed), fAlloc(alloc) {}
|
|
// When the scan converter returns early (e.g., the path is completely out of the clip), we set
|
// the type to empty to signal that the record has been computed and it's empty. This is
|
// required only for DEBUG where we check that the type must not be kToBeComputed after
|
// init-once.
|
void setEmpty() { fType = Type::kEmpty; }
|
static inline void SetEmpty(SkDAARecord* record) { // record may be nullptr
|
#ifdef SK_DEBUG
|
// If type != kToBeComputed, then we're in the draw phase and we shouldn't set it to empty
|
// because being empty in one tile does not imply emptiness in other tiles.
|
if (record && record->fType == Type::kToBeComputed) {
|
record->setEmpty();
|
}
|
#endif
|
}
|
};
|
|
template<typename T>
|
static SK_ALWAYS_INLINE T CoverageToAlpha(const T& coverage, bool isEvenOdd, bool isInverse) {
|
T t0(0), t255(255);
|
T result;
|
if (isEvenOdd) {
|
T mod17 = coverage & 0x1ffff;
|
T mod16 = coverage & 0xffff;
|
result = ((mod16 << 1) - mod17).abs() >> 8;
|
} else {
|
result = coverage.abs() >> 8;
|
}
|
result = T::Min(result, t255);
|
result = T::Max(result, t0);
|
return isInverse ? 255 - result : result;
|
}
|
|
// For convex paths (including inverse mode), the coverage is guaranteed to be
|
// between [-SK_Fixed1, SK_Fixed1] so we can skip isEvenOdd and SkTPin.
|
static SK_ALWAYS_INLINE SkAlpha ConvexCoverageToAlpha(SkFixed coverage, bool isInverse) {
|
SkASSERT(coverage >= -SK_Fixed1 && coverage <= SK_Fixed1);
|
int result = SkAbs32(coverage) >> 8;
|
result -= (result >> 8); // 256 to 255
|
return isInverse ? 255 - result : result;
|
}
|
|
template<typename T>
|
static SK_ALWAYS_INLINE T ConvexCoverageToAlpha(const T& coverage, bool isInverse) {
|
// allTrue is not implemented
|
// SkASSERT((coverage >= 0).allTrue() && (coverage <= SK_Fixed1).allTrue());
|
T result = coverage.abs() >> 8;
|
result -= (result >> 8); // 256 to 255
|
return isInverse ? 255 - result : result;
|
}
|
|
#endif
|