From 045afea97902936869d71389e656a9c6917fa32f Mon Sep 17 00:00:00 2001 From: bsalomon Date: Wed, 9 Mar 2016 11:31:18 -0800 Subject: [PATCH] Add SkImage::NewTextureFromPixmap GOLD_TRYBOT_URL= https://gold.skia.org/search2?unt=true&query=source_type%3Dgm&master=false&issue=1776913003 Review URL: https://codereview.chromium.org/1776913003 --- include/core/SkImage.h | 2 + src/gpu/SkGr.cpp | 100 +++++++++++++++++++------------------- src/gpu/SkGrPriv.h | 5 ++ src/image/SkImage.cpp | 4 ++ src/image/SkImage_Gpu.cpp | 13 +++++ tests/ImageTest.cpp | 94 +++++++++++++++++++++++++++++++++++ 6 files changed, 167 insertions(+), 51 deletions(-) diff --git a/include/core/SkImage.h b/include/core/SkImage.h index c2896beab1..0474195ae2 100644 --- a/include/core/SkImage.h +++ b/include/core/SkImage.h @@ -145,6 +145,8 @@ public: static SkImage* NewFromPicture(const SkPicture*, const SkISize& dimensions, const SkMatrix*, const SkPaint*); + static SkImage* NewTextureFromPixmap(GrContext*, const SkPixmap&, SkBudgeted budgeted); + /////////////////////////////////////////////////////////////////////////////////////////////// int width() const { return fWidth; } diff --git a/src/gpu/SkGr.cpp b/src/gpu/SkGr.cpp index a3848c77e0..d7af8258c7 100644 --- a/src/gpu/SkGr.cpp +++ b/src/gpu/SkGr.cpp @@ -7,6 +7,7 @@ #include "SkGr.h" +#include "SkGrPriv.h" #include "GrCaps.h" #include "GrContext.h" @@ -121,16 +122,10 @@ GrPixelConfig GrIsCompressedTextureDataSupported(GrContext* ctx, SkData* data, * 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()); +static void build_index8_data(void* buffer, const SkPixmap& pixmap) { + SkASSERT(kIndex_8_SkColorType == pixmap.colorType()); - SkAutoLockPixels alp(bitmap); - if (!bitmap.readyToDraw()) { - SkDEBUGFAIL("bitmap not ready to draw!"); - return; - } - - SkColorTable* ctable = bitmap.getColorTable(); + const SkColorTable* ctable = pixmap.ctable(); char* dst = (char*)buffer; const int count = ctable->count(); @@ -152,14 +147,14 @@ static void build_index8_data(void* buffer, const SkBitmap& bitmap) { // always skip a full 256 number of entries, even if we memcpy'd fewer dst += 256 * sizeof(GrColor); - if ((unsigned)bitmap.width() == bitmap.rowBytes()) { - memcpy(dst, bitmap.getPixels(), bitmap.getSize()); + if ((unsigned)pixmap.width() == pixmap.rowBytes()) { + memcpy(dst, pixmap.addr(), pixmap.getSafeSize()); } else { // need to trim off the extra bytes per row - size_t width = bitmap.width(); - size_t rowBytes = bitmap.rowBytes(); - const char* src = (const char*)bitmap.getPixels(); - for (int y = 0; y < bitmap.height(); y++) { + size_t width = pixmap.width(); + size_t rowBytes = pixmap.rowBytes(); + const uint8_t* src = pixmap.addr8(); + for (int y = 0; y < pixmap.height(); y++) { memcpy(dst, src, width); src += rowBytes; dst += width; @@ -218,58 +213,61 @@ static GrTexture* load_etc1_texture(GrContext* ctx, const SkBitmap &bm, GrSurfac return ctx->textureProvider()->createTexture(desc, SkBudgeted::kYes, startOfTexData, 0); } -GrTexture* GrUploadBitmapToTexture(GrContext* ctx, const SkBitmap& bmp) { - SkASSERT(!bmp.getTexture()); +GrTexture* GrUploadBitmapToTexture(GrContext* ctx, const SkBitmap& bitmap) { + GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bitmap.info()); + if (GrTexture *texture = load_etc1_texture(ctx, bitmap, desc)) { + return texture; + } + if (GrTexture* texture = create_texture_from_yuv(ctx, bitmap, desc)) { + return texture; + } + + SkAutoLockPixels alp(bitmap); + if (!bitmap.readyToDraw()) { + return nullptr; + } + SkPixmap pixmap; + if (!bitmap.peekPixels(&pixmap)) { + return nullptr; + } + return GrUploadPixmapToTexture(ctx, pixmap); +} + +GrTexture* GrUploadPixmapToTexture(GrContext* ctx, const SkPixmap& pixmap) { + const SkPixmap* pmap = &pixmap; + SkPixmap tmpPixmap; SkBitmap tmpBitmap; - const SkBitmap* bitmap = &bmp; - GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(bitmap->info()); + GrSurfaceDesc desc = GrImageInfoToSurfaceDesc(pixmap.info()); const GrCaps* caps = ctx->caps(); - if (kIndex_8_SkColorType == bitmap->colorType()) { + if (kIndex_8_SkColorType == pixmap.colorType()) { if (caps->isConfigTexturable(kIndex_8_GrPixelConfig)) { size_t imageSize = GrCompressedFormatDataSize(kIndex_8_GrPixelConfig, - bitmap->width(), bitmap->height()); + pixmap.width(), pixmap.height()); SkAutoMalloc storage(imageSize); - build_index8_data(storage.get(), bmp); + build_index8_data(storage.get(), pixmap); // our compressed data will be trimmed, so pass width() for its // "rowBytes", since they are the same now. return ctx->textureProvider()->createTexture(desc, SkBudgeted::kYes, storage.get(), - bitmap->width()); + pixmap.width()); } else { - bmp.copyTo(&tmpBitmap, kN32_SkColorType); - // now bitmap points to our temp, which has been promoted to 32bits - bitmap = &tmpBitmap; - desc.fConfig = SkImageInfo2GrPixelConfig(bitmap->info()); - } - } else if (!bitmap->readyToDraw()) { - // If the bitmap had compressed data and was then uncompressed, it'll still return - // 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); - if (texture) { - return texture; + SkImageInfo info = SkImageInfo::MakeN32Premul(pixmap.width(), pixmap.height()); + tmpBitmap.allocPixels(info); + if (!pixmap.readPixels(info, tmpBitmap.getPixels(), tmpBitmap.rowBytes())) { + return nullptr; + } + if (!tmpBitmap.peekPixels(&tmpPixmap)) { + return nullptr; + } + pmap = &tmpPixmap; } } - GrTexture *texture = create_texture_from_yuv(ctx, *bitmap, desc); - if (texture) { - return texture; - } - - SkAutoLockPixels alp(*bitmap); - if (!bitmap->readyToDraw()) { - return nullptr; - } - - return ctx->textureProvider()->createTexture(desc, SkBudgeted::kYes, bitmap->getPixels(), - bitmap->rowBytes()); + return ctx->textureProvider()->createTexture(desc, SkBudgeted::kYes, pmap->addr(), + pmap->rowBytes()); } diff --git a/src/gpu/SkGrPriv.h b/src/gpu/SkGrPriv.h index 205698300b..555f415513 100644 --- a/src/gpu/SkGrPriv.h +++ b/src/gpu/SkGrPriv.h @@ -121,6 +121,11 @@ GrTexture* GrUploadBitmapToTexture(GrContext*, const SkBitmap&); GrTexture* GrGenerateMipMapsAndUploadToTexture(GrContext*, const SkBitmap&); +/** + * Creates a new texture for the pixmap. + */ +GrTexture* GrUploadPixmapToTexture(GrContext*, const SkPixmap&); + ////////////////////////////////////////////////////////////////////////////// GR_STATIC_ASSERT((int)kZero_GrBlendCoeff == (int)SkXfermode::kZero_Coeff); diff --git a/src/image/SkImage.cpp b/src/image/SkImage.cpp index 674a735743..07279501a3 100644 --- a/src/image/SkImage.cpp +++ b/src/image/SkImage.cpp @@ -343,6 +343,10 @@ bool SkImage::isLazyGenerated() const { #if !SK_SUPPORT_GPU +SkImage* SkImage::NewTextureFromPixmap(GrContext*, const SkPixmap&, SkBudgeted budgeted) { + return nullptr; +} + SkImage* SkImage::NewFromTexture(GrContext*, const GrBackendTextureDesc&, SkAlphaType, TextureReleaseProc, ReleaseContext) { return nullptr; diff --git a/src/image/SkImage_Gpu.cpp b/src/image/SkImage_Gpu.cpp index 57ee33957b..b87e50ba0a 100644 --- a/src/image/SkImage_Gpu.cpp +++ b/src/image/SkImage_Gpu.cpp @@ -310,6 +310,19 @@ SkImage* SkImage::newTextureImage(GrContext *context) const { return create_image_from_maker(&maker, at, this->uniqueID()); } +SkImage* SkImage::NewTextureFromPixmap(GrContext* ctx, const SkPixmap& pixmap, + SkBudgeted budgeted) { + if (!ctx) { + return nullptr; + } + SkAutoTUnref texture(GrUploadPixmapToTexture(ctx, pixmap)); + if (!texture) { + return nullptr; + } + return new SkImage_Gpu(texture->width(), texture->height(), kNeedNewImageUniqueID, + pixmap.alphaType(), texture, budgeted); +} + /////////////////////////////////////////////////////////////////////////////////////////////////// GrTexture* GrDeepCopyTexture(GrTexture* src, SkBudgeted budgeted) { diff --git a/tests/ImageTest.cpp b/tests/ImageTest.cpp index 2ec1f342b3..603a309acf 100644 --- a/tests/ImageTest.cpp +++ b/tests/ImageTest.cpp @@ -6,6 +6,7 @@ */ #include +#include #include "DMGpuSupport.h" #include "SkBitmap.h" @@ -67,6 +68,31 @@ static SkImage* create_image() { draw_image_test_pattern(surface->getCanvas()); return surface->newImageSnapshot(); } +static SkImage* create_image_565() { + const SkImageInfo info = SkImageInfo::Make(20, 20, kRGB_565_SkColorType, kOpaque_SkAlphaType); + SkAutoTUnref surface(SkSurface::NewRaster(info)); + draw_image_test_pattern(surface->getCanvas()); + return surface->newImageSnapshot(); +} +#if 0 +static SkImage* create_image_ct() { + SkPMColor colors[] = { + SkPreMultiplyARGB(0xFF, 0xFF, 0xFF, 0x00), + SkPreMultiplyARGB(0x80, 0x00, 0xA0, 0xFF), + SkPreMultiplyARGB(0xFF, 0xBB, 0x00, 0xBB) + }; + SkAutoTUnref colorTable(new SkColorTable(colors, SK_ARRAY_COUNT(colors))); + uint8_t data[] = { + 0, 0, 0, 0, 0, + 0, 1, 1, 1, 0, + 0, 1, 2, 1, 0, + 0, 1, 1, 1, 0, + 0, 0, 0, 0, 0 + }; + SkImageInfo info = SkImageInfo::Make(5, 5, kIndex_8_SkColorType, kPremul_SkAlphaType); + return SkImage::NewRasterCopy(info, data, 5, colorTable); +} +#endif static SkData* create_image_data(SkImageInfo* info) { *info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType); const size_t rowBytes = info->minRowBytes(); @@ -737,4 +763,72 @@ DEF_GPUTEST_FOR_NATIVE_CONTEXT(SkImage_NewFromTexture, reporter, context) { refImg.reset(nullptr); // force a release of the image REPORTER_ASSERT(reporter, 1 == releaseChecker.fReleaseCount); } + +static void check_images_same(skiatest::Reporter* reporter, const SkImage* a, const SkImage* b) { + if (a->width() != b->width() || a->height() != b->height()) { + ERRORF(reporter, "Images must have the same size"); + return; + } + if (a->isOpaque() != b->isOpaque()) { + ERRORF(reporter, "Images must have the same opaquness"); + return; + } + + SkImageInfo info = SkImageInfo::MakeN32Premul(a->width(), a->height()); + SkAutoPixmapStorage apm; + SkAutoPixmapStorage bpm; + + apm.alloc(info); + bpm.alloc(info); + + if (!a->readPixels(apm, 0, 0)) { + ERRORF(reporter, "Could not read image a's pixels"); + return; + } + if (!b->readPixels(bpm, 0, 0)) { + ERRORF(reporter, "Could not read image b's pixels"); + return; + } + + for (auto y = 0; y < info.height(); ++y) { + for (auto x = 0; x < info.width(); ++x) { + uint32_t pixelA = *apm.addr32(x, y); + uint32_t pixelB = *bpm.addr32(x, y); + if (pixelA != pixelB) { + ERRORF(reporter, "Expected image pixels to be the same. At %d,%d 0x%08x != 0x%08x", + x, y, pixelA, pixelB); + return; + } + } + } +} + +DEF_GPUTEST_FOR_RENDERING_CONTEXTS(NewTextureFromPixmap, reporter, context) { + for (auto create : {&create_image, + &create_image_565 +#if 0 // peekPixels on color table images is currently broken. + , &create_image_ct +#endif + }) { + SkAutoTUnref image((*create)()); + if (!image) { + ERRORF(reporter, "Could not create image"); + return; + } + + SkPixmap pixmap; + if (!image->peekPixels(&pixmap)) { + ERRORF(reporter, "peek failed"); + } else { + SkAutoTUnref texImage(SkImage::NewTextureFromPixmap(context, pixmap, + SkBudgeted::kNo)); + if (!texImage) { + ERRORF(reporter, "NewTextureFromPixmap failed."); + } else { + check_images_same(reporter, image, texImage); + } + } + } +} + #endif