Esempio n. 1
0
void basic_clear_test(skiatest::Reporter* reporter, GrContext* context, GrPixelConfig config) {
    GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu());
    gpu->discard(NULL);
    SkAutoTMalloc<GrColor> buffer(25);

    GrSurfaceDesc surfDesc;
    surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
    surfDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
    surfDesc.fWidth = 5;
    surfDesc.fHeight = 5;
    surfDesc.fConfig = config;
    surfDesc.fSampleCnt = 0;
    GrTexture* tex = gpu->createTexture(surfDesc, false, nullptr, 0);
    SkASSERT(tex);
    SkASSERT(tex->asRenderTarget());
    SkIRect rect = SkIRect::MakeWH(5, 5);

    gpu->clear(rect, GrColor_TRANSPARENT_BLACK, tex->asRenderTarget());

    gpu->readPixels(tex, 0, 0, 5, 5, config, (void*)buffer.get(), 0);

    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(),
                                                                     GrColor_TRANSPARENT_BLACK,
                                                                     config,
                                                                     5,
                                                                     5));

    gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget());

    gpu->readPixels(tex, 0, 0, 5, 5, config, (void*)buffer.get(), 0);

    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(),
                                                                     GrColor_WHITE,
                                                                     config,
                                                                     5,
                                                                     5));

    GrColor myColor = GrColorPackRGBA(0xFF, 0x7F, 0x40, 0x20);

    gpu->clear(rect, myColor, tex->asRenderTarget());

    gpu->readPixels(tex, 0, 0, 5, 5, config, (void*)buffer.get(), 0);

    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(),
                                                                     myColor,
                                                                     config,
                                                                     5,
                                                                     5));
}
Esempio n. 2
0
static GrRenderTarget* random_render_target(GrTextureProvider* textureProvider, SkRandom* random,
                                            const GrCaps* caps) {
    // setup render target
    GrTextureParams params;
    GrSurfaceDesc texDesc;
    texDesc.fWidth = kRenderTargetWidth;
    texDesc.fHeight = kRenderTargetHeight;
    texDesc.fFlags = kRenderTarget_GrSurfaceFlag;
    texDesc.fConfig = kRGBA_8888_GrPixelConfig;
    texDesc.fOrigin = random->nextBool() == true ? kTopLeft_GrSurfaceOrigin :
                                                   kBottomLeft_GrSurfaceOrigin;
    texDesc.fSampleCnt = random->nextBool() == true ? SkTMin(4, caps->maxSampleCount()) : 0;

    GrUniqueKey key;
    static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain();
    GrUniqueKey::Builder builder(&key, kDomain, 2);
    builder[0] = texDesc.fOrigin;
    builder[1] = texDesc.fSampleCnt;
    builder.finish();

    GrTexture* texture = textureProvider->findAndRefTextureByUniqueKey(key);
    if (!texture) {
        texture = textureProvider->createTexture(texDesc, true);
        if (texture) {
            textureProvider->assignUniqueKeyToTexture(key, texture);
        }
    }
    return texture ? texture->asRenderTarget() : nullptr;
}
Esempio n. 3
0
GrTexture* GrGpu::createTexture(const GrTextureDesc& desc,
                                const void* srcData, size_t rowBytes) {
    this->handleDirtyContext();
    GrTexture* tex = this->onCreateTexture(desc, srcData, rowBytes);
    if (NULL != tex && 
        (kRenderTarget_GrTextureFlagBit & desc.fFlags) &&
        !(kNoStencil_GrTextureFlagBit & desc.fFlags)) {
        GrAssert(NULL != tex->asRenderTarget());
        // TODO: defer this and attach dynamically
        if (!this->attachStencilBufferToRenderTarget(tex->asRenderTarget())) {
            tex->unref();
            return NULL;
        }
    }
    return tex;
}
Esempio n. 4
0
// Create a mask of 'devPath' and place the result in 'mask'.
static GrTexture* create_mask_GPU(GrContext* context,
                                  const SkRect& maskRect,
                                  const SkPath& devPath,
                                  const GrStrokeInfo& strokeInfo,
                                  bool doAA,
                                  int sampleCnt) {
    GrSurfaceDesc desc;
    desc.fFlags = kRenderTarget_GrSurfaceFlag;
    desc.fWidth = SkScalarCeilToInt(maskRect.width());
    desc.fHeight = SkScalarCeilToInt(maskRect.height());
    desc.fSampleCnt = doAA ? sampleCnt : 0;
    // We actually only need A8, but it often isn't supported as a
    // render target so default to RGBA_8888
    desc.fConfig = kRGBA_8888_GrPixelConfig;

    if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, desc.fSampleCnt > 0)) {
        desc.fConfig = kAlpha_8_GrPixelConfig;
    }

    GrTexture* mask = context->textureProvider()->createApproxTexture(desc);
    if (nullptr == mask) {
        return nullptr;
    }

    SkRect clipRect = SkRect::MakeWH(maskRect.width(), maskRect.height());

    GrDrawContext* drawContext = context->drawContext();
    if (!drawContext) {
        return nullptr;
    }

    drawContext->clear(mask->asRenderTarget(), nullptr, 0x0, true);

    GrPaint tempPaint;
    tempPaint.setAntiAlias(doAA);
    tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);

    // setup new clip
    GrClip clip(clipRect);

    // Draw the mask into maskTexture with the path's top-left at the origin using tempPaint.
    SkMatrix translate;
    translate.setTranslate(-maskRect.fLeft, -maskRect.fTop);
    drawContext->drawPath(mask->asRenderTarget(), clip, tempPaint, translate, devPath, strokeInfo);
    return mask;
}
Esempio n. 5
0
bool GrDrawTarget::checkDraw(GrPrimitiveType type, int startVertex,
                             int startIndex, int vertexCount,
                             int indexCount) const {
    const GrDrawState& drawState = this->getDrawState();
#if GR_DEBUG
    const GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
    int maxVertex = startVertex + vertexCount;
    int maxValidVertex;
    switch (geoSrc.fVertexSrc) {
        case kNone_GeometrySrcType:
            GrCrash("Attempting to draw without vertex src.");
        case kReserved_GeometrySrcType: // fallthrough
        case kArray_GeometrySrcType:
            maxValidVertex = geoSrc.fVertexCount;
            break;
        case kBuffer_GeometrySrcType:
            maxValidVertex = geoSrc.fVertexBuffer->sizeInBytes() / VertexSize(geoSrc.fVertexLayout);
            break;
    }
    if (maxVertex > maxValidVertex) {
        GrCrash("Drawing outside valid vertex range.");
    }
    if (indexCount > 0) {
        int maxIndex = startIndex + indexCount;
        int maxValidIndex;
        switch (geoSrc.fIndexSrc) {
            case kNone_GeometrySrcType:
                GrCrash("Attempting to draw indexed geom without index src.");
            case kReserved_GeometrySrcType: // fallthrough
            case kArray_GeometrySrcType:
                maxValidIndex = geoSrc.fIndexCount;
                break;
            case kBuffer_GeometrySrcType:
                maxValidIndex = geoSrc.fIndexBuffer->sizeInBytes() / sizeof(uint16_t);
                break;
        }
        if (maxIndex > maxValidIndex) {
            GrCrash("Index reads outside valid index range.");
        }
    }

    GrAssert(NULL != drawState.getRenderTarget());
    for (int s = 0; s < GrDrawState::kNumStages; ++s) {
        if (drawState.isStageEnabled(s)) {
            const GrEffect* effect = drawState.getStage(s).getEffect();
            int numTextures = effect->numTextures();
            for (int t = 0; t < numTextures; ++t) {
                GrTexture* texture = effect->texture(t);
                GrAssert(texture->asRenderTarget() != drawState.getRenderTarget());
            }
        }
    }
#endif
    if (NULL == drawState.getRenderTarget()) {
        return false;
    }
    return true;
}
Esempio n. 6
0
static SkGrPixelRef* copyToTexturePixelRef(GrTexture* texture, const SkIRect* subset) {
    if (NULL == texture) {
        return NULL;
    }
    GrContext* context = texture->getContext();
    if (NULL == context) {
        return NULL;
    }
    GrTextureDesc desc;

    SkIPoint pointStorage;
    SkIPoint* topLeft;
    if (subset != NULL) {
        SkASSERT(SkIRect::MakeWH(texture->width(), texture->height()).contains(*subset));
        // Create a new texture that is the size of subset.
        desc.fWidth = subset->width();
        desc.fHeight = subset->height();
        pointStorage.set(subset->x(), subset->y());
        topLeft = &pointStorage;
    } else {
        desc.fWidth  = texture->width();
        desc.fHeight = texture->height();
        topLeft = NULL;
    }
    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
    desc.fConfig = texture->config();

    SkImageInfo info;
    if (!GrPixelConfig2ColorType(desc.fConfig, &info.fColorType)) {
        return NULL;
    }
    info.fWidth = desc.fWidth;
    info.fHeight = desc.fHeight;
    info.fAlphaType = kPremul_SkAlphaType;

    GrTexture* dst = context->createUncachedTexture(desc, NULL, 0);
    if (NULL == dst) {
        return NULL;
    }

    context->copyTexture(texture, dst->asRenderTarget(), topLeft);

    // TODO: figure out if this is responsible for Chrome canvas errors
#if 0
    // The render texture we have created (to perform the copy) isn't fully
    // functional (since it doesn't have a stencil buffer). Release it here
    // so the caller doesn't try to render to it.
    // TODO: we can undo this release when dynamic stencil buffer attach/
    // detach has been implemented
    dst->releaseRenderTarget();
#endif

    SkGrPixelRef* pixelRef = SkNEW_ARGS(SkGrPixelRef, (info, dst));
    SkSafeUnref(dst);
    return pixelRef;
}
Esempio n. 7
0
// Create a mask of 'devPath' and place the result in 'mask'.
static GrTexture* create_mask_GPU(GrContext* context,
                                  SkRect* maskRect,
                                  const SkPath& devPath,
                                  const GrStrokeInfo& strokeInfo,
                                  bool doAA,
                                  int sampleCnt) {
    // This mask will ultimately be drawn as a non-AA rect (see draw_mask).
    // Non-AA rects have a bad habit of snapping arbitrarily. Integerize here
    // so the mask draws in a reproducible manner.
    *maskRect = SkRect::Make(maskRect->roundOut());

    GrSurfaceDesc desc;
    desc.fFlags = kRenderTarget_GrSurfaceFlag;
    desc.fWidth = SkScalarCeilToInt(maskRect->width());
    desc.fHeight = SkScalarCeilToInt(maskRect->height());
    desc.fSampleCnt = doAA ? sampleCnt : 0;
    // We actually only need A8, but it often isn't supported as a
    // render target so default to RGBA_8888
    desc.fConfig = kRGBA_8888_GrPixelConfig;

    if (context->caps()->isConfigRenderable(kAlpha_8_GrPixelConfig, desc.fSampleCnt > 0)) {
        desc.fConfig = kAlpha_8_GrPixelConfig;
    }

    GrTexture* mask = context->textureProvider()->createApproxTexture(desc);
    if (nullptr == mask) {
        return nullptr;
    }

    SkRect clipRect = SkRect::MakeWH(maskRect->width(), maskRect->height());

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

    drawContext->clear(nullptr, 0x0, true);

    GrPaint tempPaint;
    tempPaint.setAntiAlias(doAA);
    tempPaint.setCoverageSetOpXPFactory(SkRegion::kReplace_Op);

    // setup new clip
    GrClip clip(clipRect);

    // Draw the mask into maskTexture with the path's integerized top-left at
    // the origin using tempPaint.
    SkMatrix translate;
    translate.setTranslate(-maskRect->fLeft, -maskRect->fTop);
    drawContext->drawPath(clip, tempPaint, translate, devPath, strokeInfo);
    return mask;
}
Esempio n. 8
0
GrTexture* GrGpu::createPlatformTexture(const GrPlatformTextureDesc& desc) {
    this->handleDirtyContext();
    GrTexture* tex = this->onCreatePlatformTexture(desc);
    // TODO: defer this and attach dynamically
    GrRenderTarget* tgt = tex->asRenderTarget();
    if (NULL != tgt &&
        !this->attachStencilBufferToRenderTarget(tgt)) {
        tex->unref();
        return NULL;
    } else {
        return tex;
    }
}
Esempio n. 9
0
GrTexture* GrGpu::createTexture(const GrTextureDesc& desc,
                                const void* srcData, size_t rowBytes) {
    if (!this->caps()->isConfigTexturable(desc.fConfig)) {
        return NULL;
    }

    if ((desc.fFlags & kRenderTarget_GrTextureFlagBit) &&
        !this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
        return NULL;
    }

    GrTexture *tex = NULL;
    if (GrPixelConfigIsCompressed(desc.fConfig)) {
        // We shouldn't be rendering into this
        SkASSERT((desc.fFlags & kRenderTarget_GrTextureFlagBit) == 0);

        if (!this->caps()->npotTextureTileSupport() &&
            (!SkIsPow2(desc.fWidth) || !SkIsPow2(desc.fHeight))) {
            return NULL;
        }

        this->handleDirtyContext();
        tex = this->onCreateCompressedTexture(desc, srcData);
    } else {
        this->handleDirtyContext();
        tex = this->onCreateTexture(desc, srcData, rowBytes);
        if (tex &&
            (kRenderTarget_GrTextureFlagBit & desc.fFlags) &&
            !(kNoStencil_GrTextureFlagBit & desc.fFlags)) {
            SkASSERT(tex->asRenderTarget());
            // TODO: defer this and attach dynamically
            if (!this->attachStencilBufferToRenderTarget(tex->asRenderTarget())) {
                tex->unref();
                return NULL;
            }
        }
    }
    return tex;
}
Esempio n. 10
0
GrTexture* GrGpu::wrapBackendTexture(const GrBackendTextureDesc& desc, GrWrapOwnership ownership) {
    this->handleDirtyContext();
    GrTexture* tex = this->onWrapBackendTexture(desc, ownership);
    if (NULL == tex) {
        return NULL;
    }
    // TODO: defer this and attach dynamically
    GrRenderTarget* tgt = tex->asRenderTarget();
    if (tgt && !this->attachStencilAttachmentToRenderTarget(tgt)) {
        tex->unref();
        return NULL;
    } else {
        return tex;
    }
}
Esempio n. 11
0
GrTexture* GrGpu::createTexture(const GrTextureDesc& desc,
                                const void* srcData, size_t rowBytes) {
    if (kUnknown_GrPixelConfig == desc.fConfig) {
        return NULL;
    }
    if ((desc.fFlags & kRenderTarget_GrTextureFlagBit) &&
        !this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
        return NULL;
    }

    this->handleDirtyContext();
    GrTexture* tex = this->onCreateTexture(desc, srcData, rowBytes);
    if (NULL != tex &&
        (kRenderTarget_GrTextureFlagBit & desc.fFlags) &&
        !(kNoStencil_GrTextureFlagBit & desc.fFlags)) {
        SkASSERT(NULL != tex->asRenderTarget());
        // TODO: defer this and attach dynamically
        if (!this->attachStencilBufferToRenderTarget(tex->asRenderTarget())) {
            tex->unref();
            return NULL;
        }
    }
    return tex;
}
Esempio n. 12
0
static SkGrPixelRef* copy_to_new_texture_pixelref(GrTexture* texture, SkColorType dstCT,
                                                  SkColorProfileType dstPT, const SkIRect* subset) {
    if (NULL == texture || kUnknown_SkColorType == dstCT) {
        return NULL;
    }
    GrContext* context = texture->getContext();
    if (NULL == context) {
        return NULL;
    }
    GrSurfaceDesc desc;

    SkIRect srcRect;

    if (!subset) {
        desc.fWidth  = texture->width();
        desc.fHeight = texture->height();
        srcRect = SkIRect::MakeWH(texture->width(), texture->height());
    } else {
        SkASSERT(SkIRect::MakeWH(texture->width(), texture->height()).contains(*subset));
        // Create a new texture that is the size of subset.
        desc.fWidth = subset->width();
        desc.fHeight = subset->height();
        srcRect = *subset;
    }
    desc.fFlags = kRenderTarget_GrSurfaceFlag;
    desc.fConfig = SkImageInfo2GrPixelConfig(dstCT, kPremul_SkAlphaType, dstPT);

    GrTexture* dst = context->createTexture(desc, false, NULL, 0);
    if (NULL == dst) {
        return NULL;
    }

    // Blink is relying on the above copy being sent to GL immediately in the case when the source
    // is a WebGL canvas backing store. We could have a TODO to remove this flush flag, but we have
    // a larger TODO to remove SkGrPixelRef entirely.
    context->copySurface(dst->asRenderTarget(), texture, srcRect, SkIPoint::Make(0,0),
                         GrContext::kFlushWrites_PixelOp);
  
    SkImageInfo info = SkImageInfo::Make(desc.fWidth, desc.fHeight, dstCT, kPremul_SkAlphaType,
                                         dstPT);
    SkGrPixelRef* pixelRef = SkNEW_ARGS(SkGrPixelRef, (info, dst));
    SkSafeUnref(dst);
    return pixelRef;
}
Esempio n. 13
0
GrTexture* GrGpu::wrapBackendTexture(const GrBackendTextureDesc& desc, GrWrapOwnership ownership) {
    this->handleDirtyContext();
    if (!this->caps()->isConfigTexturable(desc.fConfig)) {
        return nullptr;
    }
    if ((desc.fFlags & kRenderTarget_GrBackendTextureFlag) &&
        !this->caps()->isConfigRenderable(desc.fConfig, desc.fSampleCnt > 0)) {
        return nullptr;
    }
    GrTexture* tex = this->onWrapBackendTexture(desc, ownership);
    if (nullptr == tex) {
        return nullptr;
    }
    // TODO: defer this and attach dynamically
    GrRenderTarget* tgt = tex->asRenderTarget();
    if (tgt && !fContext->resourceProvider()->attachStencilAttachment(tgt)) {
        tex->unref();
        return nullptr;
    } else {
        return tex;
    }
}
Esempio n. 14
0
////////////////////////////////////////////////////////////////////////////////
// Create a 8-bit clip mask in alpha
GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t clipStackGenID,
                                                  InitialState initialState,
                                                  const ElementList& elements,
                                                  const SkIRect& clipSpaceIBounds) {
    GrAssert(kNone_ClipMaskType == fCurrClipMaskType);

    GrTexture* result;
    if (this->getMaskTexture(clipStackGenID, clipSpaceIBounds, &result)) {
        fCurrClipMaskType = kAlpha_ClipMaskType;
        return result;
    }

    if (NULL == result) {
        fAACache.reset();
        return NULL;
    }

    GrDrawTarget::AutoGeometryAndStatePush agasp(fGpu, GrDrawTarget::kReset_ASRInit);
    GrDrawState* drawState = fGpu->drawState();

    // The top-left of the mask corresponds to the top-left corner of the bounds.
    SkVector clipToMaskOffset = {
        SkIntToScalar(-clipSpaceIBounds.fLeft),
        SkIntToScalar(-clipSpaceIBounds.fTop)
    };
    // The texture may be larger than necessary, this rect represents the part of the texture
    // we populate with a rasterization of the clip.
    SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height());

    // We're drawing a coverage mask and want coverage to be run through the blend function.
    drawState->enableState(GrDrawState::kCoverageDrawing_StateBit);

    // Set the matrix so that rendered clip elements are transformed to mask space from clip space.
    drawState->viewMatrix()->setTranslate(clipToMaskOffset);

    // The scratch texture that we are drawing into can be substantially larger than the mask. Only
    // clear the part that we care about.
    fGpu->clear(&maskSpaceIBounds,
                kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000,
                result->asRenderTarget());

    // When we use the stencil in the below loop it is important to have this clip installed.
    // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first
    // pass must not set values outside of this bounds or stencil values outside the rect won't be
    // cleared.
    GrDrawTarget::AutoClipRestore acr(fGpu, maskSpaceIBounds);
    drawState->enableState(GrDrawState::kClip_StateBit);

    GrAutoScratchTexture temp;
    // walk through each clip element and perform its set op
    for (ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) {
        const Element* element = iter.get();
        SkRegion::Op op = element->getOp();
        bool invert = element->isInverseFilled();

        if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
            GrPathRenderer* pr = NULL;
            bool useTemp = !this->canStencilAndDrawElement(result, element, &pr);
            GrTexture* dst;
            // This is the bounds of the clip element in the space of the alpha-mask. The temporary
            // mask buffer can be substantially larger than the actually clip stack element. We
            // touch the minimum number of pixels necessary and use decal mode to combine it with
            // the accumulator.
            GrIRect maskSpaceElementIBounds;

            if (useTemp) {
                if (invert) {
                    maskSpaceElementIBounds = maskSpaceIBounds;
                } else {
                    GrRect elementBounds = element->getBounds();
                    elementBounds.offset(clipToMaskOffset);
                    elementBounds.roundOut(&maskSpaceElementIBounds);
                }

                this->getTemp(maskSpaceIBounds.fRight, maskSpaceIBounds.fBottom, &temp);
                if (NULL == temp.texture()) {
                    fAACache.reset();
                    return NULL;
                }
                dst = temp.texture();
                // clear the temp target and set blend to replace
                fGpu->clear(&maskSpaceElementIBounds,
                            invert ? 0xffffffff : 0x00000000,
                            dst->asRenderTarget());
                setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);

            } else {
                // draw directly into the result with the stencil set to make the pixels affected
                // by the clip shape be non-zero.
                dst = result;
                GR_STATIC_CONST_SAME_STENCIL(kStencilInElement,
                                             kReplace_StencilOp,
                                             kReplace_StencilOp,
                                             kAlways_StencilFunc,
                                             0xffff,
                                             0xffff,
                                             0xffff);
                drawState->setStencil(kStencilInElement);
                setup_boolean_blendcoeffs(drawState, op);
            }

            drawState->setAlpha(invert ? 0x00 : 0xff);

            if (!this->drawElement(dst, element, pr)) {
                fAACache.reset();
                return NULL;
            }

            if (useTemp) {
                // Now draw into the accumulator using the real operation and the temp buffer as a
                // texture
                this->mergeMask(result,
                                temp.texture(),
                                op,
                                maskSpaceIBounds,
                                maskSpaceElementIBounds);
            } else {
                // Draw to the exterior pixels (those with a zero stencil value).
                drawState->setAlpha(invert ? 0xff : 0x00);
                GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement,
                                             kZero_StencilOp,
                                             kZero_StencilOp,
                                             kEqual_StencilFunc,
                                             0xffff,
                                             0x0000,
                                             0xffff);
                drawState->setStencil(kDrawOutsideElement);
                fGpu->drawSimpleRect(clipSpaceIBounds);
                drawState->disableStencil();
            }
        } else {
            // all the remaining ops can just be directly draw into the accumulation buffer
            drawState->setAlpha(0xff);
            setup_boolean_blendcoeffs(drawState, op);
            this->drawElement(result, element);
        }
    }

    fCurrClipMaskType = kAlpha_ClipMaskType;
    return result;
}
Esempio n. 15
0
GrTexture* GaussianBlur(GrContext* context,
                        GrTexture* srcTexture,
                        bool canClobberSrc,
                        const SkRect& rect,
                        bool cropToRect,
                        float sigmaX,
                        float sigmaY) {
    SkASSERT(context);

    SkIRect clearRect;
    int scaleFactorX, radiusX;
    int scaleFactorY, radiusY;
    int maxTextureSize = context->caps()->maxTextureSize();
    sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX);
    sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY);

    SkRect srcRect(rect);
    scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
    srcRect.roundOut(&srcRect);
    scale_rect(&srcRect, static_cast<float>(scaleFactorX),
                         static_cast<float>(scaleFactorY));

    // setup new clip
    GrClip clip(SkRect::MakeWH(srcRect.width(), srcRect.height()));

    SkASSERT(kBGRA_8888_GrPixelConfig == srcTexture->config() ||
             kRGBA_8888_GrPixelConfig == srcTexture->config() ||
             kAlpha_8_GrPixelConfig == srcTexture->config());

    GrSurfaceDesc desc;
    desc.fFlags = kRenderTarget_GrSurfaceFlag;
    desc.fWidth = SkScalarFloorToInt(srcRect.width());
    desc.fHeight = SkScalarFloorToInt(srcRect.height());
    desc.fConfig = srcTexture->config();

    GrTexture* dstTexture;
    GrTexture* tempTexture;
    SkAutoTUnref<GrTexture> temp1, temp2;

    temp1.reset(context->textureProvider()->refScratchTexture(
        desc, GrTextureProvider::kApprox_ScratchTexMatch));
    dstTexture = temp1.get();
    if (canClobberSrc) {
        tempTexture = srcTexture;
    } else {
        temp2.reset(context->textureProvider()->refScratchTexture(
            desc, GrTextureProvider::kApprox_ScratchTexMatch));
        tempTexture = temp2.get();
    }

    if (NULL == dstTexture || NULL == tempTexture) {
        return NULL;
    }

    GrDrawContext* drawContext = context->drawContext();
    if (!drawContext) {
        return NULL;
    }

    for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
        GrPaint paint;
        SkMatrix matrix;
        matrix.setIDiv(srcTexture->width(), srcTexture->height());
        SkRect dstRect(srcRect);
        if (cropToRect && i == 1) {
            dstRect.offset(-dstRect.fLeft, -dstRect.fTop);
            SkRect domain;
            matrix.mapRect(&domain, rect);
            domain.inset(i < scaleFactorX ? SK_ScalarHalf / srcTexture->width() : 0.0f,
                         i < scaleFactorY ? SK_ScalarHalf / srcTexture->height() : 0.0f);
            SkAutoTUnref<GrFragmentProcessor> fp(   GrTextureDomainEffect::Create(
                paint.getProcessorDataManager(),
                srcTexture,
                matrix,
                domain,
                GrTextureDomain::kDecal_Mode,
                GrTextureParams::kBilerp_FilterMode));
            paint.addColorProcessor(fp);
        } else {
            GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
            paint.addColorTextureProcessor(srcTexture, matrix, params);
        }
        scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f,
                             i < scaleFactorY ? 0.5f : 1.0f);
        drawContext->drawNonAARectToRect(dstTexture->asRenderTarget(), clip, paint, SkMatrix::I(),
                                         dstRect, srcRect);
        srcRect = dstRect;
        srcTexture = dstTexture;
        SkTSwap(dstTexture, tempTexture);
    }

    const SkIRect srcIRect = srcRect.roundOut();

    // For really small blurs(Certainly no wider than 5x5 on desktop gpus) it is faster to just
    // launch a single non separable kernel vs two launches
    if (sigmaX > 0.0f && sigmaY > 0 &&
            (2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) {
        // We shouldn't be scaling because this is a small size blur
        SkASSERT((scaleFactorX == scaleFactorY) == 1);
        SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
        convolve_gaussian_2d(drawContext, dstTexture->asRenderTarget(), clip, srcRect, dstRect,
                             srcTexture, radiusX, radiusY, sigmaX, sigmaY, cropToRect, srcIRect);
        srcTexture = dstTexture;
        srcRect = dstRect;
        SkTSwap(dstTexture, tempTexture);

    } else {
        if (sigmaX > 0.0f) {
            if (scaleFactorX > 1) {
                // Clear out a radius to the right of the srcRect to prevent the
                // X convolution from reading garbage.
                clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop,
                                              radiusX, srcIRect.height());
                drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false);
            }
            SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
            convolve_gaussian(drawContext, dstTexture->asRenderTarget(), clip, srcRect, dstRect,
                              srcTexture, Gr1DKernelEffect::kX_Direction, radiusX, sigmaX,
                              cropToRect);
            srcTexture = dstTexture;
            srcRect = dstRect;
            SkTSwap(dstTexture, tempTexture);
        }

        if (sigmaY > 0.0f) {
            if (scaleFactorY > 1 || sigmaX > 0.0f) {
                // Clear out a radius below the srcRect to prevent the Y
                // convolution from reading garbage.
                clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom,
                                              srcIRect.width(), radiusY);
                drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false);
            }

            SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
            convolve_gaussian(drawContext, dstTexture->asRenderTarget(), clip, srcRect,
                              dstRect, srcTexture, Gr1DKernelEffect::kY_Direction, radiusY, sigmaY,
                              cropToRect);
            srcTexture = dstTexture;
            srcRect = dstRect;
            SkTSwap(dstTexture, tempTexture);
        }
    }

    if (scaleFactorX > 1 || scaleFactorY > 1) {
        // Clear one pixel to the right and below, to accommodate bilinear
        // upsampling.
        clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom,
                                      srcIRect.width() + 1, 1);
        drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false);
        clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop,
                                      1, srcIRect.height());
        drawContext->clear(srcTexture->asRenderTarget(), &clearRect, 0x0, false);
        SkMatrix matrix;
        matrix.setIDiv(srcTexture->width(), srcTexture->height());

        GrPaint paint;
        // FIXME:  this should be mitchell, not bilinear.
        GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
        paint.addColorTextureProcessor(srcTexture, matrix, params);

        SkRect dstRect(srcRect);
        scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY);
        drawContext->drawNonAARectToRect(dstTexture->asRenderTarget(), clip, paint,
                                         SkMatrix::I(), dstRect, srcRect);
        srcRect = dstRect;
        srcTexture = dstTexture;
        SkTSwap(dstTexture, tempTexture);
    }
    return SkRef(srcTexture);
}
static sk_sp<SkSpecialImage> apply_morphology(GrContext* context,
                                              SkSpecialImage* input,
                                              const SkIRect& rect,
                                              GrMorphologyEffect::MorphologyType morphType,
                                              SkISize radius) {
    SkAutoTUnref<GrTexture> srcTexture(input->asTextureRef(context));
    SkASSERT(srcTexture);

    // setup new clip
    GrClip clip(SkRect::MakeWH(SkIntToScalar(srcTexture->width()),
                               SkIntToScalar(srcTexture->height())));

    SkIRect dstRect = SkIRect::MakeWH(rect.width(), rect.height());
    GrSurfaceDesc desc;
    desc.fFlags = kRenderTarget_GrSurfaceFlag;
    desc.fWidth = rect.width();
    desc.fHeight = rect.height();
    desc.fConfig = kSkia8888_GrPixelConfig;
    SkIRect srcRect = rect;

    SkASSERT(radius.width() > 0 || radius.height() > 0);

    if (radius.fWidth > 0) {
        GrTexture* scratch = context->textureProvider()->createApproxTexture(desc);
        if (!scratch) {
            return nullptr;
        }
        SkAutoTUnref<GrDrawContext> dstDrawContext(
                                                context->drawContext(scratch->asRenderTarget()));
        if (!dstDrawContext) {
            return nullptr;
        }

        apply_morphology_pass(dstDrawContext, clip, srcTexture,
                              srcRect, dstRect, radius.fWidth, morphType,
                              Gr1DKernelEffect::kX_Direction);
        SkIRect clearRect = SkIRect::MakeXYWH(dstRect.fLeft, dstRect.fBottom,
                                              dstRect.width(), radius.fHeight);
        GrColor clearColor = GrMorphologyEffect::kErode_MorphologyType == morphType ?
                                SK_ColorWHITE :
                                SK_ColorTRANSPARENT;
        dstDrawContext->clear(&clearRect, clearColor, false);

        srcTexture.reset(scratch);
        srcRect = dstRect;
    }
    if (radius.fHeight > 0) {
        GrTexture* scratch = context->textureProvider()->createApproxTexture(desc);
        if (!scratch) {
            return nullptr;
        }
        SkAutoTUnref<GrDrawContext> dstDrawContext(
                                                context->drawContext(scratch->asRenderTarget()));
        if (!dstDrawContext) {
            return nullptr;
        }

        apply_morphology_pass(dstDrawContext, clip, srcTexture,
                              srcRect, dstRect, radius.fHeight, morphType,
                              Gr1DKernelEffect::kY_Direction);

        srcTexture.reset(scratch);
    }

    return SkSpecialImage::MakeFromGpu(input->internal_getProxy(),
                                       SkIRect::MakeWH(rect.width(), rect.height()),
                                       kNeedNewImageUniqueID_SpecialImage,
                                       srcTexture, &input->props());
}
Esempio n. 17
0
////////////////////////////////////////////////////////////////////////////////
// Create a 8-bit clip mask in alpha
bool GrClipMaskManager::createAlphaClipMask(const GrClipData& clipDataIn,
                                            GrTexture** result,
                                            GrIRect *devResultBounds) {
    GrAssert(NULL != devResultBounds);
    GrAssert(kNone_ClipMaskType == fCurrClipMaskType);

    if (this->clipMaskPreamble(clipDataIn, result, devResultBounds)) {
        fCurrClipMaskType = kAlpha_ClipMaskType;
        return true;
    }

    // Note: 'resultBounds' is in device (as opposed to canvas) coordinates

    GrTexture* accum = fAACache.getLastMask();
    if (NULL == accum) {
        fAACache.reset();
        return false;
    }

    GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
    GrDrawState* drawState = fGpu->drawState();

    GrDrawTarget::AutoGeometryPush agp(fGpu);

    // The mask we generate is translated so that its upper-left corner is at devResultBounds
    // upper-left corner in device space.
    GrIRect maskResultBounds = GrIRect::MakeWH(devResultBounds->width(), devResultBounds->height());

    // Set the matrix so that rendered clip elements are transformed from the space of the clip
    // stack to the alpha-mask. This accounts for both translation due to the clip-origin and the
    // placement of the mask within the device.
    SkVector clipToMaskOffset = {
        SkIntToScalar(-devResultBounds->fLeft - clipDataIn.fOrigin.fX),
        SkIntToScalar(-devResultBounds->fTop - clipDataIn.fOrigin.fY)
    };
    drawState->viewMatrix()->setTranslate(clipToMaskOffset);

    bool clearToInside;
    SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning

    SkClipStack::Iter iter(*clipDataIn.fClipStack,
                           SkClipStack::Iter::kBottom_IterStart);
    const SkClipStack::Iter::Clip* clip = process_initial_clip_elements(&iter,
                                                              *devResultBounds,
                                                              &clearToInside,
                                                              &firstOp,
                                                              clipDataIn);
    // The scratch texture that we are drawing into can be substantially larger than the mask. Only
    // clear the part that we care about.
    fGpu->clear(&maskResultBounds,
                clearToInside ? 0xffffffff : 0x00000000,
                accum->asRenderTarget());
    bool accumClearedToZero = !clearToInside;

    GrAutoScratchTexture temp;
    bool first = true;
    // walk through each clip element and perform its set op
    for ( ; NULL != clip; clip = iter.nextCombined()) {

        SkRegion::Op op = clip->fOp;
        if (first) {
            first = false;
            op = firstOp;
        }

        if (SkRegion::kReplace_Op == op) {
            // clear the accumulator and draw the new object directly into it
            if (!accumClearedToZero) {
                fGpu->clear(&maskResultBounds, 0x00000000, accum->asRenderTarget());
            }

            setup_boolean_blendcoeffs(drawState, op);
            this->drawClipShape(accum, clip, *devResultBounds);

        } else if (SkRegion::kReverseDifference_Op == op ||
                   SkRegion::kIntersect_Op == op) {
            // there is no point in intersecting a screen filling rectangle.
            if (SkRegion::kIntersect_Op == op && NULL != clip->fRect &&
                contains(*clip->fRect, *devResultBounds, clipDataIn.fOrigin)) {
                continue;
            }

            getTemp(*devResultBounds, &temp);
            if (NULL == temp.texture()) {
                fAACache.reset();
                return false;
            }

            // this is the bounds of the clip element in the space of the alpha-mask. The temporary
            // mask buffer can be substantially larger than the actually clip stack element. We
            // touch the minimum number of pixels necessary and use decal mode to combine it with
            // the accumulator
            GrRect elementMaskBounds = clip->getBounds();
            elementMaskBounds.offset(clipToMaskOffset);
            GrIRect elementMaskIBounds;
            elementMaskBounds.roundOut(&elementMaskIBounds);

            // clear the temp target & draw into it
            fGpu->clear(&elementMaskIBounds, 0x00000000, temp.texture()->asRenderTarget());

            setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
            this->drawClipShape(temp.texture(), clip, elementMaskIBounds);

            // Now draw into the accumulator using the real operation
            // and the temp buffer as a texture
            this->mergeMask(accum, temp.texture(), op, maskResultBounds, elementMaskIBounds);
        } else {
            // all the remaining ops can just be directly draw into
            // the accumulation buffer
            setup_boolean_blendcoeffs(drawState, op);
            this->drawClipShape(accum, clip, *devResultBounds);
        }
        accumClearedToZero = false;
    }

    *result = accum;
    fCurrClipMaskType = kAlpha_ClipMaskType;
    return true;
}
Esempio n. 18
0
bool GrDrawTarget::checkDraw(GrPrimitiveType type, int startVertex,
                             int startIndex, int vertexCount,
                             int indexCount) const {
    const GrDrawState& drawState = this->getDrawState();
#ifdef SK_DEBUG
    const GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
    int maxVertex = startVertex + vertexCount;
    int maxValidVertex;
    switch (geoSrc.fVertexSrc) {
        case kNone_GeometrySrcType:
            SkFAIL("Attempting to draw without vertex src.");
        case kReserved_GeometrySrcType: // fallthrough
        case kArray_GeometrySrcType:
            maxValidVertex = geoSrc.fVertexCount;
            break;
        case kBuffer_GeometrySrcType:
            maxValidVertex = static_cast<int>(geoSrc.fVertexBuffer->gpuMemorySize() / geoSrc.fVertexSize);
            break;
    }
    if (maxVertex > maxValidVertex) {
        SkFAIL("Drawing outside valid vertex range.");
    }
    if (indexCount > 0) {
        int maxIndex = startIndex + indexCount;
        int maxValidIndex;
        switch (geoSrc.fIndexSrc) {
            case kNone_GeometrySrcType:
                SkFAIL("Attempting to draw indexed geom without index src.");
            case kReserved_GeometrySrcType: // fallthrough
            case kArray_GeometrySrcType:
                maxValidIndex = geoSrc.fIndexCount;
                break;
            case kBuffer_GeometrySrcType:
                maxValidIndex = static_cast<int>(geoSrc.fIndexBuffer->gpuMemorySize() / sizeof(uint16_t));
                break;
        }
        if (maxIndex > maxValidIndex) {
            SkFAIL("Index reads outside valid index range.");
        }
    }

    SkASSERT(NULL != drawState.getRenderTarget());

    for (int s = 0; s < drawState.numColorStages(); ++s) {
        const GrEffectRef& effect = *drawState.getColorStage(s).getEffect();
        int numTextures = effect->numTextures();
        for (int t = 0; t < numTextures; ++t) {
            GrTexture* texture = effect->texture(t);
            SkASSERT(texture->asRenderTarget() != drawState.getRenderTarget());
        }
    }
    for (int s = 0; s < drawState.numCoverageStages(); ++s) {
        const GrEffectRef& effect = *drawState.getCoverageStage(s).getEffect();
        int numTextures = effect->numTextures();
        for (int t = 0; t < numTextures; ++t) {
            GrTexture* texture = effect->texture(t);
            SkASSERT(texture->asRenderTarget() != drawState.getRenderTarget());
        }
    }

    SkASSERT(drawState.validateVertexAttribs());
#endif
    if (NULL == drawState.getRenderTarget()) {
        return false;
    }
    return true;
}
Esempio n. 19
0
void sub_clear_test(skiatest::Reporter* reporter, GrContext* context, GrPixelConfig config) {
    const int width = 10;
    const int height = 10;
    const int subWidth = width/2;
    const int subHeight = height/2;
    GrVkGpu* gpu = static_cast<GrVkGpu*>(context->getGpu());
    gpu->discard(NULL);
    SkAutoTMalloc<GrColor> buffer(width * height);
    SkAutoTMalloc<GrColor> subBuffer(subWidth * subHeight);

    GrSurfaceDesc surfDesc;
    surfDesc.fFlags = kRenderTarget_GrSurfaceFlag;
    surfDesc.fOrigin = kTopLeft_GrSurfaceOrigin;
    surfDesc.fWidth = width;
    surfDesc.fHeight = height;
    surfDesc.fConfig = config;
    surfDesc.fSampleCnt = 0;
    GrTexture* tex = gpu->createTexture(surfDesc, false, nullptr, 0);
    SkASSERT(tex);
    SkASSERT(tex->asRenderTarget());

    SkIRect fullRect = SkIRect::MakeWH(10, 10);
    gpu->clear(fullRect, GrColor_TRANSPARENT_BLACK, tex->asRenderTarget());

    gpu->readPixels(tex, 0, 0, width, height, config, (void*)buffer.get(), 0);

    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(),
                                                                     GrColor_TRANSPARENT_BLACK,
                                                                     config,
                                                                     width,
                                                                     height));
    SkIRect rect;
    rect = SkIRect::MakeXYWH(0, 0, subWidth, subHeight);
    gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget());
    rect = SkIRect::MakeXYWH(subWidth, 0, subWidth, subHeight);
    gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget());
    rect = SkIRect::MakeXYWH(0, subHeight, subWidth, subHeight);
    gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget());

    // Should fail since bottom right sub area has not been cleared to white
    gpu->readPixels(tex, 0, 0, width, height, config, (void*)buffer.get(), 0);
    REPORTER_ASSERT(reporter, !does_full_buffer_contain_correct_color(buffer.get(),
                                                                      GrColor_WHITE,
                                                                      config,
                                                                      width,
                                                                      height));

    rect = SkIRect::MakeXYWH(subWidth, subHeight, subWidth, subHeight);
    gpu->clear(rect, GrColor_WHITE, tex->asRenderTarget());

    gpu->readPixels(tex, 0, 0, width, height, config, (void*)buffer.get(), 0);
    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(buffer.get(),
                                                                     GrColor_WHITE,
                                                                     config,
                                                                     width,
                                                                     height));

    // Try different colors and that each sub area has correct color
    GrColor subColor1 = GrColorPackRGBA(0xFF, 0x00, 0x00, 0xFF);
    GrColor subColor2 = GrColorPackRGBA(0x00, 0xFF, 0x00, 0xFF);
    GrColor subColor3 = GrColorPackRGBA(0x00, 0x00, 0xFF, 0xFF);
    GrColor subColor4 = GrColorPackRGBA(0xFF, 0xFF, 0x00, 0xFF);

    rect = SkIRect::MakeXYWH(0, 0, subWidth, subHeight);
    gpu->clear(rect, subColor1, tex->asRenderTarget());
    rect = SkIRect::MakeXYWH(subWidth, 0, subWidth, subHeight);
    gpu->clear(rect, subColor2, tex->asRenderTarget());
    rect = SkIRect::MakeXYWH(0, subHeight, subWidth, subHeight);
    gpu->clear(rect, subColor3, tex->asRenderTarget());
    rect = SkIRect::MakeXYWH(subWidth, subHeight, subWidth, subHeight);
    gpu->clear(rect, subColor4, tex->asRenderTarget());

    gpu->readPixels(tex, 0, 0, subWidth, subHeight, config, (void*)subBuffer.get(), 0);
    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(),
                                                                     subColor1,
                                                                     config,
                                                                     subWidth,
                                                                     subHeight));
    gpu->readPixels(tex, subWidth, 0, subWidth, subHeight, config, (void*)subBuffer.get(), 0);
    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(),
                                                                     subColor2,
                                                                     config,
                                                                     subWidth,
                                                                     subHeight));
    gpu->readPixels(tex, 0, subHeight, subWidth, subHeight, config, (void*)subBuffer.get(), 0);
    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(),
                                                                     subColor3,
                                                                     config,
                                                                     subWidth,
                                                                     subHeight));
    gpu->readPixels(tex, subWidth, subHeight, subWidth, subHeight,
                    config, (void*)subBuffer.get(), 0);
    REPORTER_ASSERT(reporter, does_full_buffer_contain_correct_color(subBuffer.get(),
                                                                     subColor4,
                                                                     config,
                                                                     subWidth,
                                                                     subHeight));
}
Esempio n. 20
0
GrTexture* GrClipMaskManager::createAlphaClipMask(int32_t elementsGenID,
                                                  GrReducedClip::InitialState initialState,
                                                  const GrReducedClip::ElementList& elements,
                                                  const SkVector& clipToMaskOffset,
                                                  const SkIRect& clipSpaceIBounds) {
    GrResourceProvider* resourceProvider = fDrawTarget->cmmAccess().resourceProvider();
    GrUniqueKey key;
    GetClipMaskKey(elementsGenID, clipSpaceIBounds, &key);
    if (GrTexture* texture = resourceProvider->findAndRefTextureByUniqueKey(key)) {
        return texture;
    }

    SkAutoTUnref<GrTexture> texture(this->createCachedMask(
        clipSpaceIBounds.width(), clipSpaceIBounds.height(), key, true));

    // There's no texture in the cache. Let's try to allocate it then.
    if (!texture) {
        return nullptr;
    }

    // Set the matrix so that rendered clip elements are transformed to mask space from clip
    // space.
    SkMatrix translate;
    translate.setTranslate(clipToMaskOffset);

    // The texture may be larger than necessary, this rect represents the part of the texture
    // we populate with a rasterization of the clip.
    SkIRect maskSpaceIBounds = SkIRect::MakeWH(clipSpaceIBounds.width(), clipSpaceIBounds.height());

    // The scratch texture that we are drawing into can be substantially larger than the mask. Only
    // clear the part that we care about.
    fDrawTarget->clear(&maskSpaceIBounds,
                       GrReducedClip::kAllIn_InitialState == initialState ? 0xffffffff : 0x00000000,
                       true,
                       texture->asRenderTarget());

    // When we use the stencil in the below loop it is important to have this clip installed.
    // The second pass that zeros the stencil buffer renders the rect maskSpaceIBounds so the first
    // pass must not set values outside of this bounds or stencil values outside the rect won't be
    // cleared.
    GrClip clip(maskSpaceIBounds);
    SkAutoTUnref<GrTexture> temp;

    // walk through each clip element and perform its set op
    for (GrReducedClip::ElementList::Iter iter = elements.headIter(); iter.get(); iter.next()) {
        const Element* element = iter.get();
        SkRegion::Op op = element->getOp();
        bool invert = element->isInverseFilled();
        if (invert || SkRegion::kIntersect_Op == op || SkRegion::kReverseDifference_Op == op) {
            GrPipelineBuilder pipelineBuilder;

            pipelineBuilder.setClip(clip);
            GrPathRenderer* pr = nullptr;
            bool useTemp = !this->canStencilAndDrawElement(&pipelineBuilder, texture, &pr, element);
            GrTexture* dst;
            // This is the bounds of the clip element in the space of the alpha-mask. The temporary
            // mask buffer can be substantially larger than the actually clip stack element. We
            // touch the minimum number of pixels necessary and use decal mode to combine it with
            // the accumulator.
            SkIRect maskSpaceElementIBounds;

            if (useTemp) {
                if (invert) {
                    maskSpaceElementIBounds = maskSpaceIBounds;
                } else {
                    SkRect elementBounds = element->getBounds();
                    elementBounds.offset(clipToMaskOffset);
                    elementBounds.roundOut(&maskSpaceElementIBounds);
                }

                if (!temp) {
                    temp.reset(this->createTempMask(maskSpaceIBounds.fRight,
                                                    maskSpaceIBounds.fBottom));
                    if (!temp) {
                        texture->resourcePriv().removeUniqueKey();
                        return nullptr;
                    }
                }
                dst = temp;
                // clear the temp target and set blend to replace
                fDrawTarget->clear(&maskSpaceElementIBounds,
                                   invert ? 0xffffffff : 0x00000000,
                                   true,
                                   dst->asRenderTarget());
                set_coverage_drawing_xpf(SkRegion::kReplace_Op, invert, &pipelineBuilder);
            } else {
                // draw directly into the result with the stencil set to make the pixels affected
                // by the clip shape be non-zero.
                dst = texture;
                GR_STATIC_CONST_SAME_STENCIL(kStencilInElement,
                                             kReplace_StencilOp,
                                             kReplace_StencilOp,
                                             kAlways_StencilFunc,
                                             0xffff,
                                             0xffff,
                                             0xffff);
                pipelineBuilder.setStencil(kStencilInElement);
                set_coverage_drawing_xpf(op, invert, &pipelineBuilder);
            }

            if (!this->drawElement(&pipelineBuilder, translate, dst, element, pr)) {
                texture->resourcePriv().removeUniqueKey();
                return nullptr;
            }

            if (useTemp) {
                GrPipelineBuilder backgroundPipelineBuilder;
                backgroundPipelineBuilder.setRenderTarget(texture->asRenderTarget());

                // Now draw into the accumulator using the real operation and the temp buffer as a
                // texture
                this->mergeMask(&backgroundPipelineBuilder,
                                texture,
                                temp,
                                op,
                                maskSpaceIBounds,
                                maskSpaceElementIBounds);
            } else {
                GrPipelineBuilder backgroundPipelineBuilder;
                backgroundPipelineBuilder.setRenderTarget(texture->asRenderTarget());

                set_coverage_drawing_xpf(op, !invert, &backgroundPipelineBuilder);
                // Draw to the exterior pixels (those with a zero stencil value).
                GR_STATIC_CONST_SAME_STENCIL(kDrawOutsideElement,
                                             kZero_StencilOp,
                                             kZero_StencilOp,
                                             kEqual_StencilFunc,
                                             0xffff,
                                             0x0000,
                                             0xffff);
                backgroundPipelineBuilder.setStencil(kDrawOutsideElement);

                // The color passed in here does not matter since the coverageSetOpXP won't read it.
                fDrawTarget->drawNonAARect(backgroundPipelineBuilder, GrColor_WHITE, translate,
                                           clipSpaceIBounds);
            }
        } else {
            GrPipelineBuilder pipelineBuilder;

            // all the remaining ops can just be directly draw into the accumulation buffer
            set_coverage_drawing_xpf(op, false, &pipelineBuilder);
            // The color passed in here does not matter since the coverageSetOpXP won't read it.
            this->drawElement(&pipelineBuilder, translate, texture, element);
        }
    }

    return texture.detach();
}
Esempio n. 21
0
GrTexture* GaussianBlur(GrContext* context,
                        GrTexture* srcTexture,
                        bool canClobberSrc,
                        const SkRect& rect,
                        bool cropToRect,
                        float sigmaX,
                        float sigmaY) {
    SkASSERT(NULL != context);

    GrContext::AutoRenderTarget art(context);

    GrContext::AutoMatrix am;
    am.setIdentity(context);

    SkIRect clearRect;
    int scaleFactorX, radiusX;
    int scaleFactorY, radiusY;
    sigmaX = adjust_sigma(sigmaX, &scaleFactorX, &radiusX);
    sigmaY = adjust_sigma(sigmaY, &scaleFactorY, &radiusY);

    SkRect srcRect(rect);
    scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
    srcRect.roundOut();
    scale_rect(&srcRect, static_cast<float>(scaleFactorX),
                         static_cast<float>(scaleFactorY));

    GrContext::AutoClip acs(context, SkRect::MakeWH(srcRect.width(), srcRect.height()));

    SkASSERT(kBGRA_8888_GrPixelConfig == srcTexture->config() ||
             kRGBA_8888_GrPixelConfig == srcTexture->config() ||
             kAlpha_8_GrPixelConfig == srcTexture->config());

    GrTextureDesc desc;
    desc.fFlags = kRenderTarget_GrTextureFlagBit | kNoStencil_GrTextureFlagBit;
    desc.fWidth = SkScalarFloorToInt(srcRect.width());
    desc.fHeight = SkScalarFloorToInt(srcRect.height());
    desc.fConfig = srcTexture->config();

    GrAutoScratchTexture temp1, temp2;
    GrTexture* dstTexture = temp1.set(context, desc);
    GrTexture* tempTexture = canClobberSrc ? srcTexture : temp2.set(context, desc);
    if (NULL == dstTexture || NULL == tempTexture) {
        return NULL;
    }

    for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
        GrPaint paint;
        SkMatrix matrix;
        matrix.setIDiv(srcTexture->width(), srcTexture->height());
        context->setRenderTarget(dstTexture->asRenderTarget());
        SkRect dstRect(srcRect);
        if (cropToRect && i == 1) {
            dstRect.offset(-dstRect.fLeft, -dstRect.fTop);
            SkRect domain;
            matrix.mapRect(&domain, rect);
            domain.inset(i < scaleFactorX ? SK_ScalarHalf / srcTexture->width() : 0.0f,
                         i < scaleFactorY ? SK_ScalarHalf / srcTexture->height() : 0.0f);
            SkAutoTUnref<GrEffectRef> effect(GrTextureDomainEffect::Create(
                srcTexture,
                matrix,
                domain,
                GrTextureDomainEffect::kDecal_WrapMode,
                GrTextureParams::kBilerp_FilterMode));
            paint.addColorEffect(effect);
        } else {
            GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
            paint.addColorTextureEffect(srcTexture, matrix, params);
        }
        scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f,
                             i < scaleFactorY ? 0.5f : 1.0f);
        context->drawRectToRect(paint, dstRect, srcRect);
        srcRect = dstRect;
        srcTexture = dstTexture;
        SkTSwap(dstTexture, tempTexture);
    }

    SkIRect srcIRect;
    srcRect.roundOut(&srcIRect);

    if (sigmaX > 0.0f) {
        if (scaleFactorX > 1) {
            // Clear out a radius to the right of the srcRect to prevent the
            // X convolution from reading garbage.
            clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop,
                                          radiusX, srcIRect.height());
            context->clear(&clearRect, 0x0, false);
        }
        context->setRenderTarget(dstTexture->asRenderTarget());
        SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
        convolve_gaussian(context, srcRect, dstRect, srcTexture,
                          Gr1DKernelEffect::kX_Direction, radiusX, sigmaX, cropToRect);
        srcTexture = dstTexture;
        srcRect = dstRect;
        SkTSwap(dstTexture, tempTexture);
    }

    if (sigmaY > 0.0f) {
        if (scaleFactorY > 1 || sigmaX > 0.0f) {
            // Clear out a radius below the srcRect to prevent the Y
            // convolution from reading garbage.
            clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom,
                                          srcIRect.width(), radiusY);
            context->clear(&clearRect, 0x0, false);
        }

        context->setRenderTarget(dstTexture->asRenderTarget());
        SkRect dstRect = SkRect::MakeWH(srcRect.width(), srcRect.height());
        convolve_gaussian(context, srcRect, dstRect, srcTexture,
                          Gr1DKernelEffect::kY_Direction, radiusY, sigmaY, cropToRect);
        srcTexture = dstTexture;
        srcRect = dstRect;
        SkTSwap(dstTexture, tempTexture);
    }

    if (scaleFactorX > 1 || scaleFactorY > 1) {
        // Clear one pixel to the right and below, to accommodate bilinear
        // upsampling.
        clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom,
                                      srcIRect.width() + 1, 1);
        context->clear(&clearRect, 0x0, false);
        clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop,
                                      1, srcIRect.height());
        context->clear(&clearRect, 0x0, false);
        SkMatrix matrix;
        matrix.setIDiv(srcTexture->width(), srcTexture->height());
        context->setRenderTarget(dstTexture->asRenderTarget());

        GrPaint paint;
        // FIXME:  this should be mitchell, not bilinear.
        GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
        paint.addColorTextureEffect(srcTexture, matrix, params);

        SkRect dstRect(srcRect);
        scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY);
        context->drawRectToRect(paint, dstRect, srcRect);
        srcRect = dstRect;
        srcTexture = dstTexture;
        SkTSwap(dstTexture, tempTexture);
    }
    if (srcTexture == temp1.texture()) {
        return temp1.detach();
    } else if (srcTexture == temp2.texture()) {
        return temp2.detach();
    } else {
        srcTexture->ref();
        return srcTexture;
    }
}
Esempio n. 22
0
static GrTexture* load_yuv_texture(GrContext* ctx, const GrUniqueKey& optionalKey,
                                   const SkBitmap& bm, const GrSurfaceDesc& desc) {
    // Subsets are not supported, the whole pixelRef is loaded when using YUV decoding
    SkPixelRef* pixelRef = bm.pixelRef();
    if ((NULL == pixelRef) || 
        (pixelRef->info().width()  != bm.info().width()) ||
        (pixelRef->info().height() != bm.info().height())) {
        return NULL;
    }

    const bool useCache = optionalKey.isValid();
    SkYUVPlanesCache::Info yuvInfo;
    SkAutoTUnref<SkCachedData> cachedData;
    SkAutoMalloc storage;
    if (useCache) {
        cachedData.reset(SkYUVPlanesCache::FindAndRef(pixelRef->getGenerationID(), &yuvInfo));
    }

    void* planes[3];
    if (cachedData.get()) {
        planes[0] = (void*)cachedData->data();
        planes[1] = (uint8_t*)planes[0] + yuvInfo.fSizeInMemory[0];
        planes[2] = (uint8_t*)planes[1] + yuvInfo.fSizeInMemory[1];
    } else {
        // Fetch yuv plane sizes for memory allocation. Here, width and height can be
        // rounded up to JPEG block size and be larger than the image's width and height.
        if (!pixelRef->getYUV8Planes(yuvInfo.fSize, NULL, NULL, NULL)) {
            return NULL;
        }

        // Allocate the memory for YUV
        size_t totalSize(0);
        for (int i = 0; i < 3; ++i) {
            yuvInfo.fRowBytes[i] = yuvInfo.fSize[i].fWidth;
            yuvInfo.fSizeInMemory[i] = yuvInfo.fRowBytes[i] * yuvInfo.fSize[i].fHeight;
            totalSize += yuvInfo.fSizeInMemory[i];
        }
        if (useCache) {
            cachedData.reset(SkResourceCache::NewCachedData(totalSize));
            planes[0] = cachedData->writable_data();
        } else {
            storage.reset(totalSize);
            planes[0] = storage.get();
        }
        planes[1] = (uint8_t*)planes[0] + yuvInfo.fSizeInMemory[0];
        planes[2] = (uint8_t*)planes[1] + yuvInfo.fSizeInMemory[1];

        // Get the YUV planes and update plane sizes to actual image size
        if (!pixelRef->getYUV8Planes(yuvInfo.fSize, planes, yuvInfo.fRowBytes,
                                     &yuvInfo.fColorSpace)) {
            return NULL;
        }

        if (useCache) {
            // Decoding is done, cache the resulting YUV planes
            SkYUVPlanesCache::Add(pixelRef->getGenerationID(), cachedData, &yuvInfo);
        }
    }

    GrSurfaceDesc yuvDesc;
    yuvDesc.fConfig = kAlpha_8_GrPixelConfig;
    SkAutoTUnref<GrTexture> yuvTextures[3];
    for (int i = 0; i < 3; ++i) {
        yuvDesc.fWidth  = yuvInfo.fSize[i].fWidth;
        yuvDesc.fHeight = yuvInfo.fSize[i].fHeight;
        bool needsExactTexture =
            (yuvDesc.fWidth  != yuvInfo.fSize[0].fWidth) ||
            (yuvDesc.fHeight != yuvInfo.fSize[0].fHeight);
        if (needsExactTexture) {
            yuvTextures[i].reset(ctx->textureProvider()->createTexture(yuvDesc, true));
        } else {
            yuvTextures[i].reset(ctx->textureProvider()->createApproxTexture(yuvDesc));
        }
        if (!yuvTextures[i] ||
            !yuvTextures[i]->writePixels(0, 0, yuvDesc.fWidth, yuvDesc.fHeight,
                                         yuvDesc.fConfig, planes[i], yuvInfo.fRowBytes[i])) {
            return NULL;
        }
    }

    GrSurfaceDesc rtDesc = desc;
    rtDesc.fFlags = rtDesc.fFlags | kRenderTarget_GrSurfaceFlag;

    GrTexture* result = create_texture_for_bmp(ctx, optionalKey, rtDesc, pixelRef, NULL, 0);
    if (!result) {
        return NULL;
    }

    GrRenderTarget* renderTarget = result->asRenderTarget();
    SkASSERT(renderTarget);

    GrPaint paint;
    SkAutoTUnref<GrFragmentProcessor>
        yuvToRgbProcessor(GrYUVtoRGBEffect::Create(paint.getProcessorDataManager(), yuvTextures[0],
                                                   yuvTextures[1], yuvTextures[2],
                                                   yuvInfo.fSize, yuvInfo.fColorSpace));
    paint.addColorProcessor(yuvToRgbProcessor);
    SkRect r = SkRect::MakeWH(SkIntToScalar(yuvInfo.fSize[0].fWidth),
                              SkIntToScalar(yuvInfo.fSize[0].fHeight));

    GrDrawContext* drawContext = ctx->drawContext();
    if (!drawContext) {
        return NULL;
    }

    drawContext->drawRect(renderTarget, GrClip::WideOpen(), paint, SkMatrix::I(), r);

    return result;
}
Esempio n. 23
0
bool GrDrawTarget::checkDraw(const GrPipelineBuilder& pipelineBuilder,
                             const GrGeometryProcessor* gp,
                             GrPrimitiveType type,
                             int startVertex,
                             int startIndex,
                             int vertexCount,
                             int indexCount) const {
#ifdef SK_DEBUG
    const GeometrySrcState& geoSrc = fGeoSrcStateStack.back();
    int maxVertex = startVertex + vertexCount;
    int maxValidVertex;
    switch (geoSrc.fVertexSrc) {
        case kNone_GeometrySrcType:
            SkFAIL("Attempting to draw without vertex src.");
        case kReserved_GeometrySrcType: // fallthrough
            maxValidVertex = geoSrc.fVertexCount;
            break;
        case kBuffer_GeometrySrcType:
            maxValidVertex = static_cast<int>(geoSrc.fVertexBuffer->gpuMemorySize() /
                                              geoSrc.fVertexSize);
            break;
    }
    if (maxVertex > maxValidVertex) {
        SkFAIL("Drawing outside valid vertex range.");
    }
    if (indexCount > 0) {
        int maxIndex = startIndex + indexCount;
        int maxValidIndex;
        switch (geoSrc.fIndexSrc) {
            case kNone_GeometrySrcType:
                SkFAIL("Attempting to draw indexed geom without index src.");
            case kReserved_GeometrySrcType: // fallthrough
                maxValidIndex = geoSrc.fIndexCount;
                break;
            case kBuffer_GeometrySrcType:
                maxValidIndex = static_cast<int>(geoSrc.fIndexBuffer->gpuMemorySize() /
                                                 sizeof(uint16_t));
                break;
        }
        if (maxIndex > maxValidIndex) {
            SkFAIL("Index reads outside valid index range.");
        }
    }

    SkASSERT(pipelineBuilder.getRenderTarget());

    if (gp) {
        int numTextures = gp->numTextures();
        for (int t = 0; t < numTextures; ++t) {
            GrTexture* texture = gp->texture(t);
            SkASSERT(texture->asRenderTarget() != pipelineBuilder.getRenderTarget());
        }
    }

    for (int s = 0; s < pipelineBuilder.numColorFragmentStages(); ++s) {
        const GrProcessor* effect = pipelineBuilder.getColorFragmentStage(s).processor();
        int numTextures = effect->numTextures();
        for (int t = 0; t < numTextures; ++t) {
            GrTexture* texture = effect->texture(t);
            SkASSERT(texture->asRenderTarget() != pipelineBuilder.getRenderTarget());
        }
    }
    for (int s = 0; s < pipelineBuilder.numCoverageFragmentStages(); ++s) {
        const GrProcessor* effect = pipelineBuilder.getCoverageFragmentStage(s).processor();
        int numTextures = effect->numTextures();
        for (int t = 0; t < numTextures; ++t) {
            GrTexture* texture = effect->texture(t);
            SkASSERT(texture->asRenderTarget() != pipelineBuilder.getRenderTarget());
        }
    }

#endif
    if (NULL == pipelineBuilder.getRenderTarget()) {
        return false;
    }
    return true;
}
////////////////////////////////////////////////////////////////////////////////
// Create a 8-bit clip mask in alpha
bool GrClipMaskManager::createAlphaClipMask(const GrClipData& clipDataIn,
                                            GrTexture** result,
                                            GrIRect *devResultBounds) {
    GrAssert(NULL != devResultBounds);
    GrAssert(kNone_ClipMaskType == fCurrClipMaskType);

    if (this->clipMaskPreamble(clipDataIn, result, devResultBounds)) {
        fCurrClipMaskType = kAlpha_ClipMaskType;
        return true;
    }

    // Note: 'resultBounds' is in device (as opposed to canvas) coordinates

    GrTexture* accum = fAACache.getLastMask();
    if (NULL == accum) {
        fAACache.reset();
        return false;
    }

    GrDrawTarget::AutoStateRestore asr(fGpu, GrDrawTarget::kReset_ASRInit);
    GrDrawState* drawState = fGpu->drawState();

    GrDrawTarget::AutoGeometryPush agp(fGpu);

    if (0 != devResultBounds->fTop || 0 != devResultBounds->fLeft ||
        0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) {
        // if we were able to trim down the size of the mask we need to
        // offset the paths & rects that will be used to compute it
        drawState->viewMatrix()->setTranslate(
                SkIntToScalar(-devResultBounds->fLeft-clipDataIn.fOrigin.fX),
                SkIntToScalar(-devResultBounds->fTop-clipDataIn.fOrigin.fY));
    }

    bool clearToInside;
    SkRegion::Op firstOp = SkRegion::kReplace_Op; // suppress warning

    SkClipStack::Iter iter(*clipDataIn.fClipStack,
                           SkClipStack::Iter::kBottom_IterStart);
    const SkClipStack::Iter::Clip* clip = process_initial_clip_elements(&iter,
                                                              *devResultBounds,
                                                              &clearToInside,
                                                              &firstOp,
                                                              clipDataIn);

    fGpu->clear(NULL,
                clearToInside ? 0xffffffff : 0x00000000,
                accum->asRenderTarget());

    GrAutoScratchTexture temp;
    bool first = true;
    // walk through each clip element and perform its set op
    for ( ; NULL != clip; clip = iter.next()) {

        SkRegion::Op op = clip->fOp;
        if (first) {
            first = false;
            op = firstOp;
        }

        if (SkRegion::kReplace_Op == op) {
            // clear the accumulator and draw the new object directly into it
            fGpu->clear(NULL, 0x00000000, accum->asRenderTarget());

            setup_boolean_blendcoeffs(drawState, op);
            this->drawClipShape(accum, clip, *devResultBounds);

        } else if (SkRegion::kReverseDifference_Op == op ||
                   SkRegion::kIntersect_Op == op) {
            // there is no point in intersecting a screen filling rectangle.
            if (SkRegion::kIntersect_Op == op && NULL != clip->fRect &&
                contains(*clip->fRect, *devResultBounds, clipDataIn.fOrigin)) {
                continue;
            }

            getTemp(*devResultBounds, &temp);
            if (NULL == temp.texture()) {
                fAACache.reset();
                return false;
            }

            // clear the temp target & draw into it
            fGpu->clear(NULL, 0x00000000, temp.texture()->asRenderTarget());

            setup_boolean_blendcoeffs(drawState, SkRegion::kReplace_Op);
            this->drawClipShape(temp.texture(), clip, *devResultBounds);

            // TODO: rather than adding these two translations here
            // compute the bounding box needed to render the texture
            // into temp
            if (0 != devResultBounds->fTop || 0 != devResultBounds->fLeft ||
                0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) {
                // In order for the merge of the temp clip into the accumulator
                // to work we need to disable the translation
                drawState->viewMatrix()->reset();
            }

            // Now draw into the accumulator using the real operation
            // and the temp buffer as a texture
            setup_boolean_blendcoeffs(drawState, op);
            this->drawTexture(accum, temp.texture());

            if (0 != devResultBounds->fTop || 0 != devResultBounds->fLeft ||
                0 != clipDataIn.fOrigin.fX || 0 != clipDataIn.fOrigin.fY) {
                drawState->viewMatrix()->setTranslate(
                  SkIntToScalar(-devResultBounds->fLeft-clipDataIn.fOrigin.fX),
                  SkIntToScalar(-devResultBounds->fTop-clipDataIn.fOrigin.fY));
            }

        } else {
            // all the remaining ops can just be directly draw into
            // the accumulation buffer
            setup_boolean_blendcoeffs(drawState, op);
            this->drawClipShape(accum, clip, *devResultBounds);
        }
    }

    *result = accum;
    fCurrClipMaskType = kAlpha_ClipMaskType;
    return true;
}
Esempio n. 25
0
DEF_GPUTEST(ReadWriteAlpha, reporter, factory) {
    for (int type = 0; type < GrContextFactory::kLastGLContextType; ++type) {
        GrContextFactory::GLContextType glType = static_cast<GrContextFactory::GLContextType>(type);
        if (!GrContextFactory::IsRenderingGLContext(glType)) {
            continue;
        }
        GrContext* context = factory->get(glType);
        if (NULL == context) {
            continue;
        }

        unsigned char textureData[X_SIZE][Y_SIZE];

        memset(textureData, 0, X_SIZE * Y_SIZE);

        GrSurfaceDesc desc;

        // let Skia know we will be using this texture as a render target
        desc.fFlags     = kRenderTarget_GrSurfaceFlag;
        // it is a single channel texture
        desc.fConfig    = kAlpha_8_GrPixelConfig;
        desc.fWidth     = X_SIZE;
        desc.fHeight    = Y_SIZE;

        // We are initializing the texture with zeros here
        GrTexture* texture = context->createTexture(desc, false, textureData, 0);
        if (!texture) {
            return;
        }

        SkAutoTUnref<GrTexture> au(texture);

        // create a distinctive texture
        for (int y = 0; y < Y_SIZE; ++y) {
            for (int x = 0; x < X_SIZE; ++x) {
                textureData[x][y] = x*Y_SIZE+y;
            }
        }

        // upload the texture
        texture->writePixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
                             textureData, 0);

        unsigned char readback[X_SIZE][Y_SIZE];

        // clear readback to something non-zero so we can detect readback failures
        memset(readback, 0x1, X_SIZE * Y_SIZE);

        // read the texture back
        texture->readPixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
                            readback, 0);

        // make sure the original & read back versions match
        bool match = true;

        for (int y = 0; y < Y_SIZE; ++y) {
            for (int x = 0; x < X_SIZE; ++x) {
                if (textureData[x][y] != readback[x][y]) {
                    match = false;
                }
            }
        }

        REPORTER_ASSERT(reporter, match);

        // Now try writing on the single channel texture
        SkSurfaceProps props(SkSurfaceProps::kLegacyFontHost_InitType);
        SkAutoTUnref<SkBaseDevice> device(SkGpuDevice::Create(texture->asRenderTarget(), &props));
        SkCanvas canvas(device);

        SkPaint paint;

        const SkRect rect = SkRect::MakeLTRB(-10, -10, X_SIZE + 10, Y_SIZE + 10);

        paint.setColor(SK_ColorWHITE);

        canvas.drawRect(rect, paint);

        texture->readPixels(0, 0, desc.fWidth, desc.fHeight, desc.fConfig,
                            readback, 0);

        match = true;

        for (int y = 0; y < Y_SIZE; ++y) {
            for (int x = 0; x < X_SIZE; ++x) {
                if (0xFF != readback[x][y]) {
                    match = false;
                }
            }
        }

        REPORTER_ASSERT(reporter, match);
    }
}
Esempio n. 26
0
// creates a new texture that is the input texture scaled up. If optionalKey is valid it will be
// set on the new texture. stretch controls whether the scaling is done using nearest or bilerp
// filtering and the size to stretch the texture to.
GrTexture* stretch_texture(GrTexture* inputTexture, const Stretch& stretch,
                           SkPixelRef* pixelRef,
                           const GrUniqueKey& optionalKey) {
    SkASSERT(Stretch::kNone_Type != stretch.fType);

    GrContext* context = inputTexture->getContext();
    SkASSERT(context);
    const GrCaps* caps = context->caps();

    // Either it's a cache miss or the original wasn't cached to begin with.
    GrSurfaceDesc rtDesc = inputTexture->desc();
    rtDesc.fFlags =  rtDesc.fFlags | kRenderTarget_GrSurfaceFlag;
    rtDesc.fWidth  = stretch.fWidth;
    rtDesc.fHeight = stretch.fHeight;
    rtDesc.fConfig = GrMakePixelConfigUncompressed(rtDesc.fConfig);

    // If the config isn't renderable try converting to either A8 or an 32 bit config. Otherwise,
    // fail.
    if (!caps->isConfigRenderable(rtDesc.fConfig, false)) {
        if (GrPixelConfigIsAlphaOnly(rtDesc.fConfig)) {
            if (caps->isConfigRenderable(kAlpha_8_GrPixelConfig, false)) {
                rtDesc.fConfig = kAlpha_8_GrPixelConfig;
            } else if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) {
                rtDesc.fConfig = kSkia8888_GrPixelConfig;
            } else {
                return NULL;
            }
        } else if (kRGB_GrColorComponentFlags ==
                   (kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) {
            if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) {
                rtDesc.fConfig = kSkia8888_GrPixelConfig;
            } else {
                return NULL;
            }
        } else {
            return NULL;
        }
    }

    GrTexture* stretched = create_texture_for_bmp(context, optionalKey, rtDesc, pixelRef, NULL, 0);

    if (!stretched) {
        return NULL;
    }
    GrPaint paint;

    // If filtering is not desired then we want to ensure all texels in the resampled image are
    // copies of texels from the original.
    GrTextureParams params(SkShader::kClamp_TileMode,
                           Stretch::kBilerp_Type == stretch.fType ?
                              GrTextureParams::kBilerp_FilterMode :
                              GrTextureParams::kNone_FilterMode);
    paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params);

    SkRect rect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight));
    SkRect localRect = SkRect::MakeWH(1.f, 1.f);

    GrDrawContext* drawContext = context->drawContext();
    if (!drawContext) {
        return NULL;
    }

    drawContext->drawNonAARectToRect(stretched->asRenderTarget(), GrClip::WideOpen(), paint,
                                     SkMatrix::I(), rect, localRect);

    return stretched;
}
Esempio n. 27
0
GrTexture* GaussianBlur(GrContext* context,
                        GrTexture* srcTexture,
                        bool canClobberSrc,
                        const SkRect& dstBounds,
                        const SkRect* srcBounds,
                        float sigmaX,
                        float sigmaY,
                        GrTextureProvider::SizeConstraint constraint) {
    SkASSERT(context);
    SkIRect clearRect;
    int scaleFactorX, radiusX;
    int scaleFactorY, radiusY;
    int maxTextureSize = context->caps()->maxTextureSize();
    sigmaX = adjust_sigma(sigmaX, maxTextureSize, &scaleFactorX, &radiusX);
    sigmaY = adjust_sigma(sigmaY, maxTextureSize, &scaleFactorY, &radiusY);

    SkPoint srcOffset = SkPoint::Make(-dstBounds.x(), -dstBounds.y());
    SkRect localDstBounds = SkRect::MakeWH(dstBounds.width(), dstBounds.height());
    SkRect localSrcBounds;
    SkRect srcRect;
    if (srcBounds) {
        srcRect = localSrcBounds = *srcBounds;
        srcRect.offset(srcOffset);
        srcBounds = &localSrcBounds;
    } else {
        srcRect = localDstBounds;
    }

    scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
    srcRect.roundOut(&srcRect);
    scale_rect(&srcRect, static_cast<float>(scaleFactorX),
                         static_cast<float>(scaleFactorY));

    // setup new clip
    GrClip clip(localDstBounds);

    SkASSERT(kBGRA_8888_GrPixelConfig == srcTexture->config() ||
             kRGBA_8888_GrPixelConfig == srcTexture->config() ||
             kAlpha_8_GrPixelConfig == srcTexture->config());

    GrSurfaceDesc desc;
    desc.fFlags = kRenderTarget_GrSurfaceFlag;
    desc.fWidth = SkScalarFloorToInt(dstBounds.width());
    desc.fHeight = SkScalarFloorToInt(dstBounds.height());
    desc.fConfig = srcTexture->config();

    GrTexture* dstTexture;
    GrTexture* tempTexture;
    SkAutoTUnref<GrTexture> temp1, temp2;

    temp1.reset(context->textureProvider()->createTexture(desc, constraint));
    dstTexture = temp1.get();
    if (canClobberSrc) {
        tempTexture = srcTexture;
    } else {
        temp2.reset(context->textureProvider()->createTexture(desc, constraint));
        tempTexture = temp2.get();
    }

    if (nullptr == dstTexture || nullptr == tempTexture) {
        return nullptr;
    }

    SkAutoTUnref<GrDrawContext> srcDrawContext;

    for (int i = 1; i < scaleFactorX || i < scaleFactorY; i *= 2) {
        GrPaint paint;
        SkMatrix matrix;
        matrix.setIDiv(srcTexture->width(), srcTexture->height());
        SkRect dstRect(srcRect);
        if (srcBounds && i == 1) {
            SkRect domain;
            matrix.mapRect(&domain, *srcBounds);
            domain.inset((i < scaleFactorX) ? SK_ScalarHalf / srcTexture->width() : 0.0f,
                         (i < scaleFactorY) ? SK_ScalarHalf / srcTexture->height() : 0.0f);
            SkAutoTUnref<const GrFragmentProcessor> fp(GrTextureDomainEffect::Create(
                srcTexture,
                matrix,
                domain,
                GrTextureDomain::kDecal_Mode,
                GrTextureParams::kBilerp_FilterMode));
            paint.addColorFragmentProcessor(fp);
            srcRect.offset(-srcOffset);
            srcOffset.set(0, 0);
        } else {
            GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
            paint.addColorTextureProcessor(srcTexture, matrix, params);
        }
        paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);
        scale_rect(&dstRect, i < scaleFactorX ? 0.5f : 1.0f,
                             i < scaleFactorY ? 0.5f : 1.0f);

        SkAutoTUnref<GrDrawContext> dstDrawContext(
                                             context->drawContext(dstTexture->asRenderTarget()));
        if (!dstDrawContext) {
            return nullptr;
        }
        dstDrawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect);

        srcDrawContext.swap(dstDrawContext);
        srcRect = dstRect;
        srcTexture = dstTexture;
        SkTSwap(dstTexture, tempTexture);
        localSrcBounds = srcRect;
    }

    // For really small blurs (certainly no wider than 5x5 on desktop gpus) it is faster to just
    // launch a single non separable kernel vs two launches
    srcRect = localDstBounds;
    if (sigmaX > 0.0f && sigmaY > 0.0f &&
            (2 * radiusX + 1) * (2 * radiusY + 1) <= MAX_KERNEL_SIZE) {
        // We shouldn't be scaling because this is a small size blur
        SkASSERT((1 == scaleFactorX) && (1 == scaleFactorY));

        SkAutoTUnref<GrDrawContext> dstDrawContext(
                                             context->drawContext(dstTexture->asRenderTarget()));
        if (!dstDrawContext) {
            return nullptr;
        }
        convolve_gaussian_2d(dstDrawContext, clip, srcRect, srcOffset,
                             srcTexture, radiusX, radiusY, sigmaX, sigmaY, srcBounds);

        srcDrawContext.swap(dstDrawContext);
        srcRect.offsetTo(0, 0);
        srcTexture = dstTexture;
        SkTSwap(dstTexture, tempTexture);

    } else {
        scale_rect(&srcRect, 1.0f / scaleFactorX, 1.0f / scaleFactorY);
        srcRect.roundOut(&srcRect);
        const SkIRect srcIRect = srcRect.roundOut();
        if (sigmaX > 0.0f) {
            if (scaleFactorX > 1) {
                // TODO: if we pass in the source draw context we don't need this here
                if (!srcDrawContext) {
                    srcDrawContext.reset(context->drawContext(srcTexture->asRenderTarget()));
                    if (!srcDrawContext) {
                        return nullptr;
                    }        
                }

                // Clear out a radius to the right of the srcRect to prevent the
                // X convolution from reading garbage.
                clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop,
                                              radiusX, srcIRect.height());
                srcDrawContext->clear(&clearRect, 0x0, false);
            }

            SkAutoTUnref<GrDrawContext> dstDrawContext(
                                             context->drawContext(dstTexture->asRenderTarget()));
            if (!dstDrawContext) {
                return nullptr;
            }
            convolve_gaussian(dstDrawContext, clip, srcRect,
                              srcTexture, Gr1DKernelEffect::kX_Direction, radiusX, sigmaX,
                              srcBounds, srcOffset);
            srcDrawContext.swap(dstDrawContext);
            srcTexture = dstTexture;
            srcRect.offsetTo(0, 0);
            SkTSwap(dstTexture, tempTexture);
            localSrcBounds = srcRect;
            srcOffset.set(0, 0);
        }

        if (sigmaY > 0.0f) {
            if (scaleFactorY > 1 || sigmaX > 0.0f) {
                // TODO: if we pass in the source draw context we don't need this here
                if (!srcDrawContext) {
                    srcDrawContext.reset(context->drawContext(srcTexture->asRenderTarget()));
                    if (!srcDrawContext) {
                        return nullptr;
                    }        
                }

                // Clear out a radius below the srcRect to prevent the Y
                // convolution from reading garbage.
                clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom,
                                              srcIRect.width(), radiusY);
                srcDrawContext->clear(&clearRect, 0x0, false);
            }

            SkAutoTUnref<GrDrawContext> dstDrawContext(
                                               context->drawContext(dstTexture->asRenderTarget()));
            if (!dstDrawContext) {
                return nullptr;
            }
            convolve_gaussian(dstDrawContext, clip, srcRect,
                              srcTexture, Gr1DKernelEffect::kY_Direction, radiusY, sigmaY,
                              srcBounds, srcOffset);

            srcDrawContext.swap(dstDrawContext);
            srcTexture = dstTexture;
            srcRect.offsetTo(0, 0);
            SkTSwap(dstTexture, tempTexture);
        }
    }
    const SkIRect srcIRect = srcRect.roundOut();

    if (scaleFactorX > 1 || scaleFactorY > 1) {
        SkASSERT(srcDrawContext);

        // Clear one pixel to the right and below, to accommodate bilinear
        // upsampling.
        clearRect = SkIRect::MakeXYWH(srcIRect.fLeft, srcIRect.fBottom,
                                      srcIRect.width() + 1, 1);
        srcDrawContext->clear(&clearRect, 0x0, false);
        clearRect = SkIRect::MakeXYWH(srcIRect.fRight, srcIRect.fTop,
                                      1, srcIRect.height());
        srcDrawContext->clear(&clearRect, 0x0, false);
        SkMatrix matrix;
        matrix.setIDiv(srcTexture->width(), srcTexture->height());

        GrPaint paint;
        // FIXME:  this should be mitchell, not bilinear.
        GrTextureParams params(SkShader::kClamp_TileMode, GrTextureParams::kBilerp_FilterMode);
        paint.addColorTextureProcessor(srcTexture, matrix, params);
        paint.setPorterDuffXPFactory(SkXfermode::kSrc_Mode);

        SkRect dstRect(srcRect);
        scale_rect(&dstRect, (float) scaleFactorX, (float) scaleFactorY);

        SkAutoTUnref<GrDrawContext> dstDrawContext(
                                context->drawContext(dstTexture->asRenderTarget()));
        if (!dstDrawContext) {
            return nullptr;
        }
        dstDrawContext->fillRectToRect(clip, paint, SkMatrix::I(), dstRect, srcRect);

        srcDrawContext.swap(dstDrawContext);
        srcRect = dstRect;
        srcTexture = dstTexture;
        SkTSwap(dstTexture, tempTexture);
    }

    return SkRef(srcTexture);
}