/* * 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(bottom - top); fCounts = fAlloc->makeArrayDefault((bottom - top) * 2); fMaxCounts = fCounts + bottom - top; fRows = fAlloc->makeArrayDefault(bottom - top) - top; fRows[top] = fAlloc->makeArrayDefault(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));; } 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]; } // My SIMD CoverageToAlpha seems to be only faster with SSSE3. // (On linux, even with -mavx2, my SIMD still seems to be slow...) // Even with only SSSE2, it's still faster to do SIMD_WIDTH non-SIMD computations at one // time (i.e., SIMD_WIDTH = 8 is faster than SIMD_WIDTH = 1 even if SK_CPU_SSE_LEVEL is // less than SK_CPU_SSE_LEVEL_SSSE3). Maybe the compiler is doing some SIMD by itself. #if SK_CPU_SSE_LEVEL >= SK_CPU_SSE_LEVEL_SSSE3 using SkNi = SkNx; SkNi cn = SkNi::Load(c); SkNi an = isConvex ? ConvexCoverageToAlpha(cn, isInverse) : CoverageToAlpha(cn, isEvenOdd, isInverse); SkNx_cast(an).store(maskRow + ix); #else for(int j = 0; j < SIMD_WIDTH; ++j) { maskRow[ix + j] = isConvex ? ConvexCoverageToAlpha(c[j], isInverse) : CoverageToAlpha(c[j], isEvenOdd, isInverse); } #endif } // Finally, advance to the next row deltaRow += fExpandedWidth; maskRow += fBounds.width(); } }