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