| /* | |
| * 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 |