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