blob: d14c46694e1a289938c961f5695b612b8fac22ff [file] [log] [blame]
Robert Phillips459b2952019-05-23 09:38:27 -04001/*
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
Brian Salomon77a684f2019-08-01 14:38:04 -04008#include "src/gpu/GrDataUtils.h"
9
Mike Klein8aa0edf2020-10-16 11:04:18 -050010#include "include/private/SkTPin.h"
Mike Klein04984312020-05-19 12:41:05 -050011#include "include/third_party/skcms/skcms.h"
Brian Salomonf30b1c12019-06-20 12:25:02 -040012#include "src/core/SkColorSpaceXformSteps.h"
Robert Phillips99dead92020-01-27 16:11:57 -050013#include "src/core/SkCompressedDataUtils.h"
Brian Salomon77a684f2019-08-01 14:38:04 -040014#include "src/core/SkConvertPixels.h"
Robert Phillips1a82a4e2021-07-01 10:27:44 -040015#include "src/core/SkMathPriv.h"
Mike Reed13711eb2020-07-14 17:16:32 -040016#include "src/core/SkMipmap.h"
Brian Salomonf30b1c12019-06-20 12:25:02 -040017#include "src/core/SkTLazy.h"
Brian Salomonc42eb662019-06-24 17:13:00 -040018#include "src/core/SkTraceEvent.h"
Robert Phillipsb5e331a2019-05-28 10:58:09 -040019#include "src/core/SkUtils.h"
Robert Phillips294723d2021-06-17 09:23:58 -040020#include "src/gpu/GrCaps.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040021#include "src/gpu/GrColor.h"
Brian Salomonf2ebdd92019-09-30 12:15:30 -040022#include "src/gpu/GrImageInfo.h"
Brian Salomon5392c942021-03-30 16:14:37 -040023#include "src/gpu/GrPixmap.h"
Robert Phillips294723d2021-06-17 09:23:58 -040024#include "src/gpu/GrSwizzle.h"
Robert Phillips459b2952019-05-23 09:38:27 -040025
Robert Phillips8043f322019-05-31 08:11:36 -040026struct ETC1Block {
27 uint32_t fHigh;
28 uint32_t fLow;
29};
30
Robert Phillipsd4f68312020-01-31 10:15:05 -050031constexpr uint32_t kDiffBit = 0x2; // set -> differential; not-set -> individual
32
33static inline int extend_5To8bits(int b) {
34 int c = b & 0x1f;
35 return (c << 3) | (c >> 2);
36}
37
Robert Phillips8f259a02019-12-20 11:32:27 -050038static const int kNumETC1ModifierTables = 8;
39static const int kNumETC1PixelIndices = 4;
Robert Phillips459b2952019-05-23 09:38:27 -040040
41// The index of each row in this table is the ETC1 table codeword
42// The index of each column in this table is the ETC1 pixel index value
Robert Phillips8f259a02019-12-20 11:32:27 -050043static const int kETC1ModifierTables[kNumETC1ModifierTables][kNumETC1PixelIndices] = {
Robert Phillips459b2952019-05-23 09:38:27 -040044 /* 0 */ { 2, 8, -2, -8 },
45 /* 1 */ { 5, 17, -5, -17 },
46 /* 2 */ { 9, 29, -9, -29 },
47 /* 3 */ { 13, 42, -13, -42 },
48 /* 4 */ { 18, 60, -18, -60 },
49 /* 5 */ { 24, 80, -24, -80 },
50 /* 6 */ { 33, 106, -33, -106 },
51 /* 7 */ { 47, 183, -47, -183 }
52};
53
Robert Phillips459b2952019-05-23 09:38:27 -040054// Evaluate one of the entries in 'kModifierTables' to see how close it can get (r8,g8,b8) to
55// the original color (rOrig, gOrib, bOrig).
56static int test_table_entry(int rOrig, int gOrig, int bOrig,
57 int r8, int g8, int b8,
58 int table, int offset) {
59 SkASSERT(0 <= table && table < 8);
60 SkASSERT(0 <= offset && offset < 4);
61
Robert Phillips8f259a02019-12-20 11:32:27 -050062 r8 = SkTPin<int>(r8 + kETC1ModifierTables[table][offset], 0, 255);
63 g8 = SkTPin<int>(g8 + kETC1ModifierTables[table][offset], 0, 255);
64 b8 = SkTPin<int>(b8 + kETC1ModifierTables[table][offset], 0, 255);
Robert Phillips459b2952019-05-23 09:38:27 -040065
66 return SkTAbs(rOrig - r8) + SkTAbs(gOrig - g8) + SkTAbs(bOrig - b8);
67}
68
69// Create an ETC1 compressed block that is filled with 'col'
70static void create_etc1_block(SkColor col, ETC1Block* block) {
Robert Phillips48257d72019-12-16 14:20:53 -050071 uint32_t high = 0;
72 uint32_t low = 0;
Robert Phillips459b2952019-05-23 09:38:27 -040073
74 int rOrig = SkColorGetR(col);
75 int gOrig = SkColorGetG(col);
76 int bOrig = SkColorGetB(col);
77
78 int r5 = SkMulDiv255Round(31, rOrig);
79 int g5 = SkMulDiv255Round(31, gOrig);
80 int b5 = SkMulDiv255Round(31, bOrig);
81
Robert Phillipsd4f68312020-01-31 10:15:05 -050082 int r8 = extend_5To8bits(r5);
83 int g8 = extend_5To8bits(g5);
84 int b8 = extend_5To8bits(b5);
Robert Phillips459b2952019-05-23 09:38:27 -040085
Robert Phillipsd4f68312020-01-31 10:15:05 -050086 // We always encode solid color textures in differential mode (i.e., with a 555 base color) but
87 // with zero diffs (i.e., bits 26-24, 18-16 and 10-8 are left 0).
88 high |= (r5 << 27) | (g5 << 19) | (b5 << 11) | kDiffBit;
Robert Phillips459b2952019-05-23 09:38:27 -040089
90 int bestTableIndex = 0, bestPixelIndex = 0;
91 int bestSoFar = 1024;
Robert Phillips8f259a02019-12-20 11:32:27 -050092 for (int tableIndex = 0; tableIndex < kNumETC1ModifierTables; ++tableIndex) {
93 for (int pixelIndex = 0; pixelIndex < kNumETC1PixelIndices; ++pixelIndex) {
Robert Phillips459b2952019-05-23 09:38:27 -040094 int score = test_table_entry(rOrig, gOrig, bOrig, r8, g8, b8,
95 tableIndex, pixelIndex);
96
97 if (bestSoFar > score) {
98 bestSoFar = score;
99 bestTableIndex = tableIndex;
100 bestPixelIndex = pixelIndex;
101 }
102 }
103 }
104
Robert Phillips48257d72019-12-16 14:20:53 -0500105 high |= (bestTableIndex << 5) | (bestTableIndex << 2);
Robert Phillips459b2952019-05-23 09:38:27 -0400106
Robert Phillips48257d72019-12-16 14:20:53 -0500107 if (bestPixelIndex & 0x1) {
108 low |= 0xFFFF;
Robert Phillips459b2952019-05-23 09:38:27 -0400109 }
Robert Phillips48257d72019-12-16 14:20:53 -0500110 if (bestPixelIndex & 0x2) {
111 low |= 0xFFFF0000;
112 }
113
114 block->fHigh = SkBSwap32(high);
115 block->fLow = SkBSwap32(low);
Robert Phillips459b2952019-05-23 09:38:27 -0400116}
117
Robert Phillips162e04b2020-01-28 14:22:43 -0500118static int num_4x4_blocks(int size) {
119 return ((size + 3) & ~3) >> 2;
Jim Van Verthe3671012019-09-18 09:53:31 -0400120}
121
122static int num_ETC1_blocks(int w, int h) {
Robert Phillips162e04b2020-01-28 14:22:43 -0500123 w = num_4x4_blocks(w);
124 h = num_4x4_blocks(h);
Robert Phillips459b2952019-05-23 09:38:27 -0400125
126 return w * h;
127}
128
Robert Phillips8f259a02019-12-20 11:32:27 -0500129struct BC1Block {
130 uint16_t fColor0;
131 uint16_t fColor1;
132 uint32_t fIndices;
133};
134
Robert Phillipsac908022020-01-14 16:54:17 -0500135static uint16_t to565(SkColor col) {
Robert Phillips8f259a02019-12-20 11:32:27 -0500136 int r5 = SkMulDiv255Round(31, SkColorGetR(col));
137 int g6 = SkMulDiv255Round(63, SkColorGetG(col));
138 int b5 = SkMulDiv255Round(31, SkColorGetB(col));
139
Robert Phillipsac908022020-01-14 16:54:17 -0500140 return (r5 << 11) | (g6 << 5) | b5;
141}
142
143// Create a BC1 compressed block that has two colors but is initialized to 'col0'
144static void create_BC1_block(SkColor col0, SkColor col1, BC1Block* block) {
145 block->fColor0 = to565(col0);
146 block->fColor1 = to565(col1);
Robert Phillipsd095b9f2020-02-03 16:12:51 -0500147 SkASSERT(block->fColor0 <= block->fColor1); // we always assume transparent blocks
148
Robert Phillipsb0855272020-01-15 12:56:52 -0500149 if (col0 == SK_ColorTRANSPARENT) {
150 // This sets all 16 pixels to just use color3 (under the assumption
151 // that this is a kBC1_RGBA8_UNORM texture. Note that in this case
152 // fColor0 will be opaque black.
153 block->fIndices = 0xFFFFFFFF;
154 } else {
155 // This sets all 16 pixels to just use 'fColor0'
156 block->fIndices = 0;
157 }
Robert Phillips8f259a02019-12-20 11:32:27 -0500158}
159
Greg Danielc89a7ee2020-10-12 16:50:18 -0400160size_t GrNumBlocks(SkImage::CompressionType type, SkISize baseDimensions) {
161 switch (type) {
162 case SkImage::CompressionType::kNone:
163 return baseDimensions.width() * baseDimensions.height();
164 case SkImage::CompressionType::kETC2_RGB8_UNORM:
165 case SkImage::CompressionType::kBC1_RGB8_UNORM:
166 case SkImage::CompressionType::kBC1_RGBA8_UNORM: {
167 int numBlocksWidth = num_4x4_blocks(baseDimensions.width());
168 int numBlocksHeight = num_4x4_blocks(baseDimensions.height());
169
170 return numBlocksWidth * numBlocksHeight;
171 }
172 }
173 SkUNREACHABLE;
174}
175
Jim Van Verthe3671012019-09-18 09:53:31 -0400176size_t GrCompressedRowBytes(SkImage::CompressionType type, int width) {
177 switch (type) {
Robert Phillipsab2b7222019-12-10 10:05:06 -0500178 case SkImage::CompressionType::kNone:
179 return 0;
Robert Phillipsc558f722020-01-13 13:02:26 -0500180 case SkImage::CompressionType::kETC2_RGB8_UNORM:
Robert Phillipsb0855272020-01-15 12:56:52 -0500181 case SkImage::CompressionType::kBC1_RGB8_UNORM:
182 case SkImage::CompressionType::kBC1_RGBA8_UNORM: {
Robert Phillips162e04b2020-01-28 14:22:43 -0500183 int numBlocksWidth = num_4x4_blocks(width);
Robert Phillips8f259a02019-12-20 11:32:27 -0500184
185 static_assert(sizeof(ETC1Block) == sizeof(BC1Block));
Jim Van Verthe3671012019-09-18 09:53:31 -0400186 return numBlocksWidth * sizeof(ETC1Block);
Robert Phillipsb0855272020-01-15 12:56:52 -0500187 }
Jim Van Verthe3671012019-09-18 09:53:31 -0400188 }
Robert Phillipsab2b7222019-12-10 10:05:06 -0500189 SkUNREACHABLE;
Jim Van Verthe3671012019-09-18 09:53:31 -0400190}
191
Robert Phillips41acc0e2020-01-06 13:29:53 -0500192SkISize GrCompressedDimensions(SkImage::CompressionType type, SkISize baseDimensions) {
193 switch (type) {
194 case SkImage::CompressionType::kNone:
195 return baseDimensions;
Robert Phillipsc558f722020-01-13 13:02:26 -0500196 case SkImage::CompressionType::kETC2_RGB8_UNORM:
Robert Phillipsb0855272020-01-15 12:56:52 -0500197 case SkImage::CompressionType::kBC1_RGB8_UNORM:
198 case SkImage::CompressionType::kBC1_RGBA8_UNORM: {
Robert Phillips162e04b2020-01-28 14:22:43 -0500199 int numBlocksWidth = num_4x4_blocks(baseDimensions.width());
200 int numBlocksHeight = num_4x4_blocks(baseDimensions.height());
Robert Phillips41acc0e2020-01-06 13:29:53 -0500201
202 // Each BC1_RGB8_UNORM and ETC1 block has 16 pixels
203 return { 4 * numBlocksWidth, 4 * numBlocksHeight };
Robert Phillipsb0855272020-01-15 12:56:52 -0500204 }
Robert Phillips41acc0e2020-01-06 13:29:53 -0500205 }
206 SkUNREACHABLE;
207}
208
Robert Phillips28a5a432019-06-07 12:46:21 -0400209// Fill in 'dest' with ETC1 blocks derived from 'colorf'
Robert Phillips48257d72019-12-16 14:20:53 -0500210static void fillin_ETC1_with_color(SkISize dimensions, const SkColor4f& colorf, char* dest) {
Robert Phillips459b2952019-05-23 09:38:27 -0400211 SkColor color = colorf.toSkColor();
212
213 ETC1Block block;
214 create_etc1_block(color, &block);
215
Robert Phillips48257d72019-12-16 14:20:53 -0500216 int numBlocks = num_ETC1_blocks(dimensions.width(), dimensions.height());
Robert Phillips8043f322019-05-31 08:11:36 -0400217
Robert Phillips459b2952019-05-23 09:38:27 -0400218 for (int i = 0; i < numBlocks; ++i) {
Robert Phillips48257d72019-12-16 14:20:53 -0500219 memcpy(dest, &block, sizeof(ETC1Block));
220 dest += sizeof(ETC1Block);
Robert Phillips459b2952019-05-23 09:38:27 -0400221 }
222}
223
Robert Phillips8f259a02019-12-20 11:32:27 -0500224// Fill in 'dest' with BC1 blocks derived from 'colorf'
225static void fillin_BC1_with_color(SkISize dimensions, const SkColor4f& colorf, char* dest) {
226 SkColor color = colorf.toSkColor();
227
228 BC1Block block;
Robert Phillipsac908022020-01-14 16:54:17 -0500229 create_BC1_block(color, color, &block);
Robert Phillips8f259a02019-12-20 11:32:27 -0500230
231 int numBlocks = num_ETC1_blocks(dimensions.width(), dimensions.height());
232
233 for (int i = 0; i < numBlocks; ++i) {
234 memcpy(dest, &block, sizeof(BC1Block));
235 dest += sizeof(BC1Block);
236 }
237}
238
Robert Phillipse0735522020-01-31 11:03:32 -0500239#if GR_TEST_UTILS
240
Robert Phillipsac908022020-01-14 16:54:17 -0500241// Fill in 'dstPixels' with BC1 blocks derived from the 'pixmap'.
242void GrTwoColorBC1Compress(const SkPixmap& pixmap, SkColor otherColor, char* dstPixels) {
Robert Phillips99dead92020-01-27 16:11:57 -0500243 BC1Block* dstBlocks = reinterpret_cast<BC1Block*>(dstPixels);
Robert Phillipsac908022020-01-14 16:54:17 -0500244 SkASSERT(pixmap.colorType() == SkColorType::kRGBA_8888_SkColorType);
245
246 BC1Block block;
247
248 // black -> fColor0, otherColor -> fColor1
249 create_BC1_block(SK_ColorBLACK, otherColor, &block);
250
Robert Phillips162e04b2020-01-28 14:22:43 -0500251 int numXBlocks = num_4x4_blocks(pixmap.width());
252 int numYBlocks = num_4x4_blocks(pixmap.height());
Robert Phillipsac908022020-01-14 16:54:17 -0500253
254 for (int y = 0; y < numYBlocks; ++y) {
255 for (int x = 0; x < numXBlocks; ++x) {
256 int shift = 0;
257 int offsetX = 4 * x, offsetY = 4 * y;
Robert Phillips162e04b2020-01-28 14:22:43 -0500258 block.fIndices = 0; // init all the pixels to color0 (i.e., opaque black)
Robert Phillipsac908022020-01-14 16:54:17 -0500259 for (int i = 0; i < 4; ++i) {
Robert Phillips162e04b2020-01-28 14:22:43 -0500260 for (int j = 0; j < 4; ++j, shift += 2) {
Robert Phillipsac908022020-01-14 16:54:17 -0500261 if (offsetX + j >= pixmap.width() || offsetY + i >= pixmap.height()) {
Robert Phillips162e04b2020-01-28 14:22:43 -0500262 // This can happen for the topmost levels of a mipmap and for
263 // non-multiple of 4 textures
Robert Phillipsac908022020-01-14 16:54:17 -0500264 continue;
265 }
266
267 SkColor tmp = pixmap.getColor(offsetX + j, offsetY + i);
268 if (tmp == SK_ColorTRANSPARENT) {
269 // For RGBA BC1 images color3 is set to transparent black
270 block.fIndices |= 3 << shift;
271 } else if (tmp != SK_ColorBLACK) {
272 block.fIndices |= 1 << shift; // color1
273 }
Robert Phillipsac908022020-01-14 16:54:17 -0500274 }
275 }
276
277 dstBlocks[y*numXBlocks + x] = block;
278 }
279 }
280}
281
Robert Phillipse0735522020-01-31 11:03:32 -0500282#endif
283
Brian Salomon85c3d682019-11-04 15:04:54 -0500284size_t GrComputeTightCombinedBufferSize(size_t bytesPerPixel, SkISize baseDimensions,
Brian Salomonbb8dde82019-06-27 10:52:13 -0400285 SkTArray<size_t>* individualMipOffsets, int mipLevelCount) {
Robert Phillips28a5a432019-06-07 12:46:21 -0400286 SkASSERT(individualMipOffsets && !individualMipOffsets->count());
287 SkASSERT(mipLevelCount >= 1);
288
289 individualMipOffsets->push_back(0);
290
Brian Salomon85c3d682019-11-04 15:04:54 -0500291 size_t combinedBufferSize = baseDimensions.width() * bytesPerPixel * baseDimensions.height();
292 SkISize levelDimensions = baseDimensions;
Robert Phillips28a5a432019-06-07 12:46:21 -0400293
294 // The Vulkan spec for copying a buffer to an image requires that the alignment must be at
295 // least 4 bytes and a multiple of the bytes per pixel of the image config.
296 SkASSERT(bytesPerPixel == 1 || bytesPerPixel == 2 || bytesPerPixel == 3 ||
297 bytesPerPixel == 4 || bytesPerPixel == 8 || bytesPerPixel == 16);
298 int desiredAlignment = (bytesPerPixel == 3) ? 12 : (bytesPerPixel > 4 ? bytesPerPixel : 4);
299
300 for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; ++currentMipLevel) {
Brian Osman788b9162020-02-07 10:36:46 -0500301 levelDimensions = {std::max(1, levelDimensions.width() /2),
302 std::max(1, levelDimensions.height()/2)};
Robert Phillips28a5a432019-06-07 12:46:21 -0400303
Brian Salomon85c3d682019-11-04 15:04:54 -0500304 size_t trimmedSize = levelDimensions.area() * bytesPerPixel;
Robert Phillips28a5a432019-06-07 12:46:21 -0400305 const size_t alignmentDiff = combinedBufferSize % desiredAlignment;
306 if (alignmentDiff != 0) {
307 combinedBufferSize += desiredAlignment - alignmentDiff;
308 }
309 SkASSERT((0 == combinedBufferSize % 4) && (0 == combinedBufferSize % bytesPerPixel));
310
311 individualMipOffsets->push_back(combinedBufferSize);
312 combinedBufferSize += trimmedSize;
313 }
314
315 SkASSERT(individualMipOffsets->count() == mipLevelCount);
316 return combinedBufferSize;
317}
318
Robert Phillips48257d72019-12-16 14:20:53 -0500319void GrFillInCompressedData(SkImage::CompressionType type, SkISize dimensions,
Brian Salomon7e67dca2020-07-21 09:27:25 -0400320 GrMipmapped mipMapped, char* dstPixels, const SkColor4f& colorf) {
Brian Salomonbb8dde82019-06-27 10:52:13 -0400321 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
Robert Phillips48257d72019-12-16 14:20:53 -0500322
323 int numMipLevels = 1;
Brian Salomon7e67dca2020-07-21 09:27:25 -0400324 if (mipMapped == GrMipmapped::kYes) {
Mike Reed13711eb2020-07-14 17:16:32 -0400325 numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
Robert Phillips48257d72019-12-16 14:20:53 -0500326 }
327
Robert Phillips8f259a02019-12-20 11:32:27 -0500328 size_t offset = 0;
Robert Phillips48257d72019-12-16 14:20:53 -0500329
Robert Phillips8f259a02019-12-20 11:32:27 -0500330 for (int i = 0; i < numMipLevels; ++i) {
Robert Phillips99dead92020-01-27 16:11:57 -0500331 size_t levelSize = SkCompressedDataSize(type, dimensions, nullptr, false);
Robert Phillips48257d72019-12-16 14:20:53 -0500332
Robert Phillipsc558f722020-01-13 13:02:26 -0500333 if (SkImage::CompressionType::kETC2_RGB8_UNORM == type) {
Robert Phillips48257d72019-12-16 14:20:53 -0500334 fillin_ETC1_with_color(dimensions, colorf, &dstPixels[offset]);
Robert Phillips8f259a02019-12-20 11:32:27 -0500335 } else {
Robert Phillipsb0855272020-01-15 12:56:52 -0500336 SkASSERT(type == SkImage::CompressionType::kBC1_RGB8_UNORM ||
337 type == SkImage::CompressionType::kBC1_RGBA8_UNORM);
Robert Phillips8f259a02019-12-20 11:32:27 -0500338 fillin_BC1_with_color(dimensions, colorf, &dstPixels[offset]);
Robert Phillips48257d72019-12-16 14:20:53 -0500339 }
Robert Phillips8f259a02019-12-20 11:32:27 -0500340
341 offset += levelSize;
Brian Osman788b9162020-02-07 10:36:46 -0500342 dimensions = {std::max(1, dimensions.width()/2), std::max(1, dimensions.height()/2)};
Brian Salomonbb8dde82019-06-27 10:52:13 -0400343 }
344}
345
Brian Salomon85c3d682019-11-04 15:04:54 -0500346static GrSwizzle get_load_and_src_swizzle(GrColorType ct, SkRasterPipeline::StockStage* load,
Greg Daniele877dce2019-07-11 10:52:43 -0400347 bool* isNormalized, bool* isSRGB) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400348 GrSwizzle swizzle("rgba");
349 *isNormalized = true;
Greg Daniele877dce2019-07-11 10:52:43 -0400350 *isSRGB = false;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400351 switch (ct) {
352 case GrColorType::kAlpha_8: *load = SkRasterPipeline::load_a8; break;
Robert Phillips429f0d32019-09-11 17:03:28 -0400353 case GrColorType::kAlpha_16: *load = SkRasterPipeline::load_a16; break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400354 case GrColorType::kBGR_565: *load = SkRasterPipeline::load_565; break;
355 case GrColorType::kABGR_4444: *load = SkRasterPipeline::load_4444; break;
Greg Daniel746460e2020-06-30 13:13:53 -0400356 case GrColorType::kARGB_4444: swizzle = GrSwizzle("bgra");
357 *load = SkRasterPipeline::load_4444; break;
358 case GrColorType::kBGRA_4444: swizzle = GrSwizzle("gbar");
359 *load = SkRasterPipeline::load_4444; break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400360 case GrColorType::kRGBA_8888: *load = SkRasterPipeline::load_8888; break;
361 case GrColorType::kRG_88: *load = SkRasterPipeline::load_rg88; break;
362 case GrColorType::kRGBA_1010102: *load = SkRasterPipeline::load_1010102; break;
Robert Phillips9a30ee02020-04-29 08:58:39 -0400363 case GrColorType::kBGRA_1010102: *load = SkRasterPipeline::load_1010102;
364 swizzle = GrSwizzle("bgra");
365 break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400366 case GrColorType::kAlpha_F16: *load = SkRasterPipeline::load_af16; break;
367 case GrColorType::kRGBA_F16_Clamped: *load = SkRasterPipeline::load_f16; break;
368 case GrColorType::kRG_1616: *load = SkRasterPipeline::load_rg1616; break;
369 case GrColorType::kRGBA_16161616: *load = SkRasterPipeline::load_16161616; break;
370
Greg Daniele877dce2019-07-11 10:52:43 -0400371 case GrColorType::kRGBA_8888_SRGB: *load = SkRasterPipeline::load_8888;
372 *isSRGB = true;
373 break;
Brian Salomone14cfbe2019-06-24 15:00:58 -0400374 case GrColorType::kRG_F16: *load = SkRasterPipeline::load_rgf16;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400375 *isNormalized = false;
376 break;
377 case GrColorType::kRGBA_F16: *load = SkRasterPipeline::load_f16;
378 *isNormalized = false;
379 break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400380 case GrColorType::kRGBA_F32: *load = SkRasterPipeline::load_f32;
381 *isNormalized = false;
382 break;
Brian Salomon8f8354a2019-07-31 20:12:02 -0400383 case GrColorType::kAlpha_8xxx: *load = SkRasterPipeline::load_8888;
384 swizzle = GrSwizzle("000r");
385 break;
386 case GrColorType::kAlpha_F32xxx: *load = SkRasterPipeline::load_f32;
387 swizzle = GrSwizzle("000r");
388 break;
389 case GrColorType::kGray_8xxx: *load = SkRasterPipeline::load_8888;
390 swizzle = GrSwizzle("rrr1");
391 break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400392 case GrColorType::kGray_8: *load = SkRasterPipeline::load_a8;
393 swizzle = GrSwizzle("aaa1");
394 break;
Brian Salomon01ff5382020-12-15 16:06:26 -0500395 case GrColorType::kGrayAlpha_88: *load = SkRasterPipeline::load_rg88;
396 swizzle = GrSwizzle("rrrg");
397 break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400398 case GrColorType::kBGRA_8888: *load = SkRasterPipeline::load_8888;
399 swizzle = GrSwizzle("bgra");
400 break;
401 case GrColorType::kRGB_888x: *load = SkRasterPipeline::load_8888;
402 swizzle = GrSwizzle("rgb1");
403 break;
404
Brian Salomon85c3d682019-11-04 15:04:54 -0500405 // These are color types we don't expect to ever have to load.
406 case GrColorType::kRGB_888:
407 case GrColorType::kR_8:
408 case GrColorType::kR_16:
409 case GrColorType::kR_F16:
410 case GrColorType::kGray_F16:
Brian Salomonf30b1c12019-06-20 12:25:02 -0400411 case GrColorType::kUnknown:
Brian Salomonf30b1c12019-06-20 12:25:02 -0400412 SK_ABORT("unexpected CT");
413 }
414 return swizzle;
415}
416
Brian Salomon01ff5382020-12-15 16:06:26 -0500417enum class LumMode {
418 kNone,
419 kToRGB,
420 kToAlpha
421};
422
Brian Salomonf30b1c12019-06-20 12:25:02 -0400423static GrSwizzle get_dst_swizzle_and_store(GrColorType ct, SkRasterPipeline::StockStage* store,
Brian Salomon01ff5382020-12-15 16:06:26 -0500424 LumMode* lumMode, bool* isNormalized, bool* isSRGB) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400425 GrSwizzle swizzle("rgba");
426 *isNormalized = true;
Greg Daniele877dce2019-07-11 10:52:43 -0400427 *isSRGB = false;
Brian Salomon01ff5382020-12-15 16:06:26 -0500428 *lumMode = LumMode::kNone;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400429 switch (ct) {
430 case GrColorType::kAlpha_8: *store = SkRasterPipeline::store_a8; break;
Robert Phillips429f0d32019-09-11 17:03:28 -0400431 case GrColorType::kAlpha_16: *store = SkRasterPipeline::store_a16; break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400432 case GrColorType::kBGR_565: *store = SkRasterPipeline::store_565; break;
433 case GrColorType::kABGR_4444: *store = SkRasterPipeline::store_4444; break;
Greg Daniel746460e2020-06-30 13:13:53 -0400434 case GrColorType::kARGB_4444: swizzle = GrSwizzle("bgra");
435 *store = SkRasterPipeline::store_4444; break;
436 case GrColorType::kBGRA_4444: swizzle = GrSwizzle("argb");
437 *store = SkRasterPipeline::store_4444; break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400438 case GrColorType::kRGBA_8888: *store = SkRasterPipeline::store_8888; break;
439 case GrColorType::kRG_88: *store = SkRasterPipeline::store_rg88; break;
440 case GrColorType::kRGBA_1010102: *store = SkRasterPipeline::store_1010102; break;
Robert Phillips9a30ee02020-04-29 08:58:39 -0400441 case GrColorType::kBGRA_1010102: swizzle = GrSwizzle("bgra");
442 *store = SkRasterPipeline::store_1010102;
443 break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400444 case GrColorType::kRGBA_F16_Clamped: *store = SkRasterPipeline::store_f16; break;
445 case GrColorType::kRG_1616: *store = SkRasterPipeline::store_rg1616; break;
446 case GrColorType::kRGBA_16161616: *store = SkRasterPipeline::store_16161616; break;
447
Greg Daniele877dce2019-07-11 10:52:43 -0400448 case GrColorType::kRGBA_8888_SRGB: *store = SkRasterPipeline::store_8888;
449 *isSRGB = true;
450 break;
Brian Salomone14cfbe2019-06-24 15:00:58 -0400451 case GrColorType::kRG_F16: *store = SkRasterPipeline::store_rgf16;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400452 *isNormalized = false;
453 break;
454 case GrColorType::kAlpha_F16: *store = SkRasterPipeline::store_af16;
455 *isNormalized = false;
456 break;
457 case GrColorType::kRGBA_F16: *store = SkRasterPipeline::store_f16;
458 *isNormalized = false;
459 break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400460 case GrColorType::kRGBA_F32: *store = SkRasterPipeline::store_f32;
461 *isNormalized = false;
462 break;
Brian Salomon8f8354a2019-07-31 20:12:02 -0400463 case GrColorType::kAlpha_8xxx: *store = SkRasterPipeline::store_8888;
464 swizzle = GrSwizzle("a000");
465 break;
466 case GrColorType::kAlpha_F32xxx: *store = SkRasterPipeline::store_f32;
467 swizzle = GrSwizzle("a000");
468 break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400469 case GrColorType::kBGRA_8888: swizzle = GrSwizzle("bgra");
470 *store = SkRasterPipeline::store_8888;
471 break;
472 case GrColorType::kRGB_888x: swizzle = GrSwizzle("rgb1");
473 *store = SkRasterPipeline::store_8888;
474 break;
Brian Salomon85c3d682019-11-04 15:04:54 -0500475 case GrColorType::kR_8: swizzle = GrSwizzle("agbr");
476 *store = SkRasterPipeline::store_a8;
477 break;
478 case GrColorType::kR_16: swizzle = GrSwizzle("agbr");
479 *store = SkRasterPipeline::store_a16;
480 break;
481 case GrColorType::kR_F16: swizzle = GrSwizzle("agbr");
482 *store = SkRasterPipeline::store_af16;
483 break;
Brian Salomon01ff5382020-12-15 16:06:26 -0500484 case GrColorType::kGray_F16: *lumMode = LumMode::kToAlpha;
Brian Salomon85c3d682019-11-04 15:04:54 -0500485 *store = SkRasterPipeline::store_af16;
486 break;
Brian Salomon01ff5382020-12-15 16:06:26 -0500487 case GrColorType::kGray_8: *lumMode = LumMode::kToAlpha;
Brian Salomon998937c2019-10-29 09:34:52 -0400488 *store = SkRasterPipeline::store_a8;
489 break;
Brian Salomon01ff5382020-12-15 16:06:26 -0500490 case GrColorType::kGrayAlpha_88: *lumMode = LumMode::kToRGB;
491 swizzle = GrSwizzle("ragb");
492 *store = SkRasterPipeline::store_rg88;
493 break;
494 case GrColorType::kGray_8xxx: *lumMode = LumMode::kToRGB;
Brian Salomon998937c2019-10-29 09:34:52 -0400495 *store = SkRasterPipeline::store_8888;
Brian Salomon01ff5382020-12-15 16:06:26 -0500496 swizzle = GrSwizzle("r000");
Brian Salomon998937c2019-10-29 09:34:52 -0400497 break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400498
Brian Salomon85c3d682019-11-04 15:04:54 -0500499 // These are color types we don't expect to ever have to store.
500 case GrColorType::kRGB_888: // This is handled specially in GrConvertPixels.
Brian Salomonf30b1c12019-06-20 12:25:02 -0400501 case GrColorType::kUnknown:
Brian Salomonf30b1c12019-06-20 12:25:02 -0400502 SK_ABORT("unexpected CT");
503 }
504 return swizzle;
505}
506
507static inline void append_clamp_gamut(SkRasterPipeline* pipeline) {
508 // SkRasterPipeline may not know our color type and also doesn't like caller to directly
509 // append clamp_gamut. Fake it out.
510 static SkImageInfo fakeII = SkImageInfo::MakeN32Premul(1, 1);
511 pipeline->append_gamut_clamp_if_normalized(fakeII);
512}
513
Brian Salomon5392c942021-03-30 16:14:37 -0400514bool GrConvertPixels(const GrPixmap& dst, const GrCPixmap& src, bool flipY) {
Brian Salomonc42eb662019-06-24 17:13:00 -0400515 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
Brian Salomon5392c942021-03-30 16:14:37 -0400516 if (src.dimensions().isEmpty() || dst.dimensions().isEmpty()) {
Brian Salomondd4087d2020-12-23 20:36:44 -0500517 return false;
518 }
Brian Salomon5392c942021-03-30 16:14:37 -0400519 if (src.colorType() == GrColorType::kUnknown || dst.colorType() == GrColorType::kUnknown) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400520 return false;
521 }
Brian Salomon5392c942021-03-30 16:14:37 -0400522 if (!src.hasPixels() || !dst.hasPixels()) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400523 return false;
524 }
Brian Salomon5392c942021-03-30 16:14:37 -0400525 if (dst.dimensions() != src.dimensions()) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400526 return false;
527 }
Brian Salomon5392c942021-03-30 16:14:37 -0400528 if (dst.colorType() == GrColorType::kRGB_888) {
Brian Salomon85c3d682019-11-04 15:04:54 -0500529 // SkRasterPipeline doesn't handle writing to RGB_888. So we have it write to RGB_888x and
Brian Salomon5392c942021-03-30 16:14:37 -0400530 // then do another conversion that does the 24bit packing. We could be cleverer and skip the
531 // temp pixmap if this is the only conversion but this is rare so keeping it simple.
532 GrPixmap temp = GrPixmap::Allocate(dst.info().makeColorType(GrColorType::kRGB_888x));
533 if (!GrConvertPixels(temp, src, flipY)) {
Brian Salomon85c3d682019-11-04 15:04:54 -0500534 return false;
535 }
Brian Salomon5392c942021-03-30 16:14:37 -0400536 auto* tRow = reinterpret_cast<const char*>(temp.addr());
537 auto* dRow = reinterpret_cast<char*>(dst.addr());
538 for (int y = 0; y < dst.height(); ++y, tRow += temp.rowBytes(), dRow += dst.rowBytes()) {
539 for (int x = 0; x < dst.width(); ++x) {
Brian Salomon974c8212021-04-05 16:24:00 -0400540 auto t = tRow + x*sizeof(uint32_t);
541 auto d = dRow + x*3;
Brian Salomon85c3d682019-11-04 15:04:54 -0500542 memcpy(d, t, 3);
543 }
544 }
545 return true;
Brian Salomon974c8212021-04-05 16:24:00 -0400546 } else if (src.colorType() == GrColorType::kRGB_888) {
547 // SkRasterPipeline doesn't handle reading from RGB_888. So convert it to RGB_888x and then
548 // do a recursive call if there is any remaining conversion.
549 GrPixmap temp = GrPixmap::Allocate(src.info().makeColorType(GrColorType::kRGB_888x));
550 auto* sRow = reinterpret_cast<const char*>(src.addr());
551 auto* tRow = reinterpret_cast<char*>(temp.addr());
552 for (int y = 0; y < src.height(); ++y, sRow += src.rowBytes(), tRow += temp.rowBytes()) {
553 for (int x = 0; x < src.width(); ++x) {
554 auto s = sRow + x*3;
555 auto t = tRow + x*sizeof(uint32_t);
556 memcpy(t, s, 3);
557 t[3] = static_cast<char>(0xFF);
558 }
559 }
560 return GrConvertPixels(dst, temp, flipY);
Brian Salomon85c3d682019-11-04 15:04:54 -0500561 }
Brian Salomon1d435302019-07-01 13:05:28 -0400562
Brian Salomon5392c942021-03-30 16:14:37 -0400563 size_t srcBpp = src.info().bpp();
564 size_t dstBpp = dst.info().bpp();
Brian Salomon1d435302019-07-01 13:05:28 -0400565
Brian Salomonf30b1c12019-06-20 12:25:02 -0400566 // SkRasterPipeline operates on row-pixels not row-bytes.
Brian Salomon5392c942021-03-30 16:14:37 -0400567 SkASSERT(dst.rowBytes() % dstBpp == 0);
568 SkASSERT(src.rowBytes() % srcBpp == 0);
Brian Salomonf30b1c12019-06-20 12:25:02 -0400569
Brian Salomon5392c942021-03-30 16:14:37 -0400570 bool premul = src.alphaType() == kUnpremul_SkAlphaType &&
571 dst.alphaType() == kPremul_SkAlphaType;
572 bool unpremul = src.alphaType() == kPremul_SkAlphaType &&
573 dst.alphaType() == kUnpremul_SkAlphaType;
Brian Salomon77a684f2019-08-01 14:38:04 -0400574 bool alphaOrCSConversion =
Brian Salomon5392c942021-03-30 16:14:37 -0400575 premul || unpremul || !SkColorSpace::Equals(src.colorSpace(), dst.colorSpace());
Brian Salomon77a684f2019-08-01 14:38:04 -0400576
Brian Salomon5392c942021-03-30 16:14:37 -0400577 if (src.colorType() == dst.colorType() && !alphaOrCSConversion) {
578 size_t tightRB = dstBpp * dst.width();
Brian Salomon77a684f2019-08-01 14:38:04 -0400579 if (flipY) {
Brian Salomon5392c942021-03-30 16:14:37 -0400580 auto s = static_cast<const char*>(src.addr());
581 auto d = SkTAddOffset<char>(dst.addr(), dst.rowBytes()*(dst.height() - 1));
582 for (int y = 0; y < dst.height(); ++y, d -= dst.rowBytes(), s += src.rowBytes()) {
583 memcpy(d, s, tightRB);
Brian Salomon77a684f2019-08-01 14:38:04 -0400584 }
585 } else {
Brian Salomon5392c942021-03-30 16:14:37 -0400586 SkRectMemcpy(dst.addr(), dst.rowBytes(),
587 src.addr(), src.rowBytes(),
588 tightRB, src.height());
Brian Salomon77a684f2019-08-01 14:38:04 -0400589 }
590 return true;
591 }
592
Brian Salomonf30b1c12019-06-20 12:25:02 -0400593 SkRasterPipeline::StockStage load;
594 bool srcIsNormalized;
Greg Daniele877dce2019-07-11 10:52:43 -0400595 bool srcIsSRGB;
Brian Salomon5392c942021-03-30 16:14:37 -0400596 auto loadSwizzle = get_load_and_src_swizzle(src.colorType(),
597 &load,
598 &srcIsNormalized,
599 &srcIsSRGB);
Brian Salomonf30b1c12019-06-20 12:25:02 -0400600
601 SkRasterPipeline::StockStage store;
Brian Salomon01ff5382020-12-15 16:06:26 -0500602 LumMode lumMode;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400603 bool dstIsNormalized;
Greg Daniele877dce2019-07-11 10:52:43 -0400604 bool dstIsSRGB;
Brian Salomon5392c942021-03-30 16:14:37 -0400605 auto storeSwizzle = get_dst_swizzle_and_store(dst.colorType(),
606 &store,
607 &lumMode,
608 &dstIsNormalized,
609 &dstIsSRGB);
Brian Salomonf30b1c12019-06-20 12:25:02 -0400610
Brian Salomonf30b1c12019-06-20 12:25:02 -0400611 bool clampGamut;
612 SkTLazy<SkColorSpaceXformSteps> steps;
613 GrSwizzle loadStoreSwizzle;
614 if (alphaOrCSConversion) {
Brian Salomon5392c942021-03-30 16:14:37 -0400615 steps.init(src.colorSpace(), src.alphaType(), dst.colorSpace(), dst.alphaType());
616 clampGamut = dstIsNormalized && dst.alphaType() == kPremul_SkAlphaType;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400617 } else {
Brian Salomon5392c942021-03-30 16:14:37 -0400618 clampGamut = dstIsNormalized && !srcIsNormalized && dst.alphaType() == kPremul_SkAlphaType;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400619 if (!clampGamut) {
620 loadStoreSwizzle = GrSwizzle::Concat(loadSwizzle, storeSwizzle);
621 }
622 }
623 int cnt = 1;
Brian Salomon5392c942021-03-30 16:14:37 -0400624 int height = src.height();
625 SkRasterPipeline_MemoryCtx
626 srcCtx{const_cast<void*>(src.addr()), SkToInt(src.rowBytes()/srcBpp)},
627 dstCtx{ dst.addr(), SkToInt(dst.rowBytes()/dstBpp)};
Brian Salomonf30b1c12019-06-20 12:25:02 -0400628
Brian Salomon1d435302019-07-01 13:05:28 -0400629 if (flipY) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400630 // It *almost* works to point the src at the last row and negate the stride and run the
631 // whole rectangle. However, SkRasterPipeline::run()'s control loop uses size_t loop
632 // variables so it winds up relying on unsigned overflow math. It works out in practice
633 // but UBSAN says "no!" as it's technically undefined and in theory a compiler could emit
634 // code that didn't do what is intended. So we go one row at a time. :(
Brian Salomon5392c942021-03-30 16:14:37 -0400635 srcCtx.pixels = static_cast<char*>(srcCtx.pixels) + src.rowBytes()*(height - 1);
Brian Salomonf30b1c12019-06-20 12:25:02 -0400636 std::swap(cnt, height);
637 }
Greg Daniele877dce2019-07-11 10:52:43 -0400638
Brian Salomon01ff5382020-12-15 16:06:26 -0500639 bool hasConversion = alphaOrCSConversion || clampGamut || lumMode != LumMode::kNone;
Greg Daniele877dce2019-07-11 10:52:43 -0400640
641 if (srcIsSRGB && dstIsSRGB && !hasConversion) {
642 // No need to convert from srgb if we are just going to immediately convert it back.
643 srcIsSRGB = dstIsSRGB = false;
644 }
645
646 hasConversion = hasConversion || srcIsSRGB || dstIsSRGB;
647
Brian Salomonf30b1c12019-06-20 12:25:02 -0400648 for (int i = 0; i < cnt; ++i) {
649 SkRasterPipeline_<256> pipeline;
650 pipeline.append(load, &srcCtx);
Greg Daniele877dce2019-07-11 10:52:43 -0400651 if (hasConversion) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400652 loadSwizzle.apply(&pipeline);
Greg Daniele877dce2019-07-11 10:52:43 -0400653 if (srcIsSRGB) {
Mike Klein04984312020-05-19 12:41:05 -0500654 pipeline.append_transfer_function(*skcms_sRGB_TransferFunction());
Greg Daniele877dce2019-07-11 10:52:43 -0400655 }
656 if (alphaOrCSConversion) {
Mike Kleinec8e0bf2020-05-22 11:42:38 -0500657 steps->apply(&pipeline);
Greg Daniele877dce2019-07-11 10:52:43 -0400658 }
Brian Salomonf30b1c12019-06-20 12:25:02 -0400659 if (clampGamut) {
660 append_clamp_gamut(&pipeline);
661 }
Brian Salomon01ff5382020-12-15 16:06:26 -0500662 switch (lumMode) {
663 case LumMode::kNone:
664 break;
665 case LumMode::kToRGB:
666 pipeline.append(SkRasterPipeline::StockStage::bt709_luminance_or_luma_to_rgb);
667 break;
668 case LumMode::kToAlpha:
669 pipeline.append(SkRasterPipeline::StockStage::bt709_luminance_or_luma_to_alpha);
670 // If we ever need to store srgb-encoded gray (e.g. GL_SLUMINANCE8) then we
671 // should use ToRGB and then a swizzle stage rather than ToAlpha. The subsequent
672 // transfer function stage ignores the alpha channel (where we just stashed the
673 // gray).
674 SkASSERT(!dstIsSRGB);
675 break;
Brian Salomon998937c2019-10-29 09:34:52 -0400676 }
Greg Daniele877dce2019-07-11 10:52:43 -0400677 if (dstIsSRGB) {
Mike Klein04984312020-05-19 12:41:05 -0500678 pipeline.append_transfer_function(*skcms_sRGB_Inverse_TransferFunction());
Greg Daniele877dce2019-07-11 10:52:43 -0400679 }
Brian Salomonf30b1c12019-06-20 12:25:02 -0400680 storeSwizzle.apply(&pipeline);
681 } else {
Greg Daniele877dce2019-07-11 10:52:43 -0400682 loadStoreSwizzle.apply(&pipeline);
Brian Salomonf30b1c12019-06-20 12:25:02 -0400683 }
684 pipeline.append(store, &dstCtx);
Brian Salomon5392c942021-03-30 16:14:37 -0400685 pipeline.run(0, 0, src.width(), height);
686 srcCtx.pixels = static_cast<char*>(srcCtx.pixels) - src.rowBytes();
687 dstCtx.pixels = static_cast<char*>(dstCtx.pixels) + dst.rowBytes();
Brian Salomonf30b1c12019-06-20 12:25:02 -0400688 }
689 return true;
690}
Robert Phillipsc80b0e92019-07-23 10:27:09 -0400691
Brian Salomon71283232021-04-08 12:45:58 -0400692bool GrClearImage(const GrImageInfo& dstInfo, void* dst, size_t dstRB, std::array<float, 4> color) {
Brian Salomon998937c2019-10-29 09:34:52 -0400693 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
Brian Salomon998937c2019-10-29 09:34:52 -0400694
Brian Salomon85c3d682019-11-04 15:04:54 -0500695 if (!dstInfo.isValid()) {
696 return false;
Brian Salomon998937c2019-10-29 09:34:52 -0400697 }
Brian Salomon85c3d682019-11-04 15:04:54 -0500698 if (!dst) {
699 return false;
700 }
701 if (dstRB < dstInfo.minRowBytes()) {
702 return false;
703 }
704 if (dstInfo.colorType() == GrColorType::kRGB_888) {
705 // SkRasterPipeline doesn't handle writing to RGB_888. So we handle that specially here.
Brian Salomon71283232021-04-08 12:45:58 -0400706 uint32_t rgba = SkColor4f{color[0], color[1], color[2], color[3]}.toBytes_RGBA();
Brian Salomon85c3d682019-11-04 15:04:54 -0500707 for (int y = 0; y < dstInfo.height(); ++y) {
708 char* d = static_cast<char*>(dst) + y * dstRB;
709 for (int x = 0; x < dstInfo.width(); ++x, d += 3) {
710 memcpy(d, &rgba, 3);
711 }
712 }
713 return true;
714 }
715
Brian Salomon01ff5382020-12-15 16:06:26 -0500716 LumMode lumMode;
Brian Salomon85c3d682019-11-04 15:04:54 -0500717 bool isNormalized;
718 bool dstIsSRGB;
719 SkRasterPipeline::StockStage store;
Brian Salomon01ff5382020-12-15 16:06:26 -0500720 GrSwizzle storeSwizzle = get_dst_swizzle_and_store(dstInfo.colorType(), &store, &lumMode,
Brian Salomon85c3d682019-11-04 15:04:54 -0500721 &isNormalized, &dstIsSRGB);
722 char block[64];
723 SkArenaAlloc alloc(block, sizeof(block), 1024);
724 SkRasterPipeline_<256> pipeline;
Brian Salomon71283232021-04-08 12:45:58 -0400725 pipeline.append_constant_color(&alloc, color.data());
Brian Salomon01ff5382020-12-15 16:06:26 -0500726 switch (lumMode) {
727 case LumMode::kNone:
728 break;
729 case LumMode::kToRGB:
730 pipeline.append(SkRasterPipeline::StockStage::bt709_luminance_or_luma_to_rgb);
731 break;
732 case LumMode::kToAlpha:
733 pipeline.append(SkRasterPipeline::StockStage::bt709_luminance_or_luma_to_alpha);
734 // If we ever need to store srgb-encoded gray (e.g. GL_SLUMINANCE8) then we should use
735 // ToRGB and then a swizzle stage rather than ToAlpha. The subsequent transfer function
736 // stage ignores the alpha channel (where we just stashed the gray).
737 SkASSERT(!dstIsSRGB);
738 break;
Brian Salomon85c3d682019-11-04 15:04:54 -0500739 }
740 if (dstIsSRGB) {
Mike Klein04984312020-05-19 12:41:05 -0500741 pipeline.append_transfer_function(*skcms_sRGB_Inverse_TransferFunction());
Brian Salomon85c3d682019-11-04 15:04:54 -0500742 }
743 storeSwizzle.apply(&pipeline);
744 SkRasterPipeline_MemoryCtx dstCtx{dst, SkToInt(dstRB/dstInfo.bpp())};
745 pipeline.append(store, &dstCtx);
746 pipeline.run(0, 0, dstInfo.width(), dstInfo.height());
747
748 return true;
Brian Salomon998937c2019-10-29 09:34:52 -0400749}