blob: 9dcc25bff4e6a2fb4820235552b1785c06d0e491 [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"
Brian Salomonf30b1c12019-06-20 12:25:02 -04009#include "src/core/SkColorSpaceXformSteps.h"
10#include "src/core/SkTLazy.h"
Brian Salomonc42eb662019-06-24 17:13:00 -040011#include "src/core/SkTraceEvent.h"
Robert Phillipsb5e331a2019-05-28 10:58:09 -040012#include "src/core/SkUtils.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040013#include "src/gpu/GrColor.h"
Robert Phillips459b2952019-05-23 09:38:27 -040014
Robert Phillips8043f322019-05-31 08:11:36 -040015struct ETC1Block {
16 uint32_t fHigh;
17 uint32_t fLow;
18};
19
Robert Phillips459b2952019-05-23 09:38:27 -040020static const int kNumModifierTables = 8;
21static const int kNumPixelIndices = 4;
22
23// The index of each row in this table is the ETC1 table codeword
24// The index of each column in this table is the ETC1 pixel index value
25static const int kModifierTables[kNumModifierTables][kNumPixelIndices] = {
26 /* 0 */ { 2, 8, -2, -8 },
27 /* 1 */ { 5, 17, -5, -17 },
28 /* 2 */ { 9, 29, -9, -29 },
29 /* 3 */ { 13, 42, -13, -42 },
30 /* 4 */ { 18, 60, -18, -60 },
31 /* 5 */ { 24, 80, -24, -80 },
32 /* 6 */ { 33, 106, -33, -106 },
33 /* 7 */ { 47, 183, -47, -183 }
34};
35
36static inline int convert_5To8(int b) {
37 int c = b & 0x1f;
38 return (c << 3) | (c >> 2);
39}
40
41// Evaluate one of the entries in 'kModifierTables' to see how close it can get (r8,g8,b8) to
42// the original color (rOrig, gOrib, bOrig).
43static int test_table_entry(int rOrig, int gOrig, int bOrig,
44 int r8, int g8, int b8,
45 int table, int offset) {
46 SkASSERT(0 <= table && table < 8);
47 SkASSERT(0 <= offset && offset < 4);
48
49 r8 = SkTPin<uint8_t>(r8 + kModifierTables[table][offset], 0, 255);
50 g8 = SkTPin<uint8_t>(g8 + kModifierTables[table][offset], 0, 255);
51 b8 = SkTPin<uint8_t>(b8 + kModifierTables[table][offset], 0, 255);
52
53 return SkTAbs(rOrig - r8) + SkTAbs(gOrig - g8) + SkTAbs(bOrig - b8);
54}
55
56// Create an ETC1 compressed block that is filled with 'col'
57static void create_etc1_block(SkColor col, ETC1Block* block) {
58 block->fHigh = 0;
59 block->fLow = 0;
60
61 int rOrig = SkColorGetR(col);
62 int gOrig = SkColorGetG(col);
63 int bOrig = SkColorGetB(col);
64
65 int r5 = SkMulDiv255Round(31, rOrig);
66 int g5 = SkMulDiv255Round(31, gOrig);
67 int b5 = SkMulDiv255Round(31, bOrig);
68
69 int r8 = convert_5To8(r5);
70 int g8 = convert_5To8(g5);
71 int b8 = convert_5To8(b5);
72
73 // We always encode solid color textures as 555 + zero diffs
74 block->fHigh |= (r5 << 27) | (g5 << 19) | (b5 << 11) | 0x2;
75
76 int bestTableIndex = 0, bestPixelIndex = 0;
77 int bestSoFar = 1024;
78 for (int tableIndex = 0; tableIndex < kNumModifierTables; ++tableIndex) {
79 for (int pixelIndex = 0; pixelIndex < kNumPixelIndices; ++pixelIndex) {
80 int score = test_table_entry(rOrig, gOrig, bOrig, r8, g8, b8,
81 tableIndex, pixelIndex);
82
83 if (bestSoFar > score) {
84 bestSoFar = score;
85 bestTableIndex = tableIndex;
86 bestPixelIndex = pixelIndex;
87 }
88 }
89 }
90
91 block->fHigh |= (bestTableIndex << 5) | (bestTableIndex << 2);
92
93 for (int i = 0; i < 16; ++i) {
94 block->fLow |= bestPixelIndex << 2*i;
95 }
96}
97
Robert Phillips8043f322019-05-31 08:11:36 -040098static int num_ETC1_blocks(int w, int h) {
Robert Phillips459b2952019-05-23 09:38:27 -040099 if (w < 4) {
100 w = 1;
101 } else {
102 SkASSERT((w & 3) == 0);
103 w >>= 2;
104 }
105
106 if (h < 4) {
107 h = 1;
108 } else {
109 SkASSERT((h & 3) == 0);
110 h >>= 2;
111 }
112
113 return w * h;
114}
115
Brian Salomonbb8dde82019-06-27 10:52:13 -0400116size_t GrCompressedDataSize(SkImage::CompressionType type, int width, int height) {
117 switch (type) {
118 case SkImage::kETC1_CompressionType:
119 int numBlocks = num_ETC1_blocks(width, height);
120 return numBlocks * sizeof(ETC1Block);
121 }
122 SK_ABORT("Unexpected compression type");
123 return 0;
Robert Phillips8043f322019-05-31 08:11:36 -0400124}
125
Robert Phillips28a5a432019-06-07 12:46:21 -0400126// Fill in 'dest' with ETC1 blocks derived from 'colorf'
127static void fillin_ETC1_with_color(int width, int height, const SkColor4f& colorf, void* dest) {
Robert Phillips459b2952019-05-23 09:38:27 -0400128 SkColor color = colorf.toSkColor();
129
130 ETC1Block block;
131 create_etc1_block(color, &block);
132
Robert Phillips8043f322019-05-31 08:11:36 -0400133 int numBlocks = num_ETC1_blocks(width, height);
134
Robert Phillips459b2952019-05-23 09:38:27 -0400135 for (int i = 0; i < numBlocks; ++i) {
Robert Phillipse3bd6732019-05-29 14:20:35 -0400136 ((ETC1Block*)dest)[i] = block;
Robert Phillips459b2952019-05-23 09:38:27 -0400137 }
138}
139
Robert Phillips28a5a432019-06-07 12:46:21 -0400140// Fill in the width x height 'dest' with the munged version of 'colorf' that matches 'config'
141static bool fill_buffer_with_color(GrPixelConfig config, int width, int height,
142 const SkColor4f& colorf, void* dest) {
Robert Phillips459b2952019-05-23 09:38:27 -0400143 SkASSERT(kRGB_ETC1_GrPixelConfig != config);
144
145 GrColor color = colorf.toBytes_RGBA();
146
147 uint8_t r = GrColorUnpackR(color);
148 uint8_t g = GrColorUnpackG(color);
149 uint8_t b = GrColorUnpackB(color);
150 uint8_t a = GrColorUnpackA(color);
151
Robert Phillips459b2952019-05-23 09:38:27 -0400152 switch (config) {
153 case kAlpha_8_GrPixelConfig: // fall through
154 case kAlpha_8_as_Alpha_GrPixelConfig: // fall through
155 case kAlpha_8_as_Red_GrPixelConfig: {
156 memset(dest, a, width * height);
157 break;
158 }
159 case kGray_8_GrPixelConfig: // fall through
160 case kGray_8_as_Lum_GrPixelConfig: // fall through
161 case kGray_8_as_Red_GrPixelConfig: {
162 uint8_t gray8 = SkComputeLuminance(r, g, b);
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400163
Robert Phillips459b2952019-05-23 09:38:27 -0400164 memset(dest, gray8, width * height);
165 break;
166 }
167 case kRGB_565_GrPixelConfig: {
Robert Phillips459b2952019-05-23 09:38:27 -0400168 uint16_t rgb565 = SkPack888ToRGB16(r, g, b);
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400169
170 sk_memset16((uint16_t*) dest, rgb565, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400171 break;
172 }
173 case kRGBA_4444_GrPixelConfig: {
Robert Phillips459b2952019-05-23 09:38:27 -0400174 uint8_t r4 = (r >> 4) & 0xF;
175 uint8_t g4 = (g >> 4) & 0xF;
176 uint8_t b4 = (b >> 4) & 0xF;
177 uint8_t a4 = (a >> 4) & 0xF;
178
179 uint16_t rgba4444 = r4 << SK_R4444_SHIFT | g4 << SK_G4444_SHIFT |
180 b4 << SK_B4444_SHIFT | a4 << SK_A4444_SHIFT;
181
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400182 sk_memset16((uint16_t*) dest, rgba4444, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400183 break;
184 }
185 case kRGBA_8888_GrPixelConfig: {
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400186 sk_memset32((uint32_t *) dest, color, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400187 break;
188 }
189 case kRGB_888_GrPixelConfig: {
190 uint8_t* dest8 = (uint8_t*) dest;
191 for (int i = 0; i < width * height; ++i, dest8 += 3) {
192 dest8[0] = r;
193 dest8[1] = g;
194 dest8[2] = b;
195 }
196 break;
197 }
198 case kRGB_888X_GrPixelConfig: {
199 GrColor opaque = GrColorPackRGBA(r, g, b, 0xFF);
200
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400201 sk_memset32((uint32_t *) dest, opaque, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400202 break;
203 }
204 case kRG_88_GrPixelConfig: {
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400205 uint16_t rg88 = (r << 8) | g;
206
207 sk_memset16((uint16_t*) dest, rg88, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400208 break;
209 }
210 case kBGRA_8888_GrPixelConfig: {
211 GrColor swizzled = GrColorPackRGBA(b, g, r, a);
212
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400213 sk_memset32((uint32_t *) dest, swizzled, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400214 break;
215 }
216 case kSRGBA_8888_GrPixelConfig: {
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400217 sk_memset32((uint32_t *) dest, color, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400218 break;
219 }
Robert Phillips459b2952019-05-23 09:38:27 -0400220 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 Phillipsfe18de52019-06-06 17:21:50 -0400265 case kR_16_GrPixelConfig: {
266 uint16_t r16 = SkScalarRoundToInt(colorf.fR * 65535.0f);
267 sk_memset16((uint16_t*) dest, r16, width * height);
268 break;
269 }
270 case kRG_1616_GrPixelConfig: {
271 uint16_t r16 = SkScalarRoundToInt(colorf.fR * 65535.0f);
272 uint16_t g16 = SkScalarRoundToInt(colorf.fG * 65535.0f);
273
274 uint32_t rg1616 = r16 << 16 | g16;
275
276 sk_memset32((uint32_t*) dest, rg1616, width * height);
277 break;
278 }
Robert Phillips66a46032019-06-18 08:00:42 -0400279 // Experimental (for Y416 and mutant P016/P010)
280 case kRGBA_16161616_GrPixelConfig: {
281 uint64_t r16 = SkScalarRoundToInt(colorf.fR * 65535.0f);
282 uint64_t g16 = SkScalarRoundToInt(colorf.fG * 65535.0f);
283 uint64_t b16 = SkScalarRoundToInt(colorf.fB * 65535.0f);
284 uint64_t a16 = SkScalarRoundToInt(colorf.fA * 65535.0f);
285
286 uint64_t rgba16161616 = (a16 << 48) | (b16 << 32) | (g16 << 16) | r16;
287 sk_memset64((uint64_t*) dest, rgba16161616, width * height);
288 break;
289 }
290 case kRG_half_GrPixelConfig: {
291 uint32_t rHalf = SkFloatToHalf(colorf.fR);
292 uint32_t gHalf = SkFloatToHalf(colorf.fG);
293
294 uint32_t rgHalf = (rHalf << 16) | gHalf;
295
296 sk_memset32((uint32_t *) dest, rgHalf, width * height);
297 break;
298 }
Robert Phillips459b2952019-05-23 09:38:27 -0400299 default:
300 return false;
301 break;
302 }
303
304 return true;
305}
Robert Phillips28a5a432019-06-07 12:46:21 -0400306
Brian Salomonbb8dde82019-06-27 10:52:13 -0400307size_t GrComputeTightCombinedBufferSize(size_t bytesPerPixel, int baseWidth, int baseHeight,
308 SkTArray<size_t>* individualMipOffsets, int mipLevelCount) {
Robert Phillips28a5a432019-06-07 12:46:21 -0400309 SkASSERT(individualMipOffsets && !individualMipOffsets->count());
310 SkASSERT(mipLevelCount >= 1);
311
312 individualMipOffsets->push_back(0);
313
314 size_t combinedBufferSize = baseWidth * bytesPerPixel * baseHeight;
Robert Phillips28a5a432019-06-07 12:46:21 -0400315 int currentWidth = baseWidth;
316 int currentHeight = baseHeight;
317
318 // The Vulkan spec for copying a buffer to an image requires that the alignment must be at
319 // least 4 bytes and a multiple of the bytes per pixel of the image config.
320 SkASSERT(bytesPerPixel == 1 || bytesPerPixel == 2 || bytesPerPixel == 3 ||
321 bytesPerPixel == 4 || bytesPerPixel == 8 || bytesPerPixel == 16);
322 int desiredAlignment = (bytesPerPixel == 3) ? 12 : (bytesPerPixel > 4 ? bytesPerPixel : 4);
323
324 for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; ++currentMipLevel) {
325 currentWidth = SkTMax(1, currentWidth / 2);
326 currentHeight = SkTMax(1, currentHeight / 2);
327
Brian Salomonbb8dde82019-06-27 10:52:13 -0400328 size_t trimmedSize = currentWidth * bytesPerPixel * currentHeight;
Robert Phillips28a5a432019-06-07 12:46:21 -0400329 const size_t alignmentDiff = combinedBufferSize % desiredAlignment;
330 if (alignmentDiff != 0) {
331 combinedBufferSize += desiredAlignment - alignmentDiff;
332 }
333 SkASSERT((0 == combinedBufferSize % 4) && (0 == combinedBufferSize % bytesPerPixel));
334
335 individualMipOffsets->push_back(combinedBufferSize);
336 combinedBufferSize += trimmedSize;
337 }
338
339 SkASSERT(individualMipOffsets->count() == mipLevelCount);
340 return combinedBufferSize;
341}
342
Brian Salomonbb8dde82019-06-27 10:52:13 -0400343void GrFillInData(GrPixelConfig config, int baseWidth, int baseHeight,
Robert Phillips28a5a432019-06-07 12:46:21 -0400344 const SkTArray<size_t>& individualMipOffsets, char* dstPixels,
345 const SkColor4f& colorf) {
Brian Salomonc42eb662019-06-24 17:13:00 -0400346 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
Brian Salomonbb8dde82019-06-27 10:52:13 -0400347 SkASSERT(!GrPixelConfigIsCompressed(config));
Robert Phillips28a5a432019-06-07 12:46:21 -0400348 int mipLevels = individualMipOffsets.count();
349
350 int currentWidth = baseWidth;
351 int currentHeight = baseHeight;
352 for (int currentMipLevel = 0; currentMipLevel < mipLevels; ++currentMipLevel) {
353 size_t offset = individualMipOffsets[currentMipLevel];
354
Brian Salomonbb8dde82019-06-27 10:52:13 -0400355 fill_buffer_with_color(config, currentWidth, currentHeight, colorf, &(dstPixels[offset]));
Robert Phillips28a5a432019-06-07 12:46:21 -0400356 currentWidth = SkTMax(1, currentWidth / 2);
357 currentHeight = SkTMax(1, currentHeight / 2);
358 }
359}
360
Brian Salomonbb8dde82019-06-27 10:52:13 -0400361void GrFillInCompressedData(SkImage::CompressionType type, int baseWidth, int baseHeight,
362 char* dstPixels, const SkColor4f& colorf) {
363 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
364 int currentWidth = baseWidth;
365 int currentHeight = baseHeight;
366 if (SkImage::kETC1_CompressionType == type) {
367 fillin_ETC1_with_color(currentWidth, currentHeight, colorf, dstPixels);
368 }
369}
370
Brian Salomonf30b1c12019-06-20 12:25:02 -0400371static GrSwizzle get_load_and_get_swizzle(GrColorType ct, SkRasterPipeline::StockStage* load,
372 bool* isNormalized) {
373 GrSwizzle swizzle("rgba");
374 *isNormalized = true;
375 switch (ct) {
376 case GrColorType::kAlpha_8: *load = SkRasterPipeline::load_a8; break;
377 case GrColorType::kBGR_565: *load = SkRasterPipeline::load_565; break;
378 case GrColorType::kABGR_4444: *load = SkRasterPipeline::load_4444; break;
379 case GrColorType::kRGBA_8888: *load = SkRasterPipeline::load_8888; break;
380 case GrColorType::kRG_88: *load = SkRasterPipeline::load_rg88; break;
381 case GrColorType::kRGBA_1010102: *load = SkRasterPipeline::load_1010102; break;
382 case GrColorType::kAlpha_F16: *load = SkRasterPipeline::load_af16; break;
383 case GrColorType::kRGBA_F16_Clamped: *load = SkRasterPipeline::load_f16; break;
384 case GrColorType::kRG_1616: *load = SkRasterPipeline::load_rg1616; break;
385 case GrColorType::kRGBA_16161616: *load = SkRasterPipeline::load_16161616; break;
386
Brian Salomone14cfbe2019-06-24 15:00:58 -0400387 case GrColorType::kRG_F16: *load = SkRasterPipeline::load_rgf16;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400388 *isNormalized = false;
389 break;
390 case GrColorType::kRGBA_F16: *load = SkRasterPipeline::load_f16;
391 *isNormalized = false;
392 break;
393 case GrColorType::kRG_F32: *load = SkRasterPipeline::load_rgf32;
394 *isNormalized = false;
395 break;
396 case GrColorType::kRGBA_F32: *load = SkRasterPipeline::load_f32;
397 *isNormalized = false;
398 break;
399 case GrColorType::kR_16: *load = SkRasterPipeline::load_a16;
400 swizzle = GrSwizzle("a001");
401 break;
402 case GrColorType::kGray_8: *load = SkRasterPipeline::load_a8;
403 swizzle = GrSwizzle("aaa1");
404 break;
405 case GrColorType::kBGRA_8888: *load = SkRasterPipeline::load_8888;
406 swizzle = GrSwizzle("bgra");
407 break;
408 case GrColorType::kRGB_888x: *load = SkRasterPipeline::load_8888;
409 swizzle = GrSwizzle("rgb1");
410 break;
411
412 case GrColorType::kUnknown:
Brian Salomonf30b1c12019-06-20 12:25:02 -0400413 SK_ABORT("unexpected CT");
414 }
415 return swizzle;
416}
417
418static GrSwizzle get_dst_swizzle_and_store(GrColorType ct, SkRasterPipeline::StockStage* store,
419 bool* isNormalized) {
420 GrSwizzle swizzle("rgba");
421 *isNormalized = true;
422 switch (ct) {
423 case GrColorType::kAlpha_8: *store = SkRasterPipeline::store_a8; break;
424 case GrColorType::kBGR_565: *store = SkRasterPipeline::store_565; break;
425 case GrColorType::kABGR_4444: *store = SkRasterPipeline::store_4444; break;
426 case GrColorType::kRGBA_8888: *store = SkRasterPipeline::store_8888; break;
427 case GrColorType::kRG_88: *store = SkRasterPipeline::store_rg88; break;
428 case GrColorType::kRGBA_1010102: *store = SkRasterPipeline::store_1010102; break;
429 case GrColorType::kRGBA_F16_Clamped: *store = SkRasterPipeline::store_f16; break;
430 case GrColorType::kRG_1616: *store = SkRasterPipeline::store_rg1616; break;
431 case GrColorType::kRGBA_16161616: *store = SkRasterPipeline::store_16161616; break;
432
Brian Salomone14cfbe2019-06-24 15:00:58 -0400433 case GrColorType::kRG_F16: *store = SkRasterPipeline::store_rgf16;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400434 *isNormalized = false;
435 break;
436 case GrColorType::kAlpha_F16: *store = SkRasterPipeline::store_af16;
437 *isNormalized = false;
438 break;
439 case GrColorType::kRGBA_F16: *store = SkRasterPipeline::store_f16;
440 *isNormalized = false;
441 break;
442 case GrColorType::kRG_F32: *store = SkRasterPipeline::store_rgf32;
443 *isNormalized = false;
444 break;
445 case GrColorType::kRGBA_F32: *store = SkRasterPipeline::store_f32;
446 *isNormalized = false;
447 break;
448 case GrColorType::kR_16: swizzle = GrSwizzle("000r");
449 *store = SkRasterPipeline::store_a16;
450 break;
451 case GrColorType::kBGRA_8888: swizzle = GrSwizzle("bgra");
452 *store = SkRasterPipeline::store_8888;
453 break;
454 case GrColorType::kRGB_888x: swizzle = GrSwizzle("rgb1");
455 *store = SkRasterPipeline::store_8888;
456 break;
457
458 case GrColorType::kGray_8: // not currently supported as output
459 case GrColorType::kUnknown:
Brian Salomonf30b1c12019-06-20 12:25:02 -0400460 SK_ABORT("unexpected CT");
461 }
462 return swizzle;
463}
464
465static inline void append_clamp_gamut(SkRasterPipeline* pipeline) {
466 // SkRasterPipeline may not know our color type and also doesn't like caller to directly
467 // append clamp_gamut. Fake it out.
468 static SkImageInfo fakeII = SkImageInfo::MakeN32Premul(1, 1);
469 pipeline->append_gamut_clamp_if_normalized(fakeII);
470}
471
472bool GrConvertPixels(const GrPixelInfo& dstInfo, void* dst, const GrPixelInfo& srcInfo,
473 const void* src, GrSwizzle swizzle) {
Brian Salomonc42eb662019-06-24 17:13:00 -0400474 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
475
Brian Salomonf30b1c12019-06-20 12:25:02 -0400476 if (dstInfo.fWidth != srcInfo.fWidth || srcInfo.fHeight != dstInfo.fHeight) {
477 return false;
478 }
479 if (dstInfo.fWidth <= 0 || dstInfo.fHeight <= 0) {
480 return false;
481 }
482 if (GrColorTypeComponentFlags(dstInfo.fColorInfo.fColorType) & kGray_SkColorTypeComponentFlag) {
483 // We don't currently support conversion to Gray.
484 return false;
485 }
486 size_t srcBpp = GrColorTypeBytesPerPixel(srcInfo.fColorInfo.fColorType);
487 size_t dstBpp = GrColorTypeBytesPerPixel(dstInfo.fColorInfo.fColorType);
488 if (!srcBpp || !dstBpp) {
489 // Either src or dst is compressed or kUnknown.
490 return false;
491 }
492 // SkRasterPipeline operates on row-pixels not row-bytes.
493 SkASSERT(dstInfo.fRowBytes % dstBpp == 0);
494 SkASSERT(srcInfo.fRowBytes % srcBpp == 0);
495
496 SkRasterPipeline::StockStage load;
497 bool srcIsNormalized;
498 auto loadSwizzle =
499 get_load_and_get_swizzle(srcInfo.fColorInfo.fColorType, &load, &srcIsNormalized);
500 loadSwizzle = GrSwizzle::Concat(loadSwizzle, swizzle);
501
502 SkRasterPipeline::StockStage store;
503 bool dstIsNormalized;
504 auto storeSwizzle =
505 get_dst_swizzle_and_store(dstInfo.fColorInfo.fColorType, &store, &dstIsNormalized);
506
507 bool alphaOrCSConversion =
508 (srcInfo.fColorInfo.fAlphaType != dstInfo.fColorInfo.fAlphaType &&
509 srcInfo.fColorInfo.fAlphaType != kOpaque_SkAlphaType) ||
510 !SkColorSpace::Equals(srcInfo.fColorInfo.fColorSpace, dstInfo.fColorInfo.fColorSpace);
511
512 bool clampGamut;
513 SkTLazy<SkColorSpaceXformSteps> steps;
514 GrSwizzle loadStoreSwizzle;
515 if (alphaOrCSConversion) {
516 steps.init(srcInfo.fColorInfo.fColorSpace, srcInfo.fColorInfo.fAlphaType,
517 dstInfo.fColorInfo.fColorSpace, dstInfo.fColorInfo.fAlphaType);
518 clampGamut = dstIsNormalized && dstInfo.fColorInfo.fAlphaType == kPremul_SkAlphaType;
519 } else {
520 clampGamut = dstIsNormalized && !srcIsNormalized &&
521 dstInfo.fColorInfo.fAlphaType == kPremul_SkAlphaType;
522 if (!clampGamut) {
523 loadStoreSwizzle = GrSwizzle::Concat(loadSwizzle, storeSwizzle);
524 }
525 }
526 int cnt = 1;
527 int height = srcInfo.fHeight;
528 SkRasterPipeline_MemoryCtx srcCtx{const_cast<void*>(src), SkToInt(srcInfo.fRowBytes / srcBpp)},
529 dstCtx{ dst , SkToInt(dstInfo.fRowBytes / dstBpp)};
530
531 if (srcInfo.fOrigin != dstInfo.fOrigin) {
532 // It *almost* works to point the src at the last row and negate the stride and run the
533 // whole rectangle. However, SkRasterPipeline::run()'s control loop uses size_t loop
534 // variables so it winds up relying on unsigned overflow math. It works out in practice
535 // but UBSAN says "no!" as it's technically undefined and in theory a compiler could emit
536 // code that didn't do what is intended. So we go one row at a time. :(
537 srcCtx.pixels = static_cast<char*>(srcCtx.pixels) + srcInfo.fRowBytes * (height - 1);
538 std::swap(cnt, height);
539 }
540 for (int i = 0; i < cnt; ++i) {
541 SkRasterPipeline_<256> pipeline;
542 pipeline.append(load, &srcCtx);
543
544 if (alphaOrCSConversion) {
545 loadSwizzle.apply(&pipeline);
546 steps->apply(&pipeline, srcIsNormalized);
547 if (clampGamut) {
548 append_clamp_gamut(&pipeline);
549 }
550 storeSwizzle.apply(&pipeline);
551 } else {
552 if (clampGamut) {
553 loadSwizzle.apply(&pipeline);
554 append_clamp_gamut(&pipeline);
555 storeSwizzle.apply(&pipeline);
556 } else {
557 loadStoreSwizzle.apply(&pipeline);
558 }
559 }
560 pipeline.append(store, &dstCtx);
561 pipeline.run(0, 0, srcInfo.fWidth, height);
562 srcCtx.pixels = static_cast<char*>(srcCtx.pixels) - srcInfo.fRowBytes;
563 dstCtx.pixels = static_cast<char*>(dstCtx.pixels) + dstInfo.fRowBytes;
564 }
565 return true;
566}