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