Expand backend allocation API to allow an initialization color (only for GL to start)
Change-Id: I8e676e80f85f14c81c3cec8d766d0fdc7e25f426
Reviewed-on: https://skia-review.googlesource.com/c/skia/+/214445
Reviewed-by: Greg Daniel <egdaniel@google.com>
Commit-Queue: Robert Phillips <robertphillips@google.com>
diff --git a/src/gpu/GrContextPriv.cpp b/src/gpu/GrContextPriv.cpp
index 09b5327..ab4b2f0 100644
--- a/src/gpu/GrContextPriv.cpp
+++ b/src/gpu/GrContextPriv.cpp
@@ -764,3 +764,48 @@
fContext->drawingManager()->testingOnly_removeOnFlushCallbackObject(cb);
}
#endif
+
+//////////////////////////////////////////////////////////////////////////////
+GrBackendTexture GrContextPriv::createBackendTexture(int width, int height,
+ GrBackendFormat backendFormat,
+ const SkColor4f& color,
+ GrMipMapped mipMapped,
+ GrRenderable renderable) {
+ if (!fContext->asDirectContext()) {
+ return GrBackendTexture();
+ }
+
+ if (this->abandoned()) {
+ return GrBackendTexture();
+ }
+
+ if (!backendFormat.isValid()) {
+ return GrBackendTexture();
+ }
+
+ GrGpu* gpu = fContext->fGpu.get();
+
+ return gpu->createBackendTexture(width, height, backendFormat,
+ mipMapped, renderable, nullptr, 0, color);
+}
+
+GrBackendTexture GrContextPriv::createBackendTexture(int width, int height,
+ SkColorType colorType,
+ const SkColor4f& color,
+ GrMipMapped mipMapped,
+ GrRenderable renderable) {
+ if (!fContext->asDirectContext()) {
+ return GrBackendTexture();
+ }
+
+ if (this->abandoned()) {
+ return GrBackendTexture();
+ }
+
+ GrBackendFormat format = fContext->caps()->getBackendFormatFromColorType(colorType);
+ if (!format.isValid()) {
+ return GrBackendTexture();
+ }
+
+ return this->createBackendTexture(width, height, format, color, mipMapped, renderable);
+}
diff --git a/src/gpu/GrContextPriv.h b/src/gpu/GrContextPriv.h
index 86af1f3..5dbb038 100644
--- a/src/gpu/GrContextPriv.h
+++ b/src/gpu/GrContextPriv.h
@@ -292,6 +292,20 @@
void testingOnly_flushAndRemoveOnFlushCallbackObject(GrOnFlushCallbackObject*);
#endif
+ // If possible, create a backend texture initialized to a particular color. The client should
+ // ensure that the returned backend texture is valid.
+ GrBackendTexture createBackendTexture(int width, int height,
+ GrBackendFormat, const SkColor4f& color,
+ GrMipMapped, GrRenderable);
+
+ // If possible, create a backend texture initialized to a particular color. The client should
+ // ensure that the returned backend texture is valid.
+ // If successful, the created backend texture will be compatible with the provided
+ // SkColorType.
+ GrBackendTexture createBackendTexture(int width, int height,
+ SkColorType, const SkColor4f& color,
+ GrMipMapped, GrRenderable);
+
private:
explicit GrContextPriv(GrContext* context) : fContext(context) {}
GrContextPriv(const GrContextPriv&); // unimpl
diff --git a/src/gpu/GrDataUtils.cpp b/src/gpu/GrDataUtils.cpp
new file mode 100644
index 0000000..40d69e0
--- /dev/null
+++ b/src/gpu/GrDataUtils.cpp
@@ -0,0 +1,287 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#include "GrDataUtils.h"
+
+#include "include/private/GrColor.h"
+
+static const int kNumModifierTables = 8;
+static const int kNumPixelIndices = 4;
+
+// The index of each row in this table is the ETC1 table codeword
+// The index of each column in this table is the ETC1 pixel index value
+static const int kModifierTables[kNumModifierTables][kNumPixelIndices] = {
+ /* 0 */ { 2, 8, -2, -8 },
+ /* 1 */ { 5, 17, -5, -17 },
+ /* 2 */ { 9, 29, -9, -29 },
+ /* 3 */ { 13, 42, -13, -42 },
+ /* 4 */ { 18, 60, -18, -60 },
+ /* 5 */ { 24, 80, -24, -80 },
+ /* 6 */ { 33, 106, -33, -106 },
+ /* 7 */ { 47, 183, -47, -183 }
+};
+
+static inline int convert_5To8(int b) {
+ int c = b & 0x1f;
+ return (c << 3) | (c >> 2);
+}
+
+// Evaluate one of the entries in 'kModifierTables' to see how close it can get (r8,g8,b8) to
+// the original color (rOrig, gOrib, bOrig).
+static int test_table_entry(int rOrig, int gOrig, int bOrig,
+ int r8, int g8, int b8,
+ int table, int offset) {
+ SkASSERT(0 <= table && table < 8);
+ SkASSERT(0 <= offset && offset < 4);
+
+ r8 = SkTPin<uint8_t>(r8 + kModifierTables[table][offset], 0, 255);
+ g8 = SkTPin<uint8_t>(g8 + kModifierTables[table][offset], 0, 255);
+ b8 = SkTPin<uint8_t>(b8 + kModifierTables[table][offset], 0, 255);
+
+ return SkTAbs(rOrig - r8) + SkTAbs(gOrig - g8) + SkTAbs(bOrig - b8);
+}
+
+// Create an ETC1 compressed block that is filled with 'col'
+static void create_etc1_block(SkColor col, ETC1Block* block) {
+ block->fHigh = 0;
+ block->fLow = 0;
+
+ int rOrig = SkColorGetR(col);
+ int gOrig = SkColorGetG(col);
+ int bOrig = SkColorGetB(col);
+
+ int r5 = SkMulDiv255Round(31, rOrig);
+ int g5 = SkMulDiv255Round(31, gOrig);
+ int b5 = SkMulDiv255Round(31, bOrig);
+
+ int r8 = convert_5To8(r5);
+ int g8 = convert_5To8(g5);
+ int b8 = convert_5To8(b5);
+
+ // We always encode solid color textures as 555 + zero diffs
+ block->fHigh |= (r5 << 27) | (g5 << 19) | (b5 << 11) | 0x2;
+
+ int bestTableIndex = 0, bestPixelIndex = 0;
+ int bestSoFar = 1024;
+ for (int tableIndex = 0; tableIndex < kNumModifierTables; ++tableIndex) {
+ for (int pixelIndex = 0; pixelIndex < kNumPixelIndices; ++pixelIndex) {
+ int score = test_table_entry(rOrig, gOrig, bOrig, r8, g8, b8,
+ tableIndex, pixelIndex);
+
+ if (bestSoFar > score) {
+ bestSoFar = score;
+ bestTableIndex = tableIndex;
+ bestPixelIndex = pixelIndex;
+ }
+ }
+ }
+
+ block->fHigh |= (bestTableIndex << 5) | (bestTableIndex << 2);
+
+ for (int i = 0; i < 16; ++i) {
+ block->fLow |= bestPixelIndex << 2*i;
+ }
+}
+
+int GrNumETC1Blocks(int w, int h) {
+ if (w < 4) {
+ w = 1;
+ } else {
+ SkASSERT((w & 3) == 0);
+ w >>= 2;
+ }
+
+ if (h < 4) {
+ h = 1;
+ } else {
+ SkASSERT((h & 3) == 0);
+ h >>= 2;
+ }
+
+ return w * h;
+}
+
+void GrFillInETC1WithColor(const SkColor4f& colorf, ETC1Block* blocks, int numBlocks) {
+ SkColor color = colorf.toSkColor();
+
+ ETC1Block block;
+ create_etc1_block(color, &block);
+
+ for (int i = 0; i < numBlocks; ++i) {
+ blocks[i] = block;
+ }
+}
+
+bool GrFillBufferWithColor(GrPixelConfig config, int width, int height,
+ const SkColor4f& colorf, void* dest) {
+ SkASSERT(kRGB_ETC1_GrPixelConfig != config);
+
+ GrColor color = colorf.toBytes_RGBA();
+
+ uint8_t r = GrColorUnpackR(color);
+ uint8_t g = GrColorUnpackG(color);
+ uint8_t b = GrColorUnpackB(color);
+ uint8_t a = GrColorUnpackA(color);
+
+ // TODO: use sk_memset32, sk_memset16, and SkOpts::rect_memset16/32/64
+ switch (config) {
+ case kAlpha_8_GrPixelConfig: // fall through
+ case kAlpha_8_as_Alpha_GrPixelConfig: // fall through
+ case kAlpha_8_as_Red_GrPixelConfig: {
+ memset(dest, a, width * height);
+ break;
+ }
+ case kGray_8_GrPixelConfig: // fall through
+ case kGray_8_as_Lum_GrPixelConfig: // fall through
+ case kGray_8_as_Red_GrPixelConfig: {
+ uint8_t gray8 = SkComputeLuminance(r, g, b);
+ memset(dest, gray8, width * height);
+ break;
+ }
+ case kRGB_565_GrPixelConfig: {
+ uint16_t* dest16 = (uint16_t*) dest;
+ uint16_t rgb565 = SkPack888ToRGB16(r, g, b);
+ for (int i = 0; i < width * height; ++i) {
+ dest16[i] = rgb565;
+ }
+ break;
+ }
+ case kRGBA_4444_GrPixelConfig: {
+ uint16_t* dest16 = (uint16_t*) dest;
+ uint8_t r4 = (r >> 4) & 0xF;
+ uint8_t g4 = (g >> 4) & 0xF;
+ uint8_t b4 = (b >> 4) & 0xF;
+ uint8_t a4 = (a >> 4) & 0xF;
+
+ uint16_t rgba4444 = r4 << SK_R4444_SHIFT | g4 << SK_G4444_SHIFT |
+ b4 << SK_B4444_SHIFT | a4 << SK_A4444_SHIFT;
+
+ for (int i = 0; i < width * height; ++i) {
+ dest16[i] = rgba4444;
+ }
+ break;
+ }
+ case kRGBA_8888_GrPixelConfig: {
+ GrColor* destColor = (GrColor *) dest;
+ for (int i = 0; i < width * height; ++i) {
+ destColor[i] = color;
+ }
+ break;
+ }
+ case kRGB_888_GrPixelConfig: {
+ uint8_t* dest8 = (uint8_t*) dest;
+ for (int i = 0; i < width * height; ++i, dest8 += 3) {
+ dest8[0] = r;
+ dest8[1] = g;
+ dest8[2] = b;
+ }
+ break;
+ }
+ case kRGB_888X_GrPixelConfig: {
+ GrColor opaque = GrColorPackRGBA(r, g, b, 0xFF);
+
+ GrColor* destColor = (GrColor *) dest;
+ for (int i = 0; i < width * height; ++i) {
+ destColor[i] = opaque;
+ }
+ break;
+ }
+ case kRG_88_GrPixelConfig: {
+ uint8_t* dest8 = (uint8_t*) dest;
+ for (int i = 0; i < width * height; ++i, dest8 += 2) {
+ dest8[0] = r;
+ dest8[1] = g;
+ }
+ break;
+ }
+ case kBGRA_8888_GrPixelConfig: {
+ GrColor swizzled = GrColorPackRGBA(b, g, r, a);
+
+ GrColor* destColor = (GrColor *) dest;
+ for (int i = 0; i < width * height; ++i) {
+ destColor[i] = swizzled;
+ }
+ break;
+ }
+ case kSRGBA_8888_GrPixelConfig: {
+ // TODO: ask Brian O. what to do here
+ GrColor* destColor = (GrColor *) dest;
+ for (int i = 0; i < width * height; ++i) {
+ destColor[i] = color;
+ }
+ break;
+ }
+ case kSBGRA_8888_GrPixelConfig: {
+ // TODO: ask Brian O. what to do here
+ GrColor swizzled = GrColorPackRGBA(b, g, r, a);
+
+ GrColor* destColor = (GrColor *) dest;
+ for (int i = 0; i < width * height; ++i) {
+ destColor[i] = swizzled;
+ }
+ break;
+ }
+ case kRGBA_1010102_GrPixelConfig: {
+ uint32_t r10 = SkScalarRoundToInt(colorf.fR * 1023.0f);
+ uint32_t g10 = SkScalarRoundToInt(colorf.fG * 1023.0f);
+ uint32_t b10 = SkScalarRoundToInt(colorf.fB * 1023.0f);
+ uint8_t a2 = SkScalarRoundToInt(colorf.fA * 3.0f);
+
+ uint32_t rgba1010102 = a2 << 30 | b10 << 20 | g10 << 10 | r10;
+
+ uint32_t* destColor = (uint32_t *) dest;
+ for (int i = 0; i < width * height; ++i) {
+ destColor[i] = rgba1010102;
+ }
+ break;
+ }
+ case kRGBA_float_GrPixelConfig: {
+ SkColor4f* destColor = (SkColor4f*) dest;
+ for (int i = 0; i < width * height; ++i) {
+ destColor[i] = colorf;
+ }
+ break;
+ }
+ case kRG_float_GrPixelConfig: {
+ float* destFloat = (float*) dest;
+ for (int i = 0; i < width * height; ++i, destFloat += 2) {
+ destFloat[0] = colorf.fR;
+ destFloat[1] = colorf.fG;
+ }
+ break;
+ }
+ case kAlpha_half_as_Red_GrPixelConfig: // fall through
+ case kAlpha_half_GrPixelConfig: {
+ SkHalf* destHalf = (SkHalf*) dest;
+ SkHalf alphaHalf = SkFloatToHalf(colorf.fA);
+ for (int i = 0; i < width * height; ++i) {
+ destHalf[i] = alphaHalf;
+ }
+ break;
+ }
+ case kRGBA_half_GrPixelConfig: // fall through
+ case kRGBA_half_Clamped_GrPixelConfig: {
+ SkHalf* destHalf = (SkHalf*) dest;
+ SkHalf rHalf = SkFloatToHalf(colorf.fR);
+ SkHalf gHalf = SkFloatToHalf(colorf.fG);
+ SkHalf bHalf = SkFloatToHalf(colorf.fB);
+ SkHalf aHalf = SkFloatToHalf(colorf.fA);
+ for (int i = 0; i < width * height; ++i, destHalf += 4) {
+ destHalf[0] = rHalf;
+ destHalf[1] = gHalf;
+ destHalf[2] = bHalf;
+ destHalf[3] = aHalf;
+ }
+ break;
+ }
+ default:
+ return false;
+ break;
+ }
+
+ return true;
+}
diff --git a/src/gpu/GrDataUtils.h b/src/gpu/GrDataUtils.h
new file mode 100644
index 0000000..a700a74
--- /dev/null
+++ b/src/gpu/GrDataUtils.h
@@ -0,0 +1,28 @@
+/*
+ * Copyright 2019 Google Inc.
+ *
+ * Use of this source code is governed by a BSD-style license that can be
+ * found in the LICENSE file.
+ */
+
+#ifndef GrDataUtils_DEFINED
+#define GrDataUtils_DEFINED
+
+#include "include/core/SkColor.h"
+#include "include/private/GrTypesPriv.h"
+
+// Fill in the width x height 'dest' with the munged version of 'color' that matches 'config'
+bool GrFillBufferWithColor(GrPixelConfig config, int width, int height,
+ const SkColor4f& color, void* dest);
+
+struct ETC1Block {
+ uint32_t fHigh;
+ uint32_t fLow;
+};
+
+int GrNumETC1Blocks(int w, int h);
+
+// Fill in 'blocks' with ETC1 blocks derived from 'color'
+void GrFillInETC1WithColor(const SkColor4f& color, ETC1Block* blocks, int numBlocks);
+
+#endif
diff --git a/src/gpu/GrGpu.h b/src/gpu/GrGpu.h
index 86aa109..630644a 100644
--- a/src/gpu/GrGpu.h
+++ b/src/gpu/GrGpu.h
@@ -403,10 +403,14 @@
/**
* Creates a texture directly in the backend API without wrapping it in a GrTexture.
* Must be matched with a call to deleteBackendTexture().
+ * Right now, the color is ignored if pixel data is provided.
+ * In the future, if neither a color nor pixels are provided then the backend texture
+ * will be uninitialized.
*/
virtual GrBackendTexture createBackendTexture(int w, int h, const GrBackendFormat&,
GrMipMapped, GrRenderable,
- const void* pixels, size_t rowBytes) = 0;
+ const void* pixels, size_t rowBytes,
+ const SkColor4f& = SkColors::kTransparent) = 0;
/**
* Frees a texture created by createBackendTexture(). If ownership of the backend
diff --git a/src/gpu/gl/GrGLGpu.cpp b/src/gpu/gl/GrGLGpu.cpp
index 07adb70..de653bc 100644
--- a/src/gpu/gl/GrGLGpu.cpp
+++ b/src/gpu/gl/GrGLGpu.cpp
@@ -21,6 +21,7 @@
#include "src/core/SkTraceEvent.h"
#include "src/gpu/GrContextPriv.h"
#include "src/gpu/GrCpuBuffer.h"
+#include "src/gpu/GrDataUtils.h"
#include "src/gpu/GrFixedClip.h"
#include "src/gpu/GrGpuResourcePriv.h"
#include "src/gpu/GrMesh.h"
@@ -4057,7 +4058,8 @@
const GrBackendFormat& format,
GrMipMapped mipMapped,
GrRenderable renderable,
- const void* pixels, size_t rowBytes) {
+ const void* pixels, size_t rowBytes,
+ const SkColor4f& colorf) {
this->handleDirtyContext();
const GrGLenum* glFormat = format.getGLFormat();
@@ -4123,6 +4125,13 @@
SkASSERT(GrRenderable::kNo == renderable);
SkASSERT(0 == rowBytes);
+ int numBlocks = GrNumETC1Blocks(w, h);
+ SkAutoTMalloc<ETC1Block> defaultStorage(numBlocks);
+ if (!pixels) {
+ GrFillInETC1WithColor(colorf, defaultStorage.get(), numBlocks);
+ pixels = defaultStorage.get();
+ }
+
for (int i = 0; i < mipLevelCount; ++i) {
// TODO: this isn't correct when pixels for additional mip levels are passed in
texels.get()[i] = { pixels, 0 };
@@ -4140,8 +4149,11 @@
size_t baseLayerSize = bpp * w * h;
SkAutoMalloc defaultStorage(baseLayerSize);
if (!pixels) {
- // Fill in the texture with all zeros so we don't have random garbage
- memset(defaultStorage.get(), 0, baseLayerSize);
+ if (!GrFillBufferWithColor(config, w, h, colorf, defaultStorage.get())) {
+ GL_CALL(DeleteTextures(1, &(info.fID)));
+ return GrBackendTexture();
+ }
+
pixels = defaultStorage.get();
rowBytes = trimRowBytes;
}
diff --git a/src/gpu/gl/GrGLGpu.h b/src/gpu/gl/GrGLGpu.h
index feb7597..869a3f2 100644
--- a/src/gpu/gl/GrGLGpu.h
+++ b/src/gpu/gl/GrGLGpu.h
@@ -138,7 +138,8 @@
int height) override;
GrBackendTexture createBackendTexture(int w, int h, const GrBackendFormat&,
GrMipMapped, GrRenderable,
- const void* pixels, size_t rowBytes) override;
+ const void* pixels, size_t rowBytes,
+ const SkColor4f& color = SkColors::kTransparent) override;
void deleteBackendTexture(const GrBackendTexture&) override;
#if GR_TEST_UTILS
diff --git a/src/gpu/mock/GrMockGpu.cpp b/src/gpu/mock/GrMockGpu.cpp
index 5102ef6..3c67b63 100644
--- a/src/gpu/mock/GrMockGpu.cpp
+++ b/src/gpu/mock/GrMockGpu.cpp
@@ -201,7 +201,8 @@
GrMipMapped mipMapped,
GrRenderable /* renderable */,
const void* /* pixels */,
- size_t /* rowBytes */) {
+ size_t /* rowBytes */,
+ const SkColor4f& /* color */) {
const GrPixelConfig* pixelConfig = format.getMockFormat();
if (!pixelConfig) {
diff --git a/src/gpu/mock/GrMockGpu.h b/src/gpu/mock/GrMockGpu.h
index 97c06cf..cf49972 100644
--- a/src/gpu/mock/GrMockGpu.h
+++ b/src/gpu/mock/GrMockGpu.h
@@ -123,7 +123,8 @@
int height) override;
GrBackendTexture createBackendTexture(int w, int h, const GrBackendFormat&,
GrMipMapped, GrRenderable,
- const void* pixels, size_t rowBytes) override;
+ const void* pixels, size_t rowBytes,
+ const SkColor4f& color = SkColors::kTransparent) override;
void deleteBackendTexture(const GrBackendTexture&) override;
#if GR_TEST_UTILS
diff --git a/src/gpu/mtl/GrMtlGpu.h b/src/gpu/mtl/GrMtlGpu.h
index 6a8c082..14a47b3 100644
--- a/src/gpu/mtl/GrMtlGpu.h
+++ b/src/gpu/mtl/GrMtlGpu.h
@@ -62,7 +62,9 @@
GrBackendTexture createBackendTexture(int w, int h, const GrBackendFormat&,
GrMipMapped, GrRenderable,
- const void* pixels, size_t rowBytes) override;
+ const void* pixels, size_t rowBytes,
+ const SkColor4f& color = SkColors::kTransparent) override;
+
void deleteBackendTexture(const GrBackendTexture&) override;
#if GR_TEST_UTILS
diff --git a/src/gpu/mtl/GrMtlGpu.mm b/src/gpu/mtl/GrMtlGpu.mm
index 5506f9c..8fae4b6 100644
--- a/src/gpu/mtl/GrMtlGpu.mm
+++ b/src/gpu/mtl/GrMtlGpu.mm
@@ -770,7 +770,8 @@
const GrBackendFormat& format,
GrMipMapped mipMapped,
GrRenderable renderable,
- const void* pixels, size_t rowBytes) {
+ const void* pixels, size_t rowBytes,
+ const SkColor4f& color) {
if (w > this->caps()->maxTextureSize() || h > this->caps()->maxTextureSize()) {
return GrBackendTexture();
}
diff --git a/src/gpu/vk/GrVkGpu.cpp b/src/gpu/vk/GrVkGpu.cpp
index 5cdbc70..7d5a9d5 100644
--- a/src/gpu/vk/GrVkGpu.cpp
+++ b/src/gpu/vk/GrVkGpu.cpp
@@ -1894,7 +1894,8 @@
const GrBackendFormat& format,
GrMipMapped mipMapped,
GrRenderable renderable,
- const void* srcData, size_t rowBytes) {
+ const void* srcData, size_t rowBytes,
+ const SkColor4f& color) {
this->handleDirtyContext();
diff --git a/src/gpu/vk/GrVkGpu.h b/src/gpu/vk/GrVkGpu.h
index 9b24df1..1caa2f0 100644
--- a/src/gpu/vk/GrVkGpu.h
+++ b/src/gpu/vk/GrVkGpu.h
@@ -82,7 +82,8 @@
GrBackendTexture createBackendTexture(int w, int h, const GrBackendFormat&,
GrMipMapped, GrRenderable,
- const void* pixels, size_t rowBytes) override;
+ const void* pixels, size_t rowBytes,
+ const SkColor4f& color = SkColors::kTransparent) override;
void deleteBackendTexture(const GrBackendTexture&) override;
#if GR_TEST_UTILS
bool isTestingOnlyBackendTexture(const GrBackendTexture&) const override;