Flutter Engine
The Flutter Engine
SkCompressedDataUtils.cpp
Go to the documentation of this file.
1/*
2 * Copyright 2020 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
9
13#include "include/core/SkData.h"
15#include "include/core/SkSize.h"
19#include "src/base/SkMathPriv.h"
20#include "src/core/SkMipmap.h"
21
22#include <algorithm>
23#include <cstdint>
24
25using namespace skia_private;
26
27struct ETC1Block {
28 uint32_t fHigh;
29 uint32_t fLow;
30};
31
32constexpr uint32_t kFlipBit = 0x1; // set -> T/B sub-blocks; not-set -> L/R sub-blocks
33constexpr uint32_t kDiffBit = 0x2; // set -> differential; not-set -> individual
34
35static inline int extend_4To8bits(int b) {
36 int c = b & 0xf;
37 return (c << 4) | c;
38}
39
40static inline int extend_5To8bits(int b) {
41 int c = b & 0x1f;
42 return (c << 3) | (c >> 2);
43}
44
45static inline int extend_5plus3To8Bits(int base, int diff) {
46 static const int kLookup[8] = { 0, 1, 2, 3, -4, -3, -2, -1 };
47
48 return extend_5To8bits((0x1f & base) + kLookup[0x7 & diff]);
49}
50
51static const int kNumETC1ModifierTables = 8;
52static const int kNumETC1PixelIndices = 4;
53
54// The index of each row in this table is the ETC1 table codeword
55// The index of each column in this table is the ETC1 pixel index value
57 /* 0 */ { 2, 8, -2, -8 },
58 /* 1 */ { 5, 17, -5, -17 },
59 /* 2 */ { 9, 29, -9, -29 },
60 /* 3 */ { 13, 42, -13, -42 },
61 /* 4 */ { 18, 60, -18, -60 },
62 /* 5 */ { 24, 80, -24, -80 },
63 /* 6 */ { 33, 106, -33, -106 },
64 /* 7 */ { 47, 183, -47, -183 }
65};
66
67static int num_4x4_blocks(int size) {
68 return ((size + 3) & ~3) >> 2;
69}
70
71// Return which sub-block a given x,y location in the overall 4x4 block belongs to
72static int xy_to_subblock_index(int x, int y, bool flip) {
73 SkASSERT(x >= 0 && x < 4);
74 SkASSERT(y >= 0 && y < 4);
75
76 if (flip) {
77 return y < 2 ? 0 : 1; // sub-block 1 is on top of sub-block 2
78 } else {
79 return x < 2 ? 0 : 1; // sub-block 1 is to the left of sub-block 2
80 }
81}
82
83struct IColor {
84 int fR, fG, fB;
85};
86
87static SkPMColor add_delta_and_clamp(const IColor& col, int delta) {
88 int r8 = SkTPin(col.fR + delta, 0, 255);
89 int g8 = SkTPin(col.fG + delta, 0, 255);
90 int b8 = SkTPin(col.fB + delta, 0, 255);
91
92 return SkPackARGB32(0xFF, r8, g8, b8);
93}
94
95static bool decompress_etc1(SkISize dimensions, const uint8_t* srcData, SkBitmap* dst) {
96 const ETC1Block* srcBlocks = reinterpret_cast<const ETC1Block*>(srcData);
97
98 int numXBlocks = num_4x4_blocks(dimensions.width());
99 int numYBlocks = num_4x4_blocks(dimensions.height());
100
101 for (int y = 0; y < numYBlocks; ++y) {
102 for (int x = 0; x < numXBlocks; ++x) {
103 const ETC1Block* curBlock1 = &srcBlocks[y * numXBlocks + x];
104 uint32_t high = SkBSwap32(curBlock1->fHigh);
105 uint32_t low = SkBSwap32(curBlock1->fLow);
106
107 bool flipped = SkToBool(high & kFlipBit);
108 bool differential = SkToBool(high & kDiffBit);
109
110 IColor colors[2];
111
112 if (differential) {
113 colors[0].fR = extend_5To8bits(high >> 27);
114 colors[1].fR = extend_5plus3To8Bits(high >> 27, high >> 24);
115 colors[0].fG = extend_5To8bits(high >> 19);
116 colors[1].fG = extend_5plus3To8Bits(high >> 19, high >> 16);
117 colors[0].fB = extend_5To8bits(high >> 11);
118 colors[1].fB = extend_5plus3To8Bits(high >> 11, high >> 8);
119 } else {
120 colors[0].fR = extend_4To8bits(high >> 28);
121 colors[1].fR = extend_4To8bits(high >> 24);
122 colors[0].fG = extend_4To8bits(high >> 20);
123 colors[1].fG = extend_4To8bits(high >> 16);
124 colors[0].fB = extend_4To8bits(high >> 12);
125 colors[1].fB = extend_4To8bits(high >> 8);
126 }
127
128 int tableIndex0 = (high >> 5) & 0x7;
129 int tableIndex1 = (high >> 2) & 0x7;
130 const int* tables[2] = {
131 kETC1ModifierTables[tableIndex0],
132 kETC1ModifierTables[tableIndex1]
133 };
134
135 int baseShift = 0;
136 int offsetX = 4 * x, offsetY = 4 * y;
137 for (int i = 0; i < 4; ++i, ++baseShift) {
138 for (int j = 0; j < 4; ++j) {
139 if (offsetX + j >= dst->width() || offsetY + i >= dst->height()) {
140 // This can happen for the topmost levels of a mipmap and for
141 // non-multiple of 4 textures
142 continue;
143 }
144
145 int subBlockIndex = xy_to_subblock_index(j, i, flipped);
146 int pixelIndex = ((low >> (baseShift+(j*4))) & 0x1) |
147 (low >> (baseShift+(j*4)+15) & 0x2);
148
149 SkASSERT(subBlockIndex == 0 || subBlockIndex == 1);
150 SkASSERT(pixelIndex >= 0 && pixelIndex < 4);
151
152 int delta = tables[subBlockIndex][pixelIndex];
153 *dst->getAddr32(offsetX + j, offsetY + i) =
154 add_delta_and_clamp(colors[subBlockIndex], delta);
155 }
156 }
157 }
158 }
159
160 return true;
161}
162
163//------------------------------------------------------------------------------------------------
164struct BC1Block {
165 uint16_t fColor0;
166 uint16_t fColor1;
167 uint32_t fIndices;
168};
169
170static SkPMColor from565(uint16_t rgb565) {
171 uint8_t r8 = SkR16ToR32((rgb565 >> 11) & 0x1F);
172 uint8_t g8 = SkG16ToG32((rgb565 >> 5) & 0x3F);
173 uint8_t b8 = SkB16ToB32(rgb565 & 0x1F);
174
175 return SkPackARGB32(0xFF, r8, g8, b8);
176}
177
178// return t*col0 + (1-t)*col1
179static SkPMColor lerp(float t, SkPMColor col0, SkPMColor col1) {
180 SkASSERT(SkGetPackedA32(col0) == 0xFF && SkGetPackedA32(col1) == 0xFF);
181
182 // TODO: given 't' is only either 1/3 or 2/3 this could be done faster
183 uint8_t r8 = SkScalarRoundToInt(t * SkGetPackedR32(col0) + (1.0f - t) * SkGetPackedR32(col1));
184 uint8_t g8 = SkScalarRoundToInt(t * SkGetPackedG32(col0) + (1.0f - t) * SkGetPackedG32(col1));
185 uint8_t b8 = SkScalarRoundToInt(t * SkGetPackedB32(col0) + (1.0f - t) * SkGetPackedB32(col1));
186 return SkPackARGB32(0xFF, r8, g8, b8);
187}
188
189static bool decompress_bc1(SkISize dimensions, const uint8_t* srcData,
190 bool isOpaque, SkBitmap* dst) {
191 const BC1Block* srcBlocks = reinterpret_cast<const BC1Block*>(srcData);
192
193 int numXBlocks = num_4x4_blocks(dimensions.width());
194 int numYBlocks = num_4x4_blocks(dimensions.height());
195
196 SkPMColor colors[4];
197
198 for (int y = 0; y < numYBlocks; ++y) {
199 for (int x = 0; x < numXBlocks; ++x) {
200 const BC1Block* curBlock = &srcBlocks[y * numXBlocks + x];
201
202 colors[0] = from565(curBlock->fColor0);
203 colors[1] = from565(curBlock->fColor1);
204 if (curBlock->fColor0 <= curBlock->fColor1) { // signal for a transparent block
205 colors[2] = SkPackARGB32(
206 0xFF,
209 (SkGetPackedB32(colors[0]) + SkGetPackedB32(colors[1])) >> 1);
210 // The opacity of the overall texture trumps the per-block transparency
211 colors[3] = SkPackARGB32(isOpaque ? 0xFF : 0, 0, 0, 0);
212 } else {
213 colors[2] = lerp(2.0f/3.0f, colors[0], colors[1]);
214 colors[3] = lerp(1.0f/3.0f, colors[0], colors[1]);
215 }
216
217 int shift = 0;
218 int offsetX = 4 * x, offsetY = 4 * y;
219 for (int i = 0; i < 4; ++i) {
220 for (int j = 0; j < 4; ++j, shift += 2) {
221 if (offsetX + j >= dst->width() || offsetY + i >= dst->height()) {
222 // This can happen for the topmost levels of a mipmap and for
223 // non-multiple of 4 textures
224 continue;
225 }
226
227 int index = (curBlock->fIndices >> shift) & 0x3;
228 *dst->getAddr32(offsetX + j, offsetY + i) = colors[index];
229 }
230 }
231 }
232 }
233
234 return true;
235}
236
238 SkISize dimensions,
239 SkTextureCompressionType compressionType,
240 SkBitmap* dst) {
242
243 const uint8_t* bytes = data->bytes();
244 switch (compressionType) {
245 case Type::kNone: return false;
246 case Type::kETC2_RGB8_UNORM: return decompress_etc1(dimensions, bytes, dst);
247 case Type::kBC1_RGB8_UNORM: return decompress_bc1(dimensions, bytes, true, dst);
248 case Type::kBC1_RGBA8_UNORM: return decompress_bc1(dimensions, bytes, false, dst);
249 }
250
252}
253
255 TArray<size_t>* individualMipOffsets, bool mipmapped) {
256 SkASSERT(!individualMipOffsets || individualMipOffsets->empty());
257
258 int numMipLevels = 1;
259 if (mipmapped) {
260 numMipLevels = SkMipmap::ComputeLevelCount(dimensions.width(), dimensions.height()) + 1;
261 }
262
263 size_t totalSize = 0;
264 switch (type) {
266 break;
270 for (int i = 0; i < numMipLevels; ++i) {
271 int numBlocks = num_4x4_blocks(dimensions.width()) *
272 num_4x4_blocks(dimensions.height());
273
274 if (individualMipOffsets) {
275 individualMipOffsets->push_back(totalSize);
276 }
277
278 static_assert(sizeof(ETC1Block) == sizeof(BC1Block));
279 totalSize += numBlocks * sizeof(ETC1Block);
280
281 dimensions = {std::max(1, dimensions.width()/2), std::max(1, dimensions.height()/2)};
282 }
283 break;
284 }
285 }
286
287 return totalSize;
288}
289
291 switch (type) {
293 return 0;
295 return sizeof(ETC1Block);
298 return sizeof(BC1Block);
299 }
301}
302
304 SkISize dimensions, bool mipmapped) {
305 return SkCompressedDataSize(compressionType, dimensions, nullptr, mipmapped);
306}
static const uint16_t rgb565[kNumPixels]
#define SkUNREACHABLE
Definition: SkAssert.h:135
#define SkASSERT(cond)
Definition: SkAssert.h:116
static unsigned SkB16ToB32(unsigned b)
Definition: SkColorData.h:42
static unsigned SkG16ToG32(unsigned g)
Definition: SkColorData.h:38
static unsigned SkR16ToR32(unsigned r)
Definition: SkColorData.h:34
#define SkGetPackedB32(packed)
Definition: SkColorPriv.h:95
#define SkGetPackedR32(packed)
Definition: SkColorPriv.h:93
#define SkGetPackedA32(packed)
Definition: SkColorPriv.h:92
#define SkGetPackedG32(packed)
Definition: SkColorPriv.h:94
static SkPMColor SkPackARGB32(U8CPU a, U8CPU r, U8CPU g, U8CPU b)
Definition: SkColorPriv.h:106
uint32_t SkPMColor
Definition: SkColor.h:205
bool SkDecompress(sk_sp< SkData > data, SkISize dimensions, SkTextureCompressionType compressionType, SkBitmap *dst)
static int extend_5To8bits(int b)
static SkPMColor lerp(float t, SkPMColor col0, SkPMColor col1)
static const int kNumETC1PixelIndices
static bool decompress_bc1(SkISize dimensions, const uint8_t *srcData, bool isOpaque, SkBitmap *dst)
static int xy_to_subblock_index(int x, int y, bool flip)
static int num_4x4_blocks(int size)
static SkPMColor add_delta_and_clamp(const IColor &col, int delta)
size_t SkCompressedFormatDataSize(SkTextureCompressionType compressionType, SkISize dimensions, bool mipmapped)
constexpr uint32_t kDiffBit
static const int kNumETC1ModifierTables
static bool decompress_etc1(SkISize dimensions, const uint8_t *srcData, SkBitmap *dst)
size_t SkCompressedDataSize(SkTextureCompressionType type, SkISize dimensions, TArray< size_t > *individualMipOffsets, bool mipmapped)
static int extend_4To8bits(int b)
static const int kETC1ModifierTables[kNumETC1ModifierTables][kNumETC1PixelIndices]
static SkPMColor from565(uint16_t rgb565)
static int extend_5plus3To8Bits(int base, int diff)
constexpr uint32_t kFlipBit
size_t SkCompressedBlockSize(SkTextureCompressionType type)
static uint32_t SkBSwap32(uint32_t v)
Definition: SkMathPriv.h:123
#define SkScalarRoundToInt(x)
Definition: SkScalar.h:37
static constexpr const T & SkTPin(const T &x, const T &lo, const T &hi)
Definition: SkTPin.h:19
SkTextureCompressionType
static constexpr bool SkToBool(const T &x)
Definition: SkTo.h:35
GLenum type
static int ComputeLevelCount(int baseWidth, int baseHeight)
Definition: SkMipmap.cpp:134
bool empty() const
Definition: SkTArray.h:199
static const int kLookup[8]
Definition: etc1.cpp:128
static bool b
static float max(float r, float g, float b)
Definition: hsl.cpp:49
double y
double x
PODArray< SkColor > colors
Definition: SkRecords.h:276
it will be possible to load the file into Perfetto s trace viewer disable asset Prevents usage of any non test fonts unless they were explicitly Loaded via prefetched default font Indicates whether the embedding started a prefetch of the default font manager before creating the engine run In non interactive keep the shell running after the Dart script has completed enable serial On low power devices with low core running concurrent GC tasks on threads can cause them to contend with the UI thread which could potentially lead to jank This option turns off all concurrent GC activities domain network JSON encoded network policy per domain This overrides the DisallowInsecureConnections switch Embedder can specify whether to allow or disallow insecure connections at a domain level old gen heap size
Definition: switches.h:259
dst
Definition: cp.py:12
SkScalar offsetX
SkScalar offsetY
uint16_t fColor0
uint32_t fIndices
uint16_t fColor1
Definition: SkSize.h:16
constexpr int32_t width() const
Definition: SkSize.h:36
constexpr int32_t height() const
Definition: SkSize.h:37
std::shared_ptr< const fml::Mapping > data
Definition: texture_gles.cc:63