1 // Copyright 2015 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 // See the following specification for details on the ETC1 format:
6 // https://www.khronos.org/registry/gles/extensions/OES/OES_compressed_ETC1_RGB8_texture.txt
8 #include "cc/resources/texture_compressor_etc1.h"
13 #include "base/logging.h"
15 // Defining the following macro will cause the error metric function to weigh
16 // each color channel differently depending on how the human eye can perceive
17 // them. This can give a slight improvement in image quality at the cost of a
19 // #define USE_PERCEIVED_ERROR_METRIC
24 inline T
clamp(T val
, T min
, T max
) {
25 return val
< min
? min
: (val
> max
? max
: val
);
28 inline uint8_t round_to_5_bits(float val
) {
29 return clamp
<uint8_t>(val
* 31.0f
/ 255.0f
+ 0.5f
, 0, 31);
32 inline uint8_t round_to_4_bits(float val
) {
33 return clamp
<uint8_t>(val
* 15.0f
/ 255.0f
+ 0.5f
, 0, 15);
37 struct BgraColorType
{
43 uint8_t components
[4];
51 static const int16_t g_codeword_tables
[8][4] = {{-8, -2, 2, 8},
58 {-183, -47, 47, 183}};
61 * Maps modifier indices to pixel index values.
64 static const uint8_t g_mod_to_pix
[4] = {3, 2, 0, 1};
67 * The ETC1 specification index texels as follows:
69 * [a][e][i][m] [ 0][ 4][ 8][12]
70 * [b][f][j][n] <-> [ 1][ 5][ 9][13]
71 * [c][g][k][o] [ 2][ 6][10][14]
72 * [d][h][l][p] [ 3][ 7][11][15]
74 * However, when extracting sub blocks from BGRA data the natural array
75 * indexing order ends up different:
77 * vertical0: [a][e][b][f] horizontal0: [a][e][i][m]
78 * [c][g][d][h] [b][f][j][n]
79 * vertical1: [i][m][j][n] horizontal1: [c][g][k][o]
80 * [k][o][l][p] [d][h][l][p]
82 * In order to translate from the natural array indices in a sub block to the
83 * indices (number) used by specification and hardware we use this table.
85 static const uint8_t g_idx_to_num
[4][8] = {
86 {0, 4, 1, 5, 2, 6, 3, 7}, // Vertical block 0.
87 {8, 12, 9, 13, 10, 14, 11, 15}, // Vertical block 1.
88 {0, 4, 8, 12, 1, 5, 9, 13}, // Horizontal block 0.
89 {2, 6, 10, 14, 3, 7, 11, 15} // Horizontal block 1.
92 inline void WriteColors444(uint8_t* block
,
94 const Color
& color1
) {
95 block
[0] = (color0
.channels
.r
& 0xf0) | (color1
.channels
.r
>> 4);
96 block
[1] = (color0
.channels
.g
& 0xf0) | (color1
.channels
.g
>> 4);
97 block
[2] = (color0
.channels
.b
& 0xf0) | (color1
.channels
.b
>> 4);
100 inline void WriteColors555(uint8_t* block
,
102 const Color
& color1
) {
103 // Table for conversion to 3-bit two complement format.
104 static const uint8_t two_compl_trans_table
[8] = {
116 static_cast<int16_t>(color1
.channels
.r
>> 3) - (color0
.channels
.r
>> 3);
118 static_cast<int16_t>(color1
.channels
.g
>> 3) - (color0
.channels
.g
>> 3);
120 static_cast<int16_t>(color1
.channels
.b
>> 3) - (color0
.channels
.b
>> 3);
121 DCHECK(delta_r
>= -4 && delta_r
<= 3);
122 DCHECK(delta_g
>= -4 && delta_g
<= 3);
123 DCHECK(delta_b
>= -4 && delta_b
<= 3);
125 block
[0] = (color0
.channels
.r
& 0xf8) | two_compl_trans_table
[delta_r
+ 4];
126 block
[1] = (color0
.channels
.g
& 0xf8) | two_compl_trans_table
[delta_g
+ 4];
127 block
[2] = (color0
.channels
.b
& 0xf8) | two_compl_trans_table
[delta_b
+ 4];
130 inline void WriteCodewordTable(uint8_t* block
,
131 uint8_t sub_block_id
,
133 DCHECK_LT(sub_block_id
, 2);
136 uint8_t shift
= (2 + (3 - sub_block_id
* 3));
137 block
[3] &= ~(0x07 << shift
);
138 block
[3] |= table
<< shift
;
141 inline void WritePixelData(uint8_t* block
, uint32_t pixel_data
) {
142 block
[4] |= pixel_data
>> 24;
143 block
[5] |= (pixel_data
>> 16) & 0xff;
144 block
[6] |= (pixel_data
>> 8) & 0xff;
145 block
[7] |= pixel_data
& 0xff;
148 inline void WriteFlip(uint8_t* block
, bool flip
) {
150 block
[3] |= static_cast<uint8_t>(flip
);
153 inline void WriteDiff(uint8_t* block
, bool diff
) {
155 block
[3] |= static_cast<uint8_t>(diff
) << 1;
159 * Compress and rounds BGR888 into BGR444. The resulting BGR444 color is
160 * expanded to BGR888 as it would be in hardware after decompression. The
161 * actual 444-bit data is available in the four most significant bits of each
164 inline Color
MakeColor444(const float* bgr
) {
165 uint8_t b4
= round_to_4_bits(bgr
[0]);
166 uint8_t g4
= round_to_4_bits(bgr
[1]);
167 uint8_t r4
= round_to_4_bits(bgr
[2]);
169 bgr444
.channels
.b
= (b4
<< 4) | b4
;
170 bgr444
.channels
.g
= (g4
<< 4) | g4
;
171 bgr444
.channels
.r
= (r4
<< 4) | r4
;
176 * Compress and rounds BGR888 into BGR555. The resulting BGR555 color is
177 * expanded to BGR888 as it would be in hardware after decompression. The
178 * actual 555-bit data is available in the five most significant bits of each
181 inline Color
MakeColor555(const float* bgr
) {
182 uint8_t b5
= round_to_5_bits(bgr
[0]);
183 uint8_t g5
= round_to_5_bits(bgr
[1]);
184 uint8_t r5
= round_to_5_bits(bgr
[2]);
186 bgr555
.channels
.b
= (b5
<< 3) | (b5
>> 2);
187 bgr555
.channels
.g
= (g5
<< 3) | (g5
>> 2);
188 bgr555
.channels
.r
= (r5
<< 3) | (r5
>> 2);
193 * Constructs a color from a given base color and luminance value.
195 inline Color
MakeColor(const Color
& base
, int16_t lum
) {
196 int b
= static_cast<int>(base
.channels
.b
) + lum
;
197 int g
= static_cast<int>(base
.channels
.g
) + lum
;
198 int r
= static_cast<int>(base
.channels
.r
) + lum
;
200 color
.channels
.b
= static_cast<uint8_t>(clamp(b
, 0, 255));
201 color
.channels
.g
= static_cast<uint8_t>(clamp(g
, 0, 255));
202 color
.channels
.r
= static_cast<uint8_t>(clamp(r
, 0, 255));
207 * Calculates the error metric for two colors. A small error signals that the
208 * colors are similar to each other, a large error the signals the opposite.
210 inline uint32_t GetColorError(const Color
& u
, const Color
& v
) {
211 #ifdef USE_PERCEIVED_ERROR_METRIC
212 float delta_b
= static_cast<float>(u
.channels
.b
) - v
.channels
.b
;
213 float delta_g
= static_cast<float>(u
.channels
.g
) - v
.channels
.g
;
214 float delta_r
= static_cast<float>(u
.channels
.r
) - v
.channels
.r
;
215 return static_cast<uint32_t>(0.299f
* delta_b
* delta_b
+
216 0.587f
* delta_g
* delta_g
+
217 0.114f
* delta_r
* delta_r
);
219 int delta_b
= static_cast<int>(u
.channels
.b
) - v
.channels
.b
;
220 int delta_g
= static_cast<int>(u
.channels
.g
) - v
.channels
.g
;
221 int delta_r
= static_cast<int>(u
.channels
.r
) - v
.channels
.r
;
222 return delta_b
* delta_b
+ delta_g
* delta_g
+ delta_r
* delta_r
;
226 void GetAverageColor(const Color
* src
, float* avg_color
) {
227 uint32_t sum_b
= 0, sum_g
= 0, sum_r
= 0;
229 for (unsigned int i
= 0; i
< 8; ++i
) {
230 sum_b
+= src
[i
].channels
.b
;
231 sum_g
+= src
[i
].channels
.g
;
232 sum_r
+= src
[i
].channels
.r
;
235 const float kInv8
= 1.0f
/ 8.0f
;
236 avg_color
[0] = static_cast<float>(sum_b
) * kInv8
;
237 avg_color
[1] = static_cast<float>(sum_g
) * kInv8
;
238 avg_color
[2] = static_cast<float>(sum_r
) * kInv8
;
241 void ComputeLuminance(uint8_t* block
,
245 const uint8_t* idx_to_num_tab
) {
246 uint32_t best_tbl_err
= std::numeric_limits
<uint32_t>::max();
247 uint8_t best_tbl_idx
= 0;
248 uint8_t best_mod_idx
[8][8]; // [table][texel]
250 // Try all codeword tables to find the one giving the best results for this
252 for (unsigned int tbl_idx
= 0; tbl_idx
< 8; ++tbl_idx
) {
253 // Pre-compute all the candidate colors; combinations of the base color and
254 // all available luminance values.
255 Color candidate_color
[4]; // [modifier]
256 for (unsigned int mod_idx
= 0; mod_idx
< 4; ++mod_idx
) {
257 int16_t lum
= g_codeword_tables
[tbl_idx
][mod_idx
];
258 candidate_color
[mod_idx
] = MakeColor(base
, lum
);
261 uint32_t tbl_err
= 0;
263 for (unsigned int i
= 0; i
< 8; ++i
) {
264 // Try all modifiers in the current table to find which one gives the
266 uint32_t best_mod_err
= std::numeric_limits
<uint32_t>::max();
267 for (unsigned int mod_idx
= 0; mod_idx
< 4; ++mod_idx
) {
268 const Color
& color
= candidate_color
[mod_idx
];
270 uint32_t mod_err
= GetColorError(src
[i
], color
);
271 if (mod_err
< best_mod_err
) {
272 best_mod_idx
[tbl_idx
][i
] = mod_idx
;
273 best_mod_err
= mod_err
;
276 break; // We cannot do any better than this.
280 tbl_err
+= best_mod_err
;
281 if (tbl_err
> best_tbl_err
)
282 break; // We're already doing worse than the best table so skip.
285 if (tbl_err
< best_tbl_err
) {
286 best_tbl_err
= tbl_err
;
287 best_tbl_idx
= tbl_idx
;
290 break; // We cannot do any better than this.
294 WriteCodewordTable(block
, sub_block_id
, best_tbl_idx
);
296 uint32_t pix_data
= 0;
298 for (unsigned int i
= 0; i
< 8; ++i
) {
299 uint8_t mod_idx
= best_mod_idx
[best_tbl_idx
][i
];
300 uint8_t pix_idx
= g_mod_to_pix
[mod_idx
];
302 uint32_t lsb
= pix_idx
& 0x1;
303 uint32_t msb
= pix_idx
>> 1;
305 // Obtain the texel number as specified in the standard.
306 int texel_num
= idx_to_num_tab
[i
];
307 pix_data
|= msb
<< (texel_num
+ 16);
308 pix_data
|= lsb
<< (texel_num
);
311 WritePixelData(block
, pix_data
);
315 * Tries to compress the block under the assumption that it's a single color
316 * block. If it's not the function will bail out without writing anything to
317 * the destination buffer.
319 bool TryCompressSolidBlock(uint8_t* dst
, const Color
* src
) {
320 for (unsigned int i
= 1; i
< 16; ++i
) {
321 if (src
[i
].bits
!= src
[0].bits
)
325 // Clear destination buffer so that we can "or" in the results.
328 float src_color_float
[3] = {static_cast<float>(src
->channels
.b
),
329 static_cast<float>(src
->channels
.g
),
330 static_cast<float>(src
->channels
.r
)};
331 Color base
= MakeColor555(src_color_float
);
333 WriteDiff(dst
, true);
334 WriteFlip(dst
, false);
335 WriteColors555(dst
, base
, base
);
337 uint8_t best_tbl_idx
= 0;
338 uint8_t best_mod_idx
= 0;
339 uint32_t best_mod_err
= std::numeric_limits
<uint32_t>::max();
341 // Try all codeword tables to find the one giving the best results for this
343 for (unsigned int tbl_idx
= 0; tbl_idx
< 8; ++tbl_idx
) {
344 // Try all modifiers in the current table to find which one gives the
346 for (unsigned int mod_idx
= 0; mod_idx
< 4; ++mod_idx
) {
347 int16_t lum
= g_codeword_tables
[tbl_idx
][mod_idx
];
348 const Color
& color
= MakeColor(base
, lum
);
350 uint32_t mod_err
= GetColorError(*src
, color
);
351 if (mod_err
< best_mod_err
) {
352 best_tbl_idx
= tbl_idx
;
353 best_mod_idx
= mod_idx
;
354 best_mod_err
= mod_err
;
357 break; // We cannot do any better than this.
361 if (best_mod_err
== 0)
365 WriteCodewordTable(dst
, 0, best_tbl_idx
);
366 WriteCodewordTable(dst
, 1, best_tbl_idx
);
368 uint8_t pix_idx
= g_mod_to_pix
[best_mod_idx
];
369 uint32_t lsb
= pix_idx
& 0x1;
370 uint32_t msb
= pix_idx
>> 1;
372 uint32_t pix_data
= 0;
373 for (unsigned int i
= 0; i
< 2; ++i
) {
374 for (unsigned int j
= 0; j
< 8; ++j
) {
375 // Obtain the texel number as specified in the standard.
376 int texel_num
= g_idx_to_num
[i
][j
];
377 pix_data
|= msb
<< (texel_num
+ 16);
378 pix_data
|= lsb
<< (texel_num
);
382 WritePixelData(dst
, pix_data
);
386 void CompressBlock(uint8_t* dst
, const Color
* ver_src
, const Color
* hor_src
) {
387 if (TryCompressSolidBlock(dst
, ver_src
))
390 const Color
* sub_block_src
[4] = {ver_src
, ver_src
+ 8, hor_src
, hor_src
+ 8};
392 Color sub_block_avg
[4];
393 bool use_differential
[2] = {true, true};
395 // Compute the average color for each sub block and determine if differential
396 // coding can be used.
397 for (unsigned int i
= 0, j
= 1; i
< 4; i
+= 2, j
+= 2) {
398 float avg_color_0
[3];
399 GetAverageColor(sub_block_src
[i
], avg_color_0
);
400 Color avg_color_555_0
= MakeColor555(avg_color_0
);
402 float avg_color_1
[3];
403 GetAverageColor(sub_block_src
[j
], avg_color_1
);
404 Color avg_color_555_1
= MakeColor555(avg_color_1
);
406 for (unsigned int light_idx
= 0; light_idx
< 3; ++light_idx
) {
407 int u
= avg_color_555_0
.components
[light_idx
] >> 3;
408 int v
= avg_color_555_1
.components
[light_idx
] >> 3;
410 int component_diff
= v
- u
;
411 if (component_diff
< -4 || component_diff
> 3) {
412 use_differential
[i
/ 2] = false;
413 sub_block_avg
[i
] = MakeColor444(avg_color_0
);
414 sub_block_avg
[j
] = MakeColor444(avg_color_1
);
416 sub_block_avg
[i
] = avg_color_555_0
;
417 sub_block_avg
[j
] = avg_color_555_1
;
422 // Compute the error of each sub block before adjusting for luminance. These
423 // error values are later used for determining if we should flip the sub
425 uint32_t sub_block_err
[4] = {0};
426 for (unsigned int i
= 0; i
< 4; ++i
) {
427 for (unsigned int j
= 0; j
< 8; ++j
) {
428 sub_block_err
[i
] += GetColorError(sub_block_avg
[i
], sub_block_src
[i
][j
]);
433 sub_block_err
[2] + sub_block_err
[3] < sub_block_err
[0] + sub_block_err
[1];
435 // Clear destination buffer so that we can "or" in the results.
438 WriteDiff(dst
, use_differential
[!!flip
]);
439 WriteFlip(dst
, flip
);
441 uint8_t sub_block_off_0
= flip
? 2 : 0;
442 uint8_t sub_block_off_1
= sub_block_off_0
+ 1;
444 if (use_differential
[!!flip
]) {
445 WriteColors555(dst
, sub_block_avg
[sub_block_off_0
],
446 sub_block_avg
[sub_block_off_1
]);
448 WriteColors444(dst
, sub_block_avg
[sub_block_off_0
],
449 sub_block_avg
[sub_block_off_1
]);
452 // Compute luminance for the first sub block.
453 ComputeLuminance(dst
, sub_block_src
[sub_block_off_0
],
454 sub_block_avg
[sub_block_off_0
], 0,
455 g_idx_to_num
[sub_block_off_0
]);
456 // Compute luminance for the second sub block.
457 ComputeLuminance(dst
, sub_block_src
[sub_block_off_1
],
458 sub_block_avg
[sub_block_off_1
], 1,
459 g_idx_to_num
[sub_block_off_1
]);
466 void TextureCompressorETC1::Compress(const uint8_t* src
,
471 DCHECK(width
>= 4 && (width
& 3) == 0);
472 DCHECK(height
>= 4 && (height
& 3) == 0);
474 Color ver_blocks
[16];
475 Color hor_blocks
[16];
477 for (int y
= 0; y
< height
; y
+= 4, src
+= width
* 4 * 4) {
478 for (int x
= 0; x
< width
; x
+= 4, dst
+= 8) {
479 const Color
* row0
= reinterpret_cast<const Color
*>(src
+ x
* 4);
480 const Color
* row1
= row0
+ width
;
481 const Color
* row2
= row1
+ width
;
482 const Color
* row3
= row2
+ width
;
484 memcpy(ver_blocks
, row0
, 8);
485 memcpy(ver_blocks
+ 2, row1
, 8);
486 memcpy(ver_blocks
+ 4, row2
, 8);
487 memcpy(ver_blocks
+ 6, row3
, 8);
488 memcpy(ver_blocks
+ 8, row0
+ 2, 8);
489 memcpy(ver_blocks
+ 10, row1
+ 2, 8);
490 memcpy(ver_blocks
+ 12, row2
+ 2, 8);
491 memcpy(ver_blocks
+ 14, row3
+ 2, 8);
493 memcpy(hor_blocks
, row0
, 16);
494 memcpy(hor_blocks
+ 4, row1
, 16);
495 memcpy(hor_blocks
+ 8, row2
, 16);
496 memcpy(hor_blocks
+ 12, row3
, 16);
498 CompressBlock(dst
, ver_blocks
, hor_blocks
);