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