--- /dev/null
+/*
+ * Copyright 2016 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+#include <vector>
+
+#include "gm.h"
+#include "GrContext.h"
+#include "Resources.h"
+#include "SkImage.h"
+
+#if SK_SUPPORT_GPU
+
+// Helper function that uploads the given SkImage using MakdeFromDeferredTextureImageData and then
+// draws the uploaded version at the specified coordinates.
+static bool DrawDeferredTextureImageData(GrContext* context, SkCanvas* canvas, SkImage* image,
+ SkImage::DeferredTextureImageUsageParams* params,
+ SkScalar x, SkScalar y) {
+ size_t deferredSize =
+ image->getDeferredTextureImageData(*context->threadSafeProxy(), params, 1, nullptr);
+ if (deferredSize == 0) {
+ SkDebugf("\nCould not create DeferredTextureImageData.\n");
+ return false;
+ }
+
+ std::vector<uint8_t> memory;
+ memory.resize(deferredSize);
+ image->getDeferredTextureImageData(*context->threadSafeProxy(), params, 1, memory.data());
+ sk_sp<SkImage> uploadedImage =
+ SkImage::MakeFromDeferredTextureImageData(context, memory.data(), SkBudgeted::kNo);
+ canvas->drawImage(uploadedImage, x, y);
+
+ return true;
+}
+
+DEF_SIMPLE_GM(deferred_texture_image_data, canvas, 60, 10) {
+ GrContext* context = canvas->getGrContext();
+ if (!context) {
+ skiagm::GM::DrawGpuOnlyMessage(canvas);
+ return;
+ }
+
+ sk_sp<SkImage> encodedImage = GetResourceAsImage("randPixels.png");
+ if (!encodedImage) {
+ SkDebugf("\nCould not load resource.\n");
+ return;
+ }
+
+ SkBitmap bitmap;
+ if (!GetResourceAsBitmap("randPixels.png", &bitmap)) {
+ SkDebugf("\nCould not decode resource.\n");
+ return;
+ }
+
+ sk_sp<SkImage> decodedImage = SkImage::MakeFromBitmap(bitmap);
+
+ // Draw both encoded and decoded image via deferredTextureImageData.
+ SkImage::DeferredTextureImageUsageParams params;
+ DrawDeferredTextureImageData(context, canvas, encodedImage.get(), ¶ms, 0, 0);
+ DrawDeferredTextureImageData(context, canvas, decodedImage.get(), ¶ms, 10, 0);
+
+ // Draw 50% scaled versions of the encoded and decoded images at medium quality.
+ SkImage::DeferredTextureImageUsageParams mediumScaledParams;
+ mediumScaledParams.fPreScaleMipLevel = 1;
+ mediumScaledParams.fQuality = kMedium_SkFilterQuality;
+
+ DrawDeferredTextureImageData(context, canvas, encodedImage.get(), &mediumScaledParams, 20, 0);
+ DrawDeferredTextureImageData(context, canvas, decodedImage.get(), &mediumScaledParams, 30, 0);
+
+ // Draw 50% scaled versions of the encoded and decoded images at none quality.
+ SkImage::DeferredTextureImageUsageParams noneScaledParams;
+ noneScaledParams.fPreScaleMipLevel = 1;
+ noneScaledParams.fQuality = kNone_SkFilterQuality;
+
+ DrawDeferredTextureImageData(context, canvas, encodedImage.get(), &noneScaledParams, 40, 0);
+ DrawDeferredTextureImageData(context, canvas, decodedImage.get(), &noneScaledParams, 50, 0);
+}
+
+#endif
#include "SkGrPixelRef.h"
#include "SkGrPriv.h"
#include "SkImage_Gpu.h"
+#include "SkMipMap.h"
#include "SkPixelRef.h"
SkImage_Gpu::SkImage_Gpu(int w, int h, uint32_t uniqueID, SkAlphaType at, GrTexture* tex,
};
size_t SkImage::getDeferredTextureImageData(const GrContextThreadSafeProxy& proxy,
- const DeferredTextureImageUsageParams[],
+ const DeferredTextureImageUsageParams params[],
int paramCnt, void* buffer) const {
+ // Extract relevant min/max values from the params array.
+ int lowestPreScaleMipLevel = params[0].fPreScaleMipLevel;
+ SkFilterQuality highestFilterQuality = params[0].fQuality;
+ for (int i = 1; i < paramCnt; ++i) {
+ if (lowestPreScaleMipLevel > params[i].fPreScaleMipLevel)
+ lowestPreScaleMipLevel = params[i].fPreScaleMipLevel;
+ if (highestFilterQuality < params[i].fQuality)
+ highestFilterQuality = params[i].fQuality;
+ }
+
const bool fillMode = SkToBool(buffer);
if (fillMode && !SkIsAlign8(reinterpret_cast<intptr_t>(buffer))) {
return 0;
}
+ // Calculate scaling parameters.
+ bool isScaled = lowestPreScaleMipLevel != 0;
+
+ SkISize scaledSize;
+ if (isScaled) {
+ // SkMipMap::ComputeLevelSize takes an index into an SkMipMap. SkMipMaps don't contain the
+ // base level, so to get an SkMipMap index we must subtract one from the GL MipMap level.
+ scaledSize = SkMipMap::ComputeLevelSize(this->width(), this->height(),
+ lowestPreScaleMipLevel - 1);
+ } else {
+ scaledSize = SkISize::Make(this->width(), this->height());
+ }
+
+ // We never want to scale at higher than SW medium quality, as SW medium matches GPU high.
+ SkFilterQuality scaleFilterQuality = highestFilterQuality;
+ if (scaleFilterQuality > kMedium_SkFilterQuality) {
+ scaleFilterQuality = kMedium_SkFilterQuality;
+ }
+
const int maxTextureSize = proxy.fCaps->maxTextureSize();
- if (width() > maxTextureSize || height() > maxTextureSize) {
+ if (scaledSize.width() > maxTextureSize || scaledSize.height() > maxTextureSize) {
return 0;
}
size_t pixelSize = 0;
size_t ctSize = 0;
int ctCount = 0;
- if (this->peekPixels(&pixmap)) {
+ if (!isScaled && this->peekPixels(&pixmap)) {
info = pixmap.info();
pixelSize = SkAlign8(pixmap.getSafeSize());
if (pixmap.ctable()) {
// In the future we will access the cacherator and get the exact data that we want to (e.g.
// yuv planes) upload.
SkAutoTUnref<SkData> data(this->refEncoded());
- if (!data) {
+ if (!data && !this->peekPixels(nullptr)) {
return 0;
}
SkAlphaType at = this->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType;
- info = SkImageInfo::MakeN32(this->width(), this->height(), at);
+ info = SkImageInfo::MakeN32(scaledSize.width(), scaledSize.height(), at);
pixelSize = SkAlign8(SkAutoPixmapStorage::AllocSize(info, nullptr));
if (fillMode) {
pixmap.alloc(info);
- if (!this->readPixels(pixmap, 0, 0, SkImage::kDisallow_CachingHint)) {
- return 0;
+ if (isScaled) {
+ if (!this->scalePixels(pixmap, scaleFilterQuality,
+ SkImage::kDisallow_CachingHint)) {
+ return 0;
+ }
+ } else {
+ if (!this->readPixels(pixmap, 0, 0, SkImage::kDisallow_CachingHint)) {
+ return 0;
+ }
}
SkASSERT(!pixmap.ctable());
}
#include <functional>
#include <initializer_list>
+#include <vector>
#include "DMGpuSupport.h"
#include "SkAutoPixmapStorage.h"
draw_image_test_pattern(surface->getCanvas());
return surface->makeImageSnapshot();
}
+static sk_sp<SkImage> create_image_large() {
+ const SkImageInfo info = SkImageInfo::MakeN32(32000, 32, kOpaque_SkAlphaType);
+ auto surface(SkSurface::MakeRaster(info));
+ surface->getCanvas()->clear(SK_ColorWHITE);
+ SkPaint paint;
+ paint.setColor(SK_ColorBLACK);
+ surface->getCanvas()->drawRect(SkRect::MakeXYWH(4000, 2, 28000, 30), paint);
+ return surface->makeImageSnapshot();
+}
static SkData* create_image_data(SkImageInfo* info) {
*info = SkImageInfo::MakeN32(20, 20, kOpaque_SkAlphaType);
testContext->makeCurrent();
REPORTER_ASSERT(reporter, proxy);
struct {
- std::function<sk_sp<SkImage> ()> fImageFactory;
- bool fExpectation;
+ std::function<sk_sp<SkImage> ()> fImageFactory;
+ std::vector<SkImage::DeferredTextureImageUsageParams> fParams;
+ SkFilterQuality fExpectedQuality;
+ int fExpectedScaleFactor;
+ bool fExpectation;
} testCases[] = {
- { create_image, true },
- { create_codec_image, true },
- { create_data_image, true },
- { create_picture_image, false },
- { [context] { return create_gpu_image(context); }, false },
+ { create_image, {{}}, kNone_SkFilterQuality, 1, true },
+ { create_codec_image, {{}}, kNone_SkFilterQuality, 1, true },
+ { create_data_image, {{}}, kNone_SkFilterQuality, 1, true },
+ { create_picture_image, {{}}, kNone_SkFilterQuality, 1, false },
+ { [context] { return create_gpu_image(context); }, {{}}, kNone_SkFilterQuality, 1, false },
// Create a texture image in a another GrContext.
{ [testContext, otherContextInfo] {
otherContextInfo.testContext()->makeCurrent();
sk_sp<SkImage> otherContextImage = create_gpu_image(otherContextInfo.grContext());
testContext->makeCurrent();
return otherContextImage;
- }, false },
+ }, {{}}, kNone_SkFilterQuality, 1, false },
+ // Create an image that is too large to upload.
+ { create_image_large, {{}}, kNone_SkFilterQuality, 1, false },
+ // Create an image that is too large, but is scaled to an acceptable size.
+ { create_image_large, {{SkMatrix::I(), kMedium_SkFilterQuality, 4}},
+ kMedium_SkFilterQuality, 16, true},
+ // Create an image with multiple low filter qualities, make sure we round up.
+ { create_image_large, {{SkMatrix::I(), kNone_SkFilterQuality, 4},
+ {SkMatrix::I(), kMedium_SkFilterQuality, 4}},
+ kMedium_SkFilterQuality, 16, true},
+ // Create an image with multiple prescale levels, make sure we chose the minimum scale.
+ { create_image_large, {{SkMatrix::I(), kMedium_SkFilterQuality, 5},
+ {SkMatrix::I(), kMedium_SkFilterQuality, 4}},
+ kMedium_SkFilterQuality, 16, true},
};
for (auto testCase : testCases) {
sk_sp<SkImage> image(testCase.fImageFactory());
-
- // This isn't currently used in the implementation, just set any old values.
- SkImage::DeferredTextureImageUsageParams params;
- params.fQuality = kLow_SkFilterQuality;
- params.fMatrix = SkMatrix::I();
-
- size_t size = image->getDeferredTextureImageData(*proxy, ¶ms, 1, nullptr);
+ size_t size = image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
+ static_cast<int>(testCase.fParams.size()),
+ nullptr);
static const char *const kFS[] = { "fail", "succeed" };
if (SkToBool(size) != testCase.fExpectation) {
if (size) {
void* buffer = sk_malloc_throw(size);
void* misaligned = reinterpret_cast<void*>(reinterpret_cast<intptr_t>(buffer) + 3);
- if (image->getDeferredTextureImageData(*proxy, ¶ms, 1, misaligned)) {
+ if (image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
+ static_cast<int>(testCase.fParams.size()),
+ misaligned)) {
ERRORF(reporter, "Should fail when buffer is misaligned.");
}
- if (!image->getDeferredTextureImageData(*proxy, ¶ms, 1, buffer)) {
+ if (!image->getDeferredTextureImageData(*proxy, testCase.fParams.data(),
+ static_cast<int>(testCase.fParams.size()),
+ buffer)) {
ERRORF(reporter, "deferred image size succeeded but creation failed.");
} else {
for (auto budgeted : { SkBudgeted::kNo, SkBudgeted::kYes }) {
SkImage::MakeFromDeferredTextureImageData(context, buffer, budgeted));
REPORTER_ASSERT(reporter, newImage != nullptr);
if (newImage) {
- check_images_same(reporter, image.get(), newImage.get());
+ // Scale the image in software for comparison.
+ SkImageInfo scaled_info = SkImageInfo::MakeN32(
+ image->width() / testCase.fExpectedScaleFactor,
+ image->height() / testCase.fExpectedScaleFactor,
+ image->isOpaque() ? kOpaque_SkAlphaType : kPremul_SkAlphaType);
+ SkAutoPixmapStorage scaled;
+ scaled.alloc(scaled_info);
+ image->scalePixels(scaled, testCase.fExpectedQuality);
+ sk_sp<SkImage> scaledImage = SkImage::MakeRasterCopy(scaled);
+ check_images_same(reporter, scaledImage.get(), newImage.get());
}
// The other context should not be able to create images from texture data
// created by the original context.