Example #1
0
static void test_restriction(skiatest::Reporter* reporter, SkCanvas* canvas) {
    REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == gBaseRestrictedR);

    const SkIRect restrictionR = { 2, 2, 8, 8 };
    canvas->androidFramework_setDeviceClipRestriction(restrictionR);
    REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == restrictionR);

    const SkIRect clipR = { 4, 4, 6, 6 };
    canvas->clipRect(SkRect::Make(clipR), SkClipOp::kIntersect);
    REPORTER_ASSERT(reporter, canvas->getDeviceClipBounds() == clipR);

#ifdef SK_SUPPORT_DEPRECATED_CLIPOPS
    // now test that expanding clipops can't exceed the restriction
    const SkClipOp expanders[] = {
        SkClipOp::kUnion_deprecated,
        SkClipOp::kXOR_deprecated,
        SkClipOp::kReverseDifference_deprecated,
        SkClipOp::kReplace_deprecated,
    };

    const SkRect expandR = { 0, 0, 5, 9 };
    SkASSERT(!SkRect::Make(restrictionR).contains(expandR));

    for (SkClipOp op : expanders) {
        canvas->save();
        canvas->clipRect(expandR, op);
        REPORTER_ASSERT(reporter, gBaseRestrictedR.contains(canvas->getDeviceClipBounds()));
        canvas->restore();
    }
#endif
}
Example #2
0
void TiledPage::draw(float transparency, const SkIRect& tileBounds)
{
    if (!m_glWebViewState)
        return;

    const float tileWidth = TilesManager::tileWidth() * m_invScale;
    const float tileHeight = TilesManager::tileHeight() * m_invScale;

    SkIRect actualTileBounds = tileBounds;
    actualTileBounds.fTop -= m_glWebViewState->expandedTileBoundsY();
    actualTileBounds.fBottom += m_glWebViewState->expandedTileBoundsY();
    actualTileBounds.fLeft -= m_glWebViewState->expandedTileBoundsX();
    actualTileBounds.fRight += m_glWebViewState->expandedTileBoundsX();

    actualTileBounds.fTop = std::max(0, actualTileBounds.fTop);
    actualTileBounds.fLeft = std::max(0, actualTileBounds.fLeft);

    for (int j = 0; j < m_baseTileSize; j++) {
        BaseTile& tile = m_baseTiles[j];
        bool tileInView = actualTileBounds.contains(tile.x(), tile.y());
        if (tileInView) {
            SkRect rect;
            rect.fLeft = tile.x() * tileWidth;
            rect.fTop = tile.y() * tileHeight;
            rect.fRight = rect.fLeft + tileWidth;
            rect.fBottom = rect.fTop + tileHeight;

            tile.draw(transparency, rect, m_scale);
        }

        TilesManager::instance()->getProfiler()->nextTile(tile, m_invScale, tileInView);
    }
}
Example #3
0
bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitmap& src,
                                  SkIPoint* srcOffset, SkIRect* bounds, SkBitmap* dst) const {
    SkIRect srcBounds;
    src.getBounds(&srcBounds);
    srcBounds.offset(*srcOffset);
    SkIRect dstBounds;
    this->onFilterNodeBounds(srcBounds, ctx.ctm(), &dstBounds, kForward_MapDirection);
    fCropRect.applyTo(dstBounds, ctx.ctm(), bounds);
    if (!bounds->intersect(ctx.clipBounds())) {
        return false;
    }

    if (srcBounds.contains(*bounds)) {
        *dst = src;
        return true;
    } else {
        SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds->width(), bounds->height()));
        if (!device) {
            return false;
        }
        SkCanvas canvas(device);
        canvas.clear(0x00000000);
        canvas.drawBitmap(src, srcOffset->x() - bounds->x(), srcOffset->y() - bounds->y());
        *srcOffset = SkIPoint::Make(bounds->x(), bounds->y());
        *dst = device->accessBitmap(false);
        return true;
    }
}
static void imagescaleproc(SkCanvas* canvas, SkImage* image, const SkBitmap&, const SkIRect& srcIR,
                           const SkRect& dstR) {
    const int newW = SkScalarRoundToInt(dstR.width());
    const int newH = SkScalarRoundToInt(dstR.height());
    SkAutoTUnref<SkImage> newImage(image->newImage(newW, newH, &srcIR));

#ifdef SK_DEBUG
    const SkIRect baseR = SkIRect::MakeWH(image->width(), image->height());
    const bool containsSubset = baseR.contains(srcIR);
#endif

    if (newImage) {
        SkASSERT(containsSubset);
        canvas->drawImage(newImage, dstR.x(), dstR.y());
    } else {
        // newImage() does not support subsets that are not contained by the original
        // but drawImageRect does, so we just draw an X in its place to indicate that we are
        // deliberately not drawing here.
        SkASSERT(!containsSubset);
        SkPaint paint;
        paint.setStyle(SkPaint::kStroke_Style);
        paint.setStrokeWidth(4);
        canvas->drawLine(4, 4, newW - 4.0f, newH - 4.0f, paint);
        canvas->drawLine(4, newH - 4.0f, newW - 4.0f, 4, paint);
    }
}
Example #5
0
SkImageCacherator* SkImageCacherator::NewFromGenerator(SkImageGenerator* gen,
                                                       const SkIRect* subset) {
    if (!gen) {
        return nullptr;
    }

    // We are required to take ownership of gen, regardless of if we return a cacherator or not
    SkAutoTDelete<SkImageGenerator> genHolder(gen);

    const SkImageInfo& info = gen->getInfo();
    if (info.isEmpty()) {
        return nullptr;
    }

    uint32_t uniqueID = gen->uniqueID();
    const SkIRect bounds = SkIRect::MakeWH(info.width(), info.height());
    if (subset) {
        if (!bounds.contains(*subset)) {
            return nullptr;
        }
        if (*subset != bounds) {
            // we need a different uniqueID since we really are a subset of the raw generator
            uniqueID = SkNextID::ImageID();
        }
    } else {
        subset = &bounds;
    }

    // Now that we know we can hand-off the generator (to be owned by the cacherator) we can
    // release our holder. (we DONT want to delete it here anymore)
    genHolder.detach();

    return new SkImageCacherator(gen, gen->getInfo().makeWH(subset->width(), subset->height()),
                                 SkIPoint::Make(subset->x(), subset->y()), uniqueID);
}
bool TiledPage::updateTileDirtiness(const SkIRect& tileBounds)
{
    if (!m_glWebViewState || tileBounds.isEmpty()) {
        m_invalRegion.setEmpty();
        m_invalTilesRegion.setEmpty();
        return false;
    }

    bool visibleTileIsDirty = false;
    for (int x = 0; x < m_baseTileSize; x++) {

        BaseTile& tile = m_baseTiles[x];

        // if the tile is in the dirty region then we must invalidate it
        if (m_invalRegion.contains(tile.x(), tile.y())) {
            tile.markAsDirty(m_latestPictureInval, m_invalTilesRegion);
            if (tileBounds.contains(tile.x(), tile.y()))
                visibleTileIsDirty = true;
        }
    }

    // clear the invalidated region as all tiles within that region have now
    // been marked as dirty.
    m_invalRegion.setEmpty();
    m_invalTilesRegion.setEmpty();
    return visibleTileIsDirty;
}
Example #7
0
int SkRTree::validateSubtree(Node* root, SkIRect bounds, bool isRoot) const {
    // make sure the pointer is pointing to a valid place
    SkASSERT(fNodes.contains(static_cast<void*>(root)));

    if (isRoot) {
        // If the root of this subtree is the overall root, we have looser standards:
        if (root->isLeaf()) {
            SkASSERT(root->fNumChildren >= 1 && root->fNumChildren <= fMaxChildren);
        } else {
            SkASSERT(root->fNumChildren >= 2 && root->fNumChildren <= fMaxChildren);
        }
    } else {
        SkASSERT(root->fNumChildren >= fMinChildren && root->fNumChildren <= fMaxChildren);
    }

    for (int i = 0; i < root->fNumChildren; ++i) {
        SkASSERT(bounds.contains(root->child(i)->fBounds));
    }

    if (root->isLeaf()) {
        SkASSERT(0 == root->fLevel);
        return root->fNumChildren;
    } else {
        int childCount = 0;
        for (int i = 0; i < root->fNumChildren; ++i) {
            SkASSERT(root->child(i)->fChild.subtree->fLevel == root->fLevel - 1);
            childCount += this->validateSubtree(root->child(i)->fChild.subtree,
                                                root->child(i)->fBounds);
        }
        return childCount;
    }
}
Example #8
0
bool GrGpu::writePixels(GrSurface* surface, GrSurfaceOrigin origin, int left, int top, int width,
                        int height, GrColorType srcColorType, const GrMipLevel texels[],
                        int mipLevelCount) {
    SkASSERT(surface);
    if (1 == mipLevelCount) {
        // We require that if we are not mipped, then the write region is contained in the surface
        SkIRect subRect = SkIRect::MakeXYWH(left, top, width, height);
        SkIRect bounds = SkIRect::MakeWH(surface->width(), surface->height());
        if (!bounds.contains(subRect)) {
            return false;
        }
    } else if (0 != left || 0 != top || width != surface->width() || height != surface->height()) {
        // We require that if the texels are mipped, than the write region is the entire surface
        return false;
    }

    for (int currentMipLevel = 0; currentMipLevel < mipLevelCount; currentMipLevel++) {
        if (!texels[currentMipLevel].fPixels ) {
            return false;
        }
    }

    this->handleDirtyContext();
    if (this->onWritePixels(surface, origin, left, top, width, height, srcColorType, texels,
                            mipLevelCount)) {
        SkIRect rect = SkIRect::MakeXYWH(left, top, width, height);
        this->didWriteToSurface(surface, origin, &rect, mipLevelCount);
        fStats.incTextureUploads();
        return true;
    }
    return false;
}
Example #9
0
// Much of readPixels is exercised by copyTo testing, since readPixels is the backend for that
// method. Here we explicitly test subset copies.
//
DEF_TEST(BitmapReadPixels, reporter) {
    const int W = 4;
    const int H = 4;
    const size_t rowBytes = W * sizeof(SkPMColor);
    const SkImageInfo srcInfo = SkImageInfo::MakeN32Premul(W, H);
    SkPMColor srcPixels[16];
    fill_4x4_pixels(srcPixels);
    SkBitmap srcBM;
    srcBM.installPixels(srcInfo, srcPixels, rowBytes);

    SkImageInfo dstInfo = SkImageInfo::MakeN32Premul(W, H);
    SkPMColor dstPixels[16];

    const struct {
        bool     fExpectedSuccess;
        SkIPoint fRequestedSrcLoc;
        SkISize  fRequestedDstSize;
        // If fExpectedSuccess, check these, otherwise ignore
        SkIPoint fExpectedDstLoc;
        SkIRect  fExpectedSrcR;
    } gRec[] = {
        { true,  { 0, 0 }, { 4, 4 }, { 0, 0 }, { 0, 0, 4, 4 } },
        { true,  { 1, 1 }, { 2, 2 }, { 0, 0 }, { 1, 1, 3, 3 } },
        { true,  { 2, 2 }, { 4, 4 }, { 0, 0 }, { 2, 2, 4, 4 } },
        { true,  {-1,-1 }, { 2, 2 }, { 1, 1 }, { 0, 0, 1, 1 } },
        { false, {-1,-1 }, { 1, 1 }, { 0, 0 }, { 0, 0, 0, 0 } },
    };

    for (size_t i = 0; i < SK_ARRAY_COUNT(gRec); ++i) {
        clear_4x4_pixels(dstPixels);

        dstInfo = dstInfo.makeWH(gRec[i].fRequestedDstSize.width(),
                                 gRec[i].fRequestedDstSize.height());
        bool success = srcBM.readPixels(dstInfo, dstPixels, rowBytes,
                                        gRec[i].fRequestedSrcLoc.x(), gRec[i].fRequestedSrcLoc.y());

        REPORTER_ASSERT(reporter, gRec[i].fExpectedSuccess == success);
        if (success) {
            const SkIRect srcR = gRec[i].fExpectedSrcR;
            const int dstX = gRec[i].fExpectedDstLoc.x();
            const int dstY = gRec[i].fExpectedDstLoc.y();
            // Walk the dst pixels, and check if we got what we expected
            for (int y = 0; y < H; ++y) {
                for (int x = 0; x < W; ++x) {
                    SkPMColor dstC = dstPixels[y*4+x];
                    // get into src coordinates
                    int sx = x - dstX + srcR.x();
                    int sy = y - dstY + srcR.y();
                    if (srcR.contains(sx, sy)) {
                        REPORTER_ASSERT(reporter, check_4x4_pixel(dstC, sx, sy));
                    } else {
                        REPORTER_ASSERT(reporter, 0 == dstC);
                    }
                }
            }
        }
    }
}
Example #10
0
void draw(SkCanvas* canvas) {
    SkIRect rect = { 30, 50, 40, 60 };
    SkIRect tests[] = { { 30, 50, 31, 51}, { 39, 49, 40, 50}, { 29, 59, 30, 60} };
    for (auto contained : tests) {
        SkDebugf("rect: (%d, %d, %d, %d) %s (%d, %d, %d, %d)\n",
                 rect.left(), rect.top(), rect.right(), rect.bottom(),
                 rect.contains(contained) ? "contains" : "does not contain",
                 contained.left(), contained.top(), contained.right(), contained.bottom());
    }
}
Example #11
0
SkCodec::Result SkCodec::startScanlineDecode(const SkImageInfo& dstInfo,
        const SkCodec::Options* options, SkPMColor ctable[], int* ctableCount) {
    // Reset fCurrScanline in case of failure.
    fCurrScanline = -1;
    // Ensure that valid color ptrs are passed in for kIndex8 color type
    if (kIndex_8_SkColorType == dstInfo.colorType()) {
        if (nullptr == ctable || nullptr == ctableCount) {
            return SkCodec::kInvalidParameters;
        }
    } else {
        if (ctableCount) {
            *ctableCount = 0;
        }
        ctableCount = nullptr;
        ctable = nullptr;
    }

    if (!this->rewindIfNeeded()) {
        return kCouldNotRewind;
    }

    // Set options.
    Options optsStorage;
    if (nullptr == options) {
        options = &optsStorage;
    } else if (options->fSubset) {
        SkIRect size = SkIRect::MakeSize(dstInfo.dimensions());
        if (!size.contains(*options->fSubset)) {
            return kInvalidInput;
        }

        // We only support subsetting in the x-dimension for scanline decoder.
        // Subsetting in the y-dimension can be accomplished using skipScanlines().
        if (options->fSubset->top() != 0 || options->fSubset->height() != dstInfo.height()) {
            return kInvalidInput;
        }
    }

    // FIXME: Support subsets somehow?
    if (!this->dimensionsSupported(dstInfo.dimensions())) {
        return kInvalidScale;
    }

    const Result result = this->onStartScanlineDecode(dstInfo, *options, ctable, ctableCount);
    if (result != SkCodec::kSuccess) {
        return result;
    }

    fCurrScanline = 0;
    fDstInfo = dstInfo;
    fOptions = *options;
    return kSuccess;
}
bool TiledPage::hasMissingContent(const SkIRect& tileBounds)
{
    int neededTiles = tileBounds.width() * tileBounds.height();
    for (int j = 0; j < m_baseTileSize; j++) {
        BaseTile& tile = m_baseTiles[j];
        if (tileBounds.contains(tile.x(), tile.y())) {
            if (tile.frontTexture())
                neededTiles--;
        }
    }
    return neededTiles > 0;
}
Example #13
0
static bool clip_to_limit(const SkRegion& orig, SkRegion* reduced) {
    // need to limit coordinates such that the width/height of our rect can be represented
    // in SkFixed (16.16). See skbug.com/7998
    const int32_t limit = 32767 >> 1;

    SkIRect limitR;
    limitR.set(-limit, -limit, limit, limit);
    if (limitR.contains(orig.getBounds())) {
        return false;
    }
    reduced->op(orig, limitR, SkRegion::kIntersect_Op);
    return true;
}
bool TiledPage::isReady(const SkIRect& tileBounds)
{
    int neededTiles = tileBounds.width() * tileBounds.height();
    XLOG("tiled page %p needs %d ready tiles", this, neededTiles);
    for (int j = 0; j < m_baseTileSize; j++) {
        BaseTile& tile = m_baseTiles[j];
        if (tileBounds.contains(tile.x(), tile.y())) {
            if (tile.isTileReady())
                neededTiles--;
        }
    }
    XLOG("tiled page %p still needs %d ready tiles", this, neededTiles);
    return neededTiles == 0;
}
Example #15
0
SkImage* SkImage::newSubset(const SkIRect& subset) const {
    if (subset.isEmpty()) {
        return nullptr;
    }

    const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());
    if (!bounds.contains(subset)) {
        return nullptr;
    }

    // optimization : return self if the subset == our bounds
    if (bounds == subset) {
        return SkRef(const_cast<SkImage*>(this));
    }
    return as_IB(this)->onNewSubset(subset);
}
Example #16
0
bool SkLatticeIter::Valid(int width, int height, const SkCanvas::Lattice& lattice) {
    SkIRect totalBounds = SkIRect::MakeWH(width, height);
    SkASSERT(lattice.fBounds);
    const SkIRect latticeBounds = *lattice.fBounds;
    if (!totalBounds.contains(latticeBounds)) {
        return false;
    }

    bool zeroXDivs = lattice.fXCount <= 0 || (1 == lattice.fXCount &&
                                              latticeBounds.fLeft == lattice.fXDivs[0]);
    bool zeroYDivs = lattice.fYCount <= 0 || (1 == lattice.fYCount &&
                                              latticeBounds.fTop == lattice.fYDivs[0]);
    if (zeroXDivs && zeroYDivs) {
        return false;
    }

    return valid_divs(lattice.fXDivs, lattice.fXCount, latticeBounds.fLeft, latticeBounds.fRight)
        && valid_divs(lattice.fYDivs, lattice.fYCount, latticeBounds.fTop, latticeBounds.fBottom);
}
Example #17
0
    void updateMC(const SkMatrix& totalMatrix, const SkRegion& totalClip,
                  const SkClipStack& clipStack, SkRegion* updateClip) {
        int x = fDevice->getOrigin().x();
        int y = fDevice->getOrigin().y();
        int width = fDevice->width();
        int height = fDevice->height();

        if ((x | y) == 0) {
            fMatrix = &totalMatrix;
            fClip = totalClip;
        } else {
            fMatrixStorage = totalMatrix;
            fMatrixStorage.postTranslate(SkIntToScalar(-x),
                                         SkIntToScalar(-y));
            fMatrix = &fMatrixStorage;

            totalClip.translate(-x, -y, &fClip);
        }

        fClip.op(0, 0, width, height, SkRegion::kIntersect_Op);

        // intersect clip, but don't translate it (yet)

        if (updateClip) {
            updateClip->op(x, y, x + width, y + height,
                           SkRegion::kDifference_Op);
        }

        fDevice->setMatrixClip(*fMatrix, fClip, clipStack);

#ifdef SK_DEBUG
        if (!fClip.isEmpty()) {
            SkIRect deviceR;
            deviceR.set(0, 0, width, height);
            SkASSERT(deviceR.contains(fClip.getBounds()));
        }
#endif
        // default is to assume no external matrix
        fMVMatrix = NULL;
        fExtMatrix = NULL;
    }
Example #18
0
void create_frustum_normal_map(SkBitmap* bm, const SkIRect& dst) {
    const SkPoint center = SkPoint::Make(dst.fLeft + (dst.width() / 2.0f),
                                         dst.fTop + (dst.height() / 2.0f));

    SkIRect inner = dst;
    inner.inset(dst.width()/4, dst.height()/4);

    SkPoint3 norm;
    const SkPoint3 left =  SkPoint3::Make(-SK_ScalarRoot2Over2, 0.0f, SK_ScalarRoot2Over2);
    const SkPoint3 up =    SkPoint3::Make(0.0f, -SK_ScalarRoot2Over2, SK_ScalarRoot2Over2);
    const SkPoint3 right = SkPoint3::Make(SK_ScalarRoot2Over2,  0.0f, SK_ScalarRoot2Over2);
    const SkPoint3 down =  SkPoint3::Make(0.0f,  SK_ScalarRoot2Over2, SK_ScalarRoot2Over2);

    for (int y = dst.fTop; y < dst.fBottom; ++y) {
        for (int x = dst.fLeft; x < dst.fRight; ++x) {
            if (inner.contains(x, y)) {
                norm.set(0.0f, 0.0f, 1.0f);
            } else {
                SkScalar locX = x + 0.5f - center.fX;
                SkScalar locY = y + 0.5f - center.fY;

                if (locX >= 0.0f) {
                    if (locY > 0.0f) {
                        norm = locX >= locY ? right : down;   // LR corner
                    } else {
                        norm = locX > -locY ? right : up;     // UR corner
                    }
                } else {
                    if (locY > 0.0f) {
                        norm = -locX > locY ? left : down;    // LL corner
                    } else {
                        norm = locX > locY ? up : left;       // UL corner
                    }
                }
            }

            norm_to_rgb(bm, x, y, norm);
        }
    }
}
Example #19
0
bool SkInstallDiscardablePixelRef(SkImageGenerator* generator, const SkIRect* subset, SkBitmap* dst,
                                  SkDiscardableMemory::Factory* factory) {
    SkAutoTDelete<SkImageGenerator> autoGenerator(generator);
    if (NULL == autoGenerator.get()) {
        return false;
    }

    SkImageInfo prInfo = autoGenerator->getInfo();
    if (prInfo.isEmpty()) {
        return false;
    }

    SkIPoint origin = SkIPoint::Make(0, 0);
    SkImageInfo bmInfo = prInfo;
    if (subset) {
        const SkIRect prBounds = SkIRect::MakeWH(prInfo.width(), prInfo.height());
        if (subset->isEmpty() || !prBounds.contains(*subset)) {
            return false;
        }
        bmInfo = prInfo.makeWH(subset->width(), subset->height());
        origin.set(subset->x(), subset->y());
    }

    // must compute our desired rowBytes w.r.t. the pixelRef's dimensions, not ours, which may be
    // smaller.
    if (!dst->setInfo(bmInfo, prInfo.minRowBytes())) {
        return false;
    }

    // Since dst->setInfo() may have changed/fixed-up info, we check from the bitmap
    SkASSERT(dst->info().colorType() != kUnknown_SkColorType);

    if (dst->empty()) {  // Use a normal pixelref.
        return dst->tryAllocPixels();
    }
    SkAutoTUnref<SkDiscardablePixelRef> ref(
            new SkDiscardablePixelRef(prInfo, autoGenerator.detach(), dst->rowBytes(), factory));
    dst->setPixelRef(ref, origin.x(), origin.y());
    return true;
}
Example #20
0
static bool generate_pixels(SkImageGenerator* gen, const SkPixmap& pmap, int originX, int originY,
                            SkTransferFunctionBehavior behavior) {
    const int genW = gen->getInfo().width();
    const int genH = gen->getInfo().height();
    const SkIRect srcR = SkIRect::MakeWH(genW, genH);
    const SkIRect dstR = SkIRect::MakeXYWH(originX, originY, pmap.width(), pmap.height());
    if (!srcR.contains(dstR)) {
        return false;
    }

    // If they are requesting a subset, we have to have a temp allocation for full image, and
    // then copy the subset into their allocation
    SkBitmap full;
    SkPixmap fullPM;
    const SkPixmap* dstPM = &pmap;
    if (srcR != dstR) {
        if (!full.tryAllocPixels(pmap.info().makeWH(genW, genH))) {
            return false;
        }
        if (!full.peekPixels(&fullPM)) {
            return false;
        }
        dstPM = &fullPM;
    }

    SkImageGenerator::Options opts;
    opts.fBehavior = behavior;
    if (!gen->getPixels(dstPM->info(), dstPM->writable_addr(), dstPM->rowBytes(), &opts)) {
        return false;
    }

    if (srcR != dstR) {
        if (!full.readPixels(pmap, originX, originY)) {
            return false;
        }
    }
    return true;
}
Example #21
0
bool GrGpu::transferPixels(GrTexture* texture, int left, int top, int width, int height,
                           GrColorType bufferColorType, GrBuffer* transferBuffer, size_t offset,
                           size_t rowBytes) {
    SkASSERT(transferBuffer);

    // We require that the write region is contained in the texture
    SkIRect subRect = SkIRect::MakeXYWH(left, top, width, height);
    SkIRect bounds = SkIRect::MakeWH(texture->width(), texture->height());
    if (!bounds.contains(subRect)) {
        return false;
    }

    this->handleDirtyContext();
    if (this->onTransferPixels(texture, left, top, width, height, bufferColorType, transferBuffer,
                               offset, rowBytes)) {
        SkIRect rect = SkIRect::MakeXYWH(left, top, width, height);
        this->didWriteToSurface(texture, kTopLeft_GrSurfaceOrigin, &rect);
        fStats.incTransfersToTexture();

        return true;
    }
    return false;
}
Example #22
0
SkImage* SkImage::newImage(int newWidth, int newHeight, const SkIRect* subset,
                           SkFilterQuality quality) const {
    if (newWidth <= 0 || newHeight <= 0) {
        return nullptr;
    }

    const SkIRect bounds = SkIRect::MakeWH(this->width(), this->height());

    if (subset) {
        if (!bounds.contains(*subset)) {
            return nullptr;
        }
        if (bounds == *subset) {
            subset = nullptr;  // and fall through to check below
        }
    }

    if (nullptr == subset && this->width() == newWidth && this->height() == newHeight) {
        return SkRef(const_cast<SkImage*>(this));
    }

    return as_IB(this)->onNewImage(newWidth, newHeight, subset, quality);
}
Example #23
0
SkImage_Lazy::Validator::Validator(sk_sp<SharedGenerator> gen, const SkIRect* subset,
                                   sk_sp<SkColorSpace> colorSpace)
        : fSharedGenerator(std::move(gen)) {
    if (!fSharedGenerator) {
        return;
    }

    // The following generator accessors are safe without acquiring the mutex (const getters).
    // TODO: refactor to use a ScopedGenerator instead, for clarity.
    const SkImageInfo& info = fSharedGenerator->fGenerator->getInfo();
    if (info.isEmpty()) {
        fSharedGenerator.reset();
        return;
    }

    fUniqueID = fSharedGenerator->fGenerator->uniqueID();
    const SkIRect bounds = SkIRect::MakeWH(info.width(), info.height());
    if (subset) {
        if (!bounds.contains(*subset)) {
            fSharedGenerator.reset();
            return;
        }
        if (*subset != bounds) {
            // we need a different uniqueID since we really are a subset of the raw generator
            fUniqueID = SkNextID::ImageID();
        }
    } else {
        subset = &bounds;
    }

    fInfo   = info.makeWH(subset->width(), subset->height());
    fOrigin = SkIPoint::Make(subset->x(), subset->y());
    if (colorSpace) {
        fInfo = fInfo.makeColorSpace(colorSpace);
        fUniqueID = SkNextID::ImageID();
    }
}
Example #24
0
sk_sp<SkSpecialImage> SkImageFilter::applyCropRect(const Context& ctx,
                                                   SkSpecialImage* src,
                                                   SkIPoint* srcOffset,
                                                   SkIRect* bounds) const {
    const SkIRect srcBounds = SkIRect::MakeXYWH(srcOffset->x(), srcOffset->y(),
                                                src->width(), src->height());

    SkIRect dstBounds = this->onFilterNodeBounds(srcBounds, ctx.ctm(), kForward_MapDirection);
    fCropRect.applyTo(dstBounds, ctx.ctm(), this->affectsTransparentBlack(), bounds);
    if (!bounds->intersect(ctx.clipBounds())) {
        return nullptr;
    }

    if (srcBounds.contains(*bounds)) {
        return sk_sp<SkSpecialImage>(SkRef(src));
    } else {
        sk_sp<SkSpecialImage> img(pad_image(src,
                                            bounds->width(), bounds->height(),
                                            srcOffset->x() - bounds->x(),
                                            srcOffset->y() - bounds->y()));
        *srcOffset = SkIPoint::Make(bounds->x(), bounds->y());
        return img;
    }
}
Example #25
0
static void clip_quads(const SkIRect& clipRect, char* currVertex, const char* blobVertices,
                       size_t vertexStride, int glyphCount) {
    for (int i = 0; i < glyphCount; ++i) {
        const SkPoint* blobPositionLT = reinterpret_cast<const SkPoint*>(blobVertices);
        const SkPoint* blobPositionRB =
                reinterpret_cast<const SkPoint*>(blobVertices + 3 * vertexStride);

        // positions for bitmap glyphs are pixel boundary aligned
        SkIRect positionRect = SkIRect::MakeLTRB(SkScalarRoundToInt(blobPositionLT->fX),
                                                 SkScalarRoundToInt(blobPositionLT->fY),
                                                 SkScalarRoundToInt(blobPositionRB->fX),
                                                 SkScalarRoundToInt(blobPositionRB->fY));
        if (clipRect.contains(positionRect)) {
            memcpy(currVertex, blobVertices, 4 * vertexStride);
            currVertex += 4 * vertexStride;
        } else {
            // Pull out some more data that we'll need.
            // In the LCD case the color will be garbage, but we'll overwrite it with the texcoords
            // and it avoids a lot of conditionals.
            auto color = *reinterpret_cast<const SkColor*>(blobVertices + sizeof(SkPoint));
            size_t coordOffset = vertexStride - 2*sizeof(uint16_t);
            auto* blobCoordsLT = reinterpret_cast<const uint16_t*>(blobVertices + coordOffset);
            auto* blobCoordsRB = reinterpret_cast<const uint16_t*>(blobVertices + 3 * vertexStride +
                                                                   coordOffset);
            // Pull out the texel coordinates and texture index bits
            uint16_t coordsRectL = blobCoordsLT[0] >> 1;
            uint16_t coordsRectT = blobCoordsLT[1] >> 1;
            uint16_t coordsRectR = blobCoordsRB[0] >> 1;
            uint16_t coordsRectB = blobCoordsRB[1] >> 1;
            uint16_t pageIndexX = blobCoordsLT[0] & 0x1;
            uint16_t pageIndexY = blobCoordsLT[1] & 0x1;

            int positionRectWidth = positionRect.width();
            int positionRectHeight = positionRect.height();
            SkASSERT(positionRectWidth == (coordsRectR - coordsRectL));
            SkASSERT(positionRectHeight == (coordsRectB - coordsRectT));

            // Clip position and texCoords to the clipRect
            unsigned int delta;
            delta = SkTMin(SkTMax(clipRect.fLeft - positionRect.fLeft, 0), positionRectWidth);
            coordsRectL += delta;
            positionRect.fLeft += delta;

            delta = SkTMin(SkTMax(clipRect.fTop - positionRect.fTop, 0), positionRectHeight);
            coordsRectT += delta;
            positionRect.fTop += delta;

            delta = SkTMin(SkTMax(positionRect.fRight - clipRect.fRight, 0), positionRectWidth);
            coordsRectR -= delta;
            positionRect.fRight -= delta;

            delta = SkTMin(SkTMax(positionRect.fBottom - clipRect.fBottom, 0), positionRectHeight);
            coordsRectB -= delta;
            positionRect.fBottom -= delta;

            // Repack texel coordinates and index
            coordsRectL = coordsRectL << 1 | pageIndexX;
            coordsRectT = coordsRectT << 1 | pageIndexY;
            coordsRectR = coordsRectR << 1 | pageIndexX;
            coordsRectB = coordsRectB << 1 | pageIndexY;

            // Set new positions and coords
            SkPoint* currPosition = reinterpret_cast<SkPoint*>(currVertex);
            currPosition->fX = positionRect.fLeft;
            currPosition->fY = positionRect.fTop;
            *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
            uint16_t* currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
            currCoords[0] = coordsRectL;
            currCoords[1] = coordsRectT;
            currVertex += vertexStride;

            currPosition = reinterpret_cast<SkPoint*>(currVertex);
            currPosition->fX = positionRect.fLeft;
            currPosition->fY = positionRect.fBottom;
            *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
            currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
            currCoords[0] = coordsRectL;
            currCoords[1] = coordsRectB;
            currVertex += vertexStride;

            currPosition = reinterpret_cast<SkPoint*>(currVertex);
            currPosition->fX = positionRect.fRight;
            currPosition->fY = positionRect.fTop;
            *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
            currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
            currCoords[0] = coordsRectR;
            currCoords[1] = coordsRectT;
            currVertex += vertexStride;

            currPosition = reinterpret_cast<SkPoint*>(currVertex);
            currPosition->fX = positionRect.fRight;
            currPosition->fY = positionRect.fBottom;
            *(reinterpret_cast<SkColor*>(currVertex + sizeof(SkPoint))) = color;
            currCoords = reinterpret_cast<uint16_t*>(currVertex + coordOffset);
            currCoords[0] = coordsRectR;
            currCoords[1] = coordsRectB;
            currVertex += vertexStride;
        }

        blobVertices += 4 * vertexStride;
    }
}
Example #26
0
static void testTightBoundsQuads(PathOpsThreadState* data) {
    SkRandom ran;
    const int bitWidth = 32;
    const int bitHeight = 32;
    const float pathMin = 1;
    const float pathMax = (float) (bitHeight - 2);
    SkBitmap& bits = *data->fBitmap;
    if (bits.width() == 0) {
        bits.allocN32Pixels(bitWidth, bitHeight);
    }
    SkCanvas canvas(bits);
    SkPaint paint;
    for (int index = 0; index < 100; ++index) {
        SkPath path;
        int contourCount = ran.nextRangeU(1, 10);
        for (int cIndex = 0; cIndex < contourCount; ++cIndex) {
            int lineCount = ran.nextRangeU(1, 10);
            path.moveTo(ran.nextRangeF(1, pathMax), ran.nextRangeF(pathMin, pathMax));
            for (int lIndex = 0; lIndex < lineCount; ++lIndex) {
                if (ran.nextBool()) {
                    path.lineTo(ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax));
                } else {
                    path.quadTo(ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax),
                            ran.nextRangeF(pathMin, pathMax), ran.nextRangeF(pathMin, pathMax));
                }
            }
            if (ran.nextBool()) {
                path.close();
            }
        }
        SkRect classicBounds = path.getBounds();
        SkRect tightBounds;
        REPORTER_ASSERT(data->fReporter, TightBounds(path, &tightBounds));
        REPORTER_ASSERT(data->fReporter, classicBounds.contains(tightBounds));
        canvas.drawColor(SK_ColorWHITE);
        canvas.drawPath(path, paint);
        SkIRect bitsWritten = {31, 31, 0, 0};
        for (int y = 0; y < bitHeight; ++y) {
            uint32_t* addr1 = data->fBitmap->getAddr32(0, y);
            bool lineWritten = false;
            for (int x = 0; x < bitWidth; ++x) {
                if (addr1[x] == (uint32_t) -1) {
                    continue;
                }
                lineWritten = true;
                bitsWritten.fLeft = SkTMin(bitsWritten.fLeft, x);
                bitsWritten.fRight = SkTMax(bitsWritten.fRight, x);
            }
            if (!lineWritten) {
                continue;
            }
            bitsWritten.fTop = SkTMin(bitsWritten.fTop, y);
            bitsWritten.fBottom = SkTMax(bitsWritten.fBottom, y);
        }
        if (!bitsWritten.isEmpty()) {
            SkIRect tightOut;
            tightBounds.roundOut(&tightOut);
            REPORTER_ASSERT(data->fReporter, tightOut.contains(bitsWritten));
        }
    }
}
Example #27
0
DEF_GPUTEST_FOR_RENDERING_CONTEXTS(CopySurface, reporter, ctxInfo) {
    GrContext* context = ctxInfo.grContext();
    static const int kW = 10;
    static const int kH = 10;
    static const size_t kRowBytes = sizeof(uint32_t) * kW;

    SkAutoTMalloc<uint32_t> srcPixels(kW * kH);
    for (int i = 0; i < kW * kH; ++i) {
        srcPixels.get()[i] = i;
    }

    SkAutoTMalloc<uint32_t> dstPixels(kW * kH);
    for (int i = 0; i < kW * kH; ++i) {
        dstPixels.get()[i] = ~i;
    }

    static const SkIRect kSrcRects[] {
        { 0,  0, kW  , kH  },
        {-1, -1, kW+1, kH+1},
        { 1,  1, kW-1, kH-1},
        { 5,  5, 6   , 6   },
    };

    static const SkIPoint kDstPoints[] {
        { 0   ,  0   },
        { 1   ,  1   },
        { kW/2,  kH/4},
        { kW-1,  kH-1},
        { kW  ,  kH  },
        { kW+1,  kH+2},
        {-1   , -1   },
    };

    static const SkImageInfo kImageInfos[] {
        SkImageInfo::Make(kW, kH, kRGBA_8888_SkColorType, kPremul_SkAlphaType),
        SkImageInfo::Make(kW, kH, kBGRA_8888_SkColorType, kPremul_SkAlphaType)
    };

    SkAutoTMalloc<uint32_t> read(kW * kH);

    for (auto sOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) {
        for (auto dOrigin : {kBottomLeft_GrSurfaceOrigin, kTopLeft_GrSurfaceOrigin}) {
            for (auto sRenderable : {GrRenderable::kYes, GrRenderable::kNo}) {
                for (auto dRenderable : {GrRenderable::kYes, GrRenderable::kNo}) {
                    for (auto srcRect : kSrcRects) {
                        for (auto dstPoint : kDstPoints) {
                            for (auto ii: kImageInfos) {
                                auto src = sk_gpu_test::MakeTextureProxyFromData(
                                        context, sRenderable, kW, kH, ii.colorType(), sOrigin,
                                        srcPixels.get(), kRowBytes);
                                auto dst = sk_gpu_test::MakeTextureProxyFromData(
                                        context, dRenderable, kW, kH, ii.colorType(), dOrigin,
                                        dstPixels.get(), kRowBytes);

                                // Should always work if the color type is RGBA, but may not work
                                // for BGRA
                                if (ii.colorType() == kRGBA_8888_SkColorType) {
                                    if (!src || !dst) {
                                        ERRORF(reporter,
                                               "Could not create surfaces for copy surface test.");
                                        continue;
                                    }
                                } else {
                                    GrPixelConfig config =
                                            SkColorType2GrPixelConfig(kBGRA_8888_SkColorType);
                                    if (!context->priv().caps()->isConfigTexturable(config)) {
                                        continue;
                                    }
                                    if (!src || !dst) {
                                        ERRORF(reporter,
                                               "Could not create surfaces for copy surface test.");
                                        continue;
                                    }
                                }

                                sk_sp<GrSurfaceContext> dstContext =
                                        context->priv().makeWrappedSurfaceContext(std::move(dst));

                                bool result = dstContext->copy(src.get(), srcRect, dstPoint);

                                bool expectedResult = true;
                                SkIPoint dstOffset = { dstPoint.fX - srcRect.fLeft,
                                                       dstPoint.fY - srcRect.fTop };
                                SkIRect copiedDstRect = SkIRect::MakeXYWH(dstPoint.fX,
                                                                          dstPoint.fY,
                                                                          srcRect.width(),
                                                                          srcRect.height());

                                SkIRect copiedSrcRect;
                                if (!copiedSrcRect.intersect(srcRect, SkIRect::MakeWH(kW, kH))) {
                                    expectedResult = false;
                                } else {
                                    // If the src rect was clipped, apply same clipping to each side
                                    // of copied dst rect.
                                    copiedDstRect.fLeft += copiedSrcRect.fLeft - srcRect.fLeft;
                                    copiedDstRect.fTop += copiedSrcRect.fTop - srcRect.fTop;
                                    copiedDstRect.fRight -= copiedSrcRect.fRight - srcRect.fRight;
                                    copiedDstRect.fBottom -= copiedSrcRect.fBottom -
                                                             srcRect.fBottom;
                                }
                                if (copiedDstRect.isEmpty() ||
                                    !copiedDstRect.intersect(SkIRect::MakeWH(kW, kH))) {
                                    expectedResult = false;
                                }
                                // To make the copied src rect correct we would apply any dst
                                // clipping back to the src rect, but we don't use it again so
                                // don't bother.
                                if (expectedResult != result) {
                                    ERRORF(reporter, "Expected return value %d from copySurface, "
                                           "got %d.", expectedResult, result);
                                    continue;
                                }

                                if (!expectedResult || !result) {
                                    continue;
                                }

                                sk_memset32(read.get(), 0, kW * kH);
                                if (!dstContext->readPixels(ii, read.get(), kRowBytes, 0, 0)) {
                                    ERRORF(reporter, "Error calling readPixels");
                                    continue;
                                }

                                bool abort = false;
                                // Validate that pixels inside copiedDstRect received the correct
                                // value from src and that those outside were not modified.
                                for (int y = 0; y < kH && !abort; ++y) {
                                    for (int x = 0; x < kW; ++x) {
                                        uint32_t r = read.get()[y * kW + x];
                                        if (copiedDstRect.contains(x, y)) {
                                            int sx = x - dstOffset.fX;
                                            int sy = y - dstOffset.fY;
                                            uint32_t s = srcPixels.get()[sy * kW + sx];
                                            if (s != r) {
                                                ERRORF(reporter, "Expected dst %d,%d to contain "
                                                       "0x%08x copied from src location %d,%d. Got "
                                                       "0x%08x", x, y, s, sx, sy, r);
                                                abort = true;
                                                break;
                                            }
                                        } else {
                                            uint32_t d = dstPixels.get()[y * kW + x];
                                            if (d != r) {
                                                ERRORF(reporter, "Expected dst %d,%d to be "
                                                       "unmodified (0x%08x). Got 0x%08x",
                                                       x, y, d, r);
                                                abort = true;
                                                break;
                                            }
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
            }
        }
    }
}
/** Determines whether a texture domain is necessary and if so what domain to use. There are two
 *  rectangles to consider:
 *  - The first is the content area specified by the texture adjuster (i.e., textureContentArea).
 *    We can *never* allow filtering to cause bleed of pixels outside this rectangle.
 *  - The second rectangle is the constraint rectangle (i.e., constraintRect), which is known to
 *    be contained by the content area. The filterConstraint specifies whether we are allowed to
 *    bleed across this rect.
 *
 *  We want to avoid using a domain if possible. We consider the above rectangles, the filter type,
 *  and whether the coords generated by the draw would all fall within the constraint rect. If the
 *  latter is true we only need to consider whether the filter would extend beyond the rects.
 */
GrTextureProducer::DomainMode GrTextureProducer::DetermineDomainMode(
        const SkRect& constraintRect,
        FilterConstraint filterConstraint,
        bool coordsLimitedToConstraintRect,
        GrTextureProxy* proxy,
        const GrSamplerState::Filter* filterModeOrNullForBicubic,
        SkRect* domainRect) {
    const SkIRect proxyBounds = SkIRect::MakeWH(proxy->width(), proxy->height());

    SkASSERT(proxyBounds.contains(constraintRect));

    const bool proxyIsExact = GrProxyProvider::IsFunctionallyExact(proxy);

    // If the constraint rectangle contains the whole proxy then no need for a domain.
    if (constraintRect.contains(proxyBounds) && proxyIsExact) {
        return kNoDomain_DomainMode;
    }

    bool restrictFilterToRect = (filterConstraint == GrTextureProducer::kYes_FilterConstraint);

    // If we can filter outside the constraint rect, and there is no non-content area of the
    // proxy, and we aren't going to generate sample coords outside the constraint rect then we
    // don't need a domain.
    if (!restrictFilterToRect && proxyIsExact && coordsLimitedToConstraintRect) {
        return kNoDomain_DomainMode;
    }

    // Get the domain inset based on sampling mode (or bail if mipped)
    SkScalar filterHalfWidth = 0.f;
    if (filterModeOrNullForBicubic) {
        switch (*filterModeOrNullForBicubic) {
            case GrSamplerState::Filter::kNearest:
                if (coordsLimitedToConstraintRect) {
                    return kNoDomain_DomainMode;
                } else {
                    filterHalfWidth = 0.f;
                }
                break;
            case GrSamplerState::Filter::kBilerp:
                filterHalfWidth = .5f;
                break;
            case GrSamplerState::Filter::kMipMap:
                if (restrictFilterToRect || !proxyIsExact) {
                    // No domain can save us here.
                    return kTightCopy_DomainMode;
                }
                return kNoDomain_DomainMode;
        }
    } else {
        // bicubic does nearest filtering internally.
        filterHalfWidth = 1.5f;
    }

    // Both bilerp and bicubic use bilinear filtering and so need to be clamped to the center
    // of the edge texel. Pinning to the texel center has no impact on nearest mode and MIP-maps

    static const SkScalar kDomainInset = 0.5f;
    // Figure out the limits of pixels we're allowed to sample from.
    // Unless we know the amount of outset and the texture matrix we have to conservatively enforce
    // the domain.
    if (restrictFilterToRect) {
        *domainRect = constraintRect.makeInset(kDomainInset, kDomainInset);
    } else if (!proxyIsExact) {
        // If we got here then: proxy is not exact, the coords are limited to the
        // constraint rect, and we're allowed to filter across the constraint rect boundary. So
        // we check whether the filter would reach across the edge of the proxy.
        // We will only set the sides that are required.

        *domainRect = SkRectPriv::MakeLargest();
        if (coordsLimitedToConstraintRect) {
            // We may be able to use the fact that the texture coords are limited to the constraint
            // rect in order to avoid having to add a domain.
            bool needContentAreaConstraint = false;
            if (proxyBounds.fRight - filterHalfWidth < constraintRect.fRight) {
                domainRect->fRight = proxyBounds.fRight - kDomainInset;
                needContentAreaConstraint = true;
            }
            if (proxyBounds.fBottom - filterHalfWidth < constraintRect.fBottom) {
                domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
                needContentAreaConstraint = true;
            }
            if (!needContentAreaConstraint) {
                return kNoDomain_DomainMode;
            }
        } else {
            // Our sample coords for the texture are allowed to be outside the constraintRect so we
            // don't consider it when computing the domain.
            domainRect->fRight = proxyBounds.fRight - kDomainInset;
            domainRect->fBottom = proxyBounds.fBottom - kDomainInset;
        }
    } else {
        return kNoDomain_DomainMode;
    }

    if (domainRect->fLeft > domainRect->fRight) {
        domainRect->fLeft = domainRect->fRight = SkScalarAve(domainRect->fLeft, domainRect->fRight);
    }
    if (domainRect->fTop > domainRect->fBottom) {
        domainRect->fTop = domainRect->fBottom = SkScalarAve(domainRect->fTop, domainRect->fBottom);
    }
    return kDomain_DomainMode;
}
Example #29
0
bool SkImageFilter::applyCropRect(const Context& ctx, Proxy* proxy, const SkBitmap& src,
                                  SkIPoint* srcOffset, SkIRect* bounds, SkBitmap* dst) const {
    SkIRect srcBounds;
    src.getBounds(&srcBounds);
    srcBounds.offset(*srcOffset);
#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
    if (!fCropRect.applyTo(srcBounds, ctx, bounds)) {
#else
    SkIRect dstBounds;
    this->onFilterNodeBounds(srcBounds, ctx.ctm(), &dstBounds, kForward_MapDirection);
    if (!fCropRect.applyTo(dstBounds, ctx, bounds)) {
#endif
        return false;
    }

    if (srcBounds.contains(*bounds)) {
        *dst = src;
        return true;
    } else {
        SkAutoTUnref<SkBaseDevice> device(proxy->createDevice(bounds->width(), bounds->height()));
        if (!device) {
            return false;
        }
        SkCanvas canvas(device);
        canvas.clear(0x00000000);
        canvas.drawBitmap(src, srcOffset->x() - bounds->x(), srcOffset->y() - bounds->y());
        *srcOffset = SkIPoint::Make(bounds->x(), bounds->y());
        *dst = device->accessBitmap(false);
        return true;
    }
}

bool SkImageFilter::onFilterBounds(const SkIRect& src, const SkMatrix& ctm,
                                   SkIRect* dst) const {
    if (fInputCount < 1) {
        *dst = src;
        return true;
    }

    SkIRect bounds, totalBounds;
    this->onFilterNodeBounds(src, ctm, &bounds, kReverse_MapDirection);
    for (int i = 0; i < fInputCount; ++i) {
        SkImageFilter* filter = this->getInput(i);
        SkIRect rect = bounds;
        if (filter && !filter->filterBounds(bounds, ctm, &rect)) {
            return false;
        }
        if (0 == i) {
            totalBounds = rect;
        } else {
            totalBounds.join(rect);
        }
    }

    // don't modify dst until now, so we don't accidentally change it in the
    // loop, but then return false on the next filter.
    *dst = totalBounds;
    return true;
}

void SkImageFilter::onFilterNodeBounds(const SkIRect& src, const SkMatrix&,
                                       SkIRect* dst, MapDirection) const {
    *dst = src;
}


SkImageFilter::Context SkImageFilter::mapContext(const Context& ctx) const {
#ifdef SK_SUPPORT_SRC_BOUNDS_BLOAT_FOR_IMAGEFILTERS
    return ctx;
#else
    SkIRect clipBounds;
    this->onFilterNodeBounds(ctx.clipBounds(), ctx.ctm(), &clipBounds,
                             MapDirection::kReverse_MapDirection);
    return Context(ctx.ctm(), clipBounds, ctx.cache());
#endif
}

bool SkImageFilter::asFragmentProcessor(GrFragmentProcessor**, GrTexture*,
                                        const SkMatrix&, const SkIRect&) const {
    return false;
}

SkImageFilter* SkImageFilter::CreateMatrixFilter(const SkMatrix& matrix,
                                                 SkFilterQuality filterQuality,
                                                 SkImageFilter* input) {
    return SkMatrixImageFilter::Create(matrix, filterQuality, input);
}

SkImageFilter* SkImageFilter::newWithLocalMatrix(const SkMatrix& matrix) const {
    // SkLocalMatrixImageFilter takes SkImage* in its factory, but logically that parameter
    // is *always* treated as a const ptr. Hence the const-cast here.
    //
    return SkLocalMatrixImageFilter::Create(matrix, const_cast<SkImageFilter*>(this));
}

#if SK_SUPPORT_GPU

bool SkImageFilter::filterInputGPU(int index, SkImageFilter::Proxy* proxy,
                                   const SkBitmap& src, const Context& ctx,
                                   SkBitmap* result, SkIPoint* offset) const {
    SkImageFilter* input = this->getInput(index);
    if (!input) {
        return true;
    }
    // Ensure that GrContext calls under filterImage and filterImageGPU below will see an identity
    // matrix with no clip and that the matrix, clip, and render target set before this function was
    // called are restored before we return to the caller.
    GrContext* context = src.getTexture()->getContext();
    if (input->filterImage(proxy, src, this->mapContext(ctx), result, offset)) {
        if (!result->getTexture()) {
            const SkImageInfo info = result->info();
            if (kUnknown_SkColorType == info.colorType()) {
                return false;
            }
            SkAutoTUnref<GrTexture> resultTex(
                GrRefCachedBitmapTexture(context, *result, GrTextureParams::ClampNoFilter()));
            if (!resultTex) {
                return false;
            }
            result->setPixelRef(new SkGrPixelRef(info, resultTex))->unref();
        }
        return true;
    } else {
        return false;
    }
}
Example #30
0
sk_sp<SkSpecialImage> SkTileImageFilter::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;
    }

    SkRect dstRect;
    ctx.ctm().mapRect(&dstRect, fDstRect);
    if (!dstRect.intersect(SkRect::Make(ctx.clipBounds()))) {
        return nullptr;
    }

    const SkIRect dstIRect = dstRect.roundOut();
    if (!fSrcRect.width() || !fSrcRect.height() || !dstIRect.width() || !dstIRect.height()) {
        return nullptr;
    }

    SkRect srcRect;
    ctx.ctm().mapRect(&srcRect, fSrcRect);
    SkIRect srcIRect;
    srcRect.roundOut(&srcIRect);
    srcIRect.offset(-inputOffset);
    const SkIRect inputBounds = SkIRect::MakeWH(input->width(), input->height());

    if (!SkIRect::Intersects(srcIRect, inputBounds)) {
        return nullptr;
    }

    // We create an SkImage here b.c. it needs to be a tight fit for the tiling
    sk_sp<SkImage> subset;
    if (inputBounds.contains(srcIRect)) {
        subset = input->makeTightSubset(srcIRect);
        if (!subset) {
            return nullptr;
        }
    } else {
        sk_sp<SkSurface> surf(input->makeTightSurface(ctx.outputProperties(), srcIRect.size()));
        if (!surf) {
            return nullptr;
        }

        SkCanvas* canvas = surf->getCanvas();
        SkASSERT(canvas);

        SkPaint paint;
        paint.setBlendMode(SkBlendMode::kSrc);

        input->draw(canvas, 
                    SkIntToScalar(inputOffset.x()), SkIntToScalar(inputOffset.y()),
                    &paint);

        subset = surf->makeImageSnapshot();
    }
    SkASSERT(subset->width() == srcIRect.width());
    SkASSERT(subset->height() == srcIRect.height());

    sk_sp<SkSpecialSurface> surf(source->makeSurface(ctx.outputProperties(), dstIRect.size()));
    if (!surf) {
        return nullptr;
    }

    SkCanvas* canvas = surf->getCanvas();
    SkASSERT(canvas);

    SkPaint paint;
    paint.setBlendMode(SkBlendMode::kSrc);
    paint.setShader(subset->makeShader(SkShader::kRepeat_TileMode, SkShader::kRepeat_TileMode));
    canvas->translate(-dstRect.fLeft, -dstRect.fTop);
    canvas->drawRect(dstRect, paint);
    offset->fX = dstIRect.fLeft;
    offset->fY = dstIRect.fTop;
    return surf->makeImageSnapshot();
}