| 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());
|
| + }
|
| +}
|
|
|