Beispiel #1
0
sk_sp<SkImage> SkImage::makeWithFilter(const SkImageFilter* filter, const SkIRect& subset,
                                       const SkIRect& clipBounds, SkIRect* outSubset,
                                       SkIPoint* offset) const {
    if (!filter || !outSubset || !offset || !this->bounds().contains(subset)) {
        return nullptr;
    }
    SkColorSpace* colorSpace = as_IB(this)->onImageInfo().colorSpace();
    sk_sp<SkSpecialImage> srcSpecialImage = SkSpecialImage::MakeFromImage(
        subset, sk_ref_sp(const_cast<SkImage*>(this)), colorSpace);
    if (!srcSpecialImage) {
        return nullptr;
    }

    sk_sp<SkImageFilterCache> cache(
        SkImageFilterCache::Create(SkImageFilterCache::kDefaultTransientSize));
    SkImageFilter::OutputProperties outputProperties(colorSpace);
    SkImageFilter::Context context(SkMatrix::I(), clipBounds, cache.get(), outputProperties);

    sk_sp<SkSpecialImage> result = filter->filterImage(srcSpecialImage.get(), context, offset);
    if (!result) {
        return nullptr;
    }

    *outSubset = SkIRect::MakeWH(result->width(), result->height());
    if (!outSubset->intersect(clipBounds.makeOffset(-offset->x(), -offset->y()))) {
        return nullptr;
    }
    offset->fX += outSubset->x();
    offset->fY += outSubset->y();

    // Note that here we're returning the special image's entire backing store, loose padding
    // and all!
    return result->asImage();
}
Beispiel #2
0
sk_sp<SkImage> SkImage_Lazy::onMakeSubset(const SkIRect& subset) const {
    SkASSERT(fInfo.bounds().contains(subset));
    SkASSERT(fInfo.bounds() != subset);

    const SkIRect generatorSubset = subset.makeOffset(fOrigin.x(), fOrigin.y());
    Validator validator(fSharedGenerator, &generatorSubset, fInfo.refColorSpace());
    return validator ? sk_sp<SkImage>(new SkImage_Lazy(&validator)) : nullptr;
}
Beispiel #3
0
SkIRect SkOffsetImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
                                                MapDirection direction) const {
    SkIPoint vec = map_offset_vector(ctm, fOffset);
    if (kReverse_MapDirection == direction) {
        SkPointPriv::Negate(vec);
    }

    return src.makeOffset(vec.fX, vec.fY);
}
SkIRect SkDropShadowImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix& ctm,
                                                    MapDirection direction) const {
    SkVector offsetVec = SkVector::Make(fDx, fDy);
    if (kReverse_MapDirection == direction) {
        offsetVec.negate();
    }
    ctm.mapVectors(&offsetVec, 1);
    SkIRect dst = src.makeOffset(SkScalarCeilToInt(offsetVec.x()),
                                 SkScalarCeilToInt(offsetVec.y()));
    SkVector sigma = SkVector::Make(fSigmaX, fSigmaY);
    ctm.mapVectors(&sigma, 1);
    dst.outset(SkScalarCeilToInt(SkScalarMul(sigma.x(), SkIntToScalar(3))),
                SkScalarCeilToInt(SkScalarMul(sigma.y(), SkIntToScalar(3))));
    if (fShadowMode == kDrawShadowAndForeground_ShadowMode) {
        dst.join(src);
    }
    return dst;
}
Beispiel #5
0
void GrCCPathCacheEntry::setCoverageCountAtlas(
        GrOnFlushResourceProvider* onFlushRP, GrCCAtlas* atlas, const SkIVector& atlasOffset,
        const GrOctoBounds& octoBounds, const SkIRect& devIBounds, const SkIVector& maskShift) {
    SkASSERT(fOnFlushRefCnt > 0);
    SkASSERT(!fCachedAtlas);  // Otherwise we would need to call releaseCachedAtlas().

    if (this->hasBeenEvicted()) {
        // This entry will never be found in the path cache again. Don't bother trying to save an
        // atlas texture for it in the GrResourceCache.
        return;
    }

    fCachedAtlas = atlas->refOrMakeCachedAtlas(onFlushRP);
    fCachedAtlas->incrOnFlushRefCnt(fOnFlushRefCnt);
    fCachedAtlas->addPathPixels(devIBounds.height() * devIBounds.width());

    fAtlasOffset = atlasOffset + maskShift;

    fOctoBounds.setOffset(octoBounds, -maskShift.fX, -maskShift.fY);
    fDevIBounds = devIBounds.makeOffset(-maskShift.fX, -maskShift.fY);
}
Beispiel #6
0
sk_sp<SkImage> SkImage::makeWithFilter(const SkImageFilter* filter, const SkIRect& subset,
                                       const SkIRect& clipBounds, SkIRect* outSubset,
                                       SkIPoint* offset) const {
  if (!filter || !outSubset || !offset || !this->bounds().contains(subset)) {
      return nullptr;
  }
  sk_sp<SkSpecialImage> srcSpecialImage = SkSpecialImage::MakeFromImage(
      subset, sk_ref_sp(const_cast<SkImage*>(this)));
  if (!srcSpecialImage) {
      return nullptr;
  }

  // FIXME: build a cache here.
  SkImageFilter::Context context(SkMatrix::I(), clipBounds, nullptr);
  sk_sp<SkSpecialImage> result =
      filter->filterImage(srcSpecialImage.get(), context, offset);

  if (!result) {
      return nullptr;
  }

  SkIRect fullSize = SkIRect::MakeWH(result->width(), result->height());
#if SK_SUPPORT_GPU
  if (result->isTextureBacked()) {
    GrContext* context = result->getContext();
    sk_sp<GrTexture> texture = result->asTextureRef(context);
    fullSize = SkIRect::MakeWH(texture->width(), texture->height());
  }
#endif
  *outSubset = SkIRect::MakeWH(result->width(), result->height());
  if (!outSubset->intersect(clipBounds.makeOffset(-offset->x(), -offset->y()))) {
      return nullptr;
  }
  offset->fX += outSubset->x();
  offset->fY += outSubset->y();
  // This isn't really a "tight" subset, but includes any texture padding.
  return result->makeTightSubset(fullSize);
}
sk_sp<SkSpecialImage> SkDisplacementMapEffect::onFilterImage(SkSpecialImage* source,
                                                             const Context& ctx,
                                                             SkIPoint* offset) const {
    SkIPoint colorOffset = SkIPoint::Make(0, 0);
    sk_sp<SkSpecialImage> color(this->filterInput(1, source, ctx, &colorOffset));
    if (!color) {
        return nullptr;
    }

    SkIPoint displOffset = SkIPoint::Make(0, 0);
    sk_sp<SkSpecialImage> displ(this->filterInput(0, source, ctx, &displOffset));
    if (!displ) {
        return nullptr;
    }

    const SkIRect srcBounds = SkIRect::MakeXYWH(colorOffset.x(), colorOffset.y(),
                                                color->width(), color->height());

    // Both paths do bounds checking on color pixel access, we don't need to
    // pad the color bitmap to bounds here.
    SkIRect bounds;
    if (!this->applyCropRect(ctx, srcBounds, &bounds)) {
        return nullptr;
    }

    SkIRect displBounds;
    displ = this->applyCropRect(ctx, displ.get(), &displOffset, &displBounds);
    if (!displ) {
        return nullptr;
    }

    if (!bounds.intersect(displBounds)) {
        return nullptr;
    }

    const SkIRect colorBounds = bounds.makeOffset(-colorOffset.x(), -colorOffset.y());

    SkVector scale = SkVector::Make(fScale, fScale);
    ctx.ctm().mapVectors(&scale, 1);

#if SK_SUPPORT_GPU
    if (source->isTextureBacked()) {
        GrContext* context = source->getContext();

        sk_sp<GrTexture> colorTexture(color->asTextureRef(context));
        sk_sp<GrTexture> displTexture(displ->asTextureRef(context));
        if (!colorTexture || !displTexture) {
            return nullptr;
        }

        GrSurfaceDesc desc;
        desc.fFlags = kRenderTarget_GrSurfaceFlag;
        desc.fWidth = bounds.width();
        desc.fHeight = bounds.height();
        desc.fConfig = kSkia8888_GrPixelConfig;

        SkAutoTUnref<GrTexture> dst(context->textureProvider()->createApproxTexture(desc));
        if (!dst) {
            return nullptr;
        }

        GrPaint paint;
        SkMatrix offsetMatrix = GrCoordTransform::MakeDivByTextureWHMatrix(displTexture.get());
        offsetMatrix.preTranslate(SkIntToScalar(colorOffset.fX - displOffset.fX),
                                  SkIntToScalar(colorOffset.fY - displOffset.fY));

        paint.addColorFragmentProcessor(
            GrDisplacementMapEffect::Create(fXChannelSelector,
                                            fYChannelSelector,
                                            scale,
                                            displTexture.get(),
                                            offsetMatrix,
                                            colorTexture.get(),
                                            SkISize::Make(color->width(),
                                                          color->height())))->unref();
        paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
        SkMatrix matrix;
        matrix.setTranslate(-SkIntToScalar(colorBounds.x()), -SkIntToScalar(colorBounds.y()));

        SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(dst->asRenderTarget()));
        if (!drawContext) {
            return nullptr;
        }

        drawContext->drawRect(GrClip::WideOpen(), paint, matrix, SkRect::Make(colorBounds));

        offset->fX = bounds.left();
        offset->fY = bounds.top();
        return SkSpecialImage::MakeFromGpu(SkIRect::MakeWH(bounds.width(), bounds.height()),
                                           kNeedNewImageUniqueID_SpecialImage,
                                           dst);
    }
#endif

    SkBitmap colorBM, displBM;

    if (!color->getROPixels(&colorBM) || !displ->getROPixels(&displBM)) {
        return nullptr;
    }

    if ((colorBM.colorType() != kN32_SkColorType) ||
        (displBM.colorType() != kN32_SkColorType)) {
        return nullptr;
    }

    SkAutoLockPixels colorLock(colorBM), displLock(displBM);
    if (!colorBM.getPixels() || !displBM.getPixels()) {
        return nullptr;
    }

    SkImageInfo info = SkImageInfo::MakeN32(bounds.width(), bounds.height(),
                                            colorBM.alphaType());

    SkBitmap dst;
    if (!dst.tryAllocPixels(info)) {
        return nullptr;
    }

    SkAutoLockPixels dstLock(dst);

    computeDisplacement(fXChannelSelector, fYChannelSelector, scale, &dst,
                        displBM, colorOffset - displOffset, colorBM, colorBounds);

    offset->fX = bounds.left();
    offset->fY = bounds.top();
    return SkSpecialImage::MakeFromRaster(SkIRect::MakeWH(bounds.width(), bounds.height()),
                                          dst);
}
sk_sp<SkSpecialImage> SkBlurImageFilter::onFilterImage(SkSpecialImage* source,
                                                       const Context& ctx,
                                                       SkIPoint* offset) const {
    SkIPoint inputOffset = SkIPoint::Make(0, 0);

    sk_sp<SkSpecialImage> input(this->filterInput(0, source, ctx, &inputOffset));
    if (!input) {
        return nullptr;
    }

    SkIRect inputBounds = SkIRect::MakeXYWH(inputOffset.fX, inputOffset.fY,
                                            input->width(), input->height());

    SkIRect dstBounds;
    if (!this->applyCropRect(this->mapContext(ctx), inputBounds, &dstBounds)) {
        return nullptr;
    }
    if (!inputBounds.intersect(dstBounds)) {
        return nullptr;
    }

    const SkVector sigma = map_sigma(fSigma, ctx.ctm());

#if SK_SUPPORT_GPU
    if (input->peekTexture() && input->peekTexture()->getContext()) {
        if (0 == sigma.x() && 0 == sigma.y()) {
            offset->fX = inputBounds.x();
            offset->fY = inputBounds.y();
            return input->makeSubset(inputBounds.makeOffset(-inputOffset.x(),
                                                            -inputOffset.y()));
        }

        GrTexture* inputTexture = input->peekTexture();

        offset->fX = dstBounds.fLeft;
        offset->fY = dstBounds.fTop;
        inputBounds.offset(-inputOffset);
        dstBounds.offset(-inputOffset);
        SkRect inputBoundsF(SkRect::Make(inputBounds));
        SkAutoTUnref<GrTexture> tex(SkGpuBlurUtils::GaussianBlur(inputTexture->getContext(),
                                                                 inputTexture,
                                                                 false,
                                                                 source->props().allowSRGBInputs(),
                                                                 SkRect::Make(dstBounds),
                                                                 &inputBoundsF,
                                                                 sigma.x(),
                                                                 sigma.y()));
        if (!tex) {
            return nullptr;
        }

        return SkSpecialImage::MakeFromGpu(source->internal_getProxy(),
                                           SkIRect::MakeWH(dstBounds.width(), dstBounds.height()),
                                           kNeedNewImageUniqueID_SpecialImage,
                                           tex, &source->props());
    }
#endif

    int kernelSizeX, kernelSizeX3, lowOffsetX, highOffsetX;
    int kernelSizeY, kernelSizeY3, lowOffsetY, highOffsetY;
    get_box3_params(sigma.x(), &kernelSizeX, &kernelSizeX3, &lowOffsetX, &highOffsetX);
    get_box3_params(sigma.y(), &kernelSizeY, &kernelSizeY3, &lowOffsetY, &highOffsetY);

    if (kernelSizeX < 0 || kernelSizeY < 0) {
        return nullptr;
    }

    if (kernelSizeX == 0 && kernelSizeY == 0) {
        offset->fX = inputBounds.x();
        offset->fY = inputBounds.y();
        return input->makeSubset(inputBounds.makeOffset(-inputOffset.x(),
                                                        -inputOffset.y()));
    }

    SkPixmap inputPixmap;

    if (!input->peekPixels(&inputPixmap)) {
        return nullptr;
    }

    if (inputPixmap.colorType() != kN32_SkColorType) {
        return nullptr;
    }

    SkImageInfo info = SkImageInfo::Make(dstBounds.width(), dstBounds.height(),
                                         inputPixmap.colorType(), inputPixmap.alphaType());

    SkBitmap tmp, dst;
    if (!tmp.tryAllocPixels(info) || !dst.tryAllocPixels(info)) {
        return nullptr;
    }

    SkAutoLockPixels tmpLock(tmp), dstLock(dst);

    offset->fX = dstBounds.fLeft;
    offset->fY = dstBounds.fTop;
    SkPMColor* t = tmp.getAddr32(0, 0);
    SkPMColor* d = dst.getAddr32(0, 0);
    int w = dstBounds.width(), h = dstBounds.height();
    const SkPMColor* s = inputPixmap.addr32(inputBounds.x() - inputOffset.x(),
                                            inputBounds.y() - inputOffset.y());
    inputBounds.offset(-dstBounds.x(), -dstBounds.y());
    dstBounds.offset(-dstBounds.x(), -dstBounds.y());
    SkIRect inputBoundsT = SkIRect::MakeLTRB(inputBounds.top(), inputBounds.left(),
                                             inputBounds.bottom(), inputBounds.right());
    SkIRect dstBoundsT = SkIRect::MakeWH(dstBounds.height(), dstBounds.width());
    int sw = int(inputPixmap.rowBytes() >> 2);

    /**
     *
     * In order to make memory accesses cache-friendly, we reorder the passes to
     * use contiguous memory reads wherever possible.
     *
     * For example, the 6 passes of the X-and-Y blur case are rewritten as
     * follows. Instead of 3 passes in X and 3 passes in Y, we perform
     * 2 passes in X, 1 pass in X transposed to Y on write, 2 passes in X,
     * then 1 pass in X transposed to Y on write.
     *
     * +----+       +----+       +----+        +---+       +---+       +---+        +----+
     * + AB + ----> | AB | ----> | AB | -----> | A | ----> | A | ----> | A | -----> | AB |
     * +----+ blurX +----+ blurX +----+ blurXY | B | blurX | B | blurX | B | blurXY +----+
     *                                         +---+       +---+       +---+
     *
     * In this way, two of the y-blurs become x-blurs applied to transposed
     * images, and all memory reads are contiguous.
     */
    if (kernelSizeX > 0 && kernelSizeY > 0) {
        SkOpts::box_blur_xx(s, sw,  inputBounds,  t, kernelSizeX,  lowOffsetX,  highOffsetX, w, h);
        SkOpts::box_blur_xx(t,  w,  dstBounds,    d, kernelSizeX,  highOffsetX, lowOffsetX,  w, h);
        SkOpts::box_blur_xy(d,  w,  dstBounds,    t, kernelSizeX3, highOffsetX, highOffsetX, w, h);
        SkOpts::box_blur_xx(t,  h,  dstBoundsT,   d, kernelSizeY,  lowOffsetY,  highOffsetY, h, w);
        SkOpts::box_blur_xx(d,  h,  dstBoundsT,   t, kernelSizeY,  highOffsetY, lowOffsetY,  h, w);
        SkOpts::box_blur_xy(t,  h,  dstBoundsT,   d, kernelSizeY3, highOffsetY, highOffsetY, h, w);
    } else if (kernelSizeX > 0) {
        SkOpts::box_blur_xx(s, sw,  inputBounds,  d, kernelSizeX,  lowOffsetX,  highOffsetX, w, h);
        SkOpts::box_blur_xx(d,  w,  dstBounds,    t, kernelSizeX,  highOffsetX, lowOffsetX,  w, h);
        SkOpts::box_blur_xx(t,  w,  dstBounds,    d, kernelSizeX3, highOffsetX, highOffsetX, w, h);
    } else if (kernelSizeY > 0) {
        SkOpts::box_blur_yx(s, sw,  inputBoundsT, d, kernelSizeY,  lowOffsetY,  highOffsetY, h, w);
        SkOpts::box_blur_xx(d,  h,  dstBoundsT,   t, kernelSizeY,  highOffsetY, lowOffsetY,  h, w);
        SkOpts::box_blur_xy(t,  h,  dstBoundsT,   d, kernelSizeY3, highOffsetY, highOffsetY, h, w);
    }

    return SkSpecialImage::MakeFromRaster(source->internal_getProxy(),
                                          SkIRect::MakeWH(dstBounds.width(),
                                                          dstBounds.height()),
                                          dst, &source->props());
}
Beispiel #9
0
void GrCCPathParser::saveParsedPath(ScissorMode scissorMode, const SkIRect& clippedDevIBounds,
                                    int16_t atlasOffsetX, int16_t atlasOffsetY) {
    SkASSERT(fParsingPath);

    fPathsInfo.emplace_back(scissorMode, atlasOffsetX, atlasOffsetY);

    // Tessellate fans from very large and/or simple paths, in order to reduce overdraw.
    int numVerbs = fGeometry.verbs().count() - fCurrPathVerbsIdx - 1;
    int64_t tessellationWork = (int64_t)numVerbs * (32 - SkCLZ(numVerbs)); // N log N.
    int64_t fanningWork = (int64_t)clippedDevIBounds.height() * clippedDevIBounds.width();
    if (tessellationWork * (50*50) + (100*100) < fanningWork) { // Don't tessellate under 100x100.
        fCurrPathPrimitiveCounts.fTriangles =
                fCurrPathPrimitiveCounts.fWeightedTriangles = 0;

        const SkTArray<GrCCGeometry::Verb, true>& verbs = fGeometry.verbs();
        const SkTArray<SkPoint, true>& pts = fGeometry.points();
        int ptsIdx = fCurrPathPointsIdx;

        // Build an SkPath of the Redbook fan. We use "winding" fill type right now because we are
        // producing a coverage count, and must fill in every region that has non-zero wind. The
        // path processor will convert coverage count to the appropriate fill type later.
        SkPath fan;
        fan.setFillType(SkPath::kWinding_FillType);
        SkASSERT(GrCCGeometry::Verb::kBeginPath == verbs[fCurrPathVerbsIdx]);
        for (int i = fCurrPathVerbsIdx + 1; i < fGeometry.verbs().count(); ++i) {
            switch (verbs[i]) {
                case GrCCGeometry::Verb::kBeginPath:
                    SK_ABORT("Invalid GrCCGeometry");
                    continue;

                case GrCCGeometry::Verb::kBeginContour:
                    fan.moveTo(pts[ptsIdx++]);
                    continue;

                case GrCCGeometry::Verb::kLineTo:
                    fan.lineTo(pts[ptsIdx++]);
                    continue;

                case GrCCGeometry::Verb::kMonotonicQuadraticTo:
                case GrCCGeometry::Verb::kMonotonicConicTo:
                    fan.lineTo(pts[ptsIdx + 1]);
                    ptsIdx += 2;
                    continue;

                case GrCCGeometry::Verb::kMonotonicCubicTo:
                    fan.lineTo(pts[ptsIdx + 2]);
                    ptsIdx += 3;
                    continue;

                case GrCCGeometry::Verb::kEndClosedContour:
                case GrCCGeometry::Verb::kEndOpenContour:
                    fan.close();
                    continue;
            }
        }
        GrTessellator::WindingVertex* vertices = nullptr;
        int count = GrTessellator::PathToVertices(fan, std::numeric_limits<float>::infinity(),
                                                  SkRect::Make(clippedDevIBounds), &vertices);
        SkASSERT(0 == count % 3);
        for (int i = 0; i < count; i += 3) {
            int tessWinding = vertices[i].fWinding;
            SkASSERT(tessWinding == vertices[i + 1].fWinding);
            SkASSERT(tessWinding == vertices[i + 2].fWinding);

            // Ensure this triangle's points actually wind in the same direction as tessWinding.
            // CCPR shaders use the sign of wind to determine which direction to bloat, so even for
            // "wound" triangles the winding sign and point ordering need to agree.
            float ax = vertices[i].fPos.fX - vertices[i + 1].fPos.fX;
            float ay = vertices[i].fPos.fY - vertices[i + 1].fPos.fY;
            float bx = vertices[i].fPos.fX - vertices[i + 2].fPos.fX;
            float by = vertices[i].fPos.fY - vertices[i + 2].fPos.fY;
            float wind = ax*by - ay*bx;
            if ((wind > 0) != (-tessWinding > 0)) { // Tessellator has opposite winding sense.
                std::swap(vertices[i + 1].fPos, vertices[i + 2].fPos);
            }

            if (1 == abs(tessWinding)) {
                ++fCurrPathPrimitiveCounts.fTriangles;
            } else {
                ++fCurrPathPrimitiveCounts.fWeightedTriangles;
            }
        }

        fPathsInfo.back().adoptFanTessellation(vertices, count);
    }

    fTotalPrimitiveCounts[(int)scissorMode] += fCurrPathPrimitiveCounts;

    if (ScissorMode::kScissored == scissorMode) {
        fScissorSubBatches.push_back() = {fTotalPrimitiveCounts[(int)ScissorMode::kScissored],
                                          clippedDevIBounds.makeOffset(atlasOffsetX, atlasOffsetY)};
    }

    SkDEBUGCODE(fParsingPath = false);
}
Beispiel #10
0
GrCCPathCache::OnFlushEntryRef GrCCPathCache::find(
        GrOnFlushResourceProvider* onFlushRP, const GrShape& shape,
        const SkIRect& clippedDrawBounds, const SkMatrix& viewMatrix, SkIVector* maskShift) {
    if (!shape.hasUnstyledKey()) {
        return OnFlushEntryRef();
    }

    WriteKeyHelper writeKeyHelper(shape);
    if (writeKeyHelper.allocCountU32() > kMaxKeyDataCountU32) {
        return OnFlushEntryRef();
    }

    SkASSERT(fScratchKey->unique());
    fScratchKey->resetDataCountU32(writeKeyHelper.allocCountU32());
    writeKeyHelper.write(shape, fScratchKey->data());

    MaskTransform m(viewMatrix, maskShift);
    GrCCPathCacheEntry* entry = nullptr;
    if (HashNode* node = fHashTable.find(*fScratchKey)) {
        entry = node->entry();
        SkASSERT(fLRU.isInList(entry));

        if (!fuzzy_equals(m, entry->fMaskTransform)) {
            // The path was reused with an incompatible matrix.
            if (entry->unique()) {
                // This entry is unique: recycle it instead of deleting and malloc-ing a new one.
                SkASSERT(0 == entry->fOnFlushRefCnt);  // Because we are unique.
                entry->fMaskTransform = m;
                entry->fHitCount = 0;
                entry->fHitRect = SkIRect::MakeEmpty();
                entry->releaseCachedAtlas(this);
            } else {
                this->evict(*fScratchKey);
                entry = nullptr;
            }
        }
    }

    if (!entry) {
        if (fHashTable.count() >= kMaxCacheCount) {
            SkDEBUGCODE(HashNode* node = fHashTable.find(*fLRU.tail()->fCacheKey));
            SkASSERT(node && node->entry() == fLRU.tail());
            this->evict(*fLRU.tail()->fCacheKey);  // We've exceeded our limit.
        }

        // Create a new entry in the cache.
        sk_sp<Key> permanentKey = Key::Make(fInvalidatedKeysInbox.uniqueID(),
                                            writeKeyHelper.allocCountU32(), fScratchKey->data());
        SkASSERT(*permanentKey == *fScratchKey);
        SkASSERT(!fHashTable.find(*permanentKey));
        entry = fHashTable.set(HashNode(this, std::move(permanentKey), m, shape))->entry();

        SkASSERT(fHashTable.count() <= kMaxCacheCount);
    } else {
        fLRU.remove(entry);  // Will be re-added at head.
    }

    SkDEBUGCODE(HashNode* node = fHashTable.find(*fScratchKey));
    SkASSERT(node && node->entry() == entry);
    fLRU.addToHead(entry);

    if (0 == entry->fOnFlushRefCnt) {
        // Only update the time stamp and hit count if we haven't seen this entry yet during the
        // current flush.
        entry->fTimestamp = this->quickPerFlushTimestamp();
        ++entry->fHitCount;

        if (entry->fCachedAtlas) {
            SkASSERT(SkToBool(entry->fCachedAtlas->peekOnFlushRefCnt()) ==
                     SkToBool(entry->fCachedAtlas->getOnFlushProxy()));
            if (!entry->fCachedAtlas->getOnFlushProxy()) {
                if (sk_sp<GrTextureProxy> onFlushProxy = onFlushRP->findOrCreateProxyByUniqueKey(
                        entry->fCachedAtlas->textureKey(), GrCCAtlas::kTextureOrigin)) {
                    onFlushProxy->priv().setIgnoredByResourceAllocator();
                    entry->fCachedAtlas->setOnFlushProxy(std::move(onFlushProxy));
                }
            }
            if (!entry->fCachedAtlas->getOnFlushProxy()) {
                // Our atlas's backing texture got purged from the GrResourceCache. Release the
                // cached atlas.
                entry->releaseCachedAtlas(this);
            }
        }
    }
    entry->fHitRect.join(clippedDrawBounds.makeOffset(-maskShift->x(), -maskShift->y()));
    SkASSERT(!entry->fCachedAtlas || entry->fCachedAtlas->getOnFlushProxy());
    return OnFlushEntryRef::OnFlushRef(entry);
}
Beispiel #11
0
void GrCCFiller::parseDeviceSpaceFill(const SkPath& path, const SkPoint* deviceSpacePts,
                                      GrScissorTest scissorTest, const SkIRect& clippedDevIBounds,
                                      const SkIVector& devToAtlasOffset) {
    SkASSERT(!fInstanceBuffer);  // Can't call after prepareToDraw().
    SkASSERT(!path.isEmpty());

    int currPathPointsIdx = fGeometry.points().count();
    int currPathVerbsIdx = fGeometry.verbs().count();
    PrimitiveTallies currPathPrimitiveCounts = PrimitiveTallies();

    fGeometry.beginPath();

    const float* conicWeights = SkPathPriv::ConicWeightData(path);
    int ptsIdx = 0;
    int conicWeightsIdx = 0;
    bool insideContour = false;

    for (SkPath::Verb verb : SkPathPriv::Verbs(path)) {
        switch (verb) {
            case SkPath::kMove_Verb:
                if (insideContour) {
                    currPathPrimitiveCounts += fGeometry.endContour();
                }
                fGeometry.beginContour(deviceSpacePts[ptsIdx]);
                ++ptsIdx;
                insideContour = true;
                continue;
            case SkPath::kClose_Verb:
                if (insideContour) {
                    currPathPrimitiveCounts += fGeometry.endContour();
                }
                insideContour = false;
                continue;
            case SkPath::kLine_Verb:
                fGeometry.lineTo(&deviceSpacePts[ptsIdx - 1]);
                ++ptsIdx;
                continue;
            case SkPath::kQuad_Verb:
                fGeometry.quadraticTo(&deviceSpacePts[ptsIdx - 1]);
                ptsIdx += 2;
                continue;
            case SkPath::kCubic_Verb:
                fGeometry.cubicTo(&deviceSpacePts[ptsIdx - 1]);
                ptsIdx += 3;
                continue;
            case SkPath::kConic_Verb:
                fGeometry.conicTo(&deviceSpacePts[ptsIdx - 1], conicWeights[conicWeightsIdx]);
                ptsIdx += 2;
                ++conicWeightsIdx;
                continue;
            default:
                SK_ABORT("Unexpected path verb.");
        }
    }
    SkASSERT(ptsIdx == path.countPoints());
    SkASSERT(conicWeightsIdx == SkPathPriv::ConicWeightCnt(path));

    if (insideContour) {
        currPathPrimitiveCounts += fGeometry.endContour();
    }

    fPathInfos.emplace_back(scissorTest, devToAtlasOffset);

    // Tessellate fans from very large and/or simple paths, in order to reduce overdraw.
    int numVerbs = fGeometry.verbs().count() - currPathVerbsIdx - 1;
    int64_t tessellationWork = (int64_t)numVerbs * (32 - SkCLZ(numVerbs)); // N log N.
    int64_t fanningWork = (int64_t)clippedDevIBounds.height() * clippedDevIBounds.width();
    if (tessellationWork * (50*50) + (100*100) < fanningWork) { // Don't tessellate under 100x100.
        fPathInfos.back().tessellateFan(fGeometry, currPathVerbsIdx, currPathPointsIdx,
                                        clippedDevIBounds, &currPathPrimitiveCounts);
    }

    fTotalPrimitiveCounts[(int)scissorTest] += currPathPrimitiveCounts;

    if (GrScissorTest::kEnabled == scissorTest) {
        fScissorSubBatches.push_back() = {fTotalPrimitiveCounts[(int)GrScissorTest::kEnabled],
                                          clippedDevIBounds.makeOffset(devToAtlasOffset.fX,
                                                                       devToAtlasOffset.fY)};
    }
}