blob: faf921ac4bfc71c56856345578381cf0bd4ab336 [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"
Robert Phillipsb5e331a2019-05-28 10:58:09 -040011#include "src/core/SkUtils.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040012#include "src/gpu/GrColor.h"
Robert Phillips459b2952019-05-23 09:38:27 -040013
Robert Phillips8043f322019-05-31 08:11:36 -040014struct ETC1Block {
15 uint32_t fHigh;
16 uint32_t fLow;
17};
18
Robert Phillips459b2952019-05-23 09:38:27 -040019static const int kNumModifierTables = 8;
20static const int kNumPixelIndices = 4;
21
22// The index of each row in this table is the ETC1 table codeword
23// The index of each column in this table is the ETC1 pixel index value
24static const int kModifierTables[kNumModifierTables][kNumPixelIndices] = {
25 /* 0 */ { 2, 8, -2, -8 },
26 /* 1 */ { 5, 17, -5, -17 },
27 /* 2 */ { 9, 29, -9, -29 },
28 /* 3 */ { 13, 42, -13, -42 },
29 /* 4 */ { 18, 60, -18, -60 },
30 /* 5 */ { 24, 80, -24, -80 },
31 /* 6 */ { 33, 106, -33, -106 },
32 /* 7 */ { 47, 183, -47, -183 }
33};
34
35static inline int convert_5To8(int b) {
36 int c = b & 0x1f;
37 return (c << 3) | (c >> 2);
38}
39
40// Evaluate one of the entries in 'kModifierTables' to see how close it can get (r8,g8,b8) to
41// the original color (rOrig, gOrib, bOrig).
42static int test_table_entry(int rOrig, int gOrig, int bOrig,
43 int r8, int g8, int b8,
44 int table, int offset) {
45 SkASSERT(0 <= table && table < 8);
46 SkASSERT(0 <= offset && offset < 4);
47
48 r8 = SkTPin<uint8_t>(r8 + kModifierTables[table][offset], 0, 255);
49 g8 = SkTPin<uint8_t>(g8 + kModifierTables[table][offset], 0, 255);
50 b8 = SkTPin<uint8_t>(b8 + kModifierTables[table][offset], 0, 255);
51
52 return SkTAbs(rOrig - r8) + SkTAbs(gOrig - g8) + SkTAbs(bOrig - b8);
53}
54
55// Create an ETC1 compressed block that is filled with 'col'
56static void create_etc1_block(SkColor col, ETC1Block* block) {
57 block->fHigh = 0;
58 block->fLow = 0;
59
60 int rOrig = SkColorGetR(col);
61 int gOrig = SkColorGetG(col);
62 int bOrig = SkColorGetB(col);
63
64 int r5 = SkMulDiv255Round(31, rOrig);
65 int g5 = SkMulDiv255Round(31, gOrig);
66 int b5 = SkMulDiv255Round(31, bOrig);
67
68 int r8 = convert_5To8(r5);
69 int g8 = convert_5To8(g5);
70 int b8 = convert_5To8(b5);
71
72 // We always encode solid color textures as 555 + zero diffs
73 block->fHigh |= (r5 << 27) | (g5 << 19) | (b5 << 11) | 0x2;
74
75 int bestTableIndex = 0, bestPixelIndex = 0;
76 int bestSoFar = 1024;
77 for (int tableIndex = 0; tableIndex < kNumModifierTables; ++tableIndex) {
78 for (int pixelIndex = 0; pixelIndex < kNumPixelIndices; ++pixelIndex) {
79 int score = test_table_entry(rOrig, gOrig, bOrig, r8, g8, b8,
80 tableIndex, pixelIndex);
81
82 if (bestSoFar > score) {
83 bestSoFar = score;
84 bestTableIndex = tableIndex;
85 bestPixelIndex = pixelIndex;
86 }
87 }
88 }
89
90 block->fHigh |= (bestTableIndex << 5) | (bestTableIndex << 2);
91
92 for (int i = 0; i < 16; ++i) {
93 block->fLow |= bestPixelIndex << 2*i;
94 }
95}
96
Robert Phillips8043f322019-05-31 08:11:36 -040097static int num_ETC1_blocks(int w, int h) {
Robert Phillips459b2952019-05-23 09:38:27 -040098 if (w < 4) {
99 w = 1;
100 } else {
101 SkASSERT((w & 3) == 0);
102 w >>= 2;
103 }
104
105 if (h < 4) {
106 h = 1;
107 } else {
108 SkASSERT((h & 3) == 0);
109 h >>= 2;
110 }
111
112 return w * h;
113}
114
Robert Phillips8043f322019-05-31 08:11:36 -0400115size_t GrETC1CompressedDataSize(int width, int height) {
116 int numBlocks = num_ETC1_blocks(width, height);
117
118 return numBlocks * sizeof(ETC1Block);
119}
120
Robert Phillips28a5a432019-06-07 12:46:21 -0400121// Fill in 'dest' with ETC1 blocks derived from 'colorf'
122static void fillin_ETC1_with_color(int width, int height, const SkColor4f& colorf, void* dest) {
Robert Phillips459b2952019-05-23 09:38:27 -0400123 SkColor color = colorf.toSkColor();
124
125 ETC1Block block;
126 create_etc1_block(color, &block);
127
Robert Phillips8043f322019-05-31 08:11:36 -0400128 int numBlocks = num_ETC1_blocks(width, height);
129
Robert Phillips459b2952019-05-23 09:38:27 -0400130 for (int i = 0; i < numBlocks; ++i) {
Robert Phillipse3bd6732019-05-29 14:20:35 -0400131 ((ETC1Block*)dest)[i] = block;
Robert Phillips459b2952019-05-23 09:38:27 -0400132 }
133}
134
Robert Phillips28a5a432019-06-07 12:46:21 -0400135// Fill in the width x height 'dest' with the munged version of 'colorf' that matches 'config'
136static bool fill_buffer_with_color(GrPixelConfig config, int width, int height,
137 const SkColor4f& colorf, void* dest) {
Robert Phillips459b2952019-05-23 09:38:27 -0400138 SkASSERT(kRGB_ETC1_GrPixelConfig != config);
139
140 GrColor color = colorf.toBytes_RGBA();
141
142 uint8_t r = GrColorUnpackR(color);
143 uint8_t g = GrColorUnpackG(color);
144 uint8_t b = GrColorUnpackB(color);
145 uint8_t a = GrColorUnpackA(color);
146
Robert Phillips459b2952019-05-23 09:38:27 -0400147 switch (config) {
148 case kAlpha_8_GrPixelConfig: // fall through
149 case kAlpha_8_as_Alpha_GrPixelConfig: // fall through
150 case kAlpha_8_as_Red_GrPixelConfig: {
151 memset(dest, a, width * height);
152 break;
153 }
154 case kGray_8_GrPixelConfig: // fall through
155 case kGray_8_as_Lum_GrPixelConfig: // fall through
156 case kGray_8_as_Red_GrPixelConfig: {
157 uint8_t gray8 = SkComputeLuminance(r, g, b);
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400158
Robert Phillips459b2952019-05-23 09:38:27 -0400159 memset(dest, gray8, width * height);
160 break;
161 }
162 case kRGB_565_GrPixelConfig: {
Robert Phillips459b2952019-05-23 09:38:27 -0400163 uint16_t rgb565 = SkPack888ToRGB16(r, g, b);
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400164
165 sk_memset16((uint16_t*) dest, rgb565, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400166 break;
167 }
168 case kRGBA_4444_GrPixelConfig: {
Robert Phillips459b2952019-05-23 09:38:27 -0400169 uint8_t r4 = (r >> 4) & 0xF;
170 uint8_t g4 = (g >> 4) & 0xF;
171 uint8_t b4 = (b >> 4) & 0xF;
172 uint8_t a4 = (a >> 4) & 0xF;
173
174 uint16_t rgba4444 = r4 << SK_R4444_SHIFT | g4 << SK_G4444_SHIFT |
175 b4 << SK_B4444_SHIFT | a4 << SK_A4444_SHIFT;
176
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400177 sk_memset16((uint16_t*) dest, rgba4444, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400178 break;
179 }
180 case kRGBA_8888_GrPixelConfig: {
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400181 sk_memset32((uint32_t *) dest, color, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400182 break;
183 }
184 case kRGB_888_GrPixelConfig: {
185 uint8_t* dest8 = (uint8_t*) dest;
186 for (int i = 0; i < width * height; ++i, dest8 += 3) {
187 dest8[0] = r;
188 dest8[1] = g;
189 dest8[2] = b;
190 }
191 break;
192 }
193 case kRGB_888X_GrPixelConfig: {
194 GrColor opaque = GrColorPackRGBA(r, g, b, 0xFF);
195
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400196 sk_memset32((uint32_t *) dest, opaque, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400197 break;
198 }
199 case kRG_88_GrPixelConfig: {
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400200 uint16_t rg88 = (r << 8) | g;
201
202 sk_memset16((uint16_t*) dest, rg88, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400203 break;
204 }
205 case kBGRA_8888_GrPixelConfig: {
206 GrColor swizzled = GrColorPackRGBA(b, g, r, a);
207
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400208 sk_memset32((uint32_t *) dest, swizzled, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400209 break;
210 }
211 case kSRGBA_8888_GrPixelConfig: {
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400212 sk_memset32((uint32_t *) dest, color, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400213 break;
214 }
215 case kSBGRA_8888_GrPixelConfig: {
Robert Phillips459b2952019-05-23 09:38:27 -0400216 GrColor swizzled = GrColorPackRGBA(b, g, r, a);
217
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400218 sk_memset32((uint32_t *) dest, swizzled, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400219 break;
220 }
221 case kRGBA_1010102_GrPixelConfig: {
222 uint32_t r10 = SkScalarRoundToInt(colorf.fR * 1023.0f);
223 uint32_t g10 = SkScalarRoundToInt(colorf.fG * 1023.0f);
224 uint32_t b10 = SkScalarRoundToInt(colorf.fB * 1023.0f);
225 uint8_t a2 = SkScalarRoundToInt(colorf.fA * 3.0f);
226
227 uint32_t rgba1010102 = a2 << 30 | b10 << 20 | g10 << 10 | r10;
228
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400229 sk_memset32((uint32_t *) dest, rgba1010102, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400230 break;
231 }
232 case kRGBA_float_GrPixelConfig: {
233 SkColor4f* destColor = (SkColor4f*) dest;
234 for (int i = 0; i < width * height; ++i) {
235 destColor[i] = colorf;
236 }
237 break;
238 }
239 case kRG_float_GrPixelConfig: {
240 float* destFloat = (float*) dest;
241 for (int i = 0; i < width * height; ++i, destFloat += 2) {
242 destFloat[0] = colorf.fR;
243 destFloat[1] = colorf.fG;
244 }
245 break;
246 }
247 case kAlpha_half_as_Red_GrPixelConfig: // fall through
248 case kAlpha_half_GrPixelConfig: {
Robert Phillips459b2952019-05-23 09:38:27 -0400249 SkHalf alphaHalf = SkFloatToHalf(colorf.fA);
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400250
251 sk_memset16((uint16_t *) dest, alphaHalf, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400252 break;
253 }
254 case kRGBA_half_GrPixelConfig: // fall through
255 case kRGBA_half_Clamped_GrPixelConfig: {
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400256 uint64_t rHalf = SkFloatToHalf(colorf.fR);
257 uint64_t gHalf = SkFloatToHalf(colorf.fG);
258 uint64_t bHalf = SkFloatToHalf(colorf.fB);
259 uint64_t aHalf = SkFloatToHalf(colorf.fA);
260
261 uint64_t rgbaHalf = (aHalf << 48) | (bHalf << 32) | (gHalf << 16) | rHalf;
262
263 sk_memset64((uint64_t *) dest, rgbaHalf, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400264 break;
265 }
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 Phillips66a46032019-06-18 08:00:42 -0400280 // Experimental (for Y416 and mutant P016/P010)
281 case kRGBA_16161616_GrPixelConfig: {
282 uint64_t r16 = SkScalarRoundToInt(colorf.fR * 65535.0f);
283 uint64_t g16 = SkScalarRoundToInt(colorf.fG * 65535.0f);
284 uint64_t b16 = SkScalarRoundToInt(colorf.fB * 65535.0f);
285 uint64_t a16 = SkScalarRoundToInt(colorf.fA * 65535.0f);
286
287 uint64_t rgba16161616 = (a16 << 48) | (b16 << 32) | (g16 << 16) | r16;
288 sk_memset64((uint64_t*) dest, rgba16161616, width * height);
289 break;
290 }
291 case kRG_half_GrPixelConfig: {
292 uint32_t rHalf = SkFloatToHalf(colorf.fR);
293 uint32_t gHalf = SkFloatToHalf(colorf.fG);
294
295 uint32_t rgHalf = (rHalf << 16) | gHalf;
296
297 sk_memset32((uint32_t *) dest, rgHalf, width * height);
298 break;
299 }
Robert Phillips459b2952019-05-23 09:38:27 -0400300 default:
301 return false;
302 break;
303 }
304
305 return true;
306}
Robert Phillips28a5a432019-06-07 12:46:21 -0400307
308size_t GrComputeTightCombinedBufferSize(GrCompression compression, size_t bytesPerPixel,
309 int baseWidth, int baseHeight,
310 SkTArray<size_t>* individualMipOffsets,
311 int mipLevelCount) {
312 SkASSERT(individualMipOffsets && !individualMipOffsets->count());
313 SkASSERT(mipLevelCount >= 1);
314
315 individualMipOffsets->push_back(0);
316
317 size_t combinedBufferSize = baseWidth * bytesPerPixel * baseHeight;
318 if (GrCompression::kETC1 == compression) {
319 SkASSERT(0 == bytesPerPixel);
320 bytesPerPixel = 4; // munge Bpp to make the following code work (and not assert)
321 combinedBufferSize = GrETC1CompressedDataSize(baseWidth, baseHeight);
322 }
323
324 int currentWidth = baseWidth;
325 int currentHeight = baseHeight;
326
327 // The Vulkan spec for copying a buffer to an image requires that the alignment must be at
328 // least 4 bytes and a multiple of the bytes per pixel of the image config.
329 SkASSERT(bytesPerPixel == 1 || bytesPerPixel == 2 || bytesPerPixel == 3 ||
330 bytesPerPixel == 4 || bytesPerPixel == 8 || bytesPerPixel == 16);
331 int desiredAlignment = (bytesPerPixel == 3) ? 12 : (bytesPerPixel > 4 ? bytesPerPixel : 4);
332
333 for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; ++currentMipLevel) {
334 currentWidth = SkTMax(1, currentWidth / 2);
335 currentHeight = SkTMax(1, currentHeight / 2);
336
337 size_t trimmedSize;
338 if (GrCompression::kETC1 == compression) {
339 trimmedSize = GrETC1CompressedDataSize(currentWidth, currentHeight);
340 } else {
341 trimmedSize = currentWidth * bytesPerPixel * currentHeight;
342 }
343 const size_t alignmentDiff = combinedBufferSize % desiredAlignment;
344 if (alignmentDiff != 0) {
345 combinedBufferSize += desiredAlignment - alignmentDiff;
346 }
347 SkASSERT((0 == combinedBufferSize % 4) && (0 == combinedBufferSize % bytesPerPixel));
348
349 individualMipOffsets->push_back(combinedBufferSize);
350 combinedBufferSize += trimmedSize;
351 }
352
353 SkASSERT(individualMipOffsets->count() == mipLevelCount);
354 return combinedBufferSize;
355}
356
357void GrFillInData(GrCompression compression, GrPixelConfig config,
358 int baseWidth, int baseHeight,
359 const SkTArray<size_t>& individualMipOffsets, char* dstPixels,
360 const SkColor4f& colorf) {
361
362 int mipLevels = individualMipOffsets.count();
363
364 int currentWidth = baseWidth;
365 int currentHeight = baseHeight;
366 for (int currentMipLevel = 0; currentMipLevel < mipLevels; ++currentMipLevel) {
367 size_t offset = individualMipOffsets[currentMipLevel];
368
369 if (GrCompression::kETC1 == compression) {
370 // TODO: compute the ETC1 block for 'colorf' just once
371 fillin_ETC1_with_color(currentWidth, currentHeight, colorf, &(dstPixels[offset]));
372 } else {
373 fill_buffer_with_color(config, currentWidth, currentHeight, colorf,
374 &(dstPixels[offset]));
375 }
376
377 currentWidth = SkTMax(1, currentWidth / 2);
378 currentHeight = SkTMax(1, currentHeight / 2);
379 }
380}
381
Brian Salomonf30b1c12019-06-20 12:25:02 -0400382static GrSwizzle get_load_and_get_swizzle(GrColorType ct, SkRasterPipeline::StockStage* load,
383 bool* isNormalized) {
384 GrSwizzle swizzle("rgba");
385 *isNormalized = true;
386 switch (ct) {
387 case GrColorType::kAlpha_8: *load = SkRasterPipeline::load_a8; break;
388 case GrColorType::kBGR_565: *load = SkRasterPipeline::load_565; break;
389 case GrColorType::kABGR_4444: *load = SkRasterPipeline::load_4444; break;
390 case GrColorType::kRGBA_8888: *load = SkRasterPipeline::load_8888; break;
391 case GrColorType::kRG_88: *load = SkRasterPipeline::load_rg88; break;
392 case GrColorType::kRGBA_1010102: *load = SkRasterPipeline::load_1010102; break;
393 case GrColorType::kAlpha_F16: *load = SkRasterPipeline::load_af16; break;
394 case GrColorType::kRGBA_F16_Clamped: *load = SkRasterPipeline::load_f16; break;
395 case GrColorType::kRG_1616: *load = SkRasterPipeline::load_rg1616; break;
396 case GrColorType::kRGBA_16161616: *load = SkRasterPipeline::load_16161616; break;
397
Brian Salomone14cfbe2019-06-24 15:00:58 -0400398 case GrColorType::kRG_F16: *load = SkRasterPipeline::load_rgf16;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400399 *isNormalized = false;
400 break;
401 case GrColorType::kRGBA_F16: *load = SkRasterPipeline::load_f16;
402 *isNormalized = false;
403 break;
404 case GrColorType::kRG_F32: *load = SkRasterPipeline::load_rgf32;
405 *isNormalized = false;
406 break;
407 case GrColorType::kRGBA_F32: *load = SkRasterPipeline::load_f32;
408 *isNormalized = false;
409 break;
410 case GrColorType::kR_16: *load = SkRasterPipeline::load_a16;
411 swizzle = GrSwizzle("a001");
412 break;
413 case GrColorType::kGray_8: *load = SkRasterPipeline::load_a8;
414 swizzle = GrSwizzle("aaa1");
415 break;
416 case GrColorType::kBGRA_8888: *load = SkRasterPipeline::load_8888;
417 swizzle = GrSwizzle("bgra");
418 break;
419 case GrColorType::kRGB_888x: *load = SkRasterPipeline::load_8888;
420 swizzle = GrSwizzle("rgb1");
421 break;
422
423 case GrColorType::kUnknown:
424 case GrColorType::kRGB_ETC1:
425 SK_ABORT("unexpected CT");
426 }
427 return swizzle;
428}
429
430static GrSwizzle get_dst_swizzle_and_store(GrColorType ct, SkRasterPipeline::StockStage* store,
431 bool* isNormalized) {
432 GrSwizzle swizzle("rgba");
433 *isNormalized = true;
434 switch (ct) {
435 case GrColorType::kAlpha_8: *store = SkRasterPipeline::store_a8; break;
436 case GrColorType::kBGR_565: *store = SkRasterPipeline::store_565; break;
437 case GrColorType::kABGR_4444: *store = SkRasterPipeline::store_4444; break;
438 case GrColorType::kRGBA_8888: *store = SkRasterPipeline::store_8888; break;
439 case GrColorType::kRG_88: *store = SkRasterPipeline::store_rg88; break;
440 case GrColorType::kRGBA_1010102: *store = SkRasterPipeline::store_1010102; break;
441 case GrColorType::kRGBA_F16_Clamped: *store = SkRasterPipeline::store_f16; break;
442 case GrColorType::kRG_1616: *store = SkRasterPipeline::store_rg1616; break;
443 case GrColorType::kRGBA_16161616: *store = SkRasterPipeline::store_16161616; break;
444
Brian Salomone14cfbe2019-06-24 15:00:58 -0400445 case GrColorType::kRG_F16: *store = SkRasterPipeline::store_rgf16;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400446 *isNormalized = false;
447 break;
448 case GrColorType::kAlpha_F16: *store = SkRasterPipeline::store_af16;
449 *isNormalized = false;
450 break;
451 case GrColorType::kRGBA_F16: *store = SkRasterPipeline::store_f16;
452 *isNormalized = false;
453 break;
454 case GrColorType::kRG_F32: *store = SkRasterPipeline::store_rgf32;
455 *isNormalized = false;
456 break;
457 case GrColorType::kRGBA_F32: *store = SkRasterPipeline::store_f32;
458 *isNormalized = false;
459 break;
460 case GrColorType::kR_16: swizzle = GrSwizzle("000r");
461 *store = SkRasterPipeline::store_a16;
462 break;
463 case GrColorType::kBGRA_8888: swizzle = GrSwizzle("bgra");
464 *store = SkRasterPipeline::store_8888;
465 break;
466 case GrColorType::kRGB_888x: swizzle = GrSwizzle("rgb1");
467 *store = SkRasterPipeline::store_8888;
468 break;
469
470 case GrColorType::kGray_8: // not currently supported as output
471 case GrColorType::kUnknown:
472 case GrColorType::kRGB_ETC1:
473 SK_ABORT("unexpected CT");
474 }
475 return swizzle;
476}
477
478static inline void append_clamp_gamut(SkRasterPipeline* pipeline) {
479 // SkRasterPipeline may not know our color type and also doesn't like caller to directly
480 // append clamp_gamut. Fake it out.
481 static SkImageInfo fakeII = SkImageInfo::MakeN32Premul(1, 1);
482 pipeline->append_gamut_clamp_if_normalized(fakeII);
483}
484
485bool GrConvertPixels(const GrPixelInfo& dstInfo, void* dst, const GrPixelInfo& srcInfo,
486 const void* src, GrSwizzle swizzle) {
487 if (dstInfo.fWidth != srcInfo.fWidth || srcInfo.fHeight != dstInfo.fHeight) {
488 return false;
489 }
490 if (dstInfo.fWidth <= 0 || dstInfo.fHeight <= 0) {
491 return false;
492 }
493 if (GrColorTypeComponentFlags(dstInfo.fColorInfo.fColorType) & kGray_SkColorTypeComponentFlag) {
494 // We don't currently support conversion to Gray.
495 return false;
496 }
497 size_t srcBpp = GrColorTypeBytesPerPixel(srcInfo.fColorInfo.fColorType);
498 size_t dstBpp = GrColorTypeBytesPerPixel(dstInfo.fColorInfo.fColorType);
499 if (!srcBpp || !dstBpp) {
500 // Either src or dst is compressed or kUnknown.
501 return false;
502 }
503 // SkRasterPipeline operates on row-pixels not row-bytes.
504 SkASSERT(dstInfo.fRowBytes % dstBpp == 0);
505 SkASSERT(srcInfo.fRowBytes % srcBpp == 0);
506
507 SkRasterPipeline::StockStage load;
508 bool srcIsNormalized;
509 auto loadSwizzle =
510 get_load_and_get_swizzle(srcInfo.fColorInfo.fColorType, &load, &srcIsNormalized);
511 loadSwizzle = GrSwizzle::Concat(loadSwizzle, swizzle);
512
513 SkRasterPipeline::StockStage store;
514 bool dstIsNormalized;
515 auto storeSwizzle =
516 get_dst_swizzle_and_store(dstInfo.fColorInfo.fColorType, &store, &dstIsNormalized);
517
518 bool alphaOrCSConversion =
519 (srcInfo.fColorInfo.fAlphaType != dstInfo.fColorInfo.fAlphaType &&
520 srcInfo.fColorInfo.fAlphaType != kOpaque_SkAlphaType) ||
521 !SkColorSpace::Equals(srcInfo.fColorInfo.fColorSpace, dstInfo.fColorInfo.fColorSpace);
522
523 bool clampGamut;
524 SkTLazy<SkColorSpaceXformSteps> steps;
525 GrSwizzle loadStoreSwizzle;
526 if (alphaOrCSConversion) {
527 steps.init(srcInfo.fColorInfo.fColorSpace, srcInfo.fColorInfo.fAlphaType,
528 dstInfo.fColorInfo.fColorSpace, dstInfo.fColorInfo.fAlphaType);
529 clampGamut = dstIsNormalized && dstInfo.fColorInfo.fAlphaType == kPremul_SkAlphaType;
530 } else {
531 clampGamut = dstIsNormalized && !srcIsNormalized &&
532 dstInfo.fColorInfo.fAlphaType == kPremul_SkAlphaType;
533 if (!clampGamut) {
534 loadStoreSwizzle = GrSwizzle::Concat(loadSwizzle, storeSwizzle);
535 }
536 }
537 int cnt = 1;
538 int height = srcInfo.fHeight;
539 SkRasterPipeline_MemoryCtx srcCtx{const_cast<void*>(src), SkToInt(srcInfo.fRowBytes / srcBpp)},
540 dstCtx{ dst , SkToInt(dstInfo.fRowBytes / dstBpp)};
541
542 if (srcInfo.fOrigin != dstInfo.fOrigin) {
543 // It *almost* works to point the src at the last row and negate the stride and run the
544 // whole rectangle. However, SkRasterPipeline::run()'s control loop uses size_t loop
545 // variables so it winds up relying on unsigned overflow math. It works out in practice
546 // but UBSAN says "no!" as it's technically undefined and in theory a compiler could emit
547 // code that didn't do what is intended. So we go one row at a time. :(
548 srcCtx.pixels = static_cast<char*>(srcCtx.pixels) + srcInfo.fRowBytes * (height - 1);
549 std::swap(cnt, height);
550 }
551 for (int i = 0; i < cnt; ++i) {
552 SkRasterPipeline_<256> pipeline;
553 pipeline.append(load, &srcCtx);
554
555 if (alphaOrCSConversion) {
556 loadSwizzle.apply(&pipeline);
557 steps->apply(&pipeline, srcIsNormalized);
558 if (clampGamut) {
559 append_clamp_gamut(&pipeline);
560 }
561 storeSwizzle.apply(&pipeline);
562 } else {
563 if (clampGamut) {
564 loadSwizzle.apply(&pipeline);
565 append_clamp_gamut(&pipeline);
566 storeSwizzle.apply(&pipeline);
567 } else {
568 loadStoreSwizzle.apply(&pipeline);
569 }
570 }
571 pipeline.append(store, &dstCtx);
572 pipeline.run(0, 0, srcInfo.fWidth, height);
573 srcCtx.pixels = static_cast<char*>(srcCtx.pixels) - srcInfo.fRowBytes;
574 dstCtx.pixels = static_cast<char*>(dstCtx.pixels) + dstInfo.fRowBytes;
575 }
576 return true;
577}