blob: 724437b3492054450f14d25b46ec39321d902f1f [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
Brian Salomon77a684f2019-08-01 14:38:04 -04008#include "src/gpu/GrDataUtils.h"
9
Brian Salomonf30b1c12019-06-20 12:25:02 -040010#include "src/core/SkColorSpaceXformSteps.h"
Brian Salomon77a684f2019-08-01 14:38:04 -040011#include "src/core/SkConvertPixels.h"
Brian Salomonf30b1c12019-06-20 12:25:02 -040012#include "src/core/SkTLazy.h"
Brian Salomonc42eb662019-06-24 17:13:00 -040013#include "src/core/SkTraceEvent.h"
Robert Phillipsb5e331a2019-05-28 10:58:09 -040014#include "src/core/SkUtils.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040015#include "src/gpu/GrColor.h"
Robert Phillips459b2952019-05-23 09:38:27 -040016
Robert Phillips8043f322019-05-31 08:11:36 -040017struct ETC1Block {
18 uint32_t fHigh;
19 uint32_t fLow;
20};
21
Robert Phillips459b2952019-05-23 09:38:27 -040022static const int kNumModifierTables = 8;
23static const int kNumPixelIndices = 4;
24
25// The index of each row in this table is the ETC1 table codeword
26// The index of each column in this table is the ETC1 pixel index value
27static const int kModifierTables[kNumModifierTables][kNumPixelIndices] = {
28 /* 0 */ { 2, 8, -2, -8 },
29 /* 1 */ { 5, 17, -5, -17 },
30 /* 2 */ { 9, 29, -9, -29 },
31 /* 3 */ { 13, 42, -13, -42 },
32 /* 4 */ { 18, 60, -18, -60 },
33 /* 5 */ { 24, 80, -24, -80 },
34 /* 6 */ { 33, 106, -33, -106 },
35 /* 7 */ { 47, 183, -47, -183 }
36};
37
38static inline int convert_5To8(int b) {
39 int c = b & 0x1f;
40 return (c << 3) | (c >> 2);
41}
42
43// Evaluate one of the entries in 'kModifierTables' to see how close it can get (r8,g8,b8) to
44// the original color (rOrig, gOrib, bOrig).
45static int test_table_entry(int rOrig, int gOrig, int bOrig,
46 int r8, int g8, int b8,
47 int table, int offset) {
48 SkASSERT(0 <= table && table < 8);
49 SkASSERT(0 <= offset && offset < 4);
50
51 r8 = SkTPin<uint8_t>(r8 + kModifierTables[table][offset], 0, 255);
52 g8 = SkTPin<uint8_t>(g8 + kModifierTables[table][offset], 0, 255);
53 b8 = SkTPin<uint8_t>(b8 + kModifierTables[table][offset], 0, 255);
54
55 return SkTAbs(rOrig - r8) + SkTAbs(gOrig - g8) + SkTAbs(bOrig - b8);
56}
57
58// Create an ETC1 compressed block that is filled with 'col'
59static void create_etc1_block(SkColor col, ETC1Block* block) {
60 block->fHigh = 0;
61 block->fLow = 0;
62
63 int rOrig = SkColorGetR(col);
64 int gOrig = SkColorGetG(col);
65 int bOrig = SkColorGetB(col);
66
67 int r5 = SkMulDiv255Round(31, rOrig);
68 int g5 = SkMulDiv255Round(31, gOrig);
69 int b5 = SkMulDiv255Round(31, bOrig);
70
71 int r8 = convert_5To8(r5);
72 int g8 = convert_5To8(g5);
73 int b8 = convert_5To8(b5);
74
75 // We always encode solid color textures as 555 + zero diffs
76 block->fHigh |= (r5 << 27) | (g5 << 19) | (b5 << 11) | 0x2;
77
78 int bestTableIndex = 0, bestPixelIndex = 0;
79 int bestSoFar = 1024;
80 for (int tableIndex = 0; tableIndex < kNumModifierTables; ++tableIndex) {
81 for (int pixelIndex = 0; pixelIndex < kNumPixelIndices; ++pixelIndex) {
82 int score = test_table_entry(rOrig, gOrig, bOrig, r8, g8, b8,
83 tableIndex, pixelIndex);
84
85 if (bestSoFar > score) {
86 bestSoFar = score;
87 bestTableIndex = tableIndex;
88 bestPixelIndex = pixelIndex;
89 }
90 }
91 }
92
93 block->fHigh |= (bestTableIndex << 5) | (bestTableIndex << 2);
94
95 for (int i = 0; i < 16; ++i) {
96 block->fLow |= bestPixelIndex << 2*i;
97 }
98}
99
Jim Van Verthe3671012019-09-18 09:53:31 -0400100static int num_ETC1_blocks_w(int w) {
Robert Phillips459b2952019-05-23 09:38:27 -0400101 if (w < 4) {
102 w = 1;
103 } else {
Jim Van Verthe3671012019-09-18 09:53:31 -0400104 SkASSERT((w & 3) == 0);
105 w >>= 2;
Robert Phillips459b2952019-05-23 09:38:27 -0400106 }
Jim Van Verthe3671012019-09-18 09:53:31 -0400107 return w;
108}
109
110static int num_ETC1_blocks(int w, int h) {
111 w = num_ETC1_blocks_w(w);
Robert Phillips459b2952019-05-23 09:38:27 -0400112
113 if (h < 4) {
114 h = 1;
115 } else {
116 SkASSERT((h & 3) == 0);
117 h >>= 2;
118 }
119
120 return w * h;
121}
122
Brian Salomonbb8dde82019-06-27 10:52:13 -0400123size_t GrCompressedDataSize(SkImage::CompressionType type, int width, int height) {
124 switch (type) {
125 case SkImage::kETC1_CompressionType:
126 int numBlocks = num_ETC1_blocks(width, height);
127 return numBlocks * sizeof(ETC1Block);
128 }
129 SK_ABORT("Unexpected compression type");
Robert Phillips8043f322019-05-31 08:11:36 -0400130}
131
Jim Van Verthe3671012019-09-18 09:53:31 -0400132size_t GrCompressedRowBytes(SkImage::CompressionType type, int width) {
133 switch (type) {
134 case SkImage::kETC1_CompressionType:
135 int numBlocksWidth = num_ETC1_blocks_w(width);
136 return numBlocksWidth * sizeof(ETC1Block);
137 }
138 SK_ABORT("Unexpected compression type");
139}
140
Robert Phillips28a5a432019-06-07 12:46:21 -0400141// Fill in 'dest' with ETC1 blocks derived from 'colorf'
142static void fillin_ETC1_with_color(int width, int height, const SkColor4f& colorf, void* dest) {
Robert Phillips459b2952019-05-23 09:38:27 -0400143 SkColor color = colorf.toSkColor();
144
145 ETC1Block block;
146 create_etc1_block(color, &block);
147
Robert Phillips8043f322019-05-31 08:11:36 -0400148 int numBlocks = num_ETC1_blocks(width, height);
149
Robert Phillips459b2952019-05-23 09:38:27 -0400150 for (int i = 0; i < numBlocks; ++i) {
Robert Phillipse3bd6732019-05-29 14:20:35 -0400151 ((ETC1Block*)dest)[i] = block;
Robert Phillips459b2952019-05-23 09:38:27 -0400152 }
153}
154
Robert Phillips28a5a432019-06-07 12:46:21 -0400155// Fill in the width x height 'dest' with the munged version of 'colorf' that matches 'config'
156static bool fill_buffer_with_color(GrPixelConfig config, int width, int height,
157 const SkColor4f& colorf, void* dest) {
Robert Phillips459b2952019-05-23 09:38:27 -0400158 SkASSERT(kRGB_ETC1_GrPixelConfig != config);
159
160 GrColor color = colorf.toBytes_RGBA();
161
162 uint8_t r = GrColorUnpackR(color);
163 uint8_t g = GrColorUnpackG(color);
164 uint8_t b = GrColorUnpackB(color);
165 uint8_t a = GrColorUnpackA(color);
166
Robert Phillips459b2952019-05-23 09:38:27 -0400167 switch (config) {
168 case kAlpha_8_GrPixelConfig: // fall through
169 case kAlpha_8_as_Alpha_GrPixelConfig: // fall through
170 case kAlpha_8_as_Red_GrPixelConfig: {
171 memset(dest, a, width * height);
172 break;
173 }
174 case kGray_8_GrPixelConfig: // fall through
175 case kGray_8_as_Lum_GrPixelConfig: // fall through
176 case kGray_8_as_Red_GrPixelConfig: {
177 uint8_t gray8 = SkComputeLuminance(r, g, b);
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400178
Robert Phillips459b2952019-05-23 09:38:27 -0400179 memset(dest, gray8, width * height);
180 break;
181 }
182 case kRGB_565_GrPixelConfig: {
Robert Phillips459b2952019-05-23 09:38:27 -0400183 uint16_t rgb565 = SkPack888ToRGB16(r, g, b);
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400184
185 sk_memset16((uint16_t*) dest, rgb565, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400186 break;
187 }
188 case kRGBA_4444_GrPixelConfig: {
Robert Phillips459b2952019-05-23 09:38:27 -0400189 uint8_t r4 = (r >> 4) & 0xF;
190 uint8_t g4 = (g >> 4) & 0xF;
191 uint8_t b4 = (b >> 4) & 0xF;
192 uint8_t a4 = (a >> 4) & 0xF;
193
194 uint16_t rgba4444 = r4 << SK_R4444_SHIFT | g4 << SK_G4444_SHIFT |
195 b4 << SK_B4444_SHIFT | a4 << SK_A4444_SHIFT;
196
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400197 sk_memset16((uint16_t*) dest, rgba4444, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400198 break;
199 }
200 case kRGBA_8888_GrPixelConfig: {
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400201 sk_memset32((uint32_t *) dest, color, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400202 break;
203 }
204 case kRGB_888_GrPixelConfig: {
205 uint8_t* dest8 = (uint8_t*) dest;
206 for (int i = 0; i < width * height; ++i, dest8 += 3) {
207 dest8[0] = r;
208 dest8[1] = g;
209 dest8[2] = b;
210 }
211 break;
212 }
213 case kRGB_888X_GrPixelConfig: {
214 GrColor opaque = GrColorPackRGBA(r, g, b, 0xFF);
215
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400216 sk_memset32((uint32_t *) dest, opaque, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400217 break;
218 }
219 case kRG_88_GrPixelConfig: {
Robert Phillipsd470e1b2019-09-04 15:05:35 -0400220 uint16_t rg88 = (g << 8) | r;
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400221
222 sk_memset16((uint16_t*) dest, rg88, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400223 break;
224 }
225 case kBGRA_8888_GrPixelConfig: {
226 GrColor swizzled = GrColorPackRGBA(b, g, r, a);
227
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400228 sk_memset32((uint32_t *) dest, swizzled, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400229 break;
230 }
231 case kSRGBA_8888_GrPixelConfig: {
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400232 sk_memset32((uint32_t *) dest, color, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400233 break;
234 }
Robert Phillips459b2952019-05-23 09:38:27 -0400235 case kRGBA_1010102_GrPixelConfig: {
236 uint32_t r10 = SkScalarRoundToInt(colorf.fR * 1023.0f);
237 uint32_t g10 = SkScalarRoundToInt(colorf.fG * 1023.0f);
238 uint32_t b10 = SkScalarRoundToInt(colorf.fB * 1023.0f);
239 uint8_t a2 = SkScalarRoundToInt(colorf.fA * 3.0f);
240
241 uint32_t rgba1010102 = a2 << 30 | b10 << 20 | g10 << 10 | r10;
242
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400243 sk_memset32((uint32_t *) dest, rgba1010102, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400244 break;
245 }
246 case kRGBA_float_GrPixelConfig: {
247 SkColor4f* destColor = (SkColor4f*) dest;
248 for (int i = 0; i < width * height; ++i) {
249 destColor[i] = colorf;
250 }
251 break;
252 }
Robert Phillipsebab03f2019-07-22 08:48:18 -0400253 case kAlpha_half_as_Lum_GrPixelConfig: // fall through
Robert Phillips459b2952019-05-23 09:38:27 -0400254 case kAlpha_half_as_Red_GrPixelConfig: // fall through
255 case kAlpha_half_GrPixelConfig: {
Robert Phillips459b2952019-05-23 09:38:27 -0400256 SkHalf alphaHalf = SkFloatToHalf(colorf.fA);
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400257
258 sk_memset16((uint16_t *) dest, alphaHalf, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400259 break;
260 }
261 case kRGBA_half_GrPixelConfig: // fall through
262 case kRGBA_half_Clamped_GrPixelConfig: {
Robert Phillipsb5e331a2019-05-28 10:58:09 -0400263 uint64_t rHalf = SkFloatToHalf(colorf.fR);
264 uint64_t gHalf = SkFloatToHalf(colorf.fG);
265 uint64_t bHalf = SkFloatToHalf(colorf.fB);
266 uint64_t aHalf = SkFloatToHalf(colorf.fA);
267
268 uint64_t rgbaHalf = (aHalf << 48) | (bHalf << 32) | (gHalf << 16) | rHalf;
269
270 sk_memset64((uint64_t *) dest, rgbaHalf, width * height);
Robert Phillips459b2952019-05-23 09:38:27 -0400271 break;
272 }
Robert Phillips429f0d32019-09-11 17:03:28 -0400273 case kAlpha_16_GrPixelConfig: {
274 uint16_t a16 = SkScalarRoundToInt(colorf.fA * 65535.0f);
275 sk_memset16((uint16_t*) dest, a16, width * height);
Robert Phillipsfe18de52019-06-06 17:21:50 -0400276 break;
277 }
278 case kRG_1616_GrPixelConfig: {
Robert Phillips429f0d32019-09-11 17:03:28 -0400279 uint32_t r16 = SkScalarRoundToInt(colorf.fR * 65535.0f);
280 uint32_t g16 = SkScalarRoundToInt(colorf.fG * 65535.0f);
Robert Phillipsfe18de52019-06-06 17:21:50 -0400281
Robert Phillips429f0d32019-09-11 17:03:28 -0400282 uint32_t rg1616 = (g16 << 16) | r16;
Robert Phillipsfe18de52019-06-06 17:21:50 -0400283
284 sk_memset32((uint32_t*) dest, rg1616, width * height);
285 break;
286 }
Robert Phillips66a46032019-06-18 08:00:42 -0400287 // Experimental (for Y416 and mutant P016/P010)
288 case kRGBA_16161616_GrPixelConfig: {
289 uint64_t r16 = SkScalarRoundToInt(colorf.fR * 65535.0f);
290 uint64_t g16 = SkScalarRoundToInt(colorf.fG * 65535.0f);
291 uint64_t b16 = SkScalarRoundToInt(colorf.fB * 65535.0f);
292 uint64_t a16 = SkScalarRoundToInt(colorf.fA * 65535.0f);
293
294 uint64_t rgba16161616 = (a16 << 48) | (b16 << 32) | (g16 << 16) | r16;
295 sk_memset64((uint64_t*) dest, rgba16161616, width * height);
296 break;
297 }
298 case kRG_half_GrPixelConfig: {
299 uint32_t rHalf = SkFloatToHalf(colorf.fR);
300 uint32_t gHalf = SkFloatToHalf(colorf.fG);
301
Robert Phillips17a3a0b2019-09-18 13:56:54 -0400302 uint32_t rgHalf = (gHalf << 16) | rHalf;
Robert Phillips66a46032019-06-18 08:00:42 -0400303
304 sk_memset32((uint32_t *) dest, rgHalf, width * height);
305 break;
306 }
Robert Phillips459b2952019-05-23 09:38:27 -0400307 default:
308 return false;
309 break;
310 }
311
312 return true;
313}
Robert Phillips28a5a432019-06-07 12:46:21 -0400314
Brian Salomonbb8dde82019-06-27 10:52:13 -0400315size_t GrComputeTightCombinedBufferSize(size_t bytesPerPixel, int baseWidth, int baseHeight,
316 SkTArray<size_t>* individualMipOffsets, int mipLevelCount) {
Robert Phillips28a5a432019-06-07 12:46:21 -0400317 SkASSERT(individualMipOffsets && !individualMipOffsets->count());
318 SkASSERT(mipLevelCount >= 1);
319
320 individualMipOffsets->push_back(0);
321
322 size_t combinedBufferSize = baseWidth * bytesPerPixel * baseHeight;
Robert Phillips28a5a432019-06-07 12:46:21 -0400323 int currentWidth = baseWidth;
324 int currentHeight = baseHeight;
325
326 // The Vulkan spec for copying a buffer to an image requires that the alignment must be at
327 // least 4 bytes and a multiple of the bytes per pixel of the image config.
328 SkASSERT(bytesPerPixel == 1 || bytesPerPixel == 2 || bytesPerPixel == 3 ||
329 bytesPerPixel == 4 || bytesPerPixel == 8 || bytesPerPixel == 16);
330 int desiredAlignment = (bytesPerPixel == 3) ? 12 : (bytesPerPixel > 4 ? bytesPerPixel : 4);
331
332 for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; ++currentMipLevel) {
333 currentWidth = SkTMax(1, currentWidth / 2);
334 currentHeight = SkTMax(1, currentHeight / 2);
335
Brian Salomonbb8dde82019-06-27 10:52:13 -0400336 size_t trimmedSize = currentWidth * bytesPerPixel * currentHeight;
Robert Phillips28a5a432019-06-07 12:46:21 -0400337 const size_t alignmentDiff = combinedBufferSize % desiredAlignment;
338 if (alignmentDiff != 0) {
339 combinedBufferSize += desiredAlignment - alignmentDiff;
340 }
341 SkASSERT((0 == combinedBufferSize % 4) && (0 == combinedBufferSize % bytesPerPixel));
342
343 individualMipOffsets->push_back(combinedBufferSize);
344 combinedBufferSize += trimmedSize;
345 }
346
347 SkASSERT(individualMipOffsets->count() == mipLevelCount);
348 return combinedBufferSize;
349}
350
Brian Salomonbb8dde82019-06-27 10:52:13 -0400351void GrFillInData(GrPixelConfig config, int baseWidth, int baseHeight,
Robert Phillips28a5a432019-06-07 12:46:21 -0400352 const SkTArray<size_t>& individualMipOffsets, char* dstPixels,
353 const SkColor4f& colorf) {
Brian Salomonc42eb662019-06-24 17:13:00 -0400354 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
Brian Salomonbb8dde82019-06-27 10:52:13 -0400355 SkASSERT(!GrPixelConfigIsCompressed(config));
Robert Phillips28a5a432019-06-07 12:46:21 -0400356 int mipLevels = individualMipOffsets.count();
357
358 int currentWidth = baseWidth;
359 int currentHeight = baseHeight;
360 for (int currentMipLevel = 0; currentMipLevel < mipLevels; ++currentMipLevel) {
361 size_t offset = individualMipOffsets[currentMipLevel];
362
Brian Salomonbb8dde82019-06-27 10:52:13 -0400363 fill_buffer_with_color(config, currentWidth, currentHeight, colorf, &(dstPixels[offset]));
Robert Phillips28a5a432019-06-07 12:46:21 -0400364 currentWidth = SkTMax(1, currentWidth / 2);
365 currentHeight = SkTMax(1, currentHeight / 2);
366 }
367}
368
Brian Salomonbb8dde82019-06-27 10:52:13 -0400369void GrFillInCompressedData(SkImage::CompressionType type, int baseWidth, int baseHeight,
370 char* dstPixels, const SkColor4f& colorf) {
371 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
372 int currentWidth = baseWidth;
373 int currentHeight = baseHeight;
374 if (SkImage::kETC1_CompressionType == type) {
375 fillin_ETC1_with_color(currentWidth, currentHeight, colorf, dstPixels);
376 }
377}
378
Brian Salomonf30b1c12019-06-20 12:25:02 -0400379static GrSwizzle get_load_and_get_swizzle(GrColorType ct, SkRasterPipeline::StockStage* load,
Greg Daniele877dce2019-07-11 10:52:43 -0400380 bool* isNormalized, bool* isSRGB) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400381 GrSwizzle swizzle("rgba");
382 *isNormalized = true;
Greg Daniele877dce2019-07-11 10:52:43 -0400383 *isSRGB = false;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400384 switch (ct) {
385 case GrColorType::kAlpha_8: *load = SkRasterPipeline::load_a8; break;
Robert Phillips429f0d32019-09-11 17:03:28 -0400386 case GrColorType::kAlpha_16: *load = SkRasterPipeline::load_a16; break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400387 case GrColorType::kBGR_565: *load = SkRasterPipeline::load_565; break;
388 case GrColorType::kABGR_4444: *load = SkRasterPipeline::load_4444; break;
389 case GrColorType::kRGBA_8888: *load = SkRasterPipeline::load_8888; break;
390 case GrColorType::kRG_88: *load = SkRasterPipeline::load_rg88; break;
391 case GrColorType::kRGBA_1010102: *load = SkRasterPipeline::load_1010102; break;
392 case GrColorType::kAlpha_F16: *load = SkRasterPipeline::load_af16; break;
393 case GrColorType::kRGBA_F16_Clamped: *load = SkRasterPipeline::load_f16; break;
394 case GrColorType::kRG_1616: *load = SkRasterPipeline::load_rg1616; break;
395 case GrColorType::kRGBA_16161616: *load = SkRasterPipeline::load_16161616; break;
396
Greg Daniele877dce2019-07-11 10:52:43 -0400397 case GrColorType::kRGBA_8888_SRGB: *load = SkRasterPipeline::load_8888;
398 *isSRGB = true;
399 break;
Brian Salomone14cfbe2019-06-24 15:00:58 -0400400 case GrColorType::kRG_F16: *load = SkRasterPipeline::load_rgf16;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400401 *isNormalized = false;
402 break;
403 case GrColorType::kRGBA_F16: *load = SkRasterPipeline::load_f16;
404 *isNormalized = false;
405 break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400406 case GrColorType::kRGBA_F32: *load = SkRasterPipeline::load_f32;
407 *isNormalized = false;
408 break;
Brian Salomon8f8354a2019-07-31 20:12:02 -0400409 case GrColorType::kAlpha_8xxx: *load = SkRasterPipeline::load_8888;
410 swizzle = GrSwizzle("000r");
411 break;
412 case GrColorType::kAlpha_F32xxx: *load = SkRasterPipeline::load_f32;
413 swizzle = GrSwizzle("000r");
414 break;
415 case GrColorType::kGray_8xxx: *load = SkRasterPipeline::load_8888;
416 swizzle = GrSwizzle("rrr1");
417 break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400418 case GrColorType::kGray_8: *load = SkRasterPipeline::load_a8;
419 swizzle = GrSwizzle("aaa1");
420 break;
421 case GrColorType::kBGRA_8888: *load = SkRasterPipeline::load_8888;
422 swizzle = GrSwizzle("bgra");
423 break;
424 case GrColorType::kRGB_888x: *load = SkRasterPipeline::load_8888;
425 swizzle = GrSwizzle("rgb1");
426 break;
427
428 case GrColorType::kUnknown:
Brian Salomonf30b1c12019-06-20 12:25:02 -0400429 SK_ABORT("unexpected CT");
430 }
431 return swizzle;
432}
433
434static GrSwizzle get_dst_swizzle_and_store(GrColorType ct, SkRasterPipeline::StockStage* store,
Greg Daniele877dce2019-07-11 10:52:43 -0400435 bool* isNormalized, bool* isSRGB) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400436 GrSwizzle swizzle("rgba");
437 *isNormalized = true;
Greg Daniele877dce2019-07-11 10:52:43 -0400438 *isSRGB = false;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400439 switch (ct) {
440 case GrColorType::kAlpha_8: *store = SkRasterPipeline::store_a8; break;
Robert Phillips429f0d32019-09-11 17:03:28 -0400441 case GrColorType::kAlpha_16: *store = SkRasterPipeline::store_a16; break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400442 case GrColorType::kBGR_565: *store = SkRasterPipeline::store_565; break;
443 case GrColorType::kABGR_4444: *store = SkRasterPipeline::store_4444; break;
444 case GrColorType::kRGBA_8888: *store = SkRasterPipeline::store_8888; break;
445 case GrColorType::kRG_88: *store = SkRasterPipeline::store_rg88; break;
446 case GrColorType::kRGBA_1010102: *store = SkRasterPipeline::store_1010102; break;
447 case GrColorType::kRGBA_F16_Clamped: *store = SkRasterPipeline::store_f16; break;
448 case GrColorType::kRG_1616: *store = SkRasterPipeline::store_rg1616; break;
449 case GrColorType::kRGBA_16161616: *store = SkRasterPipeline::store_16161616; break;
450
Greg Daniele877dce2019-07-11 10:52:43 -0400451 case GrColorType::kRGBA_8888_SRGB: *store = SkRasterPipeline::store_8888;
452 *isSRGB = true;
453 break;
Brian Salomone14cfbe2019-06-24 15:00:58 -0400454 case GrColorType::kRG_F16: *store = SkRasterPipeline::store_rgf16;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400455 *isNormalized = false;
456 break;
457 case GrColorType::kAlpha_F16: *store = SkRasterPipeline::store_af16;
458 *isNormalized = false;
459 break;
460 case GrColorType::kRGBA_F16: *store = SkRasterPipeline::store_f16;
461 *isNormalized = false;
462 break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400463 case GrColorType::kRGBA_F32: *store = SkRasterPipeline::store_f32;
464 *isNormalized = false;
465 break;
Brian Salomon8f8354a2019-07-31 20:12:02 -0400466 case GrColorType::kAlpha_8xxx: *store = SkRasterPipeline::store_8888;
467 swizzle = GrSwizzle("a000");
468 break;
469 case GrColorType::kAlpha_F32xxx: *store = SkRasterPipeline::store_f32;
470 swizzle = GrSwizzle("a000");
471 break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400472 case GrColorType::kBGRA_8888: swizzle = GrSwizzle("bgra");
473 *store = SkRasterPipeline::store_8888;
474 break;
475 case GrColorType::kRGB_888x: swizzle = GrSwizzle("rgb1");
476 *store = SkRasterPipeline::store_8888;
477 break;
478
479 case GrColorType::kGray_8: // not currently supported as output
Brian Salomon8f8354a2019-07-31 20:12:02 -0400480 case GrColorType::kGray_8xxx: // not currently supported as output
Brian Salomonf30b1c12019-06-20 12:25:02 -0400481 case GrColorType::kUnknown:
Brian Salomonf30b1c12019-06-20 12:25:02 -0400482 SK_ABORT("unexpected CT");
483 }
484 return swizzle;
485}
486
487static inline void append_clamp_gamut(SkRasterPipeline* pipeline) {
488 // SkRasterPipeline may not know our color type and also doesn't like caller to directly
489 // append clamp_gamut. Fake it out.
490 static SkImageInfo fakeII = SkImageInfo::MakeN32Premul(1, 1);
491 pipeline->append_gamut_clamp_if_normalized(fakeII);
492}
493
Brian Salomon1d435302019-07-01 13:05:28 -0400494bool GrConvertPixels(const GrPixelInfo& dstInfo, void* dst, size_t dstRB,
495 const GrPixelInfo& srcInfo, const void* src, size_t srcRB,
Brian Salomon8f8354a2019-07-31 20:12:02 -0400496 bool flipY) {
Brian Salomonc42eb662019-06-24 17:13:00 -0400497 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
Brian Salomon1d435302019-07-01 13:05:28 -0400498 if (!srcInfo.isValid() || !dstInfo.isValid()) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400499 return false;
500 }
Brian Salomon1d435302019-07-01 13:05:28 -0400501 if (!src || !dst) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400502 return false;
503 }
Brian Salomon1d435302019-07-01 13:05:28 -0400504 if (dstInfo.width() != srcInfo.width() || srcInfo.height() != dstInfo.height()) {
505 return false;
506 }
507 if (GrColorTypeComponentFlags(dstInfo.colorType()) & kGray_SkColorTypeComponentFlag) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400508 // We don't currently support conversion to Gray.
509 return false;
510 }
Brian Salomon1d435302019-07-01 13:05:28 -0400511 if (dstRB < dstInfo.minRowBytes() || srcRB < srcInfo.minRowBytes()) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400512 return false;
513 }
Brian Salomon1d435302019-07-01 13:05:28 -0400514
515 size_t srcBpp = srcInfo.bpp();
516 size_t dstBpp = dstInfo.bpp();
517
Brian Salomonf30b1c12019-06-20 12:25:02 -0400518 // SkRasterPipeline operates on row-pixels not row-bytes.
Brian Salomon1d435302019-07-01 13:05:28 -0400519 SkASSERT(dstRB % dstBpp == 0);
520 SkASSERT(srcRB % srcBpp == 0);
Brian Salomonf30b1c12019-06-20 12:25:02 -0400521
Brian Salomon77a684f2019-08-01 14:38:04 -0400522 bool premul = srcInfo.alphaType() == kUnpremul_SkAlphaType &&
523 dstInfo.alphaType() == kPremul_SkAlphaType;
524 bool unpremul = srcInfo.alphaType() == kPremul_SkAlphaType &&
525 dstInfo.alphaType() == kUnpremul_SkAlphaType;
526 bool alphaOrCSConversion =
527 premul || unpremul || !SkColorSpace::Equals(srcInfo.colorSpace(), dstInfo.colorSpace());
528
529 if (srcInfo.colorType() == dstInfo.colorType() && !alphaOrCSConversion) {
530 size_t tightRB = dstBpp * dstInfo.width();
531 if (flipY) {
532 dst = static_cast<char*>(dst) + dstRB * (dstInfo.height() - 1);
533 for (int y = 0; y < dstInfo.height(); ++y) {
534 memcpy(dst, src, tightRB);
535 src = static_cast<const char*>(src) + srcRB;
536 dst = static_cast< char*>(dst) - dstRB;
537 }
538 } else {
539 SkRectMemcpy(dst, dstRB, src, srcRB, tightRB, srcInfo.height());
540 }
541 return true;
542 }
543
Brian Salomonf30b1c12019-06-20 12:25:02 -0400544 SkRasterPipeline::StockStage load;
545 bool srcIsNormalized;
Greg Daniele877dce2019-07-11 10:52:43 -0400546 bool srcIsSRGB;
547 auto loadSwizzle = get_load_and_get_swizzle(srcInfo.colorType(), &load, &srcIsNormalized,
548 &srcIsSRGB);
Brian Salomonf30b1c12019-06-20 12:25:02 -0400549
550 SkRasterPipeline::StockStage store;
551 bool dstIsNormalized;
Greg Daniele877dce2019-07-11 10:52:43 -0400552 bool dstIsSRGB;
553 auto storeSwizzle = get_dst_swizzle_and_store(dstInfo.colorType(), &store, &dstIsNormalized,
554 &dstIsSRGB);
Brian Salomonf30b1c12019-06-20 12:25:02 -0400555
Brian Salomonf30b1c12019-06-20 12:25:02 -0400556 bool clampGamut;
557 SkTLazy<SkColorSpaceXformSteps> steps;
558 GrSwizzle loadStoreSwizzle;
559 if (alphaOrCSConversion) {
Brian Salomon1d435302019-07-01 13:05:28 -0400560 steps.init(srcInfo.colorSpace(), srcInfo.alphaType(),
561 dstInfo.colorSpace(), dstInfo.alphaType());
562 clampGamut = dstIsNormalized && dstInfo.alphaType() == kPremul_SkAlphaType;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400563 } else {
Brian Salomon1d435302019-07-01 13:05:28 -0400564 clampGamut =
565 dstIsNormalized && !srcIsNormalized && dstInfo.alphaType() == kPremul_SkAlphaType;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400566 if (!clampGamut) {
567 loadStoreSwizzle = GrSwizzle::Concat(loadSwizzle, storeSwizzle);
568 }
569 }
570 int cnt = 1;
Brian Salomon1d435302019-07-01 13:05:28 -0400571 int height = srcInfo.height();
572 SkRasterPipeline_MemoryCtx srcCtx{const_cast<void*>(src), SkToInt(srcRB / srcBpp)},
573 dstCtx{ dst , SkToInt(dstRB / dstBpp)};
Brian Salomonf30b1c12019-06-20 12:25:02 -0400574
Brian Salomon1d435302019-07-01 13:05:28 -0400575 if (flipY) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400576 // It *almost* works to point the src at the last row and negate the stride and run the
577 // whole rectangle. However, SkRasterPipeline::run()'s control loop uses size_t loop
578 // variables so it winds up relying on unsigned overflow math. It works out in practice
579 // but UBSAN says "no!" as it's technically undefined and in theory a compiler could emit
580 // code that didn't do what is intended. So we go one row at a time. :(
Brian Salomon1d435302019-07-01 13:05:28 -0400581 srcCtx.pixels = static_cast<char*>(srcCtx.pixels) + srcRB * (height - 1);
Brian Salomonf30b1c12019-06-20 12:25:02 -0400582 std::swap(cnt, height);
583 }
Greg Daniele877dce2019-07-11 10:52:43 -0400584
585 bool hasConversion = alphaOrCSConversion || clampGamut;
586
587 if (srcIsSRGB && dstIsSRGB && !hasConversion) {
588 // No need to convert from srgb if we are just going to immediately convert it back.
589 srcIsSRGB = dstIsSRGB = false;
590 }
591
592 hasConversion = hasConversion || srcIsSRGB || dstIsSRGB;
593
Brian Salomonf30b1c12019-06-20 12:25:02 -0400594 for (int i = 0; i < cnt; ++i) {
595 SkRasterPipeline_<256> pipeline;
596 pipeline.append(load, &srcCtx);
Greg Daniele877dce2019-07-11 10:52:43 -0400597 if (hasConversion) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400598 loadSwizzle.apply(&pipeline);
Greg Daniele877dce2019-07-11 10:52:43 -0400599 if (srcIsSRGB) {
600 pipeline.append(SkRasterPipeline::from_srgb);
601 }
602 if (alphaOrCSConversion) {
603 steps->apply(&pipeline, srcIsNormalized);
604 }
Brian Salomonf30b1c12019-06-20 12:25:02 -0400605 if (clampGamut) {
606 append_clamp_gamut(&pipeline);
607 }
Greg Daniele877dce2019-07-11 10:52:43 -0400608 // If we add support for storing to Gray we would add a luminance to alpha conversion
609 // here. We also wouldn't then need a to_srgb stage after since it would have not effect
610 // on the alpha channel. It would also mean we have an SRGB Gray color type which
611 // doesn't exist currently.
612 if (dstIsSRGB) {
613 pipeline.append(SkRasterPipeline::to_srgb);
614 }
Brian Salomonf30b1c12019-06-20 12:25:02 -0400615 storeSwizzle.apply(&pipeline);
616 } else {
Greg Daniele877dce2019-07-11 10:52:43 -0400617 loadStoreSwizzle.apply(&pipeline);
Brian Salomonf30b1c12019-06-20 12:25:02 -0400618 }
619 pipeline.append(store, &dstCtx);
Brian Salomon1d435302019-07-01 13:05:28 -0400620 pipeline.run(0, 0, srcInfo.width(), height);
621 srcCtx.pixels = static_cast<char*>(srcCtx.pixels) - srcRB;
622 dstCtx.pixels = static_cast<char*>(dstCtx.pixels) + dstRB;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400623 }
624 return true;
625}
Robert Phillipsc80b0e92019-07-23 10:27:09 -0400626
Robert Phillipsc80b0e92019-07-23 10:27:09 -0400627GrColorType SkColorTypeAndFormatToGrColorType(const GrCaps* caps,
628 SkColorType skCT,
629 const GrBackendFormat& format) {
630 GrColorType grCT = SkColorTypeToGrColorType(skCT);
631 // Until we support SRGB in the SkColorType we have to do this manual check here to make sure
632 // we use the correct GrColorType.
633 if (caps->isFormatSRGB(format)) {
634 if (grCT != GrColorType::kRGBA_8888) {
635 return GrColorType::kUnknown;
636 }
637 grCT = GrColorType::kRGBA_8888_SRGB;
638 }
639 return grCT;
640}