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