blob: ef4dc704b0ad5a5b0b727f697e4c1f93f350abe0 [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
Brian Salomonf30b1c12019-06-20 12:25:02 -040010#include "src/core/SkColorSpaceXformSteps.h"
Brian Salomon77a684f2019-08-01 14:38:04 -040011#include "src/core/SkConvertPixels.h"
Robert Phillips48257d72019-12-16 14:20:53 -050012#include "src/core/SkMipMap.h"
Brian Salomonf30b1c12019-06-20 12:25:02 -040013#include "src/core/SkTLazy.h"
Brian Salomonc42eb662019-06-24 17:13:00 -040014#include "src/core/SkTraceEvent.h"
Robert Phillipsb5e331a2019-05-28 10:58:09 -040015#include "src/core/SkUtils.h"
Greg Danielf91aeb22019-06-18 09:58:02 -040016#include "src/gpu/GrColor.h"
Brian Salomonf2ebdd92019-09-30 12:15:30 -040017#include "src/gpu/GrImageInfo.h"
Robert Phillips459b2952019-05-23 09:38:27 -040018
Robert Phillips8043f322019-05-31 08:11:36 -040019struct ETC1Block {
20 uint32_t fHigh;
21 uint32_t fLow;
22};
23
Robert Phillips8f259a02019-12-20 11:32:27 -050024static const int kNumETC1ModifierTables = 8;
25static const int kNumETC1PixelIndices = 4;
Robert Phillips459b2952019-05-23 09:38:27 -040026
27// The index of each row in this table is the ETC1 table codeword
28// The index of each column in this table is the ETC1 pixel index value
Robert Phillips8f259a02019-12-20 11:32:27 -050029static const int kETC1ModifierTables[kNumETC1ModifierTables][kNumETC1PixelIndices] = {
Robert Phillips459b2952019-05-23 09:38:27 -040030 /* 0 */ { 2, 8, -2, -8 },
31 /* 1 */ { 5, 17, -5, -17 },
32 /* 2 */ { 9, 29, -9, -29 },
33 /* 3 */ { 13, 42, -13, -42 },
34 /* 4 */ { 18, 60, -18, -60 },
35 /* 5 */ { 24, 80, -24, -80 },
36 /* 6 */ { 33, 106, -33, -106 },
37 /* 7 */ { 47, 183, -47, -183 }
38};
39
40static inline int convert_5To8(int b) {
41 int c = b & 0x1f;
42 return (c << 3) | (c >> 2);
43}
44
45// Evaluate one of the entries in 'kModifierTables' to see how close it can get (r8,g8,b8) to
46// the original color (rOrig, gOrib, bOrig).
47static int test_table_entry(int rOrig, int gOrig, int bOrig,
48 int r8, int g8, int b8,
49 int table, int offset) {
50 SkASSERT(0 <= table && table < 8);
51 SkASSERT(0 <= offset && offset < 4);
52
Robert Phillips8f259a02019-12-20 11:32:27 -050053 r8 = SkTPin<int>(r8 + kETC1ModifierTables[table][offset], 0, 255);
54 g8 = SkTPin<int>(g8 + kETC1ModifierTables[table][offset], 0, 255);
55 b8 = SkTPin<int>(b8 + kETC1ModifierTables[table][offset], 0, 255);
Robert Phillips459b2952019-05-23 09:38:27 -040056
57 return SkTAbs(rOrig - r8) + SkTAbs(gOrig - g8) + SkTAbs(bOrig - b8);
58}
59
60// Create an ETC1 compressed block that is filled with 'col'
61static void create_etc1_block(SkColor col, ETC1Block* block) {
Robert Phillips48257d72019-12-16 14:20:53 -050062 uint32_t high = 0;
63 uint32_t low = 0;
Robert Phillips459b2952019-05-23 09:38:27 -040064
65 int rOrig = SkColorGetR(col);
66 int gOrig = SkColorGetG(col);
67 int bOrig = SkColorGetB(col);
68
69 int r5 = SkMulDiv255Round(31, rOrig);
70 int g5 = SkMulDiv255Round(31, gOrig);
71 int b5 = SkMulDiv255Round(31, bOrig);
72
73 int r8 = convert_5To8(r5);
74 int g8 = convert_5To8(g5);
75 int b8 = convert_5To8(b5);
76
77 // We always encode solid color textures as 555 + zero diffs
Robert Phillips48257d72019-12-16 14:20:53 -050078 high |= (r5 << 27) | (g5 << 19) | (b5 << 11) | 0x2;
Robert Phillips459b2952019-05-23 09:38:27 -040079
80 int bestTableIndex = 0, bestPixelIndex = 0;
81 int bestSoFar = 1024;
Robert Phillips8f259a02019-12-20 11:32:27 -050082 for (int tableIndex = 0; tableIndex < kNumETC1ModifierTables; ++tableIndex) {
83 for (int pixelIndex = 0; pixelIndex < kNumETC1PixelIndices; ++pixelIndex) {
Robert Phillips459b2952019-05-23 09:38:27 -040084 int score = test_table_entry(rOrig, gOrig, bOrig, r8, g8, b8,
85 tableIndex, pixelIndex);
86
87 if (bestSoFar > score) {
88 bestSoFar = score;
89 bestTableIndex = tableIndex;
90 bestPixelIndex = pixelIndex;
91 }
92 }
93 }
94
Robert Phillips48257d72019-12-16 14:20:53 -050095 high |= (bestTableIndex << 5) | (bestTableIndex << 2);
Robert Phillips459b2952019-05-23 09:38:27 -040096
Robert Phillips48257d72019-12-16 14:20:53 -050097 if (bestPixelIndex & 0x1) {
98 low |= 0xFFFF;
Robert Phillips459b2952019-05-23 09:38:27 -040099 }
Robert Phillips48257d72019-12-16 14:20:53 -0500100 if (bestPixelIndex & 0x2) {
101 low |= 0xFFFF0000;
102 }
103
104 block->fHigh = SkBSwap32(high);
105 block->fLow = SkBSwap32(low);
Robert Phillips459b2952019-05-23 09:38:27 -0400106}
107
Jim Van Verthe3671012019-09-18 09:53:31 -0400108static int num_ETC1_blocks_w(int w) {
Robert Phillips459b2952019-05-23 09:38:27 -0400109 if (w < 4) {
110 w = 1;
111 } else {
Jim Van Verthe3671012019-09-18 09:53:31 -0400112 SkASSERT((w & 3) == 0);
113 w >>= 2;
Robert Phillips459b2952019-05-23 09:38:27 -0400114 }
Jim Van Verthe3671012019-09-18 09:53:31 -0400115 return w;
116}
117
118static int num_ETC1_blocks(int w, int h) {
119 w = num_ETC1_blocks_w(w);
Robert Phillips459b2952019-05-23 09:38:27 -0400120
121 if (h < 4) {
122 h = 1;
123 } else {
124 SkASSERT((h & 3) == 0);
125 h >>= 2;
126 }
127
128 return w * h;
129}
130
Robert Phillips8f259a02019-12-20 11:32:27 -0500131struct BC1Block {
132 uint16_t fColor0;
133 uint16_t fColor1;
134 uint32_t fIndices;
135};
136
Robert Phillipsac908022020-01-14 16:54:17 -0500137static uint16_t to565(SkColor col) {
Robert Phillips8f259a02019-12-20 11:32:27 -0500138 int r5 = SkMulDiv255Round(31, SkColorGetR(col));
139 int g6 = SkMulDiv255Round(63, SkColorGetG(col));
140 int b5 = SkMulDiv255Round(31, SkColorGetB(col));
141
Robert Phillipsac908022020-01-14 16:54:17 -0500142 return (r5 << 11) | (g6 << 5) | b5;
143}
144
145// Create a BC1 compressed block that has two colors but is initialized to 'col0'
146static void create_BC1_block(SkColor col0, SkColor col1, BC1Block* block) {
147 block->fColor0 = to565(col0);
148 block->fColor1 = to565(col1);
Robert Phillips8f259a02019-12-20 11:32:27 -0500149 // This sets all 16 pixels to just use 'fColor0'
150 block->fIndices = 0;
151}
152
Robert Phillips48257d72019-12-16 14:20:53 -0500153size_t GrCompressedDataSize(SkImage::CompressionType type, SkISize dimensions,
Robert Phillips0d7e2f12019-12-18 13:01:04 -0500154 SkTArray<size_t>* individualMipOffsets, GrMipMapped mipMapped) {
155 SkASSERT(!individualMipOffsets || !individualMipOffsets->count());
156
Robert Phillips48257d72019-12-16 14:20:53 -0500157 int numMipLevels = 1;
158 if (mipMapped == GrMipMapped::kYes) {
159 numMipLevels = SkMipMap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
160 }
161
162 size_t totalSize = 0;
Brian Salomonbb8dde82019-06-27 10:52:13 -0400163 switch (type) {
Robert Phillipsab2b7222019-12-10 10:05:06 -0500164 case SkImage::CompressionType::kNone:
Robert Phillips48257d72019-12-16 14:20:53 -0500165 break;
Robert Phillipsc558f722020-01-13 13:02:26 -0500166 case SkImage::CompressionType::kETC2_RGB8_UNORM:
Robert Phillips8f259a02019-12-20 11:32:27 -0500167 case SkImage::CompressionType::kBC1_RGB8_UNORM: {
Robert Phillips48257d72019-12-16 14:20:53 -0500168 for (int i = 0; i < numMipLevels; ++i) {
169 int numBlocks = num_ETC1_blocks(dimensions.width(), dimensions.height());
170
Robert Phillips0d7e2f12019-12-18 13:01:04 -0500171 if (individualMipOffsets) {
172 individualMipOffsets->push_back(totalSize);
173 }
174
Robert Phillips8f259a02019-12-20 11:32:27 -0500175 static_assert(sizeof(ETC1Block) == sizeof(BC1Block));
Robert Phillips48257d72019-12-16 14:20:53 -0500176 totalSize += numBlocks * sizeof(ETC1Block);
177
178 dimensions = {SkTMax(1, dimensions.width()/2), SkTMax(1, dimensions.height()/2)};
179 }
180 }
Brian Salomonbb8dde82019-06-27 10:52:13 -0400181 }
Robert Phillips48257d72019-12-16 14:20:53 -0500182
183 return totalSize;
Robert Phillips8043f322019-05-31 08:11:36 -0400184}
185
Robert Phillips9f744f72019-12-19 19:14:33 -0500186size_t GrCompressedFormatDataSize(SkImage::CompressionType compressionType,
187 SkISize dimensions, GrMipMapped mipMapped) {
188 return GrCompressedDataSize(compressionType, dimensions, nullptr, mipMapped);
189}
190
Jim Van Verthe3671012019-09-18 09:53:31 -0400191size_t GrCompressedRowBytes(SkImage::CompressionType type, int width) {
192 switch (type) {
Robert Phillipsab2b7222019-12-10 10:05:06 -0500193 case SkImage::CompressionType::kNone:
194 return 0;
Robert Phillips8f259a02019-12-20 11:32:27 -0500195 case SkImage::CompressionType::kBC1_RGB8_UNORM:
Robert Phillipsc558f722020-01-13 13:02:26 -0500196 case SkImage::CompressionType::kETC2_RGB8_UNORM:
Jim Van Verthe3671012019-09-18 09:53:31 -0400197 int numBlocksWidth = num_ETC1_blocks_w(width);
Robert Phillips8f259a02019-12-20 11:32:27 -0500198
199 static_assert(sizeof(ETC1Block) == sizeof(BC1Block));
Jim Van Verthe3671012019-09-18 09:53:31 -0400200 return numBlocksWidth * sizeof(ETC1Block);
201 }
Robert Phillipsab2b7222019-12-10 10:05:06 -0500202 SkUNREACHABLE;
Jim Van Verthe3671012019-09-18 09:53:31 -0400203}
204
Robert Phillips41acc0e2020-01-06 13:29:53 -0500205SkISize GrCompressedDimensions(SkImage::CompressionType type, SkISize baseDimensions) {
206 switch (type) {
207 case SkImage::CompressionType::kNone:
208 return baseDimensions;
209 case SkImage::CompressionType::kBC1_RGB8_UNORM:
Robert Phillipsc558f722020-01-13 13:02:26 -0500210 case SkImage::CompressionType::kETC2_RGB8_UNORM:
Robert Phillips41acc0e2020-01-06 13:29:53 -0500211 int numBlocksWidth = num_ETC1_blocks_w(baseDimensions.width());
212 int numBlocksHeight = num_ETC1_blocks_w(baseDimensions.height());
213
214 // Each BC1_RGB8_UNORM and ETC1 block has 16 pixels
215 return { 4 * numBlocksWidth, 4 * numBlocksHeight };
216 }
217 SkUNREACHABLE;
218}
219
220
Robert Phillips28a5a432019-06-07 12:46:21 -0400221// Fill in 'dest' with ETC1 blocks derived from 'colorf'
Robert Phillips48257d72019-12-16 14:20:53 -0500222static void fillin_ETC1_with_color(SkISize dimensions, const SkColor4f& colorf, char* dest) {
Robert Phillips459b2952019-05-23 09:38:27 -0400223 SkColor color = colorf.toSkColor();
224
225 ETC1Block block;
226 create_etc1_block(color, &block);
227
Robert Phillips48257d72019-12-16 14:20:53 -0500228 int numBlocks = num_ETC1_blocks(dimensions.width(), dimensions.height());
Robert Phillips8043f322019-05-31 08:11:36 -0400229
Robert Phillips459b2952019-05-23 09:38:27 -0400230 for (int i = 0; i < numBlocks; ++i) {
Robert Phillips48257d72019-12-16 14:20:53 -0500231 memcpy(dest, &block, sizeof(ETC1Block));
232 dest += sizeof(ETC1Block);
Robert Phillips459b2952019-05-23 09:38:27 -0400233 }
234}
235
Robert Phillips8f259a02019-12-20 11:32:27 -0500236// Fill in 'dest' with BC1 blocks derived from 'colorf'
237static void fillin_BC1_with_color(SkISize dimensions, const SkColor4f& colorf, char* dest) {
238 SkColor color = colorf.toSkColor();
239
240 BC1Block block;
Robert Phillipsac908022020-01-14 16:54:17 -0500241 create_BC1_block(color, color, &block);
Robert Phillips8f259a02019-12-20 11:32:27 -0500242
243 int numBlocks = num_ETC1_blocks(dimensions.width(), dimensions.height());
244
245 for (int i = 0; i < numBlocks; ++i) {
246 memcpy(dest, &block, sizeof(BC1Block));
247 dest += sizeof(BC1Block);
248 }
249}
250
Robert Phillipsac908022020-01-14 16:54:17 -0500251// Fill in 'dstPixels' with BC1 blocks derived from the 'pixmap'.
252void GrTwoColorBC1Compress(const SkPixmap& pixmap, SkColor otherColor, char* dstPixels) {
253 BC1Block* dstBlocks = (BC1Block*) dstPixels;
254 SkASSERT(pixmap.colorType() == SkColorType::kRGBA_8888_SkColorType);
255
256 BC1Block block;
257
258 // black -> fColor0, otherColor -> fColor1
259 create_BC1_block(SK_ColorBLACK, otherColor, &block);
260
261 int numXBlocks = num_ETC1_blocks_w(pixmap.width());
262 int numYBlocks = num_ETC1_blocks_w(pixmap.height());
263
264 for (int y = 0; y < numYBlocks; ++y) {
265 for (int x = 0; x < numXBlocks; ++x) {
266 int shift = 0;
267 int offsetX = 4 * x, offsetY = 4 * y;
268 block.fIndices = 0; // init all the pixels to color0
269 for (int i = 0; i < 4; ++i) {
270 for (int j = 0; j < 4; ++j) {
271 if (offsetX + j >= pixmap.width() || offsetY + i >= pixmap.height()) {
272 // This can happen for the topmost levels of a mipmap
273 continue;
274 }
275
276 SkColor tmp = pixmap.getColor(offsetX + j, offsetY + i);
277 if (tmp == SK_ColorTRANSPARENT) {
278 // For RGBA BC1 images color3 is set to transparent black
279 block.fIndices |= 3 << shift;
280 } else if (tmp != SK_ColorBLACK) {
281 block.fIndices |= 1 << shift; // color1
282 }
283 shift += 2;
284 }
285 }
286
287 dstBlocks[y*numXBlocks + x] = block;
288 }
289 }
290}
291
Brian Salomon85c3d682019-11-04 15:04:54 -0500292size_t GrComputeTightCombinedBufferSize(size_t bytesPerPixel, SkISize baseDimensions,
Brian Salomonbb8dde82019-06-27 10:52:13 -0400293 SkTArray<size_t>* individualMipOffsets, int mipLevelCount) {
Robert Phillips28a5a432019-06-07 12:46:21 -0400294 SkASSERT(individualMipOffsets && !individualMipOffsets->count());
295 SkASSERT(mipLevelCount >= 1);
296
297 individualMipOffsets->push_back(0);
298
Brian Salomon85c3d682019-11-04 15:04:54 -0500299 size_t combinedBufferSize = baseDimensions.width() * bytesPerPixel * baseDimensions.height();
300 SkISize levelDimensions = baseDimensions;
Robert Phillips28a5a432019-06-07 12:46:21 -0400301
302 // The Vulkan spec for copying a buffer to an image requires that the alignment must be at
303 // least 4 bytes and a multiple of the bytes per pixel of the image config.
304 SkASSERT(bytesPerPixel == 1 || bytesPerPixel == 2 || bytesPerPixel == 3 ||
305 bytesPerPixel == 4 || bytesPerPixel == 8 || bytesPerPixel == 16);
306 int desiredAlignment = (bytesPerPixel == 3) ? 12 : (bytesPerPixel > 4 ? bytesPerPixel : 4);
307
308 for (int currentMipLevel = 1; currentMipLevel < mipLevelCount; ++currentMipLevel) {
Brian Salomon85c3d682019-11-04 15:04:54 -0500309 levelDimensions = {SkTMax(1, levelDimensions.width() /2),
310 SkTMax(1, levelDimensions.height()/2)};
Robert Phillips28a5a432019-06-07 12:46:21 -0400311
Brian Salomon85c3d682019-11-04 15:04:54 -0500312 size_t trimmedSize = levelDimensions.area() * bytesPerPixel;
Robert Phillips28a5a432019-06-07 12:46:21 -0400313 const size_t alignmentDiff = combinedBufferSize % desiredAlignment;
314 if (alignmentDiff != 0) {
315 combinedBufferSize += desiredAlignment - alignmentDiff;
316 }
317 SkASSERT((0 == combinedBufferSize % 4) && (0 == combinedBufferSize % bytesPerPixel));
318
319 individualMipOffsets->push_back(combinedBufferSize);
320 combinedBufferSize += trimmedSize;
321 }
322
323 SkASSERT(individualMipOffsets->count() == mipLevelCount);
324 return combinedBufferSize;
325}
326
Robert Phillips48257d72019-12-16 14:20:53 -0500327void GrFillInCompressedData(SkImage::CompressionType type, SkISize dimensions,
328 GrMipMapped mipMapped, char* dstPixels, const SkColor4f& colorf) {
Brian Salomonbb8dde82019-06-27 10:52:13 -0400329 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
Robert Phillips48257d72019-12-16 14:20:53 -0500330
331 int numMipLevels = 1;
332 if (mipMapped == GrMipMapped::kYes) {
333 numMipLevels = SkMipMap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
334 }
335
Robert Phillips8f259a02019-12-20 11:32:27 -0500336 size_t offset = 0;
Robert Phillips48257d72019-12-16 14:20:53 -0500337
Robert Phillips8f259a02019-12-20 11:32:27 -0500338 for (int i = 0; i < numMipLevels; ++i) {
339 size_t levelSize = GrCompressedDataSize(type, dimensions, nullptr, GrMipMapped::kNo);
Robert Phillips48257d72019-12-16 14:20:53 -0500340
Robert Phillipsc558f722020-01-13 13:02:26 -0500341 if (SkImage::CompressionType::kETC2_RGB8_UNORM == type) {
Robert Phillips48257d72019-12-16 14:20:53 -0500342 fillin_ETC1_with_color(dimensions, colorf, &dstPixels[offset]);
Robert Phillips8f259a02019-12-20 11:32:27 -0500343 } else {
344 SkASSERT(type == SkImage::CompressionType::kBC1_RGB8_UNORM);
345 fillin_BC1_with_color(dimensions, colorf, &dstPixels[offset]);
Robert Phillips48257d72019-12-16 14:20:53 -0500346 }
Robert Phillips8f259a02019-12-20 11:32:27 -0500347
348 offset += levelSize;
349 dimensions = {SkTMax(1, dimensions.width()/2), SkTMax(1, dimensions.height()/2)};
Brian Salomonbb8dde82019-06-27 10:52:13 -0400350 }
351}
352
Brian Salomon85c3d682019-11-04 15:04:54 -0500353static GrSwizzle get_load_and_src_swizzle(GrColorType ct, SkRasterPipeline::StockStage* load,
Greg Daniele877dce2019-07-11 10:52:43 -0400354 bool* isNormalized, bool* isSRGB) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400355 GrSwizzle swizzle("rgba");
356 *isNormalized = true;
Greg Daniele877dce2019-07-11 10:52:43 -0400357 *isSRGB = false;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400358 switch (ct) {
359 case GrColorType::kAlpha_8: *load = SkRasterPipeline::load_a8; break;
Robert Phillips429f0d32019-09-11 17:03:28 -0400360 case GrColorType::kAlpha_16: *load = SkRasterPipeline::load_a16; break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400361 case GrColorType::kBGR_565: *load = SkRasterPipeline::load_565; break;
362 case GrColorType::kABGR_4444: *load = SkRasterPipeline::load_4444; break;
363 case GrColorType::kRGBA_8888: *load = SkRasterPipeline::load_8888; break;
364 case GrColorType::kRG_88: *load = SkRasterPipeline::load_rg88; break;
365 case GrColorType::kRGBA_1010102: *load = SkRasterPipeline::load_1010102; break;
366 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;
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
Brian Salomon85c3d682019-11-04 15:04:54 -0500402 // 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:
Brian Salomonf30b1c12019-06-20 12:25:02 -0400408 case GrColorType::kUnknown:
Brian Salomonf30b1c12019-06-20 12:25:02 -0400409 SK_ABORT("unexpected CT");
410 }
411 return swizzle;
412}
413
414static GrSwizzle get_dst_swizzle_and_store(GrColorType ct, SkRasterPipeline::StockStage* store,
Brian Salomonb3bd8642019-11-04 16:59:29 -0500415 bool* doLumToAlpha, bool* isNormalized, bool* isSRGB) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400416 GrSwizzle swizzle("rgba");
417 *isNormalized = true;
Greg Daniele877dce2019-07-11 10:52:43 -0400418 *isSRGB = false;
Brian Salomon998937c2019-10-29 09:34:52 -0400419 *doLumToAlpha = false;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400420 switch (ct) {
421 case GrColorType::kAlpha_8: *store = SkRasterPipeline::store_a8; break;
Robert Phillips429f0d32019-09-11 17:03:28 -0400422 case GrColorType::kAlpha_16: *store = SkRasterPipeline::store_a16; break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400423 case GrColorType::kBGR_565: *store = SkRasterPipeline::store_565; break;
424 case GrColorType::kABGR_4444: *store = SkRasterPipeline::store_4444; break;
425 case GrColorType::kRGBA_8888: *store = SkRasterPipeline::store_8888; break;
426 case GrColorType::kRG_88: *store = SkRasterPipeline::store_rg88; break;
427 case GrColorType::kRGBA_1010102: *store = SkRasterPipeline::store_1010102; break;
428 case GrColorType::kRGBA_F16_Clamped: *store = SkRasterPipeline::store_f16; break;
429 case GrColorType::kRG_1616: *store = SkRasterPipeline::store_rg1616; break;
430 case GrColorType::kRGBA_16161616: *store = SkRasterPipeline::store_16161616; break;
431
Greg Daniele877dce2019-07-11 10:52:43 -0400432 case GrColorType::kRGBA_8888_SRGB: *store = SkRasterPipeline::store_8888;
433 *isSRGB = true;
434 break;
Brian Salomone14cfbe2019-06-24 15:00:58 -0400435 case GrColorType::kRG_F16: *store = SkRasterPipeline::store_rgf16;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400436 *isNormalized = false;
437 break;
438 case GrColorType::kAlpha_F16: *store = SkRasterPipeline::store_af16;
439 *isNormalized = false;
440 break;
441 case GrColorType::kRGBA_F16: *store = SkRasterPipeline::store_f16;
442 *isNormalized = false;
443 break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400444 case GrColorType::kRGBA_F32: *store = SkRasterPipeline::store_f32;
445 *isNormalized = false;
446 break;
Brian Salomon8f8354a2019-07-31 20:12:02 -0400447 case GrColorType::kAlpha_8xxx: *store = SkRasterPipeline::store_8888;
448 swizzle = GrSwizzle("a000");
449 break;
450 case GrColorType::kAlpha_F32xxx: *store = SkRasterPipeline::store_f32;
451 swizzle = GrSwizzle("a000");
452 break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400453 case GrColorType::kBGRA_8888: swizzle = GrSwizzle("bgra");
454 *store = SkRasterPipeline::store_8888;
455 break;
456 case GrColorType::kRGB_888x: swizzle = GrSwizzle("rgb1");
457 *store = SkRasterPipeline::store_8888;
458 break;
Brian Salomon85c3d682019-11-04 15:04:54 -0500459 case GrColorType::kR_8: swizzle = GrSwizzle("agbr");
460 *store = SkRasterPipeline::store_a8;
461 break;
462 case GrColorType::kR_16: swizzle = GrSwizzle("agbr");
463 *store = SkRasterPipeline::store_a16;
464 break;
465 case GrColorType::kR_F16: swizzle = GrSwizzle("agbr");
466 *store = SkRasterPipeline::store_af16;
467 break;
468 case GrColorType::kGray_F16: *doLumToAlpha = true;
469 *store = SkRasterPipeline::store_af16;
470 break;
Brian Salomon998937c2019-10-29 09:34:52 -0400471 case GrColorType::kGray_8: *doLumToAlpha = true;
472 *store = SkRasterPipeline::store_a8;
473 break;
474 case GrColorType::kGray_8xxx: *doLumToAlpha = true;
475 *store = SkRasterPipeline::store_8888;
476 swizzle = GrSwizzle("a000");
477 break;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400478
Brian Salomon85c3d682019-11-04 15:04:54 -0500479 // These are color types we don't expect to ever have to store.
480 case GrColorType::kRGB_888: // This is handled specially in GrConvertPixels.
Brian Salomonf30b1c12019-06-20 12:25:02 -0400481 case GrColorType::kUnknown:
Brian Salomonf30b1c12019-06-20 12:25:02 -0400482 SK_ABORT("unexpected CT");
483 }
484 return swizzle;
485}
486
487static inline void append_clamp_gamut(SkRasterPipeline* pipeline) {
488 // SkRasterPipeline may not know our color type and also doesn't like caller to directly
489 // append clamp_gamut. Fake it out.
490 static SkImageInfo fakeII = SkImageInfo::MakeN32Premul(1, 1);
491 pipeline->append_gamut_clamp_if_normalized(fakeII);
492}
493
Brian Salomonf2ebdd92019-09-30 12:15:30 -0400494bool GrConvertPixels(const GrImageInfo& dstInfo, void* dst, size_t dstRB,
495 const GrImageInfo& srcInfo, const void* src, size_t srcRB,
Brian Salomon8f8354a2019-07-31 20:12:02 -0400496 bool flipY) {
Brian Salomonc42eb662019-06-24 17:13:00 -0400497 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
Brian Salomon85c3d682019-11-04 15:04:54 -0500498 if (srcInfo.colorType() == GrColorType::kRGB_888) {
499 // We don't expect to have to convert from this format.
500 return false;
501 }
Brian Salomon1d435302019-07-01 13:05:28 -0400502 if (!srcInfo.isValid() || !dstInfo.isValid()) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400503 return false;
504 }
Brian Salomon1d435302019-07-01 13:05:28 -0400505 if (!src || !dst) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400506 return false;
507 }
Brian Salomon85c3d682019-11-04 15:04:54 -0500508 if (dstInfo.dimensions() != srcInfo.dimensions()) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400509 return false;
510 }
Brian Salomon1d435302019-07-01 13:05:28 -0400511 if (dstRB < dstInfo.minRowBytes() || srcRB < srcInfo.minRowBytes()) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400512 return false;
513 }
Brian Salomon85c3d682019-11-04 15:04:54 -0500514 if (dstInfo.colorType() == GrColorType::kRGB_888) {
515 // SkRasterPipeline doesn't handle writing to RGB_888. So we have it write to RGB_888x and
516 // then do another conversion that does the 24bit packing.
517 auto tempDstInfo = dstInfo.makeColorType(GrColorType::kRGB_888x);
518 auto tempRB = tempDstInfo.minRowBytes();
519 std::unique_ptr<char[]> tempDst(new char[tempRB * tempDstInfo.height()]);
520 if (!GrConvertPixels(tempDstInfo, tempDst.get(), tempRB, srcInfo, src, srcRB, flipY)) {
521 return false;
522 }
523 auto* tRow = reinterpret_cast<const char*>(tempDst.get());
524 auto* dRow = reinterpret_cast<char*>(dst);
525 for (int y = 0; y < dstInfo.height(); ++y, tRow += tempRB, dRow += dstRB) {
526 for (int x = 0; x < dstInfo.width(); ++x) {
527 auto t = reinterpret_cast<const uint32_t*>(tRow + x * sizeof(uint32_t));
528 auto d = reinterpret_cast<uint32_t*>(dRow + x * 3);
529 memcpy(d, t, 3);
530 }
531 }
532 return true;
533 }
Brian Salomon1d435302019-07-01 13:05:28 -0400534
535 size_t srcBpp = srcInfo.bpp();
536 size_t dstBpp = dstInfo.bpp();
537
Brian Salomonf30b1c12019-06-20 12:25:02 -0400538 // SkRasterPipeline operates on row-pixels not row-bytes.
Brian Salomon1d435302019-07-01 13:05:28 -0400539 SkASSERT(dstRB % dstBpp == 0);
540 SkASSERT(srcRB % srcBpp == 0);
Brian Salomonf30b1c12019-06-20 12:25:02 -0400541
Brian Salomon77a684f2019-08-01 14:38:04 -0400542 bool premul = srcInfo.alphaType() == kUnpremul_SkAlphaType &&
543 dstInfo.alphaType() == kPremul_SkAlphaType;
544 bool unpremul = srcInfo.alphaType() == kPremul_SkAlphaType &&
545 dstInfo.alphaType() == kUnpremul_SkAlphaType;
546 bool alphaOrCSConversion =
547 premul || unpremul || !SkColorSpace::Equals(srcInfo.colorSpace(), dstInfo.colorSpace());
548
549 if (srcInfo.colorType() == dstInfo.colorType() && !alphaOrCSConversion) {
550 size_t tightRB = dstBpp * dstInfo.width();
551 if (flipY) {
552 dst = static_cast<char*>(dst) + dstRB * (dstInfo.height() - 1);
553 for (int y = 0; y < dstInfo.height(); ++y) {
554 memcpy(dst, src, tightRB);
555 src = static_cast<const char*>(src) + srcRB;
556 dst = static_cast< char*>(dst) - dstRB;
557 }
558 } else {
559 SkRectMemcpy(dst, dstRB, src, srcRB, tightRB, srcInfo.height());
560 }
561 return true;
562 }
563
Brian Salomonf30b1c12019-06-20 12:25:02 -0400564 SkRasterPipeline::StockStage load;
565 bool srcIsNormalized;
Greg Daniele877dce2019-07-11 10:52:43 -0400566 bool srcIsSRGB;
Brian Salomon85c3d682019-11-04 15:04:54 -0500567 auto loadSwizzle =
568 get_load_and_src_swizzle(srcInfo.colorType(), &load, &srcIsNormalized, &srcIsSRGB);
Brian Salomonf30b1c12019-06-20 12:25:02 -0400569
570 SkRasterPipeline::StockStage store;
Brian Salomon998937c2019-10-29 09:34:52 -0400571 bool doLumToAlpha;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400572 bool dstIsNormalized;
Greg Daniele877dce2019-07-11 10:52:43 -0400573 bool dstIsSRGB;
Brian Salomon998937c2019-10-29 09:34:52 -0400574 auto storeSwizzle = get_dst_swizzle_and_store(dstInfo.colorType(), &store, &doLumToAlpha,
575 &dstIsNormalized, &dstIsSRGB);
Brian Salomonf30b1c12019-06-20 12:25:02 -0400576
Brian Salomonf30b1c12019-06-20 12:25:02 -0400577 bool clampGamut;
578 SkTLazy<SkColorSpaceXformSteps> steps;
579 GrSwizzle loadStoreSwizzle;
580 if (alphaOrCSConversion) {
Brian Salomon1d435302019-07-01 13:05:28 -0400581 steps.init(srcInfo.colorSpace(), srcInfo.alphaType(),
582 dstInfo.colorSpace(), dstInfo.alphaType());
583 clampGamut = dstIsNormalized && dstInfo.alphaType() == kPremul_SkAlphaType;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400584 } else {
Brian Salomon1d435302019-07-01 13:05:28 -0400585 clampGamut =
586 dstIsNormalized && !srcIsNormalized && dstInfo.alphaType() == kPremul_SkAlphaType;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400587 if (!clampGamut) {
588 loadStoreSwizzle = GrSwizzle::Concat(loadSwizzle, storeSwizzle);
589 }
590 }
591 int cnt = 1;
Brian Salomon1d435302019-07-01 13:05:28 -0400592 int height = srcInfo.height();
593 SkRasterPipeline_MemoryCtx srcCtx{const_cast<void*>(src), SkToInt(srcRB / srcBpp)},
594 dstCtx{ dst , SkToInt(dstRB / dstBpp)};
Brian Salomonf30b1c12019-06-20 12:25:02 -0400595
Brian Salomon1d435302019-07-01 13:05:28 -0400596 if (flipY) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400597 // It *almost* works to point the src at the last row and negate the stride and run the
598 // whole rectangle. However, SkRasterPipeline::run()'s control loop uses size_t loop
599 // variables so it winds up relying on unsigned overflow math. It works out in practice
600 // but UBSAN says "no!" as it's technically undefined and in theory a compiler could emit
601 // code that didn't do what is intended. So we go one row at a time. :(
Brian Salomon1d435302019-07-01 13:05:28 -0400602 srcCtx.pixels = static_cast<char*>(srcCtx.pixels) + srcRB * (height - 1);
Brian Salomonf30b1c12019-06-20 12:25:02 -0400603 std::swap(cnt, height);
604 }
Greg Daniele877dce2019-07-11 10:52:43 -0400605
Brian Salomon85c3d682019-11-04 15:04:54 -0500606 bool hasConversion = alphaOrCSConversion || clampGamut || doLumToAlpha;
Greg Daniele877dce2019-07-11 10:52:43 -0400607
608 if (srcIsSRGB && dstIsSRGB && !hasConversion) {
609 // No need to convert from srgb if we are just going to immediately convert it back.
610 srcIsSRGB = dstIsSRGB = false;
611 }
612
613 hasConversion = hasConversion || srcIsSRGB || dstIsSRGB;
614
Brian Salomonf30b1c12019-06-20 12:25:02 -0400615 for (int i = 0; i < cnt; ++i) {
616 SkRasterPipeline_<256> pipeline;
617 pipeline.append(load, &srcCtx);
Greg Daniele877dce2019-07-11 10:52:43 -0400618 if (hasConversion) {
Brian Salomonf30b1c12019-06-20 12:25:02 -0400619 loadSwizzle.apply(&pipeline);
Greg Daniele877dce2019-07-11 10:52:43 -0400620 if (srcIsSRGB) {
621 pipeline.append(SkRasterPipeline::from_srgb);
622 }
623 if (alphaOrCSConversion) {
624 steps->apply(&pipeline, srcIsNormalized);
625 }
Brian Salomonf30b1c12019-06-20 12:25:02 -0400626 if (clampGamut) {
627 append_clamp_gamut(&pipeline);
628 }
Brian Salomon998937c2019-10-29 09:34:52 -0400629 if (doLumToAlpha) {
630 pipeline.append(SkRasterPipeline::StockStage::bt709_luminance_or_luma_to_alpha);
631 // If we ever needed to convert from linear-encoded gray to sRGB-encoded
632 // gray we'd have a problem here because the subsequent to_srgb stage
633 // ignores the alpha channel (where we just stashed the gray). There are
634 // several ways that could be fixed but given our current set of color types
635 // this should never happen.
636 SkASSERT(!dstIsSRGB);
637 }
Greg Daniele877dce2019-07-11 10:52:43 -0400638 if (dstIsSRGB) {
639 pipeline.append(SkRasterPipeline::to_srgb);
640 }
Brian Salomonf30b1c12019-06-20 12:25:02 -0400641 storeSwizzle.apply(&pipeline);
642 } else {
Greg Daniele877dce2019-07-11 10:52:43 -0400643 loadStoreSwizzle.apply(&pipeline);
Brian Salomonf30b1c12019-06-20 12:25:02 -0400644 }
645 pipeline.append(store, &dstCtx);
Brian Salomon1d435302019-07-01 13:05:28 -0400646 pipeline.run(0, 0, srcInfo.width(), height);
647 srcCtx.pixels = static_cast<char*>(srcCtx.pixels) - srcRB;
648 dstCtx.pixels = static_cast<char*>(dstCtx.pixels) + dstRB;
Brian Salomonf30b1c12019-06-20 12:25:02 -0400649 }
650 return true;
651}
Robert Phillipsc80b0e92019-07-23 10:27:09 -0400652
Brian Salomon85c3d682019-11-04 15:04:54 -0500653bool GrClearImage(const GrImageInfo& dstInfo, void* dst, size_t dstRB, SkColor4f color) {
Brian Salomon998937c2019-10-29 09:34:52 -0400654 TRACE_EVENT0("skia.gpu", TRACE_FUNC);
Brian Salomon998937c2019-10-29 09:34:52 -0400655
Brian Salomon85c3d682019-11-04 15:04:54 -0500656 if (!dstInfo.isValid()) {
657 return false;
Brian Salomon998937c2019-10-29 09:34:52 -0400658 }
Brian Salomon85c3d682019-11-04 15:04:54 -0500659 if (!dst) {
660 return false;
661 }
662 if (dstRB < dstInfo.minRowBytes()) {
663 return false;
664 }
665 if (dstInfo.colorType() == GrColorType::kRGB_888) {
666 // SkRasterPipeline doesn't handle writing to RGB_888. So we handle that specially here.
667 uint32_t rgba = color.toBytes_RGBA();
668 for (int y = 0; y < dstInfo.height(); ++y) {
669 char* d = static_cast<char*>(dst) + y * dstRB;
670 for (int x = 0; x < dstInfo.width(); ++x, d += 3) {
671 memcpy(d, &rgba, 3);
672 }
673 }
674 return true;
675 }
676
677 bool doLumToAlpha;
678 bool isNormalized;
679 bool dstIsSRGB;
680 SkRasterPipeline::StockStage store;
681 GrSwizzle storeSwizzle = get_dst_swizzle_and_store(dstInfo.colorType(), &store, &doLumToAlpha,
682 &isNormalized, &dstIsSRGB);
683 char block[64];
684 SkArenaAlloc alloc(block, sizeof(block), 1024);
685 SkRasterPipeline_<256> pipeline;
686 pipeline.append_constant_color(&alloc, color);
687 if (doLumToAlpha) {
688 pipeline.append(SkRasterPipeline::StockStage::bt709_luminance_or_luma_to_alpha);
689 // If we ever needed to convert from linear-encoded gray to sRGB-encoded
690 // gray we'd have a problem here because the subsequent to_srgb stage
691 // ignores the alpha channel (where we just stashed the gray). There are
692 // several ways that could be fixed but given our current set of color types
693 // this should never happen.
694 SkASSERT(!dstIsSRGB);
695 }
696 if (dstIsSRGB) {
697 pipeline.append(SkRasterPipeline::to_srgb);
698 }
699 storeSwizzle.apply(&pipeline);
700 SkRasterPipeline_MemoryCtx dstCtx{dst, SkToInt(dstRB/dstInfo.bpp())};
701 pipeline.append(store, &dstCtx);
702 pipeline.run(0, 0, dstInfo.width(), dstInfo.height());
703
704 return true;
Brian Salomon998937c2019-10-29 09:34:52 -0400705}
706
Robert Phillipsc80b0e92019-07-23 10:27:09 -0400707GrColorType SkColorTypeAndFormatToGrColorType(const GrCaps* caps,
708 SkColorType skCT,
709 const GrBackendFormat& format) {
710 GrColorType grCT = SkColorTypeToGrColorType(skCT);
711 // Until we support SRGB in the SkColorType we have to do this manual check here to make sure
712 // we use the correct GrColorType.
713 if (caps->isFormatSRGB(format)) {
714 if (grCT != GrColorType::kRGBA_8888) {
715 return GrColorType::kUnknown;
716 }
717 grCT = GrColorType::kRGBA_8888_SRGB;
718 }
719 return grCT;
720}