blob: 9537e732b713cb8282a06e16b400b6dc9779ed05 [file] [log] [blame]
Robert Phillips459b2952019-05-23 09:38:27 -04001/*
2 * Copyright 2019 Google Inc.
3 *
4 * Use of this source code is governed by a BSD-style license that can be
5 * found in the LICENSE file.
6 */
7
Mike Klein4b432fa2019-06-06 11:44:05 -05008#include "src/gpu/GrDataUtils.h"
Robert Phillipsc34d9932019-06-18 11:57:12 +00009
Robert Phillipsb5e331a2019-05-28 10:58:09 -040010#include "src/core/SkUtils.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040011#include "src/gpu/GrColor.h"
Robert Phillips459b2952019-05-23 09:38:27 -040012
Robert Phillips8043f322019-05-31 08:11:36 -040013struct ETC1Block {
14 uint32_t fHigh;
15 uint32_t fLow;
16};
17
Robert Phillips459b2952019-05-23 09:38:27 -040018static const int kNumModifierTables = 8;
19static const int kNumPixelIndices = 4;
20
21// The index of each row in this table is the ETC1 table codeword
22// The index of each column in this table is the ETC1 pixel index value
23static const int kModifierTables[kNumModifierTables][kNumPixelIndices] = {
24 /* 0 */ { 2, 8, -2, -8 },
25 /* 1 */ { 5, 17, -5, -17 },
26 /* 2 */ { 9, 29, -9, -29 },
27 /* 3 */ { 13, 42, -13, -42 },
28 /* 4 */ { 18, 60, -18, -60 },
29 /* 5 */ { 24, 80, -24, -80 },
30 /* 6 */ { 33, 106, -33, -106 },
31 /* 7 */ { 47, 183, -47, -183 }
32};
33
34static inline int convert_5To8(int b) {
35 int c = b & 0x1f;
36 return (c << 3) | (c >> 2);
37}
38
39// Evaluate one of the entries in 'kModifierTables' to see how close it can get (r8,g8,b8) to
40// the original color (rOrig, gOrib, bOrig).
41static int test_table_entry(int rOrig, int gOrig, int bOrig,
42 int r8, int g8, int b8,
43 int table, int offset) {
44 SkASSERT(0 <= table && table < 8);
45 SkASSERT(0 <= offset && offset < 4);
46
47 r8 = SkTPin<uint8_t>(r8 + kModifierTables[table][offset], 0, 255);
48 g8 = SkTPin<uint8_t>(g8 + kModifierTables[table][offset], 0, 255);
49 b8 = SkTPin<uint8_t>(b8 + kModifierTables[table][offset], 0, 255);
50
51 return SkTAbs(rOrig - r8) + SkTAbs(gOrig - g8) + SkTAbs(bOrig - b8);
52}
53
54// Create an ETC1 compressed block that is filled with 'col'
55static void create_etc1_block(SkColor col, ETC1Block* block) {
56 block->fHigh = 0;
57 block->fLow = 0;
58
59 int rOrig = SkColorGetR(col);
60 int gOrig = SkColorGetG(col);
61 int bOrig = SkColorGetB(col);
62
63 int r5 = SkMulDiv255Round(31, rOrig);
64 int g5 = SkMulDiv255Round(31, gOrig);
65 int b5 = SkMulDiv255Round(31, bOrig);
66
67 int r8 = convert_5To8(r5);
68 int g8 = convert_5To8(g5);
69 int b8 = convert_5To8(b5);
70
71 // We always encode solid color textures as 555 + zero diffs
72 block->fHigh |= (r5 << 27) | (g5 << 19) | (b5 << 11) | 0x2;
73
74 int bestTableIndex = 0, bestPixelIndex = 0;
75 int bestSoFar = 1024;
76 for (int tableIndex = 0; tableIndex < kNumModifierTables; ++tableIndex) {
77 for (int pixelIndex = 0; pixelIndex < kNumPixelIndices; ++pixelIndex) {
78 int score = test_table_entry(rOrig, gOrig, bOrig, r8, g8, b8,
79 tableIndex, pixelIndex);
80
81 if (bestSoFar > score) {
82 bestSoFar = score;
83 bestTableIndex = tableIndex;
84 bestPixelIndex = pixelIndex;
85 }
86 }
87 }
88
89 block->fHigh |= (bestTableIndex << 5) | (bestTableIndex << 2);
90
91 for (int i = 0; i < 16; ++i) {
92 block->fLow |= bestPixelIndex << 2*i;
93 }
94}
95
Robert Phillips8043f322019-05-31 08:11:36 -040096static int num_ETC1_blocks(int w, int h) {
Robert Phillips459b2952019-05-23 09:38:27 -040097 if (w < 4) {
98 w = 1;
99 } else {
100 SkASSERT((w & 3) == 0);
101 w >>= 2;
102 }
103
104 if (h < 4) {
105 h = 1;
106 } else {
107 SkASSERT((h & 3) == 0);
108 h >>= 2;
109 }
110
111 return w * h;
112}
113
Robert Phillips8043f322019-05-31 08:11:36 -0400114size_t GrETC1CompressedDataSize(int width, int height) {
115 int numBlocks = num_ETC1_blocks(width, height);
116
117 return numBlocks * sizeof(ETC1Block);
118}
119
Robert Phillips28a5a432019-06-07 12:46:21 -0400120// Fill in 'dest' with ETC1 blocks derived from 'colorf'
121static void fillin_ETC1_with_color(int width, int height, const SkColor4f& colorf, void* dest) {
Robert Phillips459b2952019-05-23 09:38:27 -0400122 SkColor color = colorf.toSkColor();
123
124 ETC1Block block;
125 create_etc1_block(color, &block);
126
Robert Phillips8043f322019-05-31 08:11:36 -0400127 int numBlocks = num_ETC1_blocks(width, height);
128
Robert Phillips459b2952019-05-23 09:38:27 -0400129 for (int i = 0; i < numBlocks; ++i) {
Robert Phillipse3bd6732019-05-29 14:20:35 -0400130 ((ETC1Block*)dest)[i] = block;
Robert Phillips459b2952019-05-23 09:38:27 -0400131 }
132}
133
Robert Phillips28a5a432019-06-07 12:46:21 -0400134// Fill in the width x height 'dest' with the munged version of 'colorf' that matches 'config'
135static bool fill_buffer_with_color(GrPixelConfig config, int width, int height,
136 const SkColor4f& colorf, void* dest) {
Robert Phillips459b2952019-05-23 09:38:27 -0400137 SkASSERT(kRGB_ETC1_GrPixelConfig != config);
138
139 GrColor color = colorf.toBytes_RGBA();
140
141 uint8_t r = GrColorUnpackR(color);
142 uint8_t g = GrColorUnpackG(color);
143 uint8_t b = GrColorUnpackB(color);
144 uint8_t a = GrColorUnpackA(color);
145
Robert Phillips459b2952019-05-23 09:38:27 -0400146 switch (config) {
147 case kAlpha_8_GrPixelConfig: // fall through
148 case kAlpha_8_as_Alpha_GrPixelConfig: // fall through
149 case kAlpha_8_as_Red_GrPixelConfig: {
150 memset(dest, a, width * height);
151 break;
152 }
153 case kGray_8_GrPixelConfig: // fall through
154 case kGray_8_as_Lum_GrPixelConfig: // fall through
155 case kGray_8_as_Red_GrPixelConfig: {
156 uint8_t gray8 = SkComputeLuminance(r, g, b);
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400157
Robert Phillips459b2952019-05-23 09:38:27 -0400158 memset(dest, gray8, width * height);
159 break;
160 }
161 case kRGB_565_GrPixelConfig: {
Robert Phillips459b2952019-05-23 09:38:27 -0400162 uint16_t rgb565 = SkPack888ToRGB16(r, g, b);
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400163
164 sk_memset16((uint16_t*) dest, rgb565, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400165 break;
166 }
167 case kRGBA_4444_GrPixelConfig: {
Robert Phillips459b2952019-05-23 09:38:27 -0400168 uint8_t r4 = (r >> 4) & 0xF;
169 uint8_t g4 = (g >> 4) & 0xF;
170 uint8_t b4 = (b >> 4) & 0xF;
171 uint8_t a4 = (a >> 4) & 0xF;
172
173 uint16_t rgba4444 = r4 << SK_R4444_SHIFT | g4 << SK_G4444_SHIFT |
174 b4 << SK_B4444_SHIFT | a4 << SK_A4444_SHIFT;
175
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400176 sk_memset16((uint16_t*) dest, rgba4444, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400177 break;
178 }
179 case kRGBA_8888_GrPixelConfig: {
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400180 sk_memset32((uint32_t *) dest, color, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400181 break;
182 }
183 case kRGB_888_GrPixelConfig: {
184 uint8_t* dest8 = (uint8_t*) dest;
185 for (int i = 0; i < width * height; ++i, dest8 += 3) {
186 dest8[0] = r;
187 dest8[1] = g;
188 dest8[2] = b;
189 }
190 break;
191 }
192 case kRGB_888X_GrPixelConfig: {
193 GrColor opaque = GrColorPackRGBA(r, g, b, 0xFF);
194
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400195 sk_memset32((uint32_t *) dest, opaque, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400196 break;
197 }
198 case kRG_88_GrPixelConfig: {
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400199 uint16_t rg88 = (r << 8) | g;
200
201 sk_memset16((uint16_t*) dest, rg88, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400202 break;
203 }
204 case kBGRA_8888_GrPixelConfig: {
205 GrColor swizzled = GrColorPackRGBA(b, g, r, a);
206
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400207 sk_memset32((uint32_t *) dest, swizzled, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400208 break;
209 }
210 case kSRGBA_8888_GrPixelConfig: {
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400211 sk_memset32((uint32_t *) dest, color, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400212 break;
213 }
214 case kSBGRA_8888_GrPixelConfig: {
Robert Phillips459b2952019-05-23 09:38:27 -0400215 GrColor swizzled = GrColorPackRGBA(b, g, r, a);
216
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400217 sk_memset32((uint32_t *) dest, swizzled, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400218 break;
219 }
220 case kRGBA_1010102_GrPixelConfig: {
221 uint32_t r10 = SkScalarRoundToInt(colorf.fR * 1023.0f);
222 uint32_t g10 = SkScalarRoundToInt(colorf.fG * 1023.0f);
223 uint32_t b10 = SkScalarRoundToInt(colorf.fB * 1023.0f);
224 uint8_t a2 = SkScalarRoundToInt(colorf.fA * 3.0f);
225
226 uint32_t rgba1010102 = a2 << 30 | b10 << 20 | g10 << 10 | r10;
227
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400228 sk_memset32((uint32_t *) dest, rgba1010102, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400229 break;
230 }
231 case kRGBA_float_GrPixelConfig: {
232 SkColor4f* destColor = (SkColor4f*) dest;
233 for (int i = 0; i < width * height; ++i) {
234 destColor[i] = colorf;
235 }
236 break;
237 }
238 case kRG_float_GrPixelConfig: {
239 float* destFloat = (float*) dest;
240 for (int i = 0; i < width * height; ++i, destFloat += 2) {
241 destFloat[0] = colorf.fR;
242 destFloat[1] = colorf.fG;
243 }
244 break;
245 }
246 case kAlpha_half_as_Red_GrPixelConfig: // fall through
247 case kAlpha_half_GrPixelConfig: {
Robert Phillips459b2952019-05-23 09:38:27 -0400248 SkHalf alphaHalf = SkFloatToHalf(colorf.fA);
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400249
250 sk_memset16((uint16_t *) dest, alphaHalf, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400251 break;
252 }
253 case kRGBA_half_GrPixelConfig: // fall through
254 case kRGBA_half_Clamped_GrPixelConfig: {
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400255 uint64_t rHalf = SkFloatToHalf(colorf.fR);
256 uint64_t gHalf = SkFloatToHalf(colorf.fG);
257 uint64_t bHalf = SkFloatToHalf(colorf.fB);
258 uint64_t aHalf = SkFloatToHalf(colorf.fA);
259
260 uint64_t rgbaHalf = (aHalf << 48) | (bHalf << 32) | (gHalf << 16) | rHalf;
261
262 sk_memset64((uint64_t *) dest, rgbaHalf, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400263 break;
264 }
Robert Phillipsf83c4682019-06-13 16:49:21 +0000265 // Experimental (for P016 and P010)
Robert Phillipsfe18de52019-06-06 17:21:50 -0400266 case kR_16_GrPixelConfig: {
267 uint16_t r16 = SkScalarRoundToInt(colorf.fR * 65535.0f);
268 sk_memset16((uint16_t*) dest, r16, width * height);
269 break;
270 }
271 case kRG_1616_GrPixelConfig: {
272 uint16_t r16 = SkScalarRoundToInt(colorf.fR * 65535.0f);
273 uint16_t g16 = SkScalarRoundToInt(colorf.fG * 65535.0f);
274
275 uint32_t rg1616 = r16 << 16 | g16;
276
277 sk_memset32((uint32_t*) dest, rg1616, width * height);
278 break;
279 }
Robert Phillips459b2952019-05-23 09:38:27 -0400280 default:
281 return false;
282 break;
283 }
284
285 return true;
286}
Robert Phillips28a5a432019-06-07 12:46:21 -0400287
288size_t GrComputeTightCombinedBufferSize(GrCompression compression, size_t bytesPerPixel,
289 int baseWidth, int baseHeight,
290 SkTArray<size_t>* individualMipOffsets,
291 int mipLevelCount) {
292 SkASSERT(individualMipOffsets && !individualMipOffsets->count());
293 SkASSERT(mipLevelCount >= 1);
294
295 individualMipOffsets->push_back(0);
296
297 size_t combinedBufferSize = baseWidth * bytesPerPixel * baseHeight;
298 if (GrCompression::kETC1 == compression) {
299 SkASSERT(0 == bytesPerPixel);
300 bytesPerPixel = 4; // munge Bpp to make the following code work (and not assert)
301 combinedBufferSize = GrETC1CompressedDataSize(baseWidth, baseHeight);
302 }
303
304 int currentWidth = baseWidth;
305 int currentHeight = baseHeight;
306
307 // The Vulkan spec for copying a buffer to an image requires that the alignment must be at
308 // least 4 bytes and a multiple of the bytes per pixel of the image config.
309 SkASSERT(bytesPerPixel == 1 || bytesPerPixel == 2 || bytesPerPixel == 3 ||
310 bytesPerPixel == 4 || bytesPerPixel == 8 || bytesPerPixel == 16);
311 int desiredAlignment = (bytesPerPixel == 3) ? 12 : (bytesPerPixel > 4 ? bytesPerPixel : 4);
312
313 for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; ++currentMipLevel) {
314 currentWidth = SkTMax(1, currentWidth / 2);
315 currentHeight = SkTMax(1, currentHeight / 2);
316
317 size_t trimmedSize;
318 if (GrCompression::kETC1 == compression) {
319 trimmedSize = GrETC1CompressedDataSize(currentWidth, currentHeight);
320 } else {
321 trimmedSize = currentWidth * bytesPerPixel * currentHeight;
322 }
323 const size_t alignmentDiff = combinedBufferSize % desiredAlignment;
324 if (alignmentDiff != 0) {
325 combinedBufferSize += desiredAlignment - alignmentDiff;
326 }
327 SkASSERT((0 == combinedBufferSize % 4) && (0 == combinedBufferSize % bytesPerPixel));
328
329 individualMipOffsets->push_back(combinedBufferSize);
330 combinedBufferSize += trimmedSize;
331 }
332
333 SkASSERT(individualMipOffsets->count() == mipLevelCount);
334 return combinedBufferSize;
335}
336
337void GrFillInData(GrCompression compression, GrPixelConfig config,
338 int baseWidth, int baseHeight,
339 const SkTArray<size_t>& individualMipOffsets, char* dstPixels,
340 const SkColor4f& colorf) {
341
342 int mipLevels = individualMipOffsets.count();
343
344 int currentWidth = baseWidth;
345 int currentHeight = baseHeight;
346 for (int currentMipLevel = 0; currentMipLevel < mipLevels; ++currentMipLevel) {
347 size_t offset = individualMipOffsets[currentMipLevel];
348
349 if (GrCompression::kETC1 == compression) {
350 // TODO: compute the ETC1 block for 'colorf' just once
351 fillin_ETC1_with_color(currentWidth, currentHeight, colorf, &(dstPixels[offset]));
352 } else {
353 fill_buffer_with_color(config, currentWidth, currentHeight, colorf,
354 &(dstPixels[offset]));
355 }
356
357 currentWidth = SkTMax(1, currentWidth / 2);
358 currentHeight = SkTMax(1, currentHeight / 2);
359 }
360}
361