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