1 /*
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
8 #include "src/gpu/ganesh/GrDataUtils.h"
9
10 #include "include/core/SkColorSpace.h"
11 #include "include/private/base/SkTPin.h"
12 #include "modules/skcms/skcms.h"
13 #include "src/base/SkMathPriv.h"
14 #include "src/base/SkTLazy.h"
15 #include "src/base/SkUtils.h"
16 #include "src/core/SkColorSpaceXformSteps.h"
17 #include "src/core/SkCompressedDataUtils.h"
18 #include "src/core/SkConvertPixels.h"
19 #include "src/core/SkMipmap.h"
20 #include "src/core/SkRasterPipeline.h"
21 #include "src/core/SkTraceEvent.h"
22 #include "src/gpu/Swizzle.h"
23 #include "src/gpu/ganesh/GrCaps.h"
24 #include "src/gpu/ganesh/GrColor.h"
25 #include "src/gpu/ganesh/GrImageInfo.h"
26 #include "src/gpu/ganesh/GrPixmap.h"
27
28 struct ETC1Block {
29 uint32_t fHigh;
30 uint32_t fLow;
31 };
32
33 constexpr uint32_t kDiffBit = 0x2; // set -> differential; not-set -> individual
34
extend_5To8bits(int b)35 static inline int extend_5To8bits(int b) {
36 int c = b & 0x1f;
37 return (c << 3) | (c >> 2);
38 }
39
40 static const int kNumETC1ModifierTables = 8;
41 static const int kNumETC1PixelIndices = 4;
42
43 // The index of each row in this table is the ETC1 table codeword
44 // The index of each column in this table is the ETC1 pixel index value
45 static const int kETC1ModifierTables[kNumETC1ModifierTables][kNumETC1PixelIndices] = {
46 /* 0 */ { 2, 8, -2, -8 },
47 /* 1 */ { 5, 17, -5, -17 },
48 /* 2 */ { 9, 29, -9, -29 },
49 /* 3 */ { 13, 42, -13, -42 },
50 /* 4 */ { 18, 60, -18, -60 },
51 /* 5 */ { 24, 80, -24, -80 },
52 /* 6 */ { 33, 106, -33, -106 },
53 /* 7 */ { 47, 183, -47, -183 }
54 };
55
56 // Evaluate one of the entries in 'kModifierTables' to see how close it can get (r8,g8,b8) to
57 // the original color (rOrig, gOrib, bOrig).
test_table_entry(int rOrig,int gOrig,int bOrig,int r8,int g8,int b8,int table,int offset)58 static int test_table_entry(int rOrig, int gOrig, int bOrig,
59 int r8, int g8, int b8,
60 int table, int offset) {
61 SkASSERT(0 <= table && table < 8);
62 SkASSERT(0 <= offset && offset < 4);
63
64 r8 = SkTPin<int>(r8 + kETC1ModifierTables[table][offset], 0, 255);
65 g8 = SkTPin<int>(g8 + kETC1ModifierTables[table][offset], 0, 255);
66 b8 = SkTPin<int>(b8 + kETC1ModifierTables[table][offset], 0, 255);
67
68 return SkTAbs(rOrig - r8) + SkTAbs(gOrig - g8) + SkTAbs(bOrig - b8);
69 }
70
71 // Create an ETC1 compressed block that is filled with 'col'
create_etc1_block(SkColor col,ETC1Block * block)72 static void create_etc1_block(SkColor col, ETC1Block* block) {
73 uint32_t high = 0;
74 uint32_t low = 0;
75
76 int rOrig = SkColorGetR(col);
77 int gOrig = SkColorGetG(col);
78 int bOrig = SkColorGetB(col);
79
80 int r5 = SkMulDiv255Round(31, rOrig);
81 int g5 = SkMulDiv255Round(31, gOrig);
82 int b5 = SkMulDiv255Round(31, bOrig);
83
84 int r8 = extend_5To8bits(r5);
85 int g8 = extend_5To8bits(g5);
86 int b8 = extend_5To8bits(b5);
87
88 // We always encode solid color textures in differential mode (i.e., with a 555 base color) but
89 // with zero diffs (i.e., bits 26-24, 18-16 and 10-8 are left 0).
90 high |= (r5 << 27) | (g5 << 19) | (b5 << 11) | kDiffBit;
91
92 int bestTableIndex = 0, bestPixelIndex = 0;
93 int bestSoFar = 1024;
94 for (int tableIndex = 0; tableIndex < kNumETC1ModifierTables; ++tableIndex) {
95 for (int pixelIndex = 0; pixelIndex < kNumETC1PixelIndices; ++pixelIndex) {
96 int score = test_table_entry(rOrig, gOrig, bOrig, r8, g8, b8,
97 tableIndex, pixelIndex);
98
99 if (bestSoFar > score) {
100 bestSoFar = score;
101 bestTableIndex = tableIndex;
102 bestPixelIndex = pixelIndex;
103 }
104 }
105 }
106
107 high |= (bestTableIndex << 5) | (bestTableIndex << 2);
108
109 if (bestPixelIndex & 0x1) {
110 low |= 0xFFFF;
111 }
112 if (bestPixelIndex & 0x2) {
113 low |= 0xFFFF0000;
114 }
115
116 block->fHigh = SkBSwap32(high);
117 block->fLow = SkBSwap32(low);
118 }
119
num_4x4_blocks(int size)120 static int num_4x4_blocks(int size) {
121 return ((size + 3) & ~3) >> 2;
122 }
123
num_ETC1_blocks(int w,int h)124 static int num_ETC1_blocks(int w, int h) {
125 w = num_4x4_blocks(w);
126 h = num_4x4_blocks(h);
127
128 return w * h;
129 }
130
131 struct BC1Block {
132 uint16_t fColor0;
133 uint16_t fColor1;
134 uint32_t fIndices;
135 };
136
to565(SkColor col)137 static uint16_t to565(SkColor col) {
138 int r5 = SkMulDiv255Round(31, SkColorGetR(col));
139 int g6 = SkMulDiv255Round(63, SkColorGetG(col));
140 int b5 = SkMulDiv255Round(31, SkColorGetB(col));
141
142 return (r5 << 11) | (g6 << 5) | b5;
143 }
144
145 // Create a BC1 compressed block that has two colors but is initialized to 'col0'
create_BC1_block(SkColor col0,SkColor col1,BC1Block * block)146 static void create_BC1_block(SkColor col0, SkColor col1, BC1Block* block) {
147 block->fColor0 = to565(col0);
148 block->fColor1 = to565(col1);
149 SkASSERT(block->fColor0 <= block->fColor1); // we always assume transparent blocks
150
151 if (col0 == SK_ColorTRANSPARENT) {
152 // This sets all 16 pixels to just use color3 (under the assumption
153 // that this is a kBC1_RGBA8_UNORM texture. Note that in this case
154 // fColor0 will be opaque black.
155 block->fIndices = 0xFFFFFFFF;
156 } else {
157 // This sets all 16 pixels to just use 'fColor0'
158 block->fIndices = 0;
159 }
160 }
161
GrNumBlocks(SkImage::CompressionType type,SkISize baseDimensions)162 size_t GrNumBlocks(SkImage::CompressionType type, SkISize baseDimensions) {
163 switch (type) {
164 case SkImage::CompressionType::kNone:
165 return baseDimensions.width() * baseDimensions.height();
166 case SkImage::CompressionType::kETC2_RGB8_UNORM:
167 case SkImage::CompressionType::kBC1_RGB8_UNORM:
168 case SkImage::CompressionType::kBC1_RGBA8_UNORM: {
169 int numBlocksWidth = num_4x4_blocks(baseDimensions.width());
170 int numBlocksHeight = num_4x4_blocks(baseDimensions.height());
171
172 return numBlocksWidth * numBlocksHeight;
173 }
174 }
175 SkUNREACHABLE;
176 }
177
GrCompressedRowBytes(SkImage::CompressionType type,int width)178 size_t GrCompressedRowBytes(SkImage::CompressionType type, int width) {
179 switch (type) {
180 case SkImage::CompressionType::kNone:
181 return 0;
182 case SkImage::CompressionType::kETC2_RGB8_UNORM:
183 case SkImage::CompressionType::kBC1_RGB8_UNORM:
184 case SkImage::CompressionType::kBC1_RGBA8_UNORM: {
185 int numBlocksWidth = num_4x4_blocks(width);
186
187 static_assert(sizeof(ETC1Block) == sizeof(BC1Block));
188 return numBlocksWidth * sizeof(ETC1Block);
189 }
190 }
191 SkUNREACHABLE;
192 }
193
GrCompressedDimensions(SkImage::CompressionType type,SkISize baseDimensions)194 SkISize GrCompressedDimensions(SkImage::CompressionType type, SkISize baseDimensions) {
195 switch (type) {
196 case SkImage::CompressionType::kNone:
197 return baseDimensions;
198 case SkImage::CompressionType::kETC2_RGB8_UNORM:
199 case SkImage::CompressionType::kBC1_RGB8_UNORM:
200 case SkImage::CompressionType::kBC1_RGBA8_UNORM: {
201 int numBlocksWidth = num_4x4_blocks(baseDimensions.width());
202 int numBlocksHeight = num_4x4_blocks(baseDimensions.height());
203
204 // Each BC1_RGB8_UNORM and ETC1 block has 16 pixels
205 return { 4 * numBlocksWidth, 4 * numBlocksHeight };
206 }
207 }
208 SkUNREACHABLE;
209 }
210
211 // Fill in 'dest' with ETC1 blocks derived from 'colorf'
fillin_ETC1_with_color(SkISize dimensions,const SkColor4f & colorf,char * dest)212 static void fillin_ETC1_with_color(SkISize dimensions, const SkColor4f& colorf, char* dest) {
213 SkColor color = colorf.toSkColor();
214
215 ETC1Block block;
216 create_etc1_block(color, &block);
217
218 int numBlocks = num_ETC1_blocks(dimensions.width(), dimensions.height());
219
220 for (int i = 0; i < numBlocks; ++i) {
221 memcpy(dest, &block, sizeof(ETC1Block));
222 dest += sizeof(ETC1Block);
223 }
224 }
225
226 // Fill in 'dest' with BC1 blocks derived from 'colorf'
fillin_BC1_with_color(SkISize dimensions,const SkColor4f & colorf,char * dest)227 static void fillin_BC1_with_color(SkISize dimensions, const SkColor4f& colorf, char* dest) {
228 SkColor color = colorf.toSkColor();
229
230 BC1Block block;
231 create_BC1_block(color, color, &block);
232
233 int numBlocks = num_ETC1_blocks(dimensions.width(), dimensions.height());
234
235 for (int i = 0; i < numBlocks; ++i) {
236 memcpy(dest, &block, sizeof(BC1Block));
237 dest += sizeof(BC1Block);
238 }
239 }
240
241 #if GR_TEST_UTILS
242
243 // Fill in 'dstPixels' with BC1 blocks derived from the 'pixmap'.
GrTwoColorBC1Compress(const SkPixmap & pixmap,SkColor otherColor,char * dstPixels)244 void GrTwoColorBC1Compress(const SkPixmap& pixmap, SkColor otherColor, char* dstPixels) {
245 BC1Block* dstBlocks = reinterpret_cast<BC1Block*>(dstPixels);
246 SkASSERT(pixmap.colorType() == SkColorType::kRGBA_8888_SkColorType);
247
248 BC1Block block;
249
250 // black -> fColor0, otherColor -> fColor1
251 create_BC1_block(SK_ColorBLACK, otherColor, &block);
252
253 int numXBlocks = num_4x4_blocks(pixmap.width());
254 int numYBlocks = num_4x4_blocks(pixmap.height());
255
256 for (int y = 0; y < numYBlocks; ++y) {
257 for (int x = 0; x < numXBlocks; ++x) {
258 int shift = 0;
259 int offsetX = 4 * x, offsetY = 4 * y;
260 block.fIndices = 0; // init all the pixels to color0 (i.e., opaque black)
261 for (int i = 0; i < 4; ++i) {
262 for (int j = 0; j < 4; ++j, shift += 2) {
263 if (offsetX + j >= pixmap.width() || offsetY + i >= pixmap.height()) {
264 // This can happen for the topmost levels of a mipmap and for
265 // non-multiple of 4 textures
266 continue;
267 }
268
269 SkColor tmp = pixmap.getColor(offsetX + j, offsetY + i);
270 if (tmp == SK_ColorTRANSPARENT) {
271 // For RGBA BC1 images color3 is set to transparent black
272 block.fIndices |= 3 << shift;
273 } else if (tmp != SK_ColorBLACK) {
274 block.fIndices |= 1 << shift; // color1
275 }
276 }
277 }
278
279 dstBlocks[y*numXBlocks + x] = block;
280 }
281 }
282 }
283
284 #endif
285
GrComputeTightCombinedBufferSize(size_t bytesPerPixel,SkISize baseDimensions,SkTArray<size_t> * individualMipOffsets,int mipLevelCount)286 size_t GrComputeTightCombinedBufferSize(size_t bytesPerPixel, SkISize baseDimensions,
287 SkTArray<size_t>* individualMipOffsets, int mipLevelCount) {
288 SkASSERT(individualMipOffsets && !individualMipOffsets->size());
289 SkASSERT(mipLevelCount >= 1);
290
291 individualMipOffsets->push_back(0);
292
293 size_t combinedBufferSize = baseDimensions.width() * bytesPerPixel * baseDimensions.height();
294 SkISize levelDimensions = baseDimensions;
295
296 // The Vulkan spec for copying a buffer to an image requires that the alignment must be at
297 // least 4 bytes and a multiple of the bytes per pixel of the image config.
298 SkASSERT(bytesPerPixel == 1 || bytesPerPixel == 2 || bytesPerPixel == 3 ||
299 bytesPerPixel == 4 || bytesPerPixel == 8 || bytesPerPixel == 16);
300 int desiredAlignment = (bytesPerPixel == 3) ? 12 : (bytesPerPixel > 4 ? bytesPerPixel : 4);
301
302 for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; ++currentMipLevel) {
303 levelDimensions = {std::max(1, levelDimensions.width() /2),
304 std::max(1, levelDimensions.height()/2)};
305
306 size_t trimmedSize = levelDimensions.area() * bytesPerPixel;
307 const size_t alignmentDiff = combinedBufferSize % desiredAlignment;
308 if (alignmentDiff != 0) {
309 combinedBufferSize += desiredAlignment - alignmentDiff;
310 }
311 SkASSERT((0 == combinedBufferSize % 4) && (0 == combinedBufferSize % bytesPerPixel));
312
313 individualMipOffsets->push_back(combinedBufferSize);
314 combinedBufferSize += trimmedSize;
315 }
316
317 SkASSERT(individualMipOffsets->size() == mipLevelCount);
318 return combinedBufferSize;
319 }
320
GrFillInCompressedData(SkImage::CompressionType type,SkISize dimensions,GrMipmapped mipmapped,char * dstPixels,const SkColor4f & colorf)321 void GrFillInCompressedData(SkImage::CompressionType type, SkISize dimensions,
322 GrMipmapped mipmapped, char* dstPixels, const SkColor4f& colorf) {
323 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
324
325 int numMipLevels = 1;
326 if (mipmapped == GrMipmapped::kYes) {
327 numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
328 }
329
330 size_t offset = 0;
331
332 for (int i = 0; i < numMipLevels; ++i) {
333 size_t levelSize = SkCompressedDataSize(type, dimensions, nullptr, false);
334
335 if (SkImage::CompressionType::kETC2_RGB8_UNORM == type) {
336 fillin_ETC1_with_color(dimensions, colorf, &dstPixels[offset]);
337 } else {
338 SkASSERT(type == SkImage::CompressionType::kBC1_RGB8_UNORM ||
339 type == SkImage::CompressionType::kBC1_RGBA8_UNORM);
340 fillin_BC1_with_color(dimensions, colorf, &dstPixels[offset]);
341 }
342
343 offset += levelSize;
344 dimensions = {std::max(1, dimensions.width()/2), std::max(1, dimensions.height()/2)};
345 }
346 }
347
get_load_and_src_swizzle(GrColorType ct,SkRasterPipelineOp * load,bool * isNormalized,bool * isSRGB)348 static skgpu::Swizzle get_load_and_src_swizzle(GrColorType ct, SkRasterPipelineOp* load,
349 bool* isNormalized, bool* isSRGB) {
350 skgpu::Swizzle swizzle("rgba");
351 *isNormalized = true;
352 *isSRGB = false;
353 switch (ct) {
354 case GrColorType::kAlpha_8: *load = SkRasterPipelineOp::load_a8; break;
355 case GrColorType::kAlpha_16: *load = SkRasterPipelineOp::load_a16; break;
356 case GrColorType::kBGR_565: *load = SkRasterPipelineOp::load_565; break;
357 case GrColorType::kABGR_4444: *load = SkRasterPipelineOp::load_4444; break;
358 case GrColorType::kARGB_4444: swizzle = skgpu::Swizzle("bgra");
359 *load = SkRasterPipelineOp::load_4444; break;
360 case GrColorType::kBGRA_4444: swizzle = skgpu::Swizzle("gbar");
361 *load = SkRasterPipelineOp::load_4444; break;
362 case GrColorType::kRGBA_8888: *load = SkRasterPipelineOp::load_8888; break;
363 case GrColorType::kRG_88: *load = SkRasterPipelineOp::load_rg88; break;
364 case GrColorType::kRGBA_1010102: *load = SkRasterPipelineOp::load_1010102; break;
365 case GrColorType::kBGRA_1010102: *load = SkRasterPipelineOp::load_1010102;
366 swizzle = skgpu::Swizzle("bgra");
367 break;
368 case GrColorType::kAlpha_F16: *load = SkRasterPipelineOp::load_af16; break;
369 case GrColorType::kRGBA_F16_Clamped: *load = SkRasterPipelineOp::load_f16; break;
370 case GrColorType::kRG_1616: *load = SkRasterPipelineOp::load_rg1616; break;
371 case GrColorType::kRGBA_16161616: *load = SkRasterPipelineOp::load_16161616; break;
372
373 case GrColorType::kRGBA_8888_SRGB: *load = SkRasterPipelineOp::load_8888;
374 *isSRGB = true;
375 break;
376 case GrColorType::kRG_F16: *load = SkRasterPipelineOp::load_rgf16;
377 *isNormalized = false;
378 break;
379 case GrColorType::kRGBA_F16: *load = SkRasterPipelineOp::load_f16;
380 *isNormalized = false;
381 break;
382 case GrColorType::kRGBA_F32: *load = SkRasterPipelineOp::load_f32;
383 *isNormalized = false;
384 break;
385 case GrColorType::kAlpha_8xxx: *load = SkRasterPipelineOp::load_8888;
386 swizzle = skgpu::Swizzle("000r");
387 break;
388 case GrColorType::kAlpha_F32xxx: *load = SkRasterPipelineOp::load_f32;
389 swizzle = skgpu::Swizzle("000r");
390 break;
391 case GrColorType::kGray_8xxx: *load = SkRasterPipelineOp::load_8888;
392 swizzle = skgpu::Swizzle("rrr1");
393 break;
394 case GrColorType::kGray_8: *load = SkRasterPipelineOp::load_a8;
395 swizzle = skgpu::Swizzle("aaa1");
396 break;
397 case GrColorType::kR_8xxx: *load = SkRasterPipelineOp::load_8888;
398 swizzle = skgpu::Swizzle("r001");
399 break;
400 case GrColorType::kR_8: *load = SkRasterPipelineOp::load_a8;
401 swizzle = skgpu::Swizzle("a001");
402 break;
403 case GrColorType::kGrayAlpha_88: *load = SkRasterPipelineOp::load_rg88;
404 swizzle = skgpu::Swizzle("rrrg");
405 break;
406 case GrColorType::kBGRA_8888: *load = SkRasterPipelineOp::load_8888;
407 swizzle = skgpu::Swizzle("bgra");
408 break;
409 case GrColorType::kRGB_888x: *load = SkRasterPipelineOp::load_8888;
410 swizzle = skgpu::Swizzle("rgb1");
411 break;
412
413 // These are color types we don't expect to ever have to load.
414 case GrColorType::kRGB_888:
415 case GrColorType::kR_16:
416 case GrColorType::kR_F16:
417 case GrColorType::kGray_F16:
418 case GrColorType::kUnknown:
419 SK_ABORT("unexpected CT");
420 }
421 return swizzle;
422 }
423
424 enum class LumMode {
425 kNone,
426 kToRGB,
427 kToAlpha
428 };
429
get_dst_swizzle_and_store(GrColorType ct,SkRasterPipelineOp * store,LumMode * lumMode,bool * isNormalized,bool * isSRGB)430 static skgpu::Swizzle get_dst_swizzle_and_store(GrColorType ct, SkRasterPipelineOp* store,
431 LumMode* lumMode, bool* isNormalized,
432 bool* isSRGB) {
433 skgpu::Swizzle swizzle("rgba");
434 *isNormalized = true;
435 *isSRGB = false;
436 *lumMode = LumMode::kNone;
437 switch (ct) {
438 case GrColorType::kAlpha_8: *store = SkRasterPipelineOp::store_a8; break;
439 case GrColorType::kAlpha_16: *store = SkRasterPipelineOp::store_a16; break;
440 case GrColorType::kBGR_565: *store = SkRasterPipelineOp::store_565; break;
441 case GrColorType::kABGR_4444: *store = SkRasterPipelineOp::store_4444; break;
442 case GrColorType::kARGB_4444: swizzle = skgpu::Swizzle("bgra");
443 *store = SkRasterPipelineOp::store_4444; break;
444 case GrColorType::kBGRA_4444: swizzle = skgpu::Swizzle("argb");
445 *store = SkRasterPipelineOp::store_4444; break;
446 case GrColorType::kRGBA_8888: *store = SkRasterPipelineOp::store_8888; break;
447 case GrColorType::kRG_88: *store = SkRasterPipelineOp::store_rg88; break;
448 case GrColorType::kRGBA_1010102: *store = SkRasterPipelineOp::store_1010102; break;
449 case GrColorType::kBGRA_1010102: swizzle = skgpu::Swizzle("bgra");
450 *store = SkRasterPipelineOp::store_1010102;
451 break;
452 case GrColorType::kRGBA_F16_Clamped: *store = SkRasterPipelineOp::store_f16; break;
453 case GrColorType::kRG_1616: *store = SkRasterPipelineOp::store_rg1616; break;
454 case GrColorType::kRGBA_16161616: *store = SkRasterPipelineOp::store_16161616; break;
455
456 case GrColorType::kRGBA_8888_SRGB: *store = SkRasterPipelineOp::store_8888;
457 *isSRGB = true;
458 break;
459 case GrColorType::kRG_F16: *store = SkRasterPipelineOp::store_rgf16;
460 *isNormalized = false;
461 break;
462 case GrColorType::kAlpha_F16: *store = SkRasterPipelineOp::store_af16;
463 *isNormalized = false;
464 break;
465 case GrColorType::kRGBA_F16: *store = SkRasterPipelineOp::store_f16;
466 *isNormalized = false;
467 break;
468 case GrColorType::kRGBA_F32: *store = SkRasterPipelineOp::store_f32;
469 *isNormalized = false;
470 break;
471 case GrColorType::kAlpha_8xxx: *store = SkRasterPipelineOp::store_8888;
472 swizzle = skgpu::Swizzle("a000");
473 break;
474 case GrColorType::kAlpha_F32xxx: *store = SkRasterPipelineOp::store_f32;
475 swizzle = skgpu::Swizzle("a000");
476 break;
477 case GrColorType::kBGRA_8888: swizzle = skgpu::Swizzle("bgra");
478 *store = SkRasterPipelineOp::store_8888;
479 break;
480 case GrColorType::kRGB_888x: swizzle = skgpu::Swizzle("rgb1");
481 *store = SkRasterPipelineOp::store_8888;
482 break;
483 case GrColorType::kR_8xxx: swizzle = skgpu::Swizzle("r001");
484 *store = SkRasterPipelineOp::store_8888;
485 break;
486 case GrColorType::kR_8: swizzle = skgpu::Swizzle("agbr");
487 *store = SkRasterPipelineOp::store_a8;
488 break;
489 case GrColorType::kR_16: swizzle = skgpu::Swizzle("agbr");
490 *store = SkRasterPipelineOp::store_a16;
491 break;
492 case GrColorType::kR_F16: swizzle = skgpu::Swizzle("agbr");
493 *store = SkRasterPipelineOp::store_af16;
494 break;
495 case GrColorType::kGray_F16: *lumMode = LumMode::kToAlpha;
496 *store = SkRasterPipelineOp::store_af16;
497 break;
498 case GrColorType::kGray_8: *lumMode = LumMode::kToAlpha;
499 *store = SkRasterPipelineOp::store_a8;
500 break;
501 case GrColorType::kGrayAlpha_88: *lumMode = LumMode::kToRGB;
502 swizzle = skgpu::Swizzle("ragb");
503 *store = SkRasterPipelineOp::store_rg88;
504 break;
505 case GrColorType::kGray_8xxx: *lumMode = LumMode::kToRGB;
506 *store = SkRasterPipelineOp::store_8888;
507 swizzle = skgpu::Swizzle("r000");
508 break;
509
510 // These are color types we don't expect to ever have to store.
511 case GrColorType::kRGB_888: // This is handled specially in GrConvertPixels.
512 case GrColorType::kUnknown:
513 SK_ABORT("unexpected CT");
514 }
515 return swizzle;
516 }
517
GrConvertPixels(const GrPixmap & dst,const GrCPixmap & src,bool flipY)518 bool GrConvertPixels(const GrPixmap& dst, const GrCPixmap& src, bool flipY) {
519 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
520 if (src.dimensions().isEmpty() || dst.dimensions().isEmpty()) {
521 return false;
522 }
523 if (src.colorType() == GrColorType::kUnknown || dst.colorType() == GrColorType::kUnknown) {
524 return false;
525 }
526 if (!src.hasPixels() || !dst.hasPixels()) {
527 return false;
528 }
529 if (dst.dimensions() != src.dimensions()) {
530 return false;
531 }
532 if (dst.colorType() == GrColorType::kRGB_888) {
533 // SkRasterPipeline doesn't handle writing to RGB_888. So we have it write to RGB_888x and
534 // then do another conversion that does the 24bit packing. We could be cleverer and skip the
535 // temp pixmap if this is the only conversion but this is rare so keeping it simple.
536 GrPixmap temp = GrPixmap::Allocate(dst.info().makeColorType(GrColorType::kRGB_888x));
537 if (!GrConvertPixels(temp, src, flipY)) {
538 return false;
539 }
540 auto* tRow = reinterpret_cast<const char*>(temp.addr());
541 auto* dRow = reinterpret_cast<char*>(dst.addr());
542 for (int y = 0; y < dst.height(); ++y, tRow += temp.rowBytes(), dRow += dst.rowBytes()) {
543 for (int x = 0; x < dst.width(); ++x) {
544 auto t = tRow + x*sizeof(uint32_t);
545 auto d = dRow + x*3;
546 memcpy(d, t, 3);
547 }
548 }
549 return true;
550 } else if (src.colorType() == GrColorType::kRGB_888) {
551 // SkRasterPipeline doesn't handle reading from RGB_888. So convert it to RGB_888x and then
552 // do a recursive call if there is any remaining conversion.
553 GrPixmap temp = GrPixmap::Allocate(src.info().makeColorType(GrColorType::kRGB_888x));
554 auto* sRow = reinterpret_cast<const char*>(src.addr());
555 auto* tRow = reinterpret_cast<char*>(temp.addr());
556 for (int y = 0; y < src.height(); ++y, sRow += src.rowBytes(), tRow += temp.rowBytes()) {
557 for (int x = 0; x < src.width(); ++x) {
558 auto s = sRow + x*3;
559 auto t = tRow + x*sizeof(uint32_t);
560 memcpy(t, s, 3);
561 t[3] = static_cast<char>(0xFF);
562 }
563 }
564 return GrConvertPixels(dst, temp, flipY);
565 }
566
567 size_t srcBpp = src.info().bpp();
568 size_t dstBpp = dst.info().bpp();
569
570 // SkRasterPipeline operates on row-pixels not row-bytes.
571 SkASSERT(dst.rowBytes() % dstBpp == 0);
572 SkASSERT(src.rowBytes() % srcBpp == 0);
573
574 bool premul = src.alphaType() == kUnpremul_SkAlphaType &&
575 dst.alphaType() == kPremul_SkAlphaType;
576 bool unpremul = src.alphaType() == kPremul_SkAlphaType &&
577 dst.alphaType() == kUnpremul_SkAlphaType;
578 bool alphaOrCSConversion =
579 premul || unpremul || !SkColorSpace::Equals(src.colorSpace(), dst.colorSpace());
580
581 if (src.colorType() == dst.colorType() && !alphaOrCSConversion) {
582 size_t tightRB = dstBpp * dst.width();
583 if (flipY) {
584 auto s = static_cast<const char*>(src.addr());
585 auto d = SkTAddOffset<char>(dst.addr(), dst.rowBytes()*(dst.height() - 1));
586 for (int y = 0; y < dst.height(); ++y, d -= dst.rowBytes(), s += src.rowBytes()) {
587 memcpy(d, s, tightRB);
588 }
589 } else {
590 SkRectMemcpy(dst.addr(), dst.rowBytes(),
591 src.addr(), src.rowBytes(),
592 tightRB, src.height());
593 }
594 return true;
595 }
596
597 SkRasterPipelineOp load;
598 bool srcIsNormalized;
599 bool srcIsSRGB;
600 auto loadSwizzle = get_load_and_src_swizzle(src.colorType(),
601 &load,
602 &srcIsNormalized,
603 &srcIsSRGB);
604
605 SkRasterPipelineOp store;
606 LumMode lumMode;
607 bool dstIsNormalized;
608 bool dstIsSRGB;
609 auto storeSwizzle = get_dst_swizzle_and_store(dst.colorType(),
610 &store,
611 &lumMode,
612 &dstIsNormalized,
613 &dstIsSRGB);
614
615 SkTLazy<SkColorSpaceXformSteps> steps;
616 skgpu::Swizzle loadStoreSwizzle;
617 if (alphaOrCSConversion) {
618 steps.init(src.colorSpace(), src.alphaType(), dst.colorSpace(), dst.alphaType());
619 } else {
620 loadStoreSwizzle = skgpu::Swizzle::Concat(loadSwizzle, storeSwizzle);
621 }
622 int cnt = 1;
623 int height = src.height();
624 SkRasterPipeline_MemoryCtx
625 srcCtx{const_cast<void*>(src.addr()), SkToInt(src.rowBytes()/srcBpp)},
626 dstCtx{ dst.addr(), SkToInt(dst.rowBytes()/dstBpp)};
627
628 if (flipY) {
629 // It *almost* works to point the src at the last row and negate the stride and run the
630 // whole rectangle. However, SkRasterPipeline::run()'s control loop uses size_t loop
631 // variables so it winds up relying on unsigned overflow math. It works out in practice
632 // but UBSAN says "no!" as it's technically undefined and in theory a compiler could emit
633 // code that didn't do what is intended. So we go one row at a time. :(
634 srcCtx.pixels = static_cast<char*>(srcCtx.pixels) + src.rowBytes()*(height - 1);
635 std::swap(cnt, height);
636 }
637
638 bool hasConversion = alphaOrCSConversion || lumMode != LumMode::kNone;
639
640 if (srcIsSRGB && dstIsSRGB && !hasConversion) {
641 // No need to convert from srgb if we are just going to immediately convert it back.
642 srcIsSRGB = dstIsSRGB = false;
643 }
644
645 hasConversion = hasConversion || srcIsSRGB || dstIsSRGB;
646
647 SkRasterPipeline_<256> pipeline;
648 pipeline.append(load, &srcCtx);
649 if (hasConversion) {
650 loadSwizzle.apply(&pipeline);
651 if (srcIsSRGB) {
652 pipeline.append_transfer_function(*skcms_sRGB_TransferFunction());
653 }
654 if (alphaOrCSConversion) {
655 steps->apply(&pipeline);
656 }
657 switch (lumMode) {
658 case LumMode::kNone:
659 break;
660 case LumMode::kToRGB:
661 pipeline.append(SkRasterPipelineOp::bt709_luminance_or_luma_to_rgb);
662 break;
663 case LumMode::kToAlpha:
664 pipeline.append(SkRasterPipelineOp::bt709_luminance_or_luma_to_alpha);
665 // If we ever need to store srgb-encoded gray (e.g. GL_SLUMINANCE8) then we
666 // should use ToRGB and then a swizzle stage rather than ToAlpha. The subsequent
667 // transfer function stage ignores the alpha channel (where we just stashed the
668 // gray).
669 SkASSERT(!dstIsSRGB);
670 break;
671 }
672 if (dstIsSRGB) {
673 pipeline.append_transfer_function(*skcms_sRGB_Inverse_TransferFunction());
674 }
675 storeSwizzle.apply(&pipeline);
676 } else {
677 loadStoreSwizzle.apply(&pipeline);
678 }
679 pipeline.append(store, &dstCtx);
680 auto pipelineFn = pipeline.compile();
681 for (int i = 0; i < cnt; ++i) {
682 pipelineFn(0, 0, src.width(), height);
683 srcCtx.pixels = static_cast<char*>(srcCtx.pixels) - src.rowBytes();
684 dstCtx.pixels = static_cast<char*>(dstCtx.pixels) + dst.rowBytes();
685 }
686
687 return true;
688 }
689
GrClearImage(const GrImageInfo & dstInfo,void * dst,size_t dstRB,std::array<float,4> color)690 bool GrClearImage(const GrImageInfo& dstInfo, void* dst, size_t dstRB, std::array<float, 4> color) {
691 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
692
693 if (!dstInfo.isValid()) {
694 return false;
695 }
696 if (!dst) {
697 return false;
698 }
699 if (dstRB < dstInfo.minRowBytes()) {
700 return false;
701 }
702 if (dstInfo.colorType() == GrColorType::kRGB_888) {
703 // SkRasterPipeline doesn't handle writing to RGB_888. So we handle that specially here.
704 uint32_t rgba = SkColor4f{color[0], color[1], color[2], color[3]}.toBytes_RGBA();
705 for (int y = 0; y < dstInfo.height(); ++y) {
706 char* d = static_cast<char*>(dst) + y * dstRB;
707 for (int x = 0; x < dstInfo.width(); ++x, d += 3) {
708 memcpy(d, &rgba, 3);
709 }
710 }
711 return true;
712 }
713
714 LumMode lumMode;
715 bool isNormalized;
716 bool dstIsSRGB;
717 SkRasterPipelineOp store;
718 skgpu::Swizzle storeSwizzle = get_dst_swizzle_and_store(dstInfo.colorType(), &store, &lumMode,
719 &isNormalized, &dstIsSRGB);
720 char block[64];
721 SkArenaAlloc alloc(block, sizeof(block), 1024);
722 SkRasterPipeline_<256> pipeline;
723 pipeline.append_constant_color(&alloc, color.data());
724 switch (lumMode) {
725 case LumMode::kNone:
726 break;
727 case LumMode::kToRGB:
728 pipeline.append(SkRasterPipelineOp::bt709_luminance_or_luma_to_rgb);
729 break;
730 case LumMode::kToAlpha:
731 pipeline.append(SkRasterPipelineOp::bt709_luminance_or_luma_to_alpha);
732 // If we ever need to store srgb-encoded gray (e.g. GL_SLUMINANCE8) then we should use
733 // ToRGB and then a swizzle stage rather than ToAlpha. The subsequent transfer function
734 // stage ignores the alpha channel (where we just stashed the gray).
735 SkASSERT(!dstIsSRGB);
736 break;
737 }
738 if (dstIsSRGB) {
739 pipeline.append_transfer_function(*skcms_sRGB_Inverse_TransferFunction());
740 }
741 storeSwizzle.apply(&pipeline);
742 SkRasterPipeline_MemoryCtx dstCtx{dst, SkToInt(dstRB/dstInfo.bpp())};
743 pipeline.append(store, &dstCtx);
744 pipeline.run(0, 0, dstInfo.width(), dstInfo.height());
745
746 return true;
747 }
748