Index: src/gpu/SkGr.cpp |
diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp |
index 09c83b929a8c575b1a2b9e15116c76abc4968c5c..3b99bc80fa89f4607218b1a3b5e92e4babd45be0 100644 |
--- a/src/gpu/SkGr.cpp |
+++ b/src/gpu/SkGr.cpp |
@@ -5,9 +5,12 @@ |
* found in the LICENSE file. |
*/ |
+#include "GrTextureMaker.h" |
+ |
#include "SkGr.h" |
#include "GrCaps.h" |
+#include "GrContext.h" |
#include "GrDrawContext.h" |
#include "GrXferProcessor.h" |
#include "GrYUVProvider.h" |
@@ -19,13 +22,19 @@ |
#include "SkErrorInternals.h" |
#include "SkGrPixelRef.h" |
#include "SkMessageBus.h" |
+#include "SkMath.h" |
+#include "SkMipMap.h" |
+#include "SkMipMapLevel.h" |
#include "SkPixelRef.h" |
#include "SkResourceCache.h" |
#include "SkTextureCompressor.h" |
+#include "SkTypes.h" |
#include "SkYUVPlanesCache.h" |
#include "effects/GrBicubicEffect.h" |
+#include "effects/GrConstColorProcessor.h" |
#include "effects/GrDitherEffect.h" |
#include "effects/GrPorterDuffXferProcessor.h" |
+#include "effects/GrXfermodeFragmentProcessor.h" |
#include "effects/GrYUVtoRGBEffect.h" |
#ifndef SK_IGNORE_ETC1_SUPPORT |
@@ -92,56 +101,42 @@ static void build_index8_data(void* buffer, const SkBitmap& bitmap) { |
//////////////////////////////////////////////////////////////////////////////// |
-struct Stretch { |
- enum Type { |
- kNone_Type, |
- kBilerp_Type, |
- kNearest_Type |
- } fType; |
- int fWidth; |
- int fHeight; |
-}; |
- |
-static void get_stretch(const GrContext* ctx, int width, int height, |
- const GrTextureParams* params, Stretch* stretch) { |
- stretch->fType = Stretch::kNone_Type; |
+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 && params->isTiled() && !ctx->caps()->npotTextureTileSupport() && |
+ if (params.isTiled() && !caps.npotTextureTileSupport() && |
(!SkIsPow2(width) || !SkIsPow2(height))) { |
doStretch = true; |
- stretch->fWidth = GrNextPow2(SkTMax(width, ctx->caps()->minTextureSize())); |
- stretch->fHeight = GrNextPow2(SkTMax(height, ctx->caps()->minTextureSize())); |
- } else if (width < ctx->caps()->minTextureSize() || height < ctx->caps()->minTextureSize()) { |
+ 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, ctx->caps()->minTextureSize()); |
- stretch->fHeight = SkTMax(height, ctx->caps()->minTextureSize()); |
+ stretch->fWidth = SkTMax(width, caps.minTextureSize()); |
+ stretch->fHeight = SkTMax(height, caps.minTextureSize()); |
} |
if (doStretch) { |
- if (params) { |
- switch(params->filterMode()) { |
- case GrTextureParams::kNone_FilterMode: |
- stretch->fType = Stretch::kNearest_Type; |
- break; |
- case GrTextureParams::kBilerp_FilterMode: |
- case GrTextureParams::kMipMap_FilterMode: |
- stretch->fType = Stretch::kBilerp_Type; |
- break; |
- } |
- } else { |
- stretch->fType = Stretch::kBilerp_Type; |
+ 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 = Stretch::kNone_Type; |
+ stretch->fType = SkGrStretch::kNone_Type; |
} |
} |
-static bool make_stretched_key(const GrUniqueKey& origKey, const Stretch& stretch, |
- GrUniqueKey* stretchedKey) { |
- if (origKey.isValid() && Stretch::kNone_Type != stretch.fType) { |
+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(); |
@@ -155,53 +150,39 @@ static bool make_stretched_key(const GrUniqueKey& origKey, const Stretch& stretc |
return false; |
} |
-static void make_unstretched_key(GrUniqueKey* key, uint32_t imageID, const SkIRect& subset) { |
+static void make_unstretched_key(GrUniqueKey* key, uint32_t imageID, const SkIRect& subset, bool isMipMapped = false) { |
SkASSERT(SkIsU16(subset.width())); |
SkASSERT(SkIsU16(subset.height())); |
static const GrUniqueKey::Domain kDomain = GrUniqueKey::GenerateDomain(); |
- GrUniqueKey::Builder builder(key, kDomain, 4); |
+ GrUniqueKey::Builder builder(key, kDomain, 5); |
builder[0] = imageID; |
builder[1] = subset.x(); |
builder[2] = subset.y(); |
builder[3] = subset.width() | (subset.height() << 16); |
+ builder[4] = isMipMapped; |
} |
void GrMakeKeyFromImageID(GrUniqueKey* key, uint32_t imageID, const SkIRect& subset, |
- const GrCaps& caps, SkImageUsageType usage) { |
- const Stretch::Type stretches[] = { |
- Stretch::kNone_Type, // kUntiled_SkImageUsageType |
- Stretch::kNearest_Type, // kTiled_Unfiltered_SkImageUsageType |
- Stretch::kBilerp_Type, // kTiled_Filtered_SkImageUsageType |
- }; |
- |
- const bool isPow2 = SkIsPow2(subset.width()) && SkIsPow2(subset.height()); |
- const bool needToStretch = !isPow2 && |
- usage != kUntiled_SkImageUsageType && |
- !caps.npotTextureTileSupport(); |
- |
- if (needToStretch) { |
+ 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); |
- |
- Stretch stretch; |
- stretch.fType = stretches[usage]; |
- stretch.fWidth = SkNextPow2(subset.width()); |
- stretch.fHeight = SkNextPow2(subset.height()); |
- if (!make_stretched_key(tmpKey, stretch, key)) { |
- goto UNSTRETCHED; |
+ if (!GrMakeStretchedKey(tmpKey, stretch, key)) { |
+ *key = tmpKey; |
} |
} else { |
- UNSTRETCHED: |
make_unstretched_key(key, imageID, subset); |
} |
} |
-static void make_image_keys(uint32_t imageID, const SkIRect& subset, const Stretch& stretch, |
+static void make_image_keys(uint32_t imageID, const SkIRect& subset, const SkGrStretch& stretch, |
GrUniqueKey* key, GrUniqueKey* stretchedKey) { |
make_unstretched_key(key, imageID, subset); |
- if (Stretch::kNone_Type != stretch.fType) { |
- make_stretched_key(*key, stretch, stretchedKey); |
+ if (SkGrStretch::kNone_Type != stretch.fType) { |
+ GrMakeStretchedKey(*key, stretch, stretchedKey); |
} |
} |
@@ -231,6 +212,18 @@ private: |
} // namespace |
+static void add_key_and_invalidation_listener(GrContext* ctx, |
+ const GrUniqueKey& optionalKey, |
+ SkPixelRef* pixelRefForInvalidationNotification, |
+ GrTexture& result) { |
+ if (optionalKey.isValid()) { |
+ if (pixelRefForInvalidationNotification) { |
+ BitmapInvalidator* listener = new BitmapInvalidator(optionalKey); |
+ pixelRefForInvalidationNotification->addGenIDChangeListener(listener); |
+ } |
+ ctx->textureProvider()->assignUniqueKeyToTexture(optionalKey, &result); |
+ } |
+} |
GrTexture* GrCreateTextureForPixels(GrContext* ctx, |
const GrUniqueKey& optionalKey, |
@@ -239,23 +232,33 @@ GrTexture* GrCreateTextureForPixels(GrContext* ctx, |
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); |
+ if (result) { |
+ add_key_and_invalidation_listener(ctx, optionalKey, pixelRefForInvalidationNotification, |
+ *result); |
} |
return result; |
} |
+GrTexture* GrCreateTextureForPixels(GrContext* ctx, |
+ const GrUniqueKey& optionalKey, |
+ GrSurfaceDesc desc, |
+ SkPixelRef* pixelRefForInvalidationNotification, |
+ const SkTArray<SkMipMapLevel>& texels) { |
+ GrTexture* result = ctx->textureProvider()->createTexture(desc, true, texels); |
+ if (result) { |
+ add_key_and_invalidation_listener(ctx, optionalKey, pixelRefForInvalidationNotification, |
+ *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 Stretch& stretch, |
+GrTexture* stretch_texture(GrTexture* inputTexture, const SkGrStretch& stretch, |
SkPixelRef* pixelRef, |
const GrUniqueKey& optionalKey) { |
- SkASSERT(Stretch::kNone_Type != stretch.fType); |
+ SkASSERT(SkGrStretch::kNone_Type != stretch.fType); |
GrContext* context = inputTexture->getContext(); |
SkASSERT(context); |
@@ -301,7 +304,7 @@ GrTexture* stretch_texture(GrTexture* inputTexture, const Stretch& stretch, |
// 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 ? |
+ SkGrStretch::kBilerp_Type == stretch.fType ? |
GrTextureParams::kBilerp_FilterMode : |
GrTextureParams::kNone_FilterMode); |
paint.addColorTextureProcessor(inputTexture, SkMatrix::I(), params); |
@@ -309,13 +312,12 @@ GrTexture* stretch_texture(GrTexture* inputTexture, const Stretch& stretch, |
SkRect rect = SkRect::MakeWH(SkIntToScalar(rtDesc.fWidth), SkIntToScalar(rtDesc.fHeight)); |
SkRect localRect = SkRect::MakeWH(1.f, 1.f); |
- SkAutoTUnref<GrDrawContext> drawContext(context->drawContext()); |
+ SkAutoTUnref<GrDrawContext> drawContext(context->drawContext(stretched->asRenderTarget())); |
if (!drawContext) { |
return nullptr; |
} |
- drawContext->drawNonAARectToRect(stretched->asRenderTarget(), GrClip::WideOpen(), paint, |
- SkMatrix::I(), rect, localRect); |
+ drawContext->drawNonAARectToRect(GrClip::WideOpen(), paint, SkMatrix::I(), rect, localRect); |
return stretched.detach(); |
} |
@@ -421,7 +423,7 @@ static GrTexture* load_yuv_texture(GrContext* ctx, const GrUniqueKey& optionalKe |
pixelRef->addGenIDChangeListener(listener); |
ctx->textureProvider()->assignUniqueKeyToTexture(optionalKey, texture); |
} |
- return texture; |
+ return texture; |
} |
static GrTexture* create_unstretched_bitmap_texture(GrContext* ctx, |
@@ -480,19 +482,19 @@ static GrTexture* create_unstretched_bitmap_texture(GrContext* ctx, |
bitmap->getPixels(), bitmap->rowBytes()); |
} |
-static SkBitmap stretch_on_cpu(const SkBitmap& bmp, const Stretch& stretch) { |
+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 Stretch::kNearest_Type: |
+ case SkGrStretch::kNearest_Type: |
paint.setFilterQuality(kNone_SkFilterQuality); |
break; |
- case Stretch::kBilerp_Type: |
+ case SkGrStretch::kBilerp_Type: |
paint.setFilterQuality(kLow_SkFilterQuality); |
break; |
- case Stretch::kNone_Type: |
+ case SkGrStretch::kNone_Type: |
SkDEBUGFAIL("Shouldn't get here."); |
break; |
} |
@@ -501,39 +503,14 @@ static SkBitmap stretch_on_cpu(const SkBitmap& bmp, const Stretch& stretch) { |
return stretched; |
} |
-static GrTexture* create_bitmap_texture(GrContext* ctx, |
- const SkBitmap& bmp, |
- const Stretch& stretch, |
- const GrUniqueKey& unstretchedKey, |
- const GrUniqueKey& stretchedKey) { |
- if (Stretch::kNone_Type != stretch.fType) { |
- SkAutoTUnref<GrTexture> unstretched; |
- // Check if we have the unstretched version in the cache, if not create it. |
- if (unstretchedKey.isValid()) { |
- unstretched.reset(ctx->textureProvider()->findAndRefTextureByUniqueKey(unstretchedKey)); |
- } |
- if (!unstretched) { |
- unstretched.reset(create_unstretched_bitmap_texture(ctx, bmp, unstretchedKey)); |
- if (!unstretched) { |
- // We might not have been able to create a unstrecthed texture because it is smaller |
- // than the min texture size. In that case do cpu stretching. |
- SkBitmap stretchedBmp = stretch_on_cpu(bmp, stretch); |
- return create_unstretched_bitmap_texture(ctx, stretchedBmp, stretchedKey); |
- } |
- } |
- return stretch_texture(unstretched, stretch, bmp.pixelRef(), stretchedKey); |
- } |
- return create_unstretched_bitmap_texture(ctx, bmp, unstretchedKey); |
-} |
- |
bool GrIsImageInCache(const GrContext* ctx, uint32_t imageID, const SkIRect& subset, |
- GrTexture* nativeTexture, const GrTextureParams* params) { |
- Stretch stretch; |
- get_stretch(ctx, subset.width(), subset.height(), params, &stretch); |
+ GrTexture* nativeTexture, const GrTextureParams& params) { |
+ SkGrStretch stretch; |
+ get_stretch(*ctx->caps(), subset.width(), subset.height(), params, &stretch); |
// Handle the case where the bitmap/image is explicitly texture backed. |
if (nativeTexture) { |
- if (Stretch::kNone_Type == stretch.fType) { |
+ if (SkGrStretch::kNone_Type == stretch.fType) { |
return true; |
} |
const GrUniqueKey& key = nativeTexture->getUniqueKey(); |
@@ -541,100 +518,168 @@ bool GrIsImageInCache(const GrContext* ctx, uint32_t imageID, const SkIRect& sub |
return false; |
} |
GrUniqueKey stretchedKey; |
- make_stretched_key(key, stretch, &stretchedKey); |
+ GrMakeStretchedKey(key, stretch, &stretchedKey); |
return ctx->textureProvider()->existsTextureWithUniqueKey(stretchedKey); |
} |
GrUniqueKey key, stretchedKey; |
make_image_keys(imageID, subset, stretch, &key, &stretchedKey); |
return ctx->textureProvider()->existsTextureWithUniqueKey( |
- (Stretch::kNone_Type == stretch.fType) ? key : stretchedKey); |
+ (SkGrStretch::kNone_Type == stretch.fType) ? key : stretchedKey); |
} |
-bool GrIsBitmapInCache(const GrContext* ctx, const SkBitmap& bitmap, |
- const GrTextureParams* params) { |
- if (bitmap.isVolatile()) { |
- return false; // we don't cache volatile bitmaps. |
+static GrTexture* generate_mipmaps(const SkBitmap& bitmap, |
+ GrContext* ctx) { |
+ // Make sure the value of the int fits inside a uint32_t |
+ if (bitmap.width() <= 0 && bitmap.height() <= 0) { |
+ return nullptr; |
} |
- return GrIsImageInCache(ctx, bitmap.getGenerationID(), bitmap.getSubset(), bitmap.getTexture(), |
- params); |
-} |
+ SkASSERT(sizeof(int) <= sizeof(uint32_t)); |
+ const uint32_t baseWidth = static_cast<uint32_t>(bitmap.width()); |
+ const uint32_t baseHeight = static_cast<uint32_t>(bitmap.height()); |
+ |
+ // OpenGL's spec requires that each mipmap level has height/width equal to |
+ // max(1, floor(original_height / 2^i) |
+ // (or original_height) where i is the mipmap level. |
+ // Keep scaling down until both axes are size 1. |
+ |
+ const uint32_t largestAxis = SkTMax(baseWidth, baseHeight); |
+ const int leadingZeros = SkCLZ(largestAxis); |
+ // If the value 00011010 has 3 leading 0s, it has 5 significant bits |
+ // (the bits which are not leading zeros) |
+ const int significantBits = (sizeof(uint32_t) * 8) - leadingZeros; |
+ if (significantBits < 0) |
+ { |
+ return nullptr; |
+ } |
+ const uint32_t unsignedSignificantBits = static_cast<uint32_t>(significantBits); |
+ const uint32_t mipLevelCount = unsignedSignificantBits; |
+ |
+ GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bitmap.info()); |
+ const bool isMipMapped = mipLevelCount > 1; |
+ desc.fIsMipMapped = isMipMapped; |
+ |
+ /* |
+ SkAutoPixmapUnlock srcUnlocker; |
+ if (!bitmap.requestLock(&srcUnlocker)) { |
+ return nullptr; |
+ } |
+ const SkPixmap& srcPixmap = srcUnlocker.pixmap(); |
+ if (nullptr == srcPixmap.addr()) { |
+ return nullptr; |
+ } |
+ */ |
-GrTexture* GrRefCachedBitmapTexture(GrContext* ctx, |
- const SkBitmap& bitmap, |
- const GrTextureParams* params) { |
+ SkTArray<SkMipMapLevel> texels(mipLevelCount); |
+ //SkMipMapLevel baseLevel(srcPixmap.addr(), bitmap.rowBytes(), baseWidth, baseHeight); |
+ SkMipMapLevel baseLevel(bitmap.getPixels(), bitmap.rowBytes(), baseWidth, baseHeight); |
+ texels.push_back(baseLevel); |
- Stretch stretch; |
- get_stretch(ctx, bitmap.width(), bitmap.height(), params, &stretch); |
+ SkTArray<SkBitmap> mipLevelBitmaps(mipLevelCount - 1); |
+ mipLevelBitmaps.push_back_n(mipLevelCount - 1); |
- GrTexture* result = bitmap.getTexture(); |
- if (result) { |
- if (Stretch::kNone_Type == stretch.fType) { |
- return SkRef(result); |
+ for (uint32_t i = 1; i < mipLevelCount; i++) { |
+ SkBitmap& currentMipBitmap = mipLevelBitmaps[i - 1]; |
+ |
+ uint32_t twoToTheMipLevel = 1 << (i + 1); |
+ uint32_t currentMipLevelWidth = SkTMax(1u, baseWidth / twoToTheMipLevel); |
+ uint32_t currentMipLevelHeight = SkTMax(1u, baseHeight / twoToTheMipLevel); |
+ |
+ SkImageInfo info = SkImageInfo::Make(currentMipLevelWidth, currentMipLevelHeight, |
+ currentMipBitmap.colorType(), |
+ bitmap.alphaType()); |
+ if (!currentMipBitmap.tryAllocPixels(info)) |
+ { |
+ return nullptr; |
} |
- GrUniqueKey stretchedKey; |
- // Don't create a key for the resized version if the bmp is volatile. |
- if (!bitmap.isVolatile()) { |
- const GrUniqueKey& key = result->getUniqueKey(); |
- if (key.isValid()) { |
- make_stretched_key(key, stretch, &stretchedKey); |
- GrTexture* stretched = |
- ctx->textureProvider()->findAndRefTextureByUniqueKey(stretchedKey); |
- if (stretched) { |
- return stretched; |
- } |
- } |
+ |
+ SkCanvas canvas(currentMipBitmap); |
+ canvas.clear(SK_ColorTRANSPARENT); |
+ canvas.drawBitmap(bitmap, SkIntToScalar(0), SkIntToScalar(0)); |
+ |
+ /* |
+ SkAutoPixmapUnlock mipUnlocker; |
+ if (!currentMipBitmap.requestLock(&mipUnlocker)) { |
+ return nullptr; |
} |
- return stretch_texture(result, stretch, bitmap.pixelRef(), stretchedKey); |
+ const SkPixmap& mipPixmap = mipUnlocker.pixmap(); |
+ if (nullptr == mipPixmap.addr()) { |
+ return nullptr; |
+ } |
+ */ |
+ |
+ SkMipMapLevel currentMipLevel(currentMipBitmap.getPixels(), |
+ //SkMipMapLevel currentMipLevel(mipPixmap.addr(), |
+ currentMipBitmap.rowBytes(), |
+ currentMipLevelWidth, currentMipLevelHeight); |
+ texels.push_back(currentMipLevel); |
} |
- GrUniqueKey key, resizedKey; |
- if (!bitmap.isVolatile()) { |
- // If the bitmap isn't changing try to find a cached copy first. |
- make_image_keys(bitmap.getGenerationID(), bitmap.getSubset(), stretch, &key, &resizedKey); |
+ GrUniqueKey unstretchedKey; |
+ make_unstretched_key(&unstretchedKey, bitmap.getGenerationID(), bitmap.getSubset(), isMipMapped); |
+ |
+ return GrCreateTextureForPixels(ctx, unstretchedKey, desc, bitmap.pixelRef(), texels); |
+} |
+ |
+ |
+class Bitmap_GrTextureMaker : public GrTextureMaker { |
+public: |
+ Bitmap_GrTextureMaker(const SkBitmap& bitmap) |
+ : INHERITED(bitmap.width(), bitmap.height()) |
+ , fBitmap(bitmap) |
+ {} |
+ |
+protected: |
+ GrTexture* onRefUnstretchedTexture(GrContext* ctx, const GrTextureParams* params) override { |
+ GrTexture* texture = fBitmap.getTexture(); |
+ |
+ if (params && params->filterMode() == GrTextureParams::kMipMap_FilterMode) { |
+ return generate_mipmaps(fBitmap, ctx); |
+ } |
+ |
+ if (texture) { |
+ return SkRef(texture); |
+ } |
+ |
+ GrUniqueKey unstretchedKey; |
+ make_unstretched_key(&unstretchedKey, fBitmap.getGenerationID(), fBitmap.getSubset()); |
- result = ctx->textureProvider()->findAndRefTextureByUniqueKey( |
- resizedKey.isValid() ? resizedKey : key); |
+ GrTexture* result = ctx->textureProvider()->findAndRefTextureByUniqueKey(unstretchedKey); |
if (result) { |
return result; |
} |
+ return create_unstretched_bitmap_texture(ctx, fBitmap, unstretchedKey); |
} |
- result = create_bitmap_texture(ctx, bitmap, stretch, key, resizedKey); |
- if (result) { |
- return result; |
- } |
+ bool onMakeStretchedKey(const SkGrStretch& stretch, GrUniqueKey* stretchedKey) override { |
+ if (fBitmap.isVolatile()) { |
+ return false; |
+ } |
- SkErrorInternals::SetError( kInternalError_SkError, |
- "---- failed to create texture for cache [%d %d]\n", |
- bitmap.width(), bitmap.height()); |
+ GrUniqueKey unstretchedKey; |
+ make_unstretched_key(&unstretchedKey, fBitmap.getGenerationID(), fBitmap.getSubset()); |
+ return GrMakeStretchedKey(unstretchedKey, stretch, stretchedKey); |
+ } |
- return nullptr; |
-} |
+ void onNotifyStretchCached(const GrUniqueKey& stretchedKey) override { |
+ fBitmap.pixelRef()->addGenIDChangeListener(new BitmapInvalidator(stretchedKey)); |
+ } |
-// TODO: make this be the canonical signature, and turn the version that takes GrTextureParams* |
-// into a wrapper that contains the inverse of these tables. |
-GrTexture* GrRefCachedBitmapTexture(GrContext* ctx, |
- const SkBitmap& bitmap, |
- SkImageUsageType usage) { |
- // Just need a params that will trigger the correct cache key / etc, since the usage doesn't |
- // tell us the specifics about filter level or specific tiling. |
+ bool onGetROBitmap(SkBitmap* bitmap) override { |
+ *bitmap = fBitmap; |
+ return true; |
+ } |
- const SkShader::TileMode tiles[] = { |
- SkShader::kClamp_TileMode, // kUntiled_SkImageUsageType |
- SkShader::kRepeat_TileMode, // kTiled_Unfiltered_SkImageUsageType |
- SkShader::kRepeat_TileMode, // kTiled_Filtered_SkImageUsageType |
- }; |
+private: |
+ const SkBitmap fBitmap; |
- const GrTextureParams::FilterMode filters[] = { |
- GrTextureParams::kNone_FilterMode, // kUntiled_SkImageUsageType |
- GrTextureParams::kNone_FilterMode, // kTiled_Unfiltered_SkImageUsageType |
- GrTextureParams::kBilerp_FilterMode, // kTiled_Filtered_SkImageUsageType |
- }; |
+ typedef GrTextureMaker INHERITED; |
+}; |
- GrTextureParams params(tiles[usage], filters[usage]); |
- return GrRefCachedBitmapTexture(ctx, bitmap, ¶ms); |
+GrTexture* GrRefCachedBitmapTexture(GrContext* ctx, const SkBitmap& bitmap, |
+ const GrTextureParams& params) { |
+ return Bitmap_GrTextureMaker(bitmap).refCachedTexture(ctx, params); |
} |
/////////////////////////////////////////////////////////////////////////////// |
@@ -706,46 +751,142 @@ bool GrPixelConfig2ColorAndProfileType(GrPixelConfig config, SkColorType* ctOut, |
return true; |
} |
-/////////////////////////////////////////////////////////////////////////////// |
-bool SkPaint2GrPaintNoShader(GrContext* context, const SkPaint& skPaint, GrColor paintColor, |
- bool constantColor, GrPaint* grPaint) { |
+//////////////////////////////////////////////////////////////////////////////////////////////// |
+static inline bool blend_requires_shader(const SkXfermode::Mode mode, bool primitiveIsSrc) { |
+ if (primitiveIsSrc) { |
+ return SkXfermode::kSrc_Mode != mode; |
+ } else { |
+ return SkXfermode::kDst_Mode != mode; |
+ } |
+} |
+ |
+static inline bool skpaint_to_grpaint_impl(GrContext* context, |
+ const SkPaint& skPaint, |
+ const SkMatrix& viewM, |
+ const GrFragmentProcessor** shaderProcessor, |
+ SkXfermode::Mode* primColorMode, |
+ bool primitiveIsSrc, |
+ GrPaint* grPaint) { |
grPaint->setAntiAlias(skPaint.isAntiAlias()); |
- SkXfermode* mode = skPaint.getXfermode(); |
- GrXPFactory* xpFactory = nullptr; |
- if (!SkXfermode::AsXPFactory(mode, &xpFactory)) { |
- // Fall back to src-over |
- // return false here? |
- xpFactory = GrPorterDuffXPFactory::Create(SkXfermode::kSrcOver_Mode); |
+ // Setup the initial color considering the shader, the SkPaint color, and the presence or not |
+ // of per-vertex colors. |
+ SkAutoTUnref<const GrFragmentProcessor> aufp; |
+ const GrFragmentProcessor* shaderFP = nullptr; |
+ if (!primColorMode || blend_requires_shader(*primColorMode, primitiveIsSrc)) { |
+ if (shaderProcessor) { |
+ shaderFP = *shaderProcessor; |
+ } else if (const SkShader* shader = skPaint.getShader()) { |
+ aufp.reset(shader->asFragmentProcessor(context, viewM, nullptr, |
+ skPaint.getFilterQuality())); |
+ shaderFP = aufp; |
+ if (!shaderFP) { |
+ return false; |
+ } |
+ } |
} |
- SkASSERT(xpFactory); |
- grPaint->setXPFactory(xpFactory)->unref(); |
- //set the color of the paint to the one of the parameter |
- grPaint->setColor(paintColor); |
+ // Set this in below cases if the output of the shader/paint-color/paint-alpha/primXfermode is |
+ // a known constant value. In that case we can simply apply a color filter during this |
+ // conversion without converting the color filter to a GrFragmentProcessor. |
+ bool applyColorFilterToPaintColor = false; |
+ if (shaderFP) { |
+ if (primColorMode) { |
+ // There is a blend between the primitive color and the shader color. The shader sees |
+ // the opaque paint color. The shader's output is blended using the provided mode by |
+ // the primitive color. The blended color is then modulated by the paint's alpha. |
+ |
+ // The geometry processor will insert the primitive color to start the color chain, so |
+ // the GrPaint color will be ignored. |
+ |
+ GrColor shaderInput = SkColorToOpaqueGrColor(skPaint.getColor()); |
+ |
+ shaderFP = GrFragmentProcessor::OverrideInput(shaderFP, shaderInput); |
+ aufp.reset(shaderFP); |
+ |
+ if (primitiveIsSrc) { |
+ shaderFP = GrXfermodeFragmentProcessor::CreateFromDstProcessor(shaderFP, |
+ *primColorMode); |
+ } else { |
+ shaderFP = GrXfermodeFragmentProcessor::CreateFromSrcProcessor(shaderFP, |
+ *primColorMode); |
+ } |
+ aufp.reset(shaderFP); |
+ // The above may return null if compose results in a pass through of the prim color. |
+ if (shaderFP) { |
+ grPaint->addColorFragmentProcessor(shaderFP); |
+ } |
+ |
+ GrColor paintAlpha = SkColorAlphaToGrColor(skPaint.getColor()); |
+ if (GrColor_WHITE != paintAlpha) { |
+ grPaint->addColorFragmentProcessor(GrConstColorProcessor::Create( |
+ paintAlpha, GrConstColorProcessor::kModulateRGBA_InputMode))->unref(); |
+ } |
+ } else { |
+ // The shader's FP sees the paint unpremul color |
+ grPaint->setColor(SkColorToUnpremulGrColor(skPaint.getColor())); |
+ grPaint->addColorFragmentProcessor(shaderFP); |
+ } |
+ } else { |
+ if (primColorMode) { |
+ // There is a blend between the primitive color and the paint color. The blend considers |
+ // the opaque paint color. The paint's alpha is applied to the post-blended color. |
+ SkAutoTUnref<const GrFragmentProcessor> processor( |
+ GrConstColorProcessor::Create(SkColorToOpaqueGrColor(skPaint.getColor()), |
+ GrConstColorProcessor::kIgnore_InputMode)); |
+ if (primitiveIsSrc) { |
+ processor.reset(GrXfermodeFragmentProcessor::CreateFromDstProcessor(processor, |
+ *primColorMode)); |
+ } else { |
+ processor.reset(GrXfermodeFragmentProcessor::CreateFromSrcProcessor(processor, |
+ *primColorMode)); |
+ |
+ } |
+ if (processor) { |
+ grPaint->addColorFragmentProcessor(processor); |
+ } |
+ |
+ grPaint->setColor(SkColorToOpaqueGrColor(skPaint.getColor())); |
+ |
+ GrColor paintAlpha = SkColorAlphaToGrColor(skPaint.getColor()); |
+ if (GrColor_WHITE != paintAlpha) { |
+ grPaint->addColorFragmentProcessor(GrConstColorProcessor::Create( |
+ paintAlpha, GrConstColorProcessor::kModulateRGBA_InputMode))->unref(); |
+ } |
+ } else { |
+ // No shader, no primitive color. |
+ grPaint->setColor(SkColorToPremulGrColor(skPaint.getColor())); |
+ applyColorFilterToPaintColor = true; |
+ } |
+ } |
SkColorFilter* colorFilter = skPaint.getColorFilter(); |
if (colorFilter) { |
- // if the source color is a constant then apply the filter here once rather than per pixel |
- // in a shader. |
- if (constantColor) { |
- SkColor filtered = colorFilter->filterColor(skPaint.getColor()); |
- grPaint->setColor(SkColor2GrColor(filtered)); |
+ if (applyColorFilterToPaintColor) { |
+ grPaint->setColor(SkColorToPremulGrColor(colorFilter->filterColor(skPaint.getColor()))); |
} else { |
- SkTDArray<const GrFragmentProcessor*> array; |
- // return false if failed? |
- if (colorFilter->asFragmentProcessors(context, grPaint->getProcessorDataManager(), |
- &array)) { |
- for (int i = 0; i < array.count(); ++i) { |
- grPaint->addColorFragmentProcessor(array[i]); |
- array[i]->unref(); |
- } |
+ SkAutoTUnref<const GrFragmentProcessor> cfFP( |
+ colorFilter->asFragmentProcessor(context)); |
+ if (cfFP) { |
+ grPaint->addColorFragmentProcessor(cfFP); |
+ } else { |
+ return false; |
} |
} |
} |
+ SkXfermode* mode = skPaint.getXfermode(); |
+ GrXPFactory* xpFactory = nullptr; |
+ if (!SkXfermode::AsXPFactory(mode, &xpFactory)) { |
+ // Fall back to src-over |
+ // return false here? |
+ xpFactory = GrPorterDuffXPFactory::Create(SkXfermode::kSrcOver_Mode); |
+ } |
+ SkASSERT(xpFactory); |
+ grPaint->setXPFactory(xpFactory)->unref(); |
+ |
#ifndef SK_IGNORE_GPU_DITHER |
if (skPaint.isDither() && grPaint->numColorFragmentProcessors() > 0) { |
grPaint->addColorFragmentProcessor(GrDitherEffect::Create())->unref(); |
@@ -754,29 +895,49 @@ bool SkPaint2GrPaintNoShader(GrContext* context, const SkPaint& skPaint, GrColor |
return true; |
} |
-bool SkPaint2GrPaint(GrContext* context,const SkPaint& skPaint, const SkMatrix& viewM, |
- bool constantColor, GrPaint* grPaint) { |
- SkShader* shader = skPaint.getShader(); |
- if (nullptr == shader) { |
- return SkPaint2GrPaintNoShader(context, skPaint, SkColor2GrColor(skPaint.getColor()), |
- constantColor, grPaint); |
- } |
- |
- GrColor paintColor = SkColor2GrColor(skPaint.getColor()); |
+bool SkPaintToGrPaint(GrContext* context, const SkPaint& skPaint, const SkMatrix& viewM, |
+ GrPaint* grPaint) { |
+ return skpaint_to_grpaint_impl(context, skPaint, viewM, nullptr, nullptr, false, grPaint); |
+} |
- const GrFragmentProcessor* fp = shader->asFragmentProcessor(context, viewM, NULL, |
- skPaint.getFilterQuality(), grPaint->getProcessorDataManager()); |
- if (!fp) { |
+/** Replaces the SkShader (if any) on skPaint with the passed in GrFragmentProcessor. */ |
+bool SkPaintToGrPaintReplaceShader(GrContext* context, |
+ const SkPaint& skPaint, |
+ const GrFragmentProcessor* shaderFP, |
+ GrPaint* grPaint) { |
+ if (!shaderFP) { |
return false; |
} |
- grPaint->addColorFragmentProcessor(fp)->unref(); |
- constantColor = false; |
+ return skpaint_to_grpaint_impl(context, skPaint, SkMatrix::I(), &shaderFP, nullptr, false, |
+ grPaint); |
+} |
- // The grcolor is automatically set when calling asFragmentProcessor. |
- // If the shader can be seen as an effect it returns true and adds its effect to the grpaint. |
- return SkPaint2GrPaintNoShader(context, skPaint, paintColor, constantColor, grPaint); |
+/** Ignores the SkShader (if any) on skPaint. */ |
+bool SkPaintToGrPaintNoShader(GrContext* context, |
+ const SkPaint& skPaint, |
+ GrPaint* grPaint) { |
+ // Use a ptr to a nullptr to to indicate that the SkShader is ignored and not replaced. |
+ static const GrFragmentProcessor* kNullShaderFP = nullptr; |
+ static const GrFragmentProcessor** kIgnoreShader = &kNullShaderFP; |
+ return skpaint_to_grpaint_impl(context, skPaint, SkMatrix::I(), kIgnoreShader, nullptr, false, |
+ grPaint); |
} |
+/** Blends the SkPaint's shader (or color if no shader) with a per-primitive color which must |
+be setup as a vertex attribute using the specified SkXfermode::Mode. */ |
+bool SkPaintToGrPaintWithXfermode(GrContext* context, |
+ const SkPaint& skPaint, |
+ const SkMatrix& viewM, |
+ SkXfermode::Mode primColorMode, |
+ bool primitiveIsSrc, |
+ GrPaint* grPaint) { |
+ return skpaint_to_grpaint_impl(context, skPaint, viewM, nullptr, &primColorMode, primitiveIsSrc, |
+ grPaint); |
+} |
+ |
+ |
+//////////////////////////////////////////////////////////////////////////////////////////////// |
+ |
SkImageInfo GrMakeInfoFromTexture(GrTexture* tex, int w, int h, bool isOpaque) { |
#ifdef SK_DEBUG |
const GrSurfaceDesc& desc = tex->desc(); |
@@ -840,3 +1001,54 @@ GrTextureParams::FilterMode GrSkFilterQualityToGrFilterMode(SkFilterQuality pain |
} |
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, ¶ms); |
+ } |
+ |
+ GrUniqueKey stretchedKey; |
+ if (this->onMakeStretchedKey(stretch, &stretchedKey)) { |
+ GrTexture* result = ctx->textureProvider()->findAndRefTextureByUniqueKey(stretchedKey); |
+ if (result) { |
+ return result; |
+ } |
+ } |
+ |
+ GrTexture* result = this->onGenerateStretchedTexture(ctx, stretch, ¶ms); |
+ 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, const GrTextureParams* params) { |
+ 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, params)); |
+ if (!unstretched) { |
+ return nullptr; |
+ } |
+ return stretch_texture(unstretched, stretch, nullptr, GrUniqueKey()); |
+ } |
+} |