1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
4 * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 **************************************************************************/
27 // File: vogl_dxt_image.cpp
28 #include "vogl_core.h"
29 #include "vogl_dxt_image.h"
30 #if VOGL_SUPPORT_SQUISH
31 #include "squish\squish.h"
33 #include "vogl_ryg_dxt.hpp"
34 #include "vogl_dxt_fast.h"
35 #include "vogl_console.h"
36 #include "vogl_threading.h"
38 #if VOGL_SUPPORT_ATI_COMPRESS
40 #pragma comment(lib, "ATI_Compress_MT_DLL_VC8.lib")
42 #pragma comment(lib, "ATI_Compress_MT_VC8.lib")
44 #include "..\ext\ATI_Compress\ATI_Compress.h"
51 dxt_image::dxt_image()
59 m_num_elements_per_block(0),
63 utils::zero_object(m_element_type);
64 utils::zero_object(m_element_component_index);
67 dxt_image::dxt_image(const dxt_image &other)
73 dxt_image &dxt_image::operator=(const dxt_image &rhs)
80 m_width = rhs.m_width;
81 m_height = rhs.m_height;
82 m_blocks_x = rhs.m_blocks_x;
83 m_blocks_y = rhs.m_blocks_y;
84 m_num_elements_per_block = rhs.m_num_elements_per_block;
85 m_bytes_per_block = rhs.m_bytes_per_block;
86 m_format = rhs.m_format;
87 m_total_blocks = rhs.m_total_blocks;
88 m_total_elements = rhs.m_total_elements;
90 memcpy(m_element_type, rhs.m_element_type, sizeof(m_element_type));
91 memcpy(m_element_component_index, rhs.m_element_component_index, sizeof(m_element_component_index));
95 m_elements.resize(m_total_elements);
96 memcpy(&m_elements[0], rhs.m_pElements, sizeof(element) * m_total_elements);
97 m_pElements = &m_elements[0];
103 void dxt_image::clear()
110 m_num_elements_per_block = 0;
111 m_bytes_per_block = 0;
112 m_format = cDXTInvalid;
113 utils::zero_object(m_element_type);
114 utils::zero_object(m_element_component_index);
116 m_total_elements = 0;
120 bool dxt_image::init_internal(dxt_format fmt, uint width, uint height)
122 VOGL_ASSERT((fmt != cDXTInvalid) && (width > 0) && (height > 0));
129 m_blocks_x = (m_width + 3) >> cDXTBlockShift;
130 m_blocks_y = (m_height + 3) >> cDXTBlockShift;
132 m_num_elements_per_block = 2;
133 if ((fmt == cDXT1) || (fmt == cDXT1A) || (fmt == cDXT5A) || (fmt == cETC1))
134 m_num_elements_per_block = 1;
136 m_total_blocks = m_blocks_x * m_blocks_y;
137 m_total_elements = m_total_blocks * m_num_elements_per_block;
139 VOGL_ASSUME((uint)cDXT1BytesPerBlock == (uint)cETC1BytesPerBlock);
140 m_bytes_per_block = cDXT1BytesPerBlock * m_num_elements_per_block;
149 m_element_type[0] = cColorDXT1;
150 m_element_component_index[0] = -1;
155 m_element_type[0] = cAlphaDXT3;
156 m_element_type[1] = cColorDXT1;
157 m_element_component_index[0] = 3;
158 m_element_component_index[1] = -1;
163 m_element_type[0] = cAlphaDXT5;
164 m_element_type[1] = cColorDXT1;
165 m_element_component_index[0] = 3;
166 m_element_component_index[1] = -1;
171 m_element_type[0] = cAlphaDXT5;
172 m_element_component_index[0] = 3;
177 m_element_type[0] = cAlphaDXT5;
178 m_element_type[1] = cAlphaDXT5;
179 m_element_component_index[0] = 0;
180 m_element_component_index[1] = 1;
185 m_element_type[0] = cAlphaDXT5;
186 m_element_type[1] = cAlphaDXT5;
187 m_element_component_index[0] = 1;
188 m_element_component_index[1] = 0;
193 m_element_type[0] = cColorETC1;
194 m_element_component_index[0] = -1;
208 bool dxt_image::init(dxt_format fmt, uint width, uint height, bool clear_elements)
210 if (!init_internal(fmt, width, height))
213 m_elements.resize(m_total_elements);
214 m_pElements = &m_elements[0];
217 memset(m_pElements, 0, sizeof(element) * m_total_elements);
222 bool dxt_image::init(dxt_format fmt, uint width, uint height, uint num_elements, element *pElements, bool create_copy)
224 VOGL_ASSERT(num_elements && pElements);
226 if (!init_internal(fmt, width, height))
229 if (num_elements != m_total_elements)
237 m_elements.resize(m_total_elements);
238 m_pElements = &m_elements[0];
240 memcpy(m_pElements, pElements, m_total_elements * sizeof(element));
243 m_pElements = pElements;
248 struct init_task_params
251 const image_u8 *m_pImg;
252 const dxt_image::pack_params *m_pParams;
253 vogl_thread_id_t m_main_thread;
254 atomic32_t m_canceled;
257 void dxt_image::init_task(uint64_t data, void *pData_ptr)
259 const uint thread_index = static_cast<uint>(data);
260 init_task_params *pInit_params = static_cast<init_task_params *>(pData_ptr);
262 const image_u8 &img = *pInit_params->m_pImg;
263 const pack_params &p = *pInit_params->m_pParams;
264 const bool is_main_thread = (vogl_get_current_thread_id() == pInit_params->m_main_thread);
266 uint block_index = 0;
268 set_block_pixels_context optimizer_context;
269 int prev_progress_percentage = -1;
271 for (uint block_y = 0; block_y < m_blocks_y; block_y++)
273 const uint pixel_ofs_y = block_y * cDXTBlockSize;
275 for (uint block_x = 0; block_x < m_blocks_x; block_x++, block_index++)
277 if (pInit_params->m_canceled)
280 if (p.m_pProgress_callback && is_main_thread && ((block_index & 63) == 63))
282 const uint progress_percentage = p.m_progress_start + ((block_index * p.m_progress_range + get_total_blocks() / 2) / get_total_blocks());
283 if ((int)progress_percentage != prev_progress_percentage)
285 prev_progress_percentage = progress_percentage;
286 if (!(p.m_pProgress_callback)(progress_percentage, p.m_pProgress_callback_user_data_ptr))
288 atomic_exchange32(&pInit_params->m_canceled, VOGL_TRUE);
294 if (p.m_num_helper_threads)
296 if ((block_index % (p.m_num_helper_threads + 1)) != thread_index)
300 color_quad_u8 pixels[cDXTBlockSize * cDXTBlockSize];
302 const uint pixel_ofs_x = block_x * cDXTBlockSize;
304 for (uint y = 0; y < cDXTBlockSize; y++)
306 const uint iy = math::minimum(pixel_ofs_y + y, img.get_height() - 1);
308 for (uint x = 0; x < cDXTBlockSize; x++)
310 const uint ix = math::minimum(pixel_ofs_x + x, img.get_width() - 1);
312 pixels[x + y * cDXTBlockSize] = img(ix, iy);
316 set_block_pixels(block_x, block_y, pixels, p, optimizer_context);
321 #if VOGL_SUPPORT_ATI_COMPRESS
322 bool dxt_image::init_ati_compress(dxt_format fmt, const image_u8 &img, const pack_params &p)
324 image_u8 tmp_img(img);
325 for (uint y = 0; y < img.get_height(); y++)
327 for (uint x = 0; x < img.get_width(); x++)
329 color_quad_u8 c(img(x, y));
335 ATI_TC_Texture src_tex;
336 utils::zero_object(src_tex);
337 src_tex.dwSize = sizeof(ATI_TC_Texture);
338 src_tex.dwWidth = tmp_img.get_width();
339 src_tex.dwHeight = tmp_img.get_height();
340 src_tex.dwPitch = tmp_img.get_pitch_in_bytes();
341 src_tex.format = ATI_TC_FORMAT_ARGB_8888;
342 src_tex.dwDataSize = src_tex.dwPitch * tmp_img.get_height();
343 src_tex.pData = (ATI_TC_BYTE *)tmp_img.get_ptr();
345 ATI_TC_Texture dst_tex;
346 utils::zero_object(dst_tex);
347 dst_tex.dwSize = sizeof(ATI_TC_Texture);
348 dst_tex.dwWidth = tmp_img.get_width();
349 dst_tex.dwHeight = tmp_img.get_height();
350 dst_tex.dwDataSize = get_size_in_bytes();
351 dst_tex.pData = (ATI_TC_BYTE *)get_element_ptr();
357 dst_tex.format = ATI_TC_FORMAT_DXT1;
360 dst_tex.format = ATI_TC_FORMAT_DXT3;
363 dst_tex.format = ATI_TC_FORMAT_DXT5;
366 dst_tex.format = ATI_TC_FORMAT_ATI1N;
369 dst_tex.format = ATI_TC_FORMAT_ATI2N_XY;
372 dst_tex.format = ATI_TC_FORMAT_ATI2N;
381 ATI_TC_CompressOptions options;
382 utils::zero_object(options);
383 options.dwSize = sizeof(ATI_TC_CompressOptions);
387 options.bDXT1UseAlpha = true;
388 options.nAlphaThreshold = (ATI_TC_BYTE)p.m_dxt1a_alpha_threshold;
390 options.bDisableMultiThreading = (p.m_num_helper_threads == 0);
393 case cCRNDXTQualityFast:
394 options.nCompressionSpeed = ATI_TC_Speed_Fast;
396 case cCRNDXTQualitySuperFast:
397 options.nCompressionSpeed = ATI_TC_Speed_SuperFast;
400 options.nCompressionSpeed = ATI_TC_Speed_Normal;
406 options.bUseChannelWeighting = true;
407 options.fWeightingRed = .212671f;
408 options.fWeightingGreen = .715160f;
409 options.fWeightingBlue = .072169f;
412 ATI_TC_ERROR err = ATI_TC_ConvertTexture(&src_tex, &dst_tex, &options, NULL, NULL, NULL);
413 return err == ATI_TC_OK;
417 bool dxt_image::init(dxt_format fmt, const image_u8 &img, const pack_params &p)
419 if (!init(fmt, img.get_width(), img.get_height(), false))
422 #if VOGL_SUPPORT_ATI_COMPRESS
423 if (p.m_compressor == cCRNDXTCompressorATI)
424 return init_ati_compress(fmt, img, p);
427 task_pool *pPool = p.m_pTask_pool;
432 if (!tmp_pool.init(p.m_num_helper_threads))
437 init_task_params init_params;
438 init_params.m_fmt = fmt;
439 init_params.m_pImg = &img;
440 init_params.m_pParams = &p;
441 init_params.m_main_thread = vogl_get_current_thread_id();
442 init_params.m_canceled = false;
444 for (uint i = 0; i <= p.m_num_helper_threads; i++)
445 pPool->queue_object_task(this, &dxt_image::init_task, i, &init_params);
449 if (init_params.m_canceled)
455 bool dxt_image::unpack(image_u8 &img) const
457 if (!m_total_elements)
460 img.crop(m_width, m_height);
462 color_quad_u8 pixels[cDXTBlockSize * cDXTBlockSize];
463 for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
464 pixels[i].set(0, 0, 0, 255);
466 bool all_blocks_valid = true;
467 for (uint block_y = 0; block_y < m_blocks_y; block_y++)
469 const uint pixel_ofs_y = block_y * cDXTBlockSize;
470 const uint limit_y = math::minimum<uint>(cDXTBlockSize, img.get_height() - pixel_ofs_y);
472 for (uint block_x = 0; block_x < m_blocks_x; block_x++)
474 if (!get_block_pixels(block_x, block_y, pixels))
475 all_blocks_valid = false;
477 const uint pixel_ofs_x = block_x * cDXTBlockSize;
479 const uint limit_x = math::minimum<uint>(cDXTBlockSize, img.get_width() - pixel_ofs_x);
481 for (uint y = 0; y < limit_y; y++)
483 const uint iy = pixel_ofs_y + y;
485 for (uint x = 0; x < limit_x; x++)
487 const uint ix = pixel_ofs_x + x;
489 img(ix, iy) = pixels[x + (y << cDXTBlockShift)];
495 if (!all_blocks_valid)
496 console::error("dxt_image::unpack: One or more invalid blocks encountered!\n");
498 img.reset_comp_flags();
499 img.set_component_valid(0, false);
500 img.set_component_valid(1, false);
501 img.set_component_valid(2, false);
502 for (uint i = 0; i < m_num_elements_per_block; i++)
504 if (m_element_component_index[i] < 0)
506 img.set_component_valid(0, true);
507 img.set_component_valid(1, true);
508 img.set_component_valid(2, true);
511 img.set_component_valid(m_element_component_index[i], true);
514 img.set_component_valid(3, get_dxt_format_has_alpha(m_format));
519 void dxt_image::endian_swap()
521 utils::endian_switch_words(reinterpret_cast<uint16 *>(m_elements.get_ptr()), m_elements.size_in_bytes() / sizeof(uint16));
524 const dxt_image::element &dxt_image::get_element(uint block_x, uint block_y, uint element_index) const
526 VOGL_ASSERT((block_x < m_blocks_x) && (block_y < m_blocks_y) && (element_index < m_num_elements_per_block));
527 return m_pElements[(block_x + block_y * m_blocks_x) * m_num_elements_per_block + element_index];
530 dxt_image::element &dxt_image::get_element(uint block_x, uint block_y, uint element_index)
532 VOGL_ASSERT((block_x < m_blocks_x) && (block_y < m_blocks_y) && (element_index < m_num_elements_per_block));
533 return m_pElements[(block_x + block_y * m_blocks_x) * m_num_elements_per_block + element_index];
536 bool dxt_image::has_alpha() const
542 for (uint i = 0; i < m_total_elements; i++)
544 const dxt1_block &blk = *(dxt1_block *)&m_pElements[i];
546 if (blk.get_low_color() <= blk.get_high_color())
548 for (uint y = 0; y < cDXTBlockSize; y++)
549 for (uint x = 0; x < cDXTBlockSize; x++)
550 if (blk.get_selector(x, y) == 3)
569 color_quad_u8 dxt_image::get_pixel(uint x, uint y) const
571 VOGL_ASSERT((x < m_width) && (y < m_height));
573 const uint block_x = x >> cDXTBlockShift;
574 const uint block_y = y >> cDXTBlockShift;
576 const element *pElement = reinterpret_cast<const element *>(&get_element(block_x, block_y, 0));
578 color_quad_u8 result(0, 0, 0, 255);
580 for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
582 switch (m_element_type[element_index])
586 const etc1_block &block = *reinterpret_cast<const etc1_block *>(&get_element(block_x, block_y, element_index));
588 const bool diff_flag = block.get_diff_bit();
589 const bool flip_flag = block.get_flip_bit();
590 const uint table_index0 = block.get_inten_table(0);
591 const uint table_index1 = block.get_inten_table(1);
592 color_quad_u8 subblock_colors0[4], subblock_colors1[4];
596 const uint16 base_color5 = block.get_base5_color();
597 const uint16 delta_color3 = block.get_delta3_color();
598 etc1_block::get_diff_subblock_colors(subblock_colors0, base_color5, table_index0);
599 etc1_block::get_diff_subblock_colors(subblock_colors1, base_color5, delta_color3, table_index1);
603 const uint16 base_color4_0 = block.get_base4_color(0);
604 etc1_block::get_abs_subblock_colors(subblock_colors0, base_color4_0, table_index0);
605 const uint16 base_color4_1 = block.get_base4_color(1);
606 etc1_block::get_abs_subblock_colors(subblock_colors1, base_color4_1, table_index1);
609 const uint bx = x & 3;
610 const uint by = y & 3;
612 const uint selector_index = block.get_selector(bx, by);
616 result = subblock_colors0[selector_index];
618 result = subblock_colors1[selector_index];
623 result = subblock_colors0[selector_index];
625 result = subblock_colors1[selector_index];
632 const dxt1_block *pBlock = reinterpret_cast<const dxt1_block *>(&get_element(block_x, block_y, element_index));
634 const uint l = pBlock->get_low_color();
635 const uint h = pBlock->get_high_color();
637 color_quad_u8 c0(dxt1_block::unpack_color(static_cast<uint16>(l), true));
638 color_quad_u8 c1(dxt1_block::unpack_color(static_cast<uint16>(h), true));
640 const uint s = pBlock->get_selector(x & 3, y & 3);
647 result.set_noclamp_rgb(c0.r, c0.g, c0.b);
650 result.set_noclamp_rgb(c1.r, c1.g, c1.b);
653 result.set_noclamp_rgb((c0.r * 2 + c1.r) / 3, (c0.g * 2 + c1.g) / 3, (c0.b * 2 + c1.b) / 3);
656 result.set_noclamp_rgb((c1.r * 2 + c0.r) / 3, (c1.g * 2 + c0.g) / 3, (c1.b * 2 + c0.b) / 3);
665 result.set_noclamp_rgb(c0.r, c0.g, c0.b);
668 result.set_noclamp_rgb(c1.r, c1.g, c1.b);
671 result.set_noclamp_rgb((c0.r + c1.r) >> 1U, (c0.g + c1.g) >> 1U, (c0.b + c1.b) >> 1U);
675 if (m_format <= cDXT1A)
676 result.set_noclamp_rgba(0, 0, 0, 0);
678 result.set_noclamp_rgb(0, 0, 0);
688 const int comp_index = m_element_component_index[element_index];
690 const dxt5_block *pBlock = reinterpret_cast<const dxt5_block *>(&get_element(block_x, block_y, element_index));
692 const uint l = pBlock->get_low_alpha();
693 const uint h = pBlock->get_high_alpha();
695 const uint s = pBlock->get_selector(x & 3, y & 3);
702 result[comp_index] = static_cast<uint8>(l);
705 result[comp_index] = static_cast<uint8>(h);
708 result[comp_index] = static_cast<uint8>((l * 6 + h) / 7);
711 result[comp_index] = static_cast<uint8>((l * 5 + h * 2) / 7);
714 result[comp_index] = static_cast<uint8>((l * 4 + h * 3) / 7);
717 result[comp_index] = static_cast<uint8>((l * 3 + h * 4) / 7);
720 result[comp_index] = static_cast<uint8>((l * 2 + h * 5) / 7);
723 result[comp_index] = static_cast<uint8>((l + h * 6) / 7);
732 result[comp_index] = static_cast<uint8>(l);
735 result[comp_index] = static_cast<uint8>(h);
738 result[comp_index] = static_cast<uint8>((l * 4 + h) / 5);
741 result[comp_index] = static_cast<uint8>((l * 3 + h * 2) / 5);
744 result[comp_index] = static_cast<uint8>((l * 2 + h * 3) / 5);
747 result[comp_index] = static_cast<uint8>((l + h * 4) / 5);
750 result[comp_index] = 0;
753 result[comp_index] = 255;
762 const int comp_index = m_element_component_index[element_index];
764 const dxt3_block *pBlock = reinterpret_cast<const dxt3_block *>(&get_element(block_x, block_y, element_index));
766 result[comp_index] = static_cast<uint8>(pBlock->get_alpha(x & 3, y & 3, true));
778 uint dxt_image::get_pixel_alpha(uint x, uint y, uint element_index) const
780 VOGL_ASSERT((x < m_width) && (y < m_height) && (element_index < m_num_elements_per_block));
782 const uint block_x = x >> cDXTBlockShift;
783 const uint block_y = y >> cDXTBlockShift;
785 switch (m_element_type[element_index])
789 if (m_format <= cDXT1A)
791 const dxt1_block *pBlock = reinterpret_cast<const dxt1_block *>(&get_element(block_x, block_y, element_index));
793 const uint l = pBlock->get_low_color();
794 const uint h = pBlock->get_high_color();
798 uint s = pBlock->get_selector(x & 3, y & 3);
800 return (s == 3) ? 0 : 255;
812 const dxt5_block *pBlock = reinterpret_cast<const dxt5_block *>(&get_element(block_x, block_y, element_index));
814 const uint l = pBlock->get_low_alpha();
815 const uint h = pBlock->get_high_alpha();
817 const uint s = pBlock->get_selector(x & 3, y & 3);
828 return (l * 6 + h) / 7;
830 return (l * 5 + h * 2) / 7;
832 return (l * 4 + h * 3) / 7;
834 return (l * 3 + h * 4) / 7;
836 return (l * 2 + h * 5) / 7;
838 return (l + h * 6) / 7;
850 return (l * 4 + h) / 5;
852 return (l * 3 + h * 2) / 5;
854 return (l * 2 + h * 3) / 5;
856 return (l + h * 4) / 5;
866 const dxt3_block *pBlock = reinterpret_cast<const dxt3_block *>(&get_element(block_x, block_y, element_index));
868 return pBlock->get_alpha(x & 3, y & 3, true);
877 void dxt_image::set_pixel(uint x, uint y, const color_quad_u8 &c, bool perceptual)
879 VOGL_ASSERT((x < m_width) && (y < m_height));
881 const uint block_x = x >> cDXTBlockShift;
882 const uint block_y = y >> cDXTBlockShift;
884 element *pElement = &get_element(block_x, block_y, 0);
886 for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
888 switch (m_element_type[element_index])
892 etc1_block &block = *reinterpret_cast<etc1_block *>(&get_element(block_x, block_y, element_index));
894 const bool diff_flag = block.get_diff_bit();
895 const bool flip_flag = block.get_flip_bit();
896 const uint table_index0 = block.get_inten_table(0);
897 const uint table_index1 = block.get_inten_table(1);
898 color_quad_u8 subblock_colors0[4], subblock_colors1[4];
902 const uint16 base_color5 = block.get_base5_color();
903 const uint16 delta_color3 = block.get_delta3_color();
904 etc1_block::get_diff_subblock_colors(subblock_colors0, base_color5, table_index0);
905 etc1_block::get_diff_subblock_colors(subblock_colors1, base_color5, delta_color3, table_index1);
909 const uint16 base_color4_0 = block.get_base4_color(0);
910 etc1_block::get_abs_subblock_colors(subblock_colors0, base_color4_0, table_index0);
911 const uint16 base_color4_1 = block.get_base4_color(1);
912 etc1_block::get_abs_subblock_colors(subblock_colors1, base_color4_1, table_index1);
915 const uint bx = x & 3;
916 const uint by = y & 3;
918 color_quad_u8 *pColors = subblock_colors1;
922 pColors = subblock_colors0;
927 pColors = subblock_colors0;
930 uint best_error = UINT_MAX;
931 uint best_selector = 0;
933 for (uint i = 0; i < 4; i++)
935 uint error = color::color_distance(perceptual, pColors[i], c, false);
936 if (error < best_error)
943 block.set_selector(bx, by, best_selector);
948 dxt1_block *pDXT1_block = reinterpret_cast<dxt1_block *>(pElement);
950 color_quad_u8 colors[cDXT1SelectorValues];
951 const uint n = pDXT1_block->get_block_colors(colors, static_cast<uint16>(pDXT1_block->get_low_color()), static_cast<uint16>(pDXT1_block->get_high_color()));
953 if ((m_format == cDXT1A) && (c.a < 128))
954 pDXT1_block->set_selector(x & 3, y & 3, 3);
957 uint best_error = UINT_MAX;
958 uint best_selector = 0;
960 for (uint i = 0; i < n; i++)
962 uint error = color::color_distance(perceptual, colors[i], c, false);
963 if (error < best_error)
970 pDXT1_block->set_selector(x & 3, y & 3, best_selector);
977 dxt5_block *pDXT5_block = reinterpret_cast<dxt5_block *>(pElement);
979 uint values[cDXT5SelectorValues];
980 dxt5_block::get_block_values(values, pDXT5_block->get_low_alpha(), pDXT5_block->get_high_alpha());
982 const int comp_index = m_element_component_index[element_index];
984 uint best_error = UINT_MAX;
985 uint best_selector = 0;
987 for (uint i = 0; i < cDXT5SelectorValues; i++)
989 uint error = static_cast<uint>(labs(values[i]) - c[comp_index]); // no need to square
991 if (error < best_error)
998 pDXT5_block->set_selector(x & 3, y & 3, best_selector);
1004 const int comp_index = m_element_component_index[element_index];
1006 dxt3_block *pDXT3_block = reinterpret_cast<dxt3_block *>(pElement);
1008 pDXT3_block->set_alpha(x & 3, y & 3, c[comp_index], true);
1018 bool dxt_image::get_block_pixels(uint block_x, uint block_y, color_quad_u8 *pPixels) const
1020 bool success = true;
1021 const element *pElement = &get_element(block_x, block_y, 0);
1023 for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
1025 switch (m_element_type[element_index])
1029 const etc1_block &block = *reinterpret_cast<const etc1_block *>(&get_element(block_x, block_y, element_index));
1030 // Preserve alpha if the format is something weird (like ETC1 for color and DXT5A for alpha) - which isn't currently supported.
1031 if (!rg_etc1::unpack_etc1_block(&block, (uint32 *)pPixels, m_format != cETC1))
1038 const dxt1_block *pDXT1_block = reinterpret_cast<const dxt1_block *>(pElement);
1040 color_quad_u8 colors[cDXT1SelectorValues];
1041 pDXT1_block->get_block_colors(colors, static_cast<uint16>(pDXT1_block->get_low_color()), static_cast<uint16>(pDXT1_block->get_high_color()));
1043 for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1045 uint s = pDXT1_block->get_selector(i & 3, i >> 2);
1047 pPixels[i].r = colors[s].r;
1048 pPixels[i].g = colors[s].g;
1049 pPixels[i].b = colors[s].b;
1051 if (m_format <= cDXT1A)
1052 pPixels[i].a = colors[s].a;
1059 const dxt5_block *pDXT5_block = reinterpret_cast<const dxt5_block *>(pElement);
1061 uint values[cDXT5SelectorValues];
1062 dxt5_block::get_block_values(values, pDXT5_block->get_low_alpha(), pDXT5_block->get_high_alpha());
1064 const int comp_index = m_element_component_index[element_index];
1066 for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1068 uint s = pDXT5_block->get_selector(i & 3, i >> 2);
1070 pPixels[i][comp_index] = static_cast<uint8>(values[s]);
1077 const dxt3_block *pDXT3_block = reinterpret_cast<const dxt3_block *>(pElement);
1079 const int comp_index = m_element_component_index[element_index];
1081 for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1083 uint a = pDXT3_block->get_alpha(i & 3, i >> 2, true);
1085 pPixels[i][comp_index] = static_cast<uint8>(a);
1097 void dxt_image::set_block_pixels(uint block_x, uint block_y, const color_quad_u8 *pPixels, const pack_params &p)
1099 set_block_pixels_context context;
1100 set_block_pixels(block_x, block_y, pPixels, p, context);
1103 void dxt_image::set_block_pixels(
1104 uint block_x, uint block_y, const color_quad_u8 *pPixels, const pack_params &p,
1105 set_block_pixels_context &context)
1107 element *pElement = &get_element(block_x, block_y, 0);
1109 if (m_format == cETC1)
1111 etc1_block &dst_block = *reinterpret_cast<etc1_block *>(pElement);
1113 rg_etc1::etc1_quality etc_quality = rg_etc1::cHighQuality;
1114 if (p.m_quality <= cCRNDXTQualityFast)
1115 etc_quality = rg_etc1::cLowQuality;
1116 else if (p.m_quality <= cCRNDXTQualityNormal)
1117 etc_quality = rg_etc1::cMediumQuality;
1119 rg_etc1::etc1_pack_params pack_params;
1120 pack_params.m_dithering = p.m_dithering;
1121 //pack_params.m_perceptual = p.m_perceptual;
1122 pack_params.m_quality = etc_quality;
1123 rg_etc1::pack_etc1_block(&dst_block, (uint32 *)pPixels, pack_params);
1126 #if VOGL_SUPPORT_SQUISH
1127 if ((p.m_compressor == cCRNDXTCompressorSquish) && ((m_format == cDXT1) || (m_format == cDXT1A) || (m_format == cDXT3) || (m_format == cDXT5) || (m_format == cDXT5A)))
1129 uint squish_flags = 0;
1130 if ((m_format == cDXT1) || (m_format == cDXT1A))
1131 squish_flags = squish::kDxt1;
1132 else if (m_format == cDXT3)
1133 squish_flags = squish::kDxt3;
1134 else if (m_format == cDXT5A)
1135 squish_flags = squish::kDxt5A;
1137 squish_flags = squish::kDxt5;
1140 squish_flags |= squish::kColourMetricPerceptual;
1142 squish_flags |= squish::kColourMetricUniform;
1144 if (p.m_quality >= cCRNDXTQualityBetter)
1145 squish_flags |= squish::kColourIterativeClusterFit;
1146 else if (p.m_quality == cCRNDXTQualitySuperFast)
1147 squish_flags |= squish::kColourRangeFit;
1149 color_quad_u8 pixels[cDXTBlockSize * cDXTBlockSize];
1151 memcpy(pixels, pPixels, sizeof(color_quad_u8) * cDXTBlockSize * cDXTBlockSize);
1153 if (m_format == cDXT1)
1155 for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1158 else if (m_format == cDXT1A)
1160 for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1161 if (pixels[i].a < p.m_dxt1a_alpha_threshold)
1167 squish::Compress(reinterpret_cast<const squish::u8 *>(pixels), pElement, squish_flags);
1170 #endif // VOGL_SUPPORT_SQUISH
1171 // RYG doesn't support DXT1A
1172 if ((p.m_compressor == cCRNDXTCompressorRYG) && ((m_format == cDXT1) || (m_format == cDXT5) || (m_format == cDXT5A)))
1174 color_quad_u8 pixels[cDXTBlockSize * cDXTBlockSize];
1176 for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1178 pixels[i].r = pPixels[i].b;
1179 pixels[i].g = pPixels[i].g;
1180 pixels[i].b = pPixels[i].r;
1182 if (m_format == cDXT1)
1185 pixels[i].a = pPixels[i].a;
1188 if (m_format == cDXT5A)
1189 ryg_dxt::sCompressDXT5ABlock((sU8 *)pElement, (const sU32 *)pixels, 0);
1191 ryg_dxt::sCompressDXTBlock((sU8 *)pElement, (const sU32 *)pixels, m_format == cDXT5, 0);
1193 else if ((p.m_compressor == cCRNDXTCompressorCRNF) && (m_format != cDXT1A))
1195 for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
1197 switch (m_element_type[element_index])
1201 dxt1_block *pDXT1_block = reinterpret_cast<dxt1_block *>(pElement);
1202 dxt_fast::compress_color_block(pDXT1_block, pPixels, p.m_quality >= cCRNDXTQualityNormal);
1208 dxt5_block *pDXT5_block = reinterpret_cast<dxt5_block *>(pElement);
1209 dxt_fast::compress_alpha_block(pDXT5_block, pPixels, m_element_component_index[element_index]);
1215 const int comp_index = m_element_component_index[element_index];
1217 dxt3_block *pDXT3_block = reinterpret_cast<dxt3_block *>(pElement);
1219 for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1220 pDXT3_block->set_alpha(i & 3, i >> 2, pPixels[i][comp_index], true);
1231 dxt1_endpoint_optimizer &dxt1_optimizer = context.m_dxt1_optimizer;
1232 dxt5_endpoint_optimizer &dxt5_optimizer = context.m_dxt5_optimizer;
1234 for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
1236 switch (m_element_type[element_index])
1240 dxt1_block *pDXT1_block = reinterpret_cast<dxt1_block *>(pElement);
1242 bool pixels_have_alpha = false;
1243 if (m_format == cDXT1A)
1245 for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1246 if (pPixels[i].a < p.m_dxt1a_alpha_threshold)
1248 pixels_have_alpha = true;
1253 dxt1_endpoint_optimizer::results results;
1254 uint8 selectors[cDXTBlockSize * cDXTBlockSize];
1255 results.m_pSelectors = selectors;
1257 dxt1_endpoint_optimizer::params params;
1258 params.m_block_index = block_x + block_y * m_blocks_x;
1259 params.m_quality = p.m_quality;
1260 params.m_perceptual = p.m_perceptual;
1261 params.m_grayscale_sampling = p.m_grayscale_sampling;
1262 params.m_pixels_have_alpha = pixels_have_alpha;
1263 params.m_use_alpha_blocks = p.m_use_both_block_types;
1264 params.m_use_transparent_indices_for_black = p.m_use_transparent_indices_for_black;
1265 params.m_dxt1a_alpha_threshold = p.m_dxt1a_alpha_threshold;
1266 params.m_pPixels = pPixels;
1267 params.m_num_pixels = cDXTBlockSize * cDXTBlockSize;
1268 params.m_endpoint_caching = p.m_endpoint_caching;
1269 params.m_color_weights[0] = p.m_color_weights[0];
1270 params.m_color_weights[1] = p.m_color_weights[1];
1271 params.m_color_weights[2] = p.m_color_weights[2];
1273 if ((m_format != cDXT1) && (m_format != cDXT1A))
1274 params.m_use_alpha_blocks = false;
1276 if (!dxt1_optimizer.compute(params, results))
1282 pDXT1_block->set_low_color(results.m_low_color);
1283 pDXT1_block->set_high_color(results.m_high_color);
1285 for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1286 pDXT1_block->set_selector(i & 3, i >> 2, selectors[i]);
1292 dxt5_block *pDXT5_block = reinterpret_cast<dxt5_block *>(pElement);
1294 dxt5_endpoint_optimizer::results results;
1296 uint8 selectors[cDXTBlockSize * cDXTBlockSize];
1297 results.m_pSelectors = selectors;
1299 dxt5_endpoint_optimizer::params params;
1300 params.m_block_index = block_x + block_y * m_blocks_x;
1301 params.m_pPixels = pPixels;
1302 params.m_num_pixels = cDXTBlockSize * cDXTBlockSize;
1303 params.m_comp_index = m_element_component_index[element_index];
1304 params.m_quality = p.m_quality;
1305 params.m_use_both_block_types = p.m_use_both_block_types;
1307 if (!dxt5_optimizer.compute(params, results))
1313 pDXT5_block->set_low_alpha(results.m_first_endpoint);
1314 pDXT5_block->set_high_alpha(results.m_second_endpoint);
1316 for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1317 pDXT5_block->set_selector(i & 3, i >> 2, selectors[i]);
1323 const int comp_index = m_element_component_index[element_index];
1325 dxt3_block *pDXT3_block = reinterpret_cast<dxt3_block *>(pElement);
1327 for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1328 pDXT3_block->set_alpha(i & 3, i >> 2, pPixels[i][comp_index], true);
1339 void dxt_image::get_block_endpoints(uint block_x, uint block_y, uint element_index, uint &packed_low_endpoint, uint &packed_high_endpoint) const
1341 const element &block = get_element(block_x, block_y, element_index);
1343 switch (m_element_type[element_index])
1347 const etc1_block &src_block = *reinterpret_cast<const etc1_block *>(&block);
1348 if (src_block.get_diff_bit())
1350 packed_low_endpoint = src_block.get_base5_color();
1351 packed_high_endpoint = src_block.get_delta3_color();
1355 packed_low_endpoint = src_block.get_base4_color(0);
1356 packed_high_endpoint = src_block.get_base4_color(1);
1363 const dxt1_block &block1 = *reinterpret_cast<const dxt1_block *>(&block);
1365 packed_low_endpoint = block1.get_low_color();
1366 packed_high_endpoint = block1.get_high_color();
1372 const dxt5_block &block5 = *reinterpret_cast<const dxt5_block *>(&block);
1374 packed_low_endpoint = block5.get_low_alpha();
1375 packed_high_endpoint = block5.get_high_alpha();
1381 packed_low_endpoint = 0;
1382 packed_high_endpoint = 255;
1391 int dxt_image::get_block_endpoints(uint block_x, uint block_y, uint element_index, color_quad_u8 &low_endpoint, color_quad_u8 &high_endpoint, bool scaled) const
1394 get_block_endpoints(block_x, block_y, element_index, l, h);
1396 switch (m_element_type[element_index])
1400 const etc1_block &src_block = *reinterpret_cast<const etc1_block *>(&get_element(block_x, block_y, element_index));
1401 if (src_block.get_diff_bit())
1403 low_endpoint = etc1_block::unpack_color5(static_cast<uint16>(l), scaled);
1404 etc1_block::unpack_color5(high_endpoint, static_cast<uint16>(l), static_cast<uint16>(h), scaled);
1408 low_endpoint = etc1_block::unpack_color4(static_cast<uint16>(l), scaled);
1409 high_endpoint = etc1_block::unpack_color4(static_cast<uint16>(h), scaled);
1418 dxt1_block::unpack_color(r, g, b, static_cast<uint16>(l), scaled);
1419 low_endpoint.r = static_cast<uint8>(r);
1420 low_endpoint.g = static_cast<uint8>(g);
1421 low_endpoint.b = static_cast<uint8>(b);
1423 dxt1_block::unpack_color(r, g, b, static_cast<uint16>(h), scaled);
1424 high_endpoint.r = static_cast<uint8>(r);
1425 high_endpoint.g = static_cast<uint8>(g);
1426 high_endpoint.b = static_cast<uint8>(b);
1432 const int component = m_element_component_index[element_index];
1434 low_endpoint[component] = static_cast<uint8>(l);
1435 high_endpoint[component] = static_cast<uint8>(h);
1441 const int component = m_element_component_index[element_index];
1443 low_endpoint[component] = static_cast<uint8>(l);
1444 high_endpoint[component] = static_cast<uint8>(h);
1455 uint dxt_image::get_block_colors(uint block_x, uint block_y, uint element_index, color_quad_u8 *pColors, uint subblock_index)
1457 const element &block = get_element(block_x, block_y, element_index);
1459 switch (m_element_type[element_index])
1463 const etc1_block &src_block = *reinterpret_cast<const etc1_block *>(&get_element(block_x, block_y, element_index));
1464 const uint table_index0 = src_block.get_inten_table(0);
1465 const uint table_index1 = src_block.get_inten_table(1);
1466 if (src_block.get_diff_bit())
1468 const uint16 base_color5 = src_block.get_base5_color();
1469 const uint16 delta_color3 = src_block.get_delta3_color();
1471 etc1_block::get_diff_subblock_colors(pColors, base_color5, delta_color3, table_index1);
1473 etc1_block::get_diff_subblock_colors(pColors, base_color5, table_index0);
1479 const uint16 base_color4_1 = src_block.get_base4_color(1);
1480 etc1_block::get_abs_subblock_colors(pColors, base_color4_1, table_index1);
1484 const uint16 base_color4_0 = src_block.get_base4_color(0);
1485 etc1_block::get_abs_subblock_colors(pColors, base_color4_0, table_index0);
1493 const dxt1_block &block1 = *reinterpret_cast<const dxt1_block *>(&block);
1494 return dxt1_block::get_block_colors(pColors, static_cast<uint16>(block1.get_low_color()), static_cast<uint16>(block1.get_high_color()));
1498 const dxt5_block &block5 = *reinterpret_cast<const dxt5_block *>(&block);
1500 uint values[cDXT5SelectorValues];
1502 const uint n = dxt5_block::get_block_values(values, block5.get_low_alpha(), block5.get_high_alpha());
1504 const int comp_index = m_element_component_index[element_index];
1505 for (uint i = 0; i < n; i++)
1506 pColors[i][comp_index] = static_cast<uint8>(values[i]);
1512 const int comp_index = m_element_component_index[element_index];
1513 for (uint i = 0; i < 16; i++)
1514 pColors[i][comp_index] = static_cast<uint8>((i << 4) | i);
1525 uint dxt_image::get_subblock_index(uint x, uint y, uint element_index) const
1527 if (m_element_type[element_index] != cColorETC1)
1530 const uint block_x = x >> cDXTBlockShift;
1531 const uint block_y = y >> cDXTBlockShift;
1533 const element &block = get_element(block_x, block_y, element_index);
1535 const etc1_block &src_block = *reinterpret_cast<const etc1_block *>(&block);
1536 if (src_block.get_flip_bit())
1538 return ((y & 3) >= 2) ? 1 : 0;
1542 return ((x & 3) >= 2) ? 1 : 0;
1546 uint dxt_image::get_total_subblocks(uint element_index) const
1548 return (m_element_type[element_index] == cColorETC1) ? 2 : 0;
1551 uint dxt_image::get_selector(uint x, uint y, uint element_index) const
1553 VOGL_ASSERT((x < m_width) && (y < m_height));
1555 const uint block_x = x >> cDXTBlockShift;
1556 const uint block_y = y >> cDXTBlockShift;
1558 const element &block = get_element(block_x, block_y, element_index);
1560 switch (m_element_type[element_index])
1564 const etc1_block &src_block = *reinterpret_cast<const etc1_block *>(&block);
1565 return src_block.get_selector(x & 3, y & 3);
1569 const dxt1_block &block1 = *reinterpret_cast<const dxt1_block *>(&block);
1570 return block1.get_selector(x & 3, y & 3);
1574 const dxt5_block &block5 = *reinterpret_cast<const dxt5_block *>(&block);
1575 return block5.get_selector(x & 3, y & 3);
1579 const dxt3_block &block3 = *reinterpret_cast<const dxt3_block *>(&block);
1580 return block3.get_alpha(x & 3, y & 3, false);
1589 void dxt_image::change_dxt1_to_dxt1a()
1591 if (m_format == cDXT1)
1595 void dxt_image::flip_col(uint x)
1597 const uint other_x = (m_blocks_x - 1) - x;
1598 for (uint y = 0; y < m_blocks_y; y++)
1600 for (uint e = 0; e < get_elements_per_block(); e++)
1602 element tmp[2] = { get_element(x, y, e), get_element(other_x, y, e) };
1604 for (uint i = 0; i < 2; i++)
1606 switch (get_element_type(e))
1609 reinterpret_cast<dxt1_block *>(&tmp[i])->flip_x();
1612 reinterpret_cast<dxt3_block *>(&tmp[i])->flip_x();
1615 reinterpret_cast<dxt5_block *>(&tmp[i])->flip_x();
1623 get_element(x, y, e) = tmp[1];
1624 get_element(other_x, y, e) = tmp[0];
1629 void dxt_image::flip_row(uint y)
1631 const uint other_y = (m_blocks_y - 1) - y;
1632 for (uint x = 0; x < m_blocks_x; x++)
1634 for (uint e = 0; e < get_elements_per_block(); e++)
1636 element tmp[2] = { get_element(x, y, e), get_element(x, other_y, e) };
1638 for (uint i = 0; i < 2; i++)
1640 switch (get_element_type(e))
1643 reinterpret_cast<dxt1_block *>(&tmp[i])->flip_y();
1646 reinterpret_cast<dxt3_block *>(&tmp[i])->flip_y();
1649 reinterpret_cast<dxt5_block *>(&tmp[i])->flip_y();
1657 get_element(x, y, e) = tmp[1];
1658 get_element(x, other_y, e) = tmp[0];
1663 bool dxt_image::can_flip(uint axis_index)
1665 if (m_format == cETC1)
1667 // Can't reliably flip ETC1 textures (because of asymmetry in the 555/333 differential coding of subblock colors).
1686 bool dxt_image::flip_x()
1688 if (m_format == cETC1)
1690 // Can't reliably flip ETC1 textures (because of asymmetry in the 555/333 differential coding of subblock colors).
1694 if ((m_width & 3) && (m_width > 4))
1700 const uint mid_x = m_blocks_x / 2;
1702 for (uint x = 0; x < mid_x; x++)
1707 const uint w = math::minimum(m_width, 4U);
1708 for (uint y = 0; y < m_blocks_y; y++)
1710 for (uint e = 0; e < get_elements_per_block(); e++)
1712 element tmp(get_element(mid_x, y, e));
1713 switch (get_element_type(e))
1716 reinterpret_cast<dxt1_block *>(&tmp)->flip_x(w, 4);
1719 reinterpret_cast<dxt3_block *>(&tmp)->flip_x(w, 4);
1722 reinterpret_cast<dxt5_block *>(&tmp)->flip_x(w, 4);
1728 get_element(mid_x, y, e) = tmp;
1736 bool dxt_image::flip_y()
1738 if (m_format == cETC1)
1740 // Can't reliably flip ETC1 textures (because of asymmetry in the 555/333 differential coding of subblock colors).
1744 if ((m_height & 3) && (m_height > 4))
1750 const uint mid_y = m_blocks_y / 2;
1752 for (uint y = 0; y < mid_y; y++)
1757 const uint h = math::minimum(m_height, 4U);
1758 for (uint x = 0; x < m_blocks_x; x++)
1760 for (uint e = 0; e < get_elements_per_block(); e++)
1762 element tmp(get_element(x, mid_y, e));
1763 switch (get_element_type(e))
1766 reinterpret_cast<dxt1_block *>(&tmp)->flip_y(4, h);
1769 reinterpret_cast<dxt3_block *>(&tmp)->flip_y(4, h);
1772 reinterpret_cast<dxt5_block *>(&tmp)->flip_y(4, h);
1778 get_element(x, mid_y, e) = tmp;