Index: src/gpu/SkGr.cpp |
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp |
index 42922b3e2734773756ee517d8a87a9d7a463dde1..d8444968da6ea2e788b16900fc80af3e314a1243 100644 |
--- a/src/gpu/SkGr.cpp |
+++ b/src/gpu/SkGr.cpp |
@@ -5,13 +5,13 @@ |
* found in the LICENSE file. |
*/ |
+#include "GrTextureMaker.h" |
#include "SkGr.h" |
#include "GrCaps.h" |
#include "GrContext.h" |
-#include "GrTextureParamsAdjuster.h" |
-#include "GrGpuResourcePriv.h" |
+#include "GrDrawContext.h" |
#include "GrXferProcessor.h" |
#include "GrYUVProvider.h" |
@@ -48,17 +48,64 @@ |
return desc; |
} |
-void GrMakeKeyFromImageID(GrUniqueKey* key, uint32_t imageID, const SkIRect& imageBounds) { |
- SkASSERT(key); |
- SkASSERT(imageID); |
- SkASSERT(!imageBounds.isEmpty()); |
- static const GrUniqueKey::Domain kImageIDDomain = GrUniqueKey::GenerateDomain(); |
- GrUniqueKey::Builder builder(key, kImageIDDomain, 5); |
+static void get_stretch(const GrCaps& caps, int width, int height, |
+ const GrTextureParams& params, SkGrStretch* stretch) { |
+ stretch->fType = SkGrStretch::kNone_Type; |
+ bool doStretch = false; |
+ if (params.isTiled() && !caps.npotTextureTileSupport() && |
+ (!SkIsPow2(width) || !SkIsPow2(height))) { |
+ doStretch = true; |
+ stretch->fWidth = GrNextPow2(SkTMax(width, caps.minTextureSize())); |
+ stretch->fHeight = GrNextPow2(SkTMax(height, caps.minTextureSize())); |
+ } else if (width < caps.minTextureSize() || height < caps.minTextureSize()) { |
+ // The small texture issues appear to be with tiling. Hence it seems ok to scale them |
+ // up using the GPU. If issues persist we may need to CPU-stretch. |
+ doStretch = true; |
+ stretch->fWidth = SkTMax(width, caps.minTextureSize()); |
+ stretch->fHeight = SkTMax(height, caps.minTextureSize()); |
+ } |
+ if (doStretch) { |
+ switch (params.filterMode()) { |
+ case GrTextureParams::kNone_FilterMode: |
+ stretch->fType = SkGrStretch::kNearest_Type; |
+ break; |
+ case GrTextureParams::kBilerp_FilterMode: |
+ case GrTextureParams::kMipMap_FilterMode: |
+ stretch->fType = SkGrStretch::kBilerp_Type; |
+ break; |
+ } |
+ } else { |
+ stretch->fWidth = -1; |
+ stretch->fHeight = -1; |
+ stretch->fType = SkGrStretch::kNone_Type; |
+ } |
+} |
+ |
+static void make_unstretched_key(GrUniqueKey* key, uint32_t imageID, const SkIRect& subset) { |
+ SkASSERT(SkIsU16(subset.width())); |
+ SkASSERT(SkIsU16(subset.height())); |
+ |
+ static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); |
+ GrUniqueKey::Builder builder(key, kDomain, 4); |
builder[0] = imageID; |
- builder[1] = imageBounds.fLeft; |
- builder[2] = imageBounds.fTop; |
- builder[3] = imageBounds.fRight; |
- builder[4] = imageBounds.fBottom; |
+ builder[1] = subset.x(); |
+ builder[2] = subset.y(); |
+ builder[3] = subset.width() | (subset.height() << 16); |
+} |
+ |
+void GrMakeKeyFromImageID(GrUniqueKey* key, uint32_t imageID, const SkIRect& subset, |
+ const GrCaps& caps, const GrTextureParams& params) { |
+ SkGrStretch stretch; |
+ get_stretch(caps, subset.width(), subset.height(), params, &stretch); |
+ if (SkGrStretch::kNone_Type != stretch.fType) { |
+ GrUniqueKey tmpKey; |
+ make_unstretched_key(&tmpKey, imageID, subset); |
+ if (!GrMakeStretchedKey(tmpKey, stretch, key)) { |
+ *key = tmpKey; |
+ } |
+ } else { |
+ make_unstretched_key(key, imageID, subset); |
+ } |
} |
GrPixelConfig GrIsCompressedTextureDataSupported(GrContext* ctx, SkData* data, |
@@ -103,19 +150,16 @@ |
return kUnknown_GrPixelConfig; |
} |
-////////////////////////////////////////////////////////////////////////////// |
- |
-/** |
- * Fill out buffer with the compressed format Ganesh expects from a colortable |
- * based bitmap. [palette (colortable) + indices]. |
- * |
- * At the moment Ganesh only supports 8bit version. If Ganesh allowed we others |
- * we could detect that the colortable.count is <= 16, and then repack the |
- * indices as nibbles to save RAM, but it would take more time (i.e. a lot |
- * slower than memcpy), so skipping that for now. |
- * |
- * Ganesh wants a full 256 palette entry, even though Skia's ctable is only as big |
- * as the colortable.count says it is. |
+/* Fill out buffer with the compressed format Ganesh expects from a colortable |
+ based bitmap. [palette (colortable) + indices]. |
+ |
+ At the moment Ganesh only supports 8bit version. If Ganesh allowed we others |
+ we could detect that the colortable.count is <= 16, and then repack the |
+ indices as nibbles to save RAM, but it would take more time (i.e. a lot |
+ slower than memcpy), so skipping that for now. |
+ |
+ Ganesh wants a full 256 palette entry, even though Skia's ctable is only as big |
+ as the colortable.count says it is. |
*/ |
static void build_index8_data(void* buffer, const SkBitmap& bitmap) { |
SkASSERT(kIndex_8_SkColorType == bitmap.colorType()); |
@@ -163,7 +207,130 @@ |
} |
} |
-/** |
+//////////////////////////////////////////////////////////////////////////////// |
+ |
+ |
+bool GrMakeStretchedKey(const GrUniqueKey& origKey, const SkGrStretch& stretch, |
+ GrUniqueKey* stretchedKey) { |
+ if (origKey.isValid() && SkGrStretch::kNone_Type != stretch.fType) { |
+ uint32_t width = SkToU16(stretch.fWidth); |
+ uint32_t height = SkToU16(stretch.fHeight); |
+ static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); |
+ GrUniqueKey::Builder builder(stretchedKey, origKey, kDomain, 2); |
+ builder[0] = stretch.fType; |
+ builder[1] = width | (height << 16); |
+ builder.finish(); |
+ return true; |
+ } |
+ SkASSERT(!stretchedKey->isValid()); |
+ return false; |
+} |
+ |
+namespace { |
+ |
+// When the SkPixelRef genID changes, invalidate a corresponding GrResource described by key. |
+class BitmapInvalidator : public SkPixelRef::GenIDChangeListener { |
+public: |
+ explicit BitmapInvalidator(const GrUniqueKey& key) : fMsg(key) {} |
+private: |
+ GrUniqueKeyInvalidatedMessage fMsg; |
+ |
+ void onChange() override { |
+ SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg); |
+ } |
+}; |
+ |
+} // namespace |
+ |
+ |
+GrTexture* GrCreateTextureForPixels(GrContext* ctx, |
+ const GrUniqueKey& optionalKey, |
+ GrSurfaceDesc desc, |
+ SkPixelRef* pixelRefForInvalidationNotification, |
+ const void* pixels, |
+ size_t rowBytes) { |
+ GrTexture* result = ctx->textureProvider()->createTexture(desc, true, pixels, rowBytes); |
+ if (result && optionalKey.isValid()) { |
+ if (pixelRefForInvalidationNotification) { |
+ BitmapInvalidator* listener = new BitmapInvalidator(optionalKey); |
+ pixelRefForInvalidationNotification->addGenIDChangeListener(listener); |
+ } |
+ ctx->textureProvider()->assignUniqueKeyToTexture(optionalKey, result); |
+ } |
+ return result; |
+} |
+ |
+// 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 SkGrStretch& stretch, |
+ SkPixelRef* pixelRef, |
+ const GrUniqueKey& optionalKey) { |
+ SkASSERT(SkGrStretch::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 nullptr; |
+ } |
+ } else if (kRGB_GrColorComponentFlags == |
+ (kRGB_GrColorComponentFlags & GrPixelConfigComponentMask(rtDesc.fConfig))) { |
+ if (caps->isConfigRenderable(kSkia8888_GrPixelConfig, false)) { |
+ rtDesc.fConfig = kSkia8888_GrPixelConfig; |
+ } else { |
+ return nullptr; |
+ } |
+ } else { |
+ return nullptr; |
+ } |
+ } |
+ |
+ SkAutoTUnref<GrTexture> stretched(GrCreateTextureForPixels(context, optionalKey, rtDesc, |
+ pixelRef, nullptr,0)); |
+ if (!stretched) { |
+ return nullptr; |
+ } |
+ 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, |
+ SkGrStretch::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); |
+ |
+ SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(stretched->asRenderTarget())); |
+ if (!drawContext) { |
+ return nullptr; |
+ } |
+ |
+ drawContext->drawNonAARectToRect(GrClip::WideOpen(), paint, SkMatrix::I(), rect, localRect); |
+ |
+ return stretched.detach(); |
+} |
+ |
+/* |
* Once we have made SkImages handle all lazy/deferred/generated content, the YUV apis will |
* be gone from SkPixelRef, and we can remove this subclass entirely. |
*/ |
@@ -183,22 +350,33 @@ |
} |
}; |
-static GrTexture* create_texture_from_yuv(GrContext* ctx, const SkBitmap& bm, |
- const GrSurfaceDesc& desc) { |
+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 ((nullptr == pixelRef) || |
- (pixelRef->info().width() != bm.info().width()) || |
+ (pixelRef->info().width() != bm.info().width()) || |
(pixelRef->info().height() != bm.info().height())) { |
return nullptr; |
} |
+ const bool useCache = optionalKey.isValid(); |
PixelRef_GrYUVProvider provider(pixelRef); |
- |
- return provider.refAsTexture(ctx, desc, !bm.isVolatile()); |
-} |
- |
-static GrTexture* load_etc1_texture(GrContext* ctx, const SkBitmap &bm, GrSurfaceDesc desc) { |
+ GrTexture* texture = provider.refAsTexture(ctx, desc, useCache); |
+ if (!texture) { |
+ return nullptr; |
+ } |
+ |
+ if (useCache) { |
+ BitmapInvalidator* listener = new BitmapInvalidator(optionalKey); |
+ pixelRef->addGenIDChangeListener(listener); |
+ ctx->textureProvider()->assignUniqueKeyToTexture(optionalKey, texture); |
+ } |
+ return texture; |
+} |
+ |
+static GrTexture* load_etc1_texture(GrContext* ctx, const GrUniqueKey& optionalKey, |
+ const SkBitmap &bm, GrSurfaceDesc desc) { |
SkAutoTUnref<SkData> data(bm.pixelRef()->refEncodedData()); |
if (!data) { |
return nullptr; |
@@ -211,19 +389,19 @@ |
return nullptr; |
} |
- return ctx->textureProvider()->createTexture(desc, true, startOfTexData, 0); |
-} |
- |
-GrTexture* GrUploadBitmapToTexture(GrContext* ctx, const SkBitmap& bmp) { |
- SkASSERT(!bmp.getTexture()); |
- |
- if (bmp.width() < ctx->caps()->minTextureSize() || |
- bmp.height() < ctx->caps()->minTextureSize()) { |
+ return GrCreateTextureForPixels(ctx, optionalKey, desc, bm.pixelRef(), startOfTexData, 0); |
+} |
+ |
+static GrTexture* create_unstretched_bitmap_texture(GrContext* ctx, |
+ const SkBitmap& origBitmap, |
+ const GrUniqueKey& optionalKey) { |
+ if (origBitmap.width() < ctx->caps()->minTextureSize() || |
+ origBitmap.height() < ctx->caps()->minTextureSize()) { |
return nullptr; |
} |
- |
SkBitmap tmpBitmap; |
- const SkBitmap* bitmap = &bmp; |
+ |
+ const SkBitmap* bitmap = &origBitmap; |
GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bitmap->info()); |
const GrCaps* caps = ctx->caps(); |
@@ -233,14 +411,14 @@ |
size_t imageSize = GrCompressedFormatDataSize(kIndex_8_GrPixelConfig, |
bitmap->width(), bitmap->height()); |
SkAutoMalloc storage(imageSize); |
- build_index8_data(storage.get(), bmp); |
+ build_index8_data(storage.get(), origBitmap); |
// our compressed data will be trimmed, so pass width() for its |
// "rowBytes", since they are the same now. |
- return ctx->textureProvider()->createTexture(desc, true, storage.get(), |
- bitmap->width()); |
+ return GrCreateTextureForPixels(ctx, optionalKey, desc, origBitmap.pixelRef(), |
+ storage.get(), bitmap->width()); |
} else { |
- bmp.copyTo(&tmpBitmap, kN32_SkColorType); |
+ origBitmap.copyTo(&tmpBitmap, kN32_SkColorType); |
// now bitmap points to our temp, which has been promoted to 32bits |
bitmap = &tmpBitmap; |
desc.fConfig = SkImageInfo2GrPixelConfig(bitmap->info()); |
@@ -250,16 +428,13 @@ |
// compressed data on 'refEncodedData' and upload it. Probably not good, since if |
// the bitmap has available pixels, then they might not be what the decompressed |
// data is. |
- |
- // Really?? We aren't doing this with YUV. |
- |
- GrTexture *texture = load_etc1_texture(ctx, *bitmap, desc); |
+ GrTexture *texture = load_etc1_texture(ctx, optionalKey, *bitmap, desc); |
if (texture) { |
return texture; |
} |
} |
- GrTexture *texture = create_texture_from_yuv(ctx, *bitmap, desc); |
+ GrTexture *texture = load_yuv_texture(ctx, optionalKey, *bitmap, desc); |
if (texture) { |
return texture; |
} |
@@ -269,90 +444,83 @@ |
return nullptr; |
} |
- return ctx->textureProvider()->createTexture(desc, true, bitmap->getPixels(), |
- bitmap->rowBytes()); |
-} |
- |
- |
-//////////////////////////////////////////////////////////////////////////////// |
- |
-class Bitmap_GrTextureParamsAdjuster : public GrTextureParamsAdjuster { |
+ return GrCreateTextureForPixels(ctx, optionalKey, desc, origBitmap.pixelRef(), |
+ bitmap->getPixels(), bitmap->rowBytes()); |
+} |
+ |
+static SkBitmap stretch_on_cpu(const SkBitmap& bmp, const SkGrStretch& stretch) { |
+ SkBitmap stretched; |
+ stretched.allocN32Pixels(stretch.fWidth, stretch.fHeight); |
+ SkCanvas canvas(stretched); |
+ SkPaint paint; |
+ switch (stretch.fType) { |
+ case SkGrStretch::kNearest_Type: |
+ paint.setFilterQuality(kNone_SkFilterQuality); |
+ break; |
+ case SkGrStretch::kBilerp_Type: |
+ paint.setFilterQuality(kLow_SkFilterQuality); |
+ break; |
+ case SkGrStretch::kNone_Type: |
+ SkDEBUGFAIL("Shouldn't get here."); |
+ break; |
+ } |
+ SkRect dstRect = SkRect::MakeWH(SkIntToScalar(stretch.fWidth), SkIntToScalar(stretch.fHeight)); |
+ canvas.drawBitmapRect(bmp, dstRect, &paint); |
+ return stretched; |
+} |
+ |
+class Bitmap_GrTextureMaker : public GrTextureMaker { |
public: |
- Bitmap_GrTextureParamsAdjuster(const SkBitmap& bitmap) |
+ Bitmap_GrTextureMaker(const SkBitmap& bitmap) |
: INHERITED(bitmap.width(), bitmap.height()) |
, fBitmap(bitmap) |
- { |
- if (!bitmap.isVolatile()) { |
- SkIPoint origin = bitmap.pixelRefOrigin(); |
- SkIRect subset = SkIRect::MakeXYWH(origin.fX, origin.fY, bitmap.width(), |
- bitmap.height()); |
- GrMakeKeyFromImageID(&fOriginalKey, bitmap.pixelRef()->getGenerationID(), subset); |
- } |
- } |
+ {} |
protected: |
- GrTexture* refOriginalTexture(GrContext* ctx) { |
+ GrTexture* onRefUnstretchedTexture(GrContext* ctx) override { |
GrTexture* tex = fBitmap.getTexture(); |
if (tex) { |
return SkRef(tex); |
} |
- if (fOriginalKey.isValid()) { |
- tex = ctx->textureProvider()->findAndRefTextureByUniqueKey(fOriginalKey); |
- if (tex) { |
- return tex; |
- } |
- } |
- |
- tex = GrUploadBitmapToTexture(ctx, fBitmap); |
- if (tex) { |
- tex->resourcePriv().setUniqueKey(fOriginalKey); |
- InstallInvalidator(fOriginalKey, fBitmap.pixelRef()); |
- } |
- return tex; |
- } |
- |
- void makeCopyKey(const CopyParams& copyParams, GrUniqueKey* copyKey) override { |
- if (fOriginalKey.isValid()) { |
- MakeCopyKeyFromOrigKey(fOriginalKey, copyParams, copyKey); |
- } |
- } |
- |
- void didCacheCopy(const GrUniqueKey& copyKey) override { |
- InstallInvalidator(copyKey, fBitmap.pixelRef()); |
- } |
- |
- bool getROBitmap(SkBitmap* bitmap) override { |
- SkASSERT(!fBitmap.getTexture()); |
+ GrUniqueKey unstretchedKey; |
+ make_unstretched_key(&unstretchedKey, fBitmap.getGenerationID(), fBitmap.getSubset()); |
+ |
+ GrTexture* result = ctx->textureProvider()->findAndRefTextureByUniqueKey(unstretchedKey); |
+ if (result) { |
+ return result; |
+ } |
+ return create_unstretched_bitmap_texture(ctx, fBitmap, unstretchedKey); |
+ } |
+ |
+ bool onMakeStretchedKey(const SkGrStretch& stretch, GrUniqueKey* stretchedKey) override { |
+ if (fBitmap.isVolatile()) { |
+ return false; |
+ } |
+ |
+ GrUniqueKey unstretchedKey; |
+ make_unstretched_key(&unstretchedKey, fBitmap.getGenerationID(), fBitmap.getSubset()); |
+ return GrMakeStretchedKey(unstretchedKey, stretch, stretchedKey); |
+ } |
+ |
+ void onNotifyStretchCached(const GrUniqueKey& stretchedKey) override { |
+ fBitmap.pixelRef()->addGenIDChangeListener(new BitmapInvalidator(stretchedKey)); |
+ } |
+ |
+ bool onGetROBitmap(SkBitmap* bitmap) override { |
*bitmap = fBitmap; |
return true; |
} |
private: |
- static void InstallInvalidator(const GrUniqueKey& key, SkPixelRef* pixelRef) { |
- class Invalidator : public SkPixelRef::GenIDChangeListener { |
- public: |
- explicit Invalidator(const GrUniqueKey& key) : fMsg(key) {} |
- private: |
- GrUniqueKeyInvalidatedMessage fMsg; |
- |
- void onChange() override { |
- SkMessageBus<GrUniqueKeyInvalidatedMessage>::Post(fMsg); |
- } |
- }; |
- Invalidator* listener = new Invalidator(key); |
- pixelRef->addGenIDChangeListener(listener); |
- } |
- |
- const SkBitmap fBitmap; |
- GrUniqueKey fOriginalKey; |
- |
- typedef GrTextureParamsAdjuster INHERITED; |
+ const SkBitmap fBitmap; |
+ |
+ typedef GrTextureMaker INHERITED; |
}; |
GrTexture* GrRefCachedBitmapTexture(GrContext* ctx, const SkBitmap& bitmap, |
const GrTextureParams& params) { |
- return Bitmap_GrTextureParamsAdjuster(bitmap).refTextureForParams(ctx, params); |
+ return Bitmap_GrTextureMaker(bitmap).refCachedTexture(ctx, params); |
} |
/////////////////////////////////////////////////////////////////////////////// |
@@ -424,6 +592,7 @@ |
return true; |
} |
+ |
//////////////////////////////////////////////////////////////////////////////////////////////// |
static inline bool blend_requires_shader(const SkXfermode::Mode mode, bool primitiveIsSrc) { |
@@ -673,3 +842,54 @@ |
} |
return textureFilterMode; |
} |
+ |
+//////////////////////////////////////////////////////////////////////////////////////////////// |
+ |
+GrTexture* GrTextureMaker::refCachedTexture(GrContext* ctx, const GrTextureParams& params) { |
+ SkGrStretch stretch; |
+ get_stretch(*ctx->caps(), this->width(), this->height(), params, &stretch); |
+ |
+ if (SkGrStretch::kNone_Type == stretch.fType) { |
+ return this->onRefUnstretchedTexture(ctx); |
+ } |
+ |
+ GrUniqueKey stretchedKey; |
+ if (this->onMakeStretchedKey(stretch, &stretchedKey)) { |
+ GrTexture* result = ctx->textureProvider()->findAndRefTextureByUniqueKey(stretchedKey); |
+ if (result) { |
+ return result; |
+ } |
+ } |
+ |
+ GrTexture* result = this->onGenerateStretchedTexture(ctx, stretch); |
+ if (!result) { |
+ return nullptr; |
+ } |
+ |
+ if (stretchedKey.isValid()) { |
+ ctx->textureProvider()->assignUniqueKeyToTexture(stretchedKey, result); |
+ this->onNotifyStretchCached(stretchedKey); |
+ } |
+ return result; |
+} |
+ |
+GrTexture* GrTextureMaker::onGenerateStretchedTexture(GrContext* ctx, const SkGrStretch& stretch) { |
+ if (this->width() < ctx->caps()->minTextureSize() || |
+ this->height() < ctx->caps()->minTextureSize()) |
+ { |
+ // we can't trust our ability to use HW to perform the stretch, so we request |
+ // a raster instead, and perform the stretch on the CPU. |
+ SkBitmap bitmap; |
+ if (!this->onGetROBitmap(&bitmap)) { |
+ return nullptr; |
+ } |
+ SkBitmap stretchedBmp = stretch_on_cpu(bitmap, stretch); |
+ return create_unstretched_bitmap_texture(ctx, stretchedBmp, GrUniqueKey()); |
+ } else { |
+ SkAutoTUnref<GrTexture> unstretched(this->onRefUnstretchedTexture(ctx)); |
+ if (!unstretched) { |
+ return nullptr; |
+ } |
+ return stretch_texture(unstretched, stretch, nullptr, GrUniqueKey()); |
+ } |
+} |