3030
3131#include " image_compress_basisu.h"
3232
33+ #include " core/os/os.h"
34+ #include " core/string/print_string.h"
3335#include " servers/rendering_server.h"
3436
3537#include < transcoder/basisu_transcoder.h>
@@ -46,9 +48,48 @@ void basis_universal_init() {
4648}
4749
4850#ifdef TOOLS_ENABLED
51+ template <typename T>
52+ inline void _basisu_pad_mipmap (const uint8_t *p_image_mip_data, Vector<uint8_t > &r_mip_data_padded, int p_next_width, int p_next_height, int p_width, int p_height, int64_t p_size) {
53+ // Source mip's data interpreted as 32-bit RGBA blocks to help with copying pixel data.
54+ const T *mip_src_data = reinterpret_cast <const T *>(p_image_mip_data);
55+
56+ // Reserve space in the padded buffer.
57+ r_mip_data_padded.resize (p_next_width * p_next_height * sizeof (T));
58+ T *data_padded_ptr = reinterpret_cast <T *>(r_mip_data_padded.ptrw ());
59+
60+ // Pad mipmap to the nearest block by smearing.
61+ int x = 0 , y = 0 ;
62+ for (y = 0 ; y < p_height; y++) {
63+ for (x = 0 ; x < p_width; x++) {
64+ data_padded_ptr[p_next_width * y + x] = mip_src_data[p_width * y + x];
65+ }
66+
67+ // First, smear in x.
68+ for (; x < p_next_width; x++) {
69+ data_padded_ptr[p_next_width * y + x] = data_padded_ptr[p_next_width * y + x - 1 ];
70+ }
71+ }
72+
73+ // Then, smear in y.
74+ for (; y < p_next_height; y++) {
75+ for (x = 0 ; x < p_next_width; x++) {
76+ data_padded_ptr[p_next_width * y + x] = data_padded_ptr[p_next_width * y + x - p_next_width];
77+ }
78+ }
79+ }
80+
4981Vector<uint8_t > basis_universal_packer (const Ref<Image> &p_image, Image::UsedChannels p_channels) {
82+ uint64_t start_time = OS::get_singleton ()->get_ticks_msec ();
83+
5084 Ref<Image> image = p_image->duplicate ();
51- image->convert (Image::FORMAT_RGBA8);
85+ bool is_hdr = false ;
86+
87+ if (image->get_format () <= Image::FORMAT_RGB565) {
88+ image->convert (Image::FORMAT_RGBA8);
89+ } else if (image->get_format () <= Image::FORMAT_RGBE9995) {
90+ image->convert (Image::FORMAT_RGBAF);
91+ is_hdr = true ;
92+ }
5293
5394 basisu::basis_compressor_params params;
5495
@@ -74,32 +115,42 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
74115 basisu::job_pool job_pool (OS::get_singleton ()->get_processor_count ());
75116 params.m_pJob_pool = &job_pool;
76117
77- BasisDecompressFormat decompress_format = BASIS_DECOMPRESS_RG;
78- switch (p_channels) {
79- case Image::USED_CHANNELS_L: {
80- decompress_format = BASIS_DECOMPRESS_RGB;
81- } break ;
82- case Image::USED_CHANNELS_LA: {
83- params.m_force_alpha = true ;
84- decompress_format = BASIS_DECOMPRESS_RGBA;
85- } break ;
86- case Image::USED_CHANNELS_R: {
87- decompress_format = BASIS_DECOMPRESS_R;
88- } break ;
89- case Image::USED_CHANNELS_RG: {
90- params.m_force_alpha = true ;
91- image->convert_rg_to_ra_rgba8 ();
92- decompress_format = BASIS_DECOMPRESS_RG;
93- } break ;
94- case Image::USED_CHANNELS_RGB: {
95- decompress_format = BASIS_DECOMPRESS_RGB;
96- } break ;
97- case Image::USED_CHANNELS_RGBA: {
98- params.m_force_alpha = true ;
99- decompress_format = BASIS_DECOMPRESS_RGBA;
100- } break ;
118+ BasisDecompressFormat decompress_format = BASIS_DECOMPRESS_MAX;
119+
120+ if (is_hdr) {
121+ decompress_format = BASIS_DECOMPRESS_HDR_RGB;
122+ params.m_hdr = true ;
123+ params.m_uastc_hdr_options .set_quality_level (0 );
124+
125+ } else {
126+ switch (p_channels) {
127+ case Image::USED_CHANNELS_L: {
128+ decompress_format = BASIS_DECOMPRESS_RGB;
129+ } break ;
130+ case Image::USED_CHANNELS_LA: {
131+ params.m_force_alpha = true ;
132+ decompress_format = BASIS_DECOMPRESS_RGBA;
133+ } break ;
134+ case Image::USED_CHANNELS_R: {
135+ decompress_format = BASIS_DECOMPRESS_R;
136+ } break ;
137+ case Image::USED_CHANNELS_RG: {
138+ params.m_force_alpha = true ;
139+ image->convert_rg_to_ra_rgba8 ();
140+ decompress_format = BASIS_DECOMPRESS_RG;
141+ } break ;
142+ case Image::USED_CHANNELS_RGB: {
143+ decompress_format = BASIS_DECOMPRESS_RGB;
144+ } break ;
145+ case Image::USED_CHANNELS_RGBA: {
146+ params.m_force_alpha = true ;
147+ decompress_format = BASIS_DECOMPRESS_RGBA;
148+ } break ;
149+ }
101150 }
102151
152+ ERR_FAIL_COND_V (decompress_format == BASIS_DECOMPRESS_MAX, Vector<uint8_t >());
153+
103154 // Copy the source image data with mipmaps into BasisU.
104155 {
105156 const int orig_width = image->get_width ();
@@ -113,9 +164,10 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
113164
114165 Vector<uint8_t > image_data = image->get_data ();
115166 basisu::vector<basisu::image> basisu_mipmaps;
167+ basisu::vector<basisu::imagef> basisu_mipmaps_hdr;
116168
117169 // Buffer for storing padded mipmap data.
118- Vector<uint32_t > mip_data_padded;
170+ Vector<uint8_t > mip_data_padded;
119171
120172 for (int32_t i = 0 ; i <= image->get_mipmap_count (); i++) {
121173 int64_t ofs, size;
@@ -126,31 +178,10 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
126178
127179 // Pad the mipmap's data if its resolution isn't divisible by 4.
128180 if (image->has_mipmaps () && !is_res_div_4 && (width > 2 && height > 2 ) && (width != next_width || height != next_height)) {
129- // Source mip's data interpreted as 32-bit RGBA blocks to help with copying pixel data.
130- const uint32_t *mip_src_data = reinterpret_cast <const uint32_t *>(image_mip_data);
131-
132- // Reserve space in the padded buffer.
133- mip_data_padded.resize (next_width * next_height);
134- uint32_t *data_padded_ptr = mip_data_padded.ptrw ();
135-
136- // Pad mipmap to the nearest block by smearing.
137- int x = 0 , y = 0 ;
138- for (y = 0 ; y < height; y++) {
139- for (x = 0 ; x < width; x++) {
140- data_padded_ptr[next_width * y + x] = mip_src_data[width * y + x];
141- }
142-
143- // First, smear in x.
144- for (; x < next_width; x++) {
145- data_padded_ptr[next_width * y + x] = data_padded_ptr[next_width * y + x - 1 ];
146- }
147- }
148-
149- // Then, smear in y.
150- for (; y < next_height; y++) {
151- for (x = 0 ; x < next_width; x++) {
152- data_padded_ptr[next_width * y + x] = data_padded_ptr[next_width * y + x - next_width];
153- }
181+ if (is_hdr) {
182+ _basisu_pad_mipmap<BasisRGBAF>(image_mip_data, mip_data_padded, next_width, next_height, width, height, size);
183+ } else {
184+ _basisu_pad_mipmap<uint32_t >(image_mip_data, mip_data_padded, next_width, next_height, width, height, size);
154185 }
155186
156187 // Override the image_mip_data pointer with our temporary Vector.
@@ -159,52 +190,69 @@ Vector<uint8_t> basis_universal_packer(const Ref<Image> &p_image, Image::UsedCha
159190 // Override the mipmap's properties.
160191 width = next_width;
161192 height = next_height;
162- size = mip_data_padded.size () * 4 ;
193+ size = mip_data_padded.size ();
163194 }
164195
165196 // Get the next mipmap's resolution.
166197 next_width /= 2 ;
167198 next_height /= 2 ;
168199
169200 // Copy the source mipmap's data to a BasisU image.
170- basisu::image basisu_image (width, height);
171- memcpy (basisu_image.get_ptr (), image_mip_data, size);
201+ if (is_hdr) {
202+ basisu::imagef basisu_image (width, height);
203+ memcpy (reinterpret_cast <uint8_t *>(basisu_image.get_ptr ()), image_mip_data, size);
204+
205+ if (i == 0 ) {
206+ params.m_source_images_hdr .push_back (basisu_image);
207+ } else {
208+ basisu_mipmaps_hdr.push_back (basisu_image);
209+ }
172210
173- if (i == 0 ) {
174- params.m_source_images .push_back (basisu_image);
175211 } else {
176- basisu_mipmaps.push_back (basisu_image);
212+ basisu::image basisu_image (width, height);
213+ memcpy (basisu_image.get_ptr (), image_mip_data, size);
214+
215+ if (i == 0 ) {
216+ params.m_source_images .push_back (basisu_image);
217+ } else {
218+ basisu_mipmaps.push_back (basisu_image);
219+ }
177220 }
178221 }
179222
180- params.m_source_mipmap_images .push_back (basisu_mipmaps);
223+ if (is_hdr) {
224+ params.m_source_mipmap_images_hdr .push_back (basisu_mipmaps_hdr);
225+ } else {
226+ params.m_source_mipmap_images .push_back (basisu_mipmaps);
227+ }
181228 }
182229
183230 // Encode the image data.
184- Vector<uint8_t > basisu_data;
185-
186231 basisu::basis_compressor compressor;
187232 compressor.init (params);
188233
189234 int basisu_err = compressor.process ();
190- ERR_FAIL_COND_V (basisu_err != basisu::basis_compressor::cECSuccess, basisu_data );
235+ ERR_FAIL_COND_V (basisu_err != basisu::basis_compressor::cECSuccess, Vector< uint8_t >() );
191236
192- const basisu::uint8_vec &basisu_out = compressor.get_output_basis_file ();
193- basisu_data.resize (basisu_out.size () + 4 );
237+ const basisu::uint8_vec &basisu_encoded = compressor.get_output_basis_file ();
194238
195- // Copy the encoded data to the buffer.
196- {
197- uint8_t *wb = basisu_data.ptrw ();
198- *(uint32_t *)wb = decompress_format;
239+ Vector<uint8_t > basisu_data;
240+ basisu_data.resize (basisu_encoded.size () + 4 );
241+ uint8_t *basisu_data_ptr = basisu_data.ptrw ();
199242
200- memcpy (wb + 4 , basisu_out.get_ptr (), basisu_out.size ());
201- }
243+ // Copy the encoded BasisU data into the output buffer.
244+ *(uint32_t *)basisu_data_ptr = decompress_format;
245+ memcpy (basisu_data_ptr + 4 , basisu_encoded.get_ptr (), basisu_encoded.size ());
246+
247+ print_verbose (vformat (" BasisU: Encoding a %dx%d image with %d mipmaps took %d ms." , p_image->get_width (), p_image->get_height (), p_image->get_mipmap_count (), OS::get_singleton ()->get_ticks_msec () - start_time));
202248
203249 return basisu_data;
204250}
205251#endif // TOOLS_ENABLED
206252
207253Ref<Image> basis_universal_unpacker_ptr (const uint8_t *p_data, int p_size) {
254+ uint64_t start_time = OS::get_singleton ()->get_ticks_msec ();
255+
208256 Ref<Image> image;
209257 ERR_FAIL_NULL_V_MSG (p_data, image, " Cannot unpack invalid BasisUniversal data." );
210258
@@ -320,6 +368,23 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
320368 }
321369
322370 } break ;
371+ case BASIS_DECOMPRESS_HDR_RGB: {
372+ if (bptc_supported) {
373+ basisu_format = basist::transcoder_texture_format::cTFBC6H;
374+ image_format = Image::FORMAT_BPTC_RGBFU;
375+ } else if (astc_supported) {
376+ basisu_format = basist::transcoder_texture_format::cTFASTC_HDR_4x4_RGBA;
377+ image_format = Image::FORMAT_ASTC_4x4_HDR;
378+ } else {
379+ // No supported VRAM compression formats, decompress.
380+ basisu_format = basist::transcoder_texture_format::cTFRGB_9E5;
381+ image_format = Image::FORMAT_RGBE9995;
382+ }
383+
384+ } break ;
385+ default : {
386+ ERR_FAIL_V (image);
387+ } break ;
323388 }
324389
325390 src_ptr += 4 ;
@@ -371,6 +436,9 @@ Ref<Image> basis_universal_unpacker_ptr(const uint8_t *p_data, int p_size) {
371436 }
372437 }
373438
439+ print_verbose (vformat (" BasisU: Transcoding a %dx%d image with %d mipmaps into %s took %d ms." ,
440+ image->get_width (), image->get_height (), image->get_mipmap_count (), Image::get_format_name (image_format), OS::get_singleton ()->get_ticks_msec () - start_time));
441+
374442 return image;
375443}
376444
0 commit comments