| /* |
| * Copyright 2017 Google Inc. |
| * |
| * Use of this source code is governed by a BSD-style license that can be |
| * found in the LICENSE file. |
| */ |
| |
| #include "SkCoverageDelta.h" |
| |
| SkCoverageDeltaList::SkCoverageDeltaList(SkCoverageDeltaAllocator* alloc, int top, int bottom, |
| bool forceRLE) { |
| fAlloc = alloc; |
| fTop = top; |
| fBottom = bottom; |
| fForceRLE = forceRLE; |
| |
| // Init the anti-rect to be empty |
| fAntiRect.fY = bottom; |
| fAntiRect.fHeight = 0; |
| |
| if (bottom - top <= RESERVED_HEIGHT) { |
| fSorted = fReservedSorted; |
| fCounts = fReservedCounts; |
| fMaxCounts = fReservedMaxCounts; |
| fRows = fReservedRows - top; |
| fRows[top] = fReservedStorage; |
| } else { |
| fSorted = fAlloc->makeArrayDefault<bool>(bottom - top); |
| fCounts = fAlloc->makeArrayDefault<int>((bottom - top) * 2); |
| fMaxCounts = fCounts + bottom - top; |
| fRows = fAlloc->makeArrayDefault<SkCoverageDelta*>(bottom - top) - top; |
| fRows[top] = fAlloc->makeArrayDefault<SkCoverageDelta>(INIT_ROW_SIZE * (bottom - top)); |
| } |
| |
| memset(fSorted, true, bottom - top); |
| memset(fCounts, 0, sizeof(int) * (bottom - top)); |
| |
| // Minus top so we can directly use fCounts[y] instead of fCounts[y - fTop]. |
| // Same for fMaxCounts, fRows, and fSorted. |
| fSorted -= top; |
| fCounts -= top; |
| fMaxCounts -= top; |
| |
| for(int y = top; y < bottom; ++y) { |
| fMaxCounts[y] = INIT_ROW_SIZE; |
| } |
| for(int y = top + 1; y < bottom; ++y) { |
| fRows[y] = fRows[y - 1] + INIT_ROW_SIZE; |
| } |
| } |
| |
| int SkCoverageDeltaMask::ExpandWidth(int width) { |
| int result = width + PADDING * 2; |
| return result + (SIMD_WIDTH - result % SIMD_WIDTH) % SIMD_WIDTH; |
| } |
| |
| bool SkCoverageDeltaMask::CanHandle(const SkIRect& bounds) { |
| // Expand width so we don't have to worry about the boundary |
| return ExpandWidth(bounds.width()) * bounds.height() + PADDING * 2 < MAX_MASK_SIZE; |
| } |
| |
| bool SkCoverageDeltaMask::Suitable(const SkIRect& bounds) { |
| return bounds.width() <= SUITABLE_WIDTH && CanHandle(bounds); |
| } |
| |
| SkCoverageDeltaMask::SkCoverageDeltaMask(const SkIRect& bounds) : fBounds(bounds) { |
| SkASSERT(CanHandle(bounds)); |
| |
| // Init the anti-rect to be empty |
| fAntiRect.fY = fBounds.fBottom; |
| fAntiRect.fHeight = 0; |
| |
| fExpandedWidth = ExpandWidth(fBounds.width()); |
| |
| // Add PADDING columns so we may access fDeltas[index(-PADDING, 0)] |
| // Minus index(fBounds.fLeft, fBounds.fTop) so we can directly access fDeltas[index(x, y)] |
| fDeltas = fDeltaStorage + PADDING - this->index(fBounds.fLeft, fBounds.fTop); |
| |
| memset(fDeltaStorage, 0, (fExpandedWidth * bounds.height() + PADDING * 2) * sizeof(SkFixed));; |
| } |
| |
| // TODO As this function is so performance-critical (and we're thinking so much about SIMD), use |
| // SkOpts framework to compile multiple versions of this function so we can choose the best one |
| // available at runtime. |
| void SkCoverageDeltaMask::convertCoverageToAlpha(bool isEvenOdd, bool isInverse, bool isConvex) { |
| SkFixed* deltaRow = &this->delta(fBounds.fLeft, fBounds.fTop); |
| SkAlpha* maskRow = fMask; |
| for(int iy = 0; iy < fBounds.height(); ++iy) { |
| // If we're inside fAntiRect, blit it to the mask and advance to its bottom |
| if (fAntiRect.fHeight && iy == fAntiRect.fY - fBounds.fTop) { |
| // Blit the mask |
| int L = fAntiRect.fX - fBounds.fLeft; |
| for(int i = 0; i < fAntiRect.fHeight; ++i) { |
| SkAlpha* tMask = maskRow + L; |
| if (fAntiRect.fLeftAlpha) { |
| tMask[0] = fAntiRect.fLeftAlpha; |
| } |
| memset(tMask + 1, 0xff, fAntiRect.fWidth); |
| if (fAntiRect.fRightAlpha) { |
| tMask[fAntiRect.fWidth + 1] = fAntiRect.fRightAlpha; |
| } |
| maskRow += fBounds.width(); |
| } |
| |
| // Advance to the bottom (maskRow is already advanced to the bottom). |
| deltaRow += fExpandedWidth * fAntiRect.fHeight; |
| iy += fAntiRect.fHeight - 1; // -1 because we'll ++iy after continue |
| continue; |
| } |
| |
| // Otherwise, cumulate deltas into coverages, and convert them into alphas |
| SkFixed c[SIMD_WIDTH] = {0}; // prepare SIMD_WIDTH coverages at a time |
| for(int ix = 0; ix < fExpandedWidth; ix += SIMD_WIDTH) { |
| // Future todo: is it faster to process SIMD_WIDTH rows at a time so we can use SIMD |
| // for coverage accumulation? |
| |
| // Cumulate deltas to get SIMD_WIDTH new coverages |
| c[0] = c[SIMD_WIDTH - 1] + deltaRow[ix]; |
| for(int j = 1; j < SIMD_WIDTH; ++j) { |
| c[j] = c[j - 1] + deltaRow[ix + j]; |
| } |
| |
| using SkNi = SkNx<SIMD_WIDTH, int>; |
| SkNi cn = SkNi::Load(c); |
| SkNi an = isConvex ? ConvexCoverageToAlpha(cn, isInverse) |
| : CoverageToAlpha(cn, isEvenOdd, isInverse); |
| SkNx_cast<SkAlpha>(an).store(maskRow + ix); |
| } |
| |
| // Finally, advance to the next row |
| deltaRow += fExpandedWidth; |
| maskRow += fBounds.width(); |
| } |
| } |