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/raster/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
25 // Constructs a color from a given base color and luminance value.
26 inline Color
MakeColor(const Color
& base
, int16_t lum
) {
27 int b
= static_cast<int>(base
.channels
.b
) + lum
;
28 int g
= static_cast<int>(base
.channels
.g
) + lum
;
29 int r
= static_cast<int>(base
.channels
.r
) + lum
;
31 color
.channels
.b
= static_cast<uint8_t>(clamp(b
, 0, 255));
32 color
.channels
.g
= static_cast<uint8_t>(clamp(g
, 0, 255));
33 color
.channels
.r
= static_cast<uint8_t>(clamp(r
, 0, 255));
37 // Calculates the error metric for two colors. A small error signals that the
38 // colors are similar to each other, a large error the signals the opposite.
39 inline uint32_t GetColorError(const Color
& u
, const Color
& v
) {
40 #ifdef USE_PERCEIVED_ERROR_METRIC
41 float delta_b
= static_cast<float>(u
.channels
.b
) - v
.channels
.b
;
42 float delta_g
= static_cast<float>(u
.channels
.g
) - v
.channels
.g
;
43 float delta_r
= static_cast<float>(u
.channels
.r
) - v
.channels
.r
;
44 return static_cast<uint32_t>(0.299f
* delta_b
* delta_b
+
45 0.587f
* delta_g
* delta_g
+
46 0.114f
* delta_r
* delta_r
);
48 int delta_b
= static_cast<int>(u
.channels
.b
) - v
.channels
.b
;
49 int delta_g
= static_cast<int>(u
.channels
.g
) - v
.channels
.g
;
50 int delta_r
= static_cast<int>(u
.channels
.r
) - v
.channels
.r
;
51 return delta_b
* delta_b
+ delta_g
* delta_g
+ delta_r
* delta_r
;
55 void GetAverageColor(const Color
* src
, float* avg_color
) {
56 uint32_t sum_b
= 0, sum_g
= 0, sum_r
= 0;
58 for (unsigned int i
= 0; i
< 8; ++i
) {
59 sum_b
+= src
[i
].channels
.b
;
60 sum_g
+= src
[i
].channels
.g
;
61 sum_r
+= src
[i
].channels
.r
;
64 const float kInv8
= 1.0f
/ 8.0f
;
65 avg_color
[0] = static_cast<float>(sum_b
) * kInv8
;
66 avg_color
[1] = static_cast<float>(sum_g
) * kInv8
;
67 avg_color
[2] = static_cast<float>(sum_r
) * kInv8
;
70 void ComputeLuminance(uint8_t* block
,
74 const uint8_t* idx_to_num_tab
) {
75 uint32_t best_tbl_err
= std::numeric_limits
<uint32_t>::max();
76 uint8_t best_tbl_idx
= 0;
77 uint8_t best_mod_idx
[8][8]; // [table][texel]
79 // Try all codeword tables to find the one giving the best results for this
81 for (unsigned int tbl_idx
= 0; tbl_idx
< 8; ++tbl_idx
) {
82 // Pre-compute all the candidate colors; combinations of the base color and
83 // all available luminance values.
84 Color candidate_color
[4]; // [modifier]
85 for (unsigned int mod_idx
= 0; mod_idx
< 4; ++mod_idx
) {
86 int16_t lum
= g_codeword_tables
[tbl_idx
][mod_idx
];
87 candidate_color
[mod_idx
] = MakeColor(base
, lum
);
92 for (unsigned int i
= 0; i
< 8; ++i
) {
93 // Try all modifiers in the current table to find which one gives the
95 uint32_t best_mod_err
= std::numeric_limits
<uint32_t>::max();
96 for (unsigned int mod_idx
= 0; mod_idx
< 4; ++mod_idx
) {
97 const Color
& color
= candidate_color
[mod_idx
];
99 uint32_t mod_err
= GetColorError(src
[i
], color
);
100 if (mod_err
< best_mod_err
) {
101 best_mod_idx
[tbl_idx
][i
] = mod_idx
;
102 best_mod_err
= mod_err
;
105 break; // We cannot do any better than this.
109 tbl_err
+= best_mod_err
;
110 if (tbl_err
> best_tbl_err
)
111 break; // We're already doing worse than the best table so skip.
114 if (tbl_err
< best_tbl_err
) {
115 best_tbl_err
= tbl_err
;
116 best_tbl_idx
= tbl_idx
;
119 break; // We cannot do any better than this.
123 WriteCodewordTable(block
, sub_block_id
, best_tbl_idx
);
125 uint32_t pix_data
= 0;
127 for (unsigned int i
= 0; i
< 8; ++i
) {
128 uint8_t mod_idx
= best_mod_idx
[best_tbl_idx
][i
];
129 uint8_t pix_idx
= g_mod_to_pix
[mod_idx
];
131 uint32_t lsb
= pix_idx
& 0x1;
132 uint32_t msb
= pix_idx
>> 1;
134 // Obtain the texel number as specified in the standard.
135 int texel_num
= idx_to_num_tab
[i
];
136 pix_data
|= msb
<< (texel_num
+ 16);
137 pix_data
|= lsb
<< (texel_num
);
140 WritePixelData(block
, pix_data
);
144 * Tries to compress the block under the assumption that it's a single color
145 * block. If it's not the function will bail out without writing anything to
146 * the destination buffer.
148 bool TryCompressSolidBlock(uint8_t* dst
, const Color
* src
) {
149 for (unsigned int i
= 1; i
< 16; ++i
) {
150 if (src
[i
].bits
!= src
[0].bits
)
154 // Clear destination buffer so that we can "or" in the results.
157 float src_color_float
[3] = {static_cast<float>(src
->channels
.b
),
158 static_cast<float>(src
->channels
.g
),
159 static_cast<float>(src
->channels
.r
)};
160 Color base
= MakeColor555(src_color_float
);
162 WriteDiff(dst
, true);
163 WriteFlip(dst
, false);
164 WriteColors555(dst
, base
, base
);
166 uint8_t best_tbl_idx
= 0;
167 uint8_t best_mod_idx
= 0;
168 uint32_t best_mod_err
= std::numeric_limits
<uint32_t>::max();
170 // Try all codeword tables to find the one giving the best results for this
172 for (unsigned int tbl_idx
= 0; tbl_idx
< 8; ++tbl_idx
) {
173 // Try all modifiers in the current table to find which one gives the
175 for (unsigned int mod_idx
= 0; mod_idx
< 4; ++mod_idx
) {
176 int16_t lum
= g_codeword_tables
[tbl_idx
][mod_idx
];
177 const Color
& color
= MakeColor(base
, lum
);
179 uint32_t mod_err
= GetColorError(*src
, color
);
180 if (mod_err
< best_mod_err
) {
181 best_tbl_idx
= tbl_idx
;
182 best_mod_idx
= mod_idx
;
183 best_mod_err
= mod_err
;
186 break; // We cannot do any better than this.
190 if (best_mod_err
== 0)
194 WriteCodewordTable(dst
, 0, best_tbl_idx
);
195 WriteCodewordTable(dst
, 1, best_tbl_idx
);
197 uint8_t pix_idx
= g_mod_to_pix
[best_mod_idx
];
198 uint32_t lsb
= pix_idx
& 0x1;
199 uint32_t msb
= pix_idx
>> 1;
201 uint32_t pix_data
= 0;
202 for (unsigned int i
= 0; i
< 2; ++i
) {
203 for (unsigned int j
= 0; j
< 8; ++j
) {
204 // Obtain the texel number as specified in the standard.
205 int texel_num
= g_idx_to_num
[i
][j
];
206 pix_data
|= msb
<< (texel_num
+ 16);
207 pix_data
|= lsb
<< (texel_num
);
211 WritePixelData(dst
, pix_data
);
215 void CompressBlock(uint8_t* dst
, const Color
* ver_src
, const Color
* hor_src
) {
216 if (TryCompressSolidBlock(dst
, ver_src
))
219 const Color
* sub_block_src
[4] = {ver_src
, ver_src
+ 8, hor_src
, hor_src
+ 8};
221 Color sub_block_avg
[4];
222 bool use_differential
[2] = {true, true};
224 // Compute the average color for each sub block and determine if differential
225 // coding can be used.
226 for (unsigned int i
= 0, j
= 1; i
< 4; i
+= 2, j
+= 2) {
227 float avg_color_0
[3];
228 GetAverageColor(sub_block_src
[i
], avg_color_0
);
229 Color avg_color_555_0
= MakeColor555(avg_color_0
);
231 float avg_color_1
[3];
232 GetAverageColor(sub_block_src
[j
], avg_color_1
);
233 Color avg_color_555_1
= MakeColor555(avg_color_1
);
235 for (unsigned int light_idx
= 0; light_idx
< 3; ++light_idx
) {
236 int u
= avg_color_555_0
.components
[light_idx
] >> 3;
237 int v
= avg_color_555_1
.components
[light_idx
] >> 3;
239 int component_diff
= v
- u
;
240 if (component_diff
< -4 || component_diff
> 3) {
241 use_differential
[i
/ 2] = false;
242 sub_block_avg
[i
] = MakeColor444(avg_color_0
);
243 sub_block_avg
[j
] = MakeColor444(avg_color_1
);
245 sub_block_avg
[i
] = avg_color_555_0
;
246 sub_block_avg
[j
] = avg_color_555_1
;
251 // Compute the error of each sub block before adjusting for luminance. These
252 // error values are later used for determining if we should flip the sub
254 uint32_t sub_block_err
[4] = {0};
255 for (unsigned int i
= 0; i
< 4; ++i
) {
256 for (unsigned int j
= 0; j
< 8; ++j
) {
257 sub_block_err
[i
] += GetColorError(sub_block_avg
[i
], sub_block_src
[i
][j
]);
262 sub_block_err
[2] + sub_block_err
[3] < sub_block_err
[0] + sub_block_err
[1];
264 // Clear destination buffer so that we can "or" in the results.
267 WriteDiff(dst
, use_differential
[!!flip
]);
268 WriteFlip(dst
, flip
);
270 uint8_t sub_block_off_0
= flip
? 2 : 0;
271 uint8_t sub_block_off_1
= sub_block_off_0
+ 1;
273 if (use_differential
[!!flip
]) {
274 WriteColors555(dst
, sub_block_avg
[sub_block_off_0
],
275 sub_block_avg
[sub_block_off_1
]);
277 WriteColors444(dst
, sub_block_avg
[sub_block_off_0
],
278 sub_block_avg
[sub_block_off_1
]);
281 // Compute luminance for the first sub block.
282 ComputeLuminance(dst
, sub_block_src
[sub_block_off_0
],
283 sub_block_avg
[sub_block_off_0
], 0,
284 g_idx_to_num
[sub_block_off_0
]);
285 // Compute luminance for the second sub block.
286 ComputeLuminance(dst
, sub_block_src
[sub_block_off_1
],
287 sub_block_avg
[sub_block_off_1
], 1,
288 g_idx_to_num
[sub_block_off_1
]);
293 void TextureCompressorETC1::Compress(const uint8_t* src
,
299 DCHECK_EQ((width
& 3), 0);
300 DCHECK_GE(height
, 4);
301 DCHECK_EQ((height
& 3), 0);
303 Color ver_blocks
[16];
304 Color hor_blocks
[16];
306 for (int y
= 0; y
< height
; y
+= 4, src
+= width
* 4 * 4) {
307 for (int x
= 0; x
< width
; x
+= 4, dst
+= 8) {
308 const Color
* row0
= reinterpret_cast<const Color
*>(src
+ x
* 4);
309 const Color
* row1
= row0
+ width
;
310 const Color
* row2
= row1
+ width
;
311 const Color
* row3
= row2
+ width
;
313 memcpy(ver_blocks
, row0
, 8);
314 memcpy(ver_blocks
+ 2, row1
, 8);
315 memcpy(ver_blocks
+ 4, row2
, 8);
316 memcpy(ver_blocks
+ 6, row3
, 8);
317 memcpy(ver_blocks
+ 8, row0
+ 2, 8);
318 memcpy(ver_blocks
+ 10, row1
+ 2, 8);
319 memcpy(ver_blocks
+ 12, row2
+ 2, 8);
320 memcpy(ver_blocks
+ 14, row3
+ 2, 8);
322 memcpy(hor_blocks
, row0
, 16);
323 memcpy(hor_blocks
+ 4, row1
, 16);
324 memcpy(hor_blocks
+ 8, row2
, 16);
325 memcpy(hor_blocks
+ 12, row3
, 16);
327 CompressBlock(dst
, ver_blocks
, hor_blocks
);