]> git.cworth.org Git - vogl/blob - src/voglcore/vogl_dxt_image.cpp
Initial vogl checkin
[vogl] / src / voglcore / vogl_dxt_image.cpp
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
5  * All Rights Reserved.
6  *
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:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
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
23  * THE SOFTWARE.
24  *
25  **************************************************************************/
26
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"
32 #endif
33 #include "vogl_ryg_dxt.hpp"
34 #include "vogl_dxt_fast.h"
35 #include "vogl_console.h"
36 #include "vogl_threading.h"
37
38 #if VOGL_SUPPORT_ATI_COMPRESS
39 #ifdef _DLL
40 #pragma comment(lib, "ATI_Compress_MT_DLL_VC8.lib")
41 #else
42 #pragma comment(lib, "ATI_Compress_MT_VC8.lib")
43 #endif
44 #include "..\ext\ATI_Compress\ATI_Compress.h"
45 #endif
46
47 #include "vogl_etc.h"
48
49 namespace vogl
50 {
51     dxt_image::dxt_image()
52         : m_pElements(NULL),
53           m_width(0),
54           m_height(0),
55           m_blocks_x(0),
56           m_blocks_y(0),
57           m_total_blocks(0),
58           m_total_elements(0),
59           m_num_elements_per_block(0),
60           m_bytes_per_block(0),
61           m_format(cDXTInvalid)
62     {
63         utils::zero_object(m_element_type);
64         utils::zero_object(m_element_component_index);
65     }
66
67     dxt_image::dxt_image(const dxt_image &other)
68         : m_pElements(NULL)
69     {
70         *this = other;
71     }
72
73     dxt_image &dxt_image::operator=(const dxt_image &rhs)
74     {
75         if (this == &rhs)
76             return *this;
77
78         clear();
79
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;
89         m_pElements = NULL;
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));
92
93         if (rhs.m_pElements)
94         {
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];
98         }
99
100         return *this;
101     }
102
103     void dxt_image::clear()
104     {
105         m_elements.clear();
106         m_width = 0;
107         m_height = 0;
108         m_blocks_x = 0;
109         m_blocks_y = 0;
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);
115         m_total_blocks = 0;
116         m_total_elements = 0;
117         m_pElements = NULL;
118     }
119
120     bool dxt_image::init_internal(dxt_format fmt, uint width, uint height)
121     {
122         VOGL_ASSERT((fmt != cDXTInvalid) && (width > 0) && (height > 0));
123
124         clear();
125
126         m_width = width;
127         m_height = height;
128
129         m_blocks_x = (m_width + 3) >> cDXTBlockShift;
130         m_blocks_y = (m_height + 3) >> cDXTBlockShift;
131
132         m_num_elements_per_block = 2;
133         if ((fmt == cDXT1) || (fmt == cDXT1A) || (fmt == cDXT5A) || (fmt == cETC1))
134             m_num_elements_per_block = 1;
135
136         m_total_blocks = m_blocks_x * m_blocks_y;
137         m_total_elements = m_total_blocks * m_num_elements_per_block;
138
139         VOGL_ASSUME((uint)cDXT1BytesPerBlock == (uint)cETC1BytesPerBlock);
140         m_bytes_per_block = cDXT1BytesPerBlock * m_num_elements_per_block;
141
142         m_format = fmt;
143
144         switch (m_format)
145         {
146             case cDXT1:
147             case cDXT1A:
148             {
149                 m_element_type[0] = cColorDXT1;
150                 m_element_component_index[0] = -1;
151                 break;
152             }
153             case cDXT3:
154             {
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;
159                 break;
160             }
161             case cDXT5:
162             {
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;
167                 break;
168             }
169             case cDXT5A:
170             {
171                 m_element_type[0] = cAlphaDXT5;
172                 m_element_component_index[0] = 3;
173                 break;
174             }
175             case cDXN_XY:
176             {
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;
181                 break;
182             }
183             case cDXN_YX:
184             {
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;
189                 break;
190             }
191             case cETC1:
192             {
193                 m_element_type[0] = cColorETC1;
194                 m_element_component_index[0] = -1;
195                 break;
196             }
197             default:
198             {
199                 VOGL_ASSERT_ALWAYS;
200                 clear();
201                 return false;
202             }
203         }
204
205         return true;
206     }
207
208     bool dxt_image::init(dxt_format fmt, uint width, uint height, bool clear_elements)
209     {
210         if (!init_internal(fmt, width, height))
211             return false;
212
213         m_elements.resize(m_total_elements);
214         m_pElements = &m_elements[0];
215
216         if (clear_elements)
217             memset(m_pElements, 0, sizeof(element) * m_total_elements);
218
219         return true;
220     }
221
222     bool dxt_image::init(dxt_format fmt, uint width, uint height, uint num_elements, element *pElements, bool create_copy)
223     {
224         VOGL_ASSERT(num_elements && pElements);
225
226         if (!init_internal(fmt, width, height))
227             return false;
228
229         if (num_elements != m_total_elements)
230         {
231             clear();
232             return false;
233         }
234
235         if (create_copy)
236         {
237             m_elements.resize(m_total_elements);
238             m_pElements = &m_elements[0];
239
240             memcpy(m_pElements, pElements, m_total_elements * sizeof(element));
241         }
242         else
243             m_pElements = pElements;
244
245         return true;
246     }
247
248     struct init_task_params
249     {
250         dxt_format m_fmt;
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;
255     };
256
257     void dxt_image::init_task(uint64_t data, void *pData_ptr)
258     {
259         const uint thread_index = static_cast<uint>(data);
260         init_task_params *pInit_params = static_cast<init_task_params *>(pData_ptr);
261
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);
265
266         uint block_index = 0;
267
268         set_block_pixels_context optimizer_context;
269         int prev_progress_percentage = -1;
270
271         for (uint block_y = 0; block_y < m_blocks_y; block_y++)
272         {
273             const uint pixel_ofs_y = block_y * cDXTBlockSize;
274
275             for (uint block_x = 0; block_x < m_blocks_x; block_x++, block_index++)
276             {
277                 if (pInit_params->m_canceled)
278                     return;
279
280                 if (p.m_pProgress_callback && is_main_thread && ((block_index & 63) == 63))
281                 {
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)
284                     {
285                         prev_progress_percentage = progress_percentage;
286                         if (!(p.m_pProgress_callback)(progress_percentage, p.m_pProgress_callback_user_data_ptr))
287                         {
288                             atomic_exchange32(&pInit_params->m_canceled, VOGL_TRUE);
289                             return;
290                         }
291                     }
292                 }
293
294                 if (p.m_num_helper_threads)
295                 {
296                     if ((block_index % (p.m_num_helper_threads + 1)) != thread_index)
297                         continue;
298                 }
299
300                 color_quad_u8 pixels[cDXTBlockSize * cDXTBlockSize];
301
302                 const uint pixel_ofs_x = block_x * cDXTBlockSize;
303
304                 for (uint y = 0; y < cDXTBlockSize; y++)
305                 {
306                     const uint iy = math::minimum(pixel_ofs_y + y, img.get_height() - 1);
307
308                     for (uint x = 0; x < cDXTBlockSize; x++)
309                     {
310                         const uint ix = math::minimum(pixel_ofs_x + x, img.get_width() - 1);
311
312                         pixels[x + y * cDXTBlockSize] = img(ix, iy);
313                     }
314                 }
315
316                 set_block_pixels(block_x, block_y, pixels, p, optimizer_context);
317             }
318         }
319     }
320
321 #if VOGL_SUPPORT_ATI_COMPRESS
322     bool dxt_image::init_ati_compress(dxt_format fmt, const image_u8 &img, const pack_params &p)
323     {
324         image_u8 tmp_img(img);
325         for (uint y = 0; y < img.get_height(); y++)
326         {
327             for (uint x = 0; x < img.get_width(); x++)
328             {
329                 color_quad_u8 c(img(x, y));
330                 std::swap(c.r, c.b);
331                 tmp_img(x, y) = c;
332             }
333         }
334
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();
344
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();
352
353         switch (fmt)
354         {
355             case cDXT1:
356             case cDXT1A:
357                 dst_tex.format = ATI_TC_FORMAT_DXT1;
358                 break;
359             case cDXT3:
360                 dst_tex.format = ATI_TC_FORMAT_DXT3;
361                 break;
362             case cDXT5:
363                 dst_tex.format = ATI_TC_FORMAT_DXT5;
364                 break;
365             case cDXT5A:
366                 dst_tex.format = ATI_TC_FORMAT_ATI1N;
367                 break;
368             case cDXN_XY:
369                 dst_tex.format = ATI_TC_FORMAT_ATI2N_XY;
370                 break;
371             case cDXN_YX:
372                 dst_tex.format = ATI_TC_FORMAT_ATI2N;
373                 break;
374             default:
375             {
376                 VOGL_ASSERT(false);
377                 return false;
378             }
379         }
380
381         ATI_TC_CompressOptions options;
382         utils::zero_object(options);
383         options.dwSize = sizeof(ATI_TC_CompressOptions);
384
385         if (fmt == cDXT1A)
386         {
387             options.bDXT1UseAlpha = true;
388             options.nAlphaThreshold = (ATI_TC_BYTE)p.m_dxt1a_alpha_threshold;
389         }
390         options.bDisableMultiThreading = (p.m_num_helper_threads == 0);
391         switch (p.m_quality)
392         {
393             case cCRNDXTQualityFast:
394                 options.nCompressionSpeed = ATI_TC_Speed_Fast;
395                 break;
396             case cCRNDXTQualitySuperFast:
397                 options.nCompressionSpeed = ATI_TC_Speed_SuperFast;
398                 break;
399             default:
400                 options.nCompressionSpeed = ATI_TC_Speed_Normal;
401                 break;
402         }
403
404         if (p.m_perceptual)
405         {
406             options.bUseChannelWeighting = true;
407             options.fWeightingRed = .212671f;
408             options.fWeightingGreen = .715160f;
409             options.fWeightingBlue = .072169f;
410         }
411
412         ATI_TC_ERROR err = ATI_TC_ConvertTexture(&src_tex, &dst_tex, &options, NULL, NULL, NULL);
413         return err == ATI_TC_OK;
414     }
415 #endif
416
417     bool dxt_image::init(dxt_format fmt, const image_u8 &img, const pack_params &p)
418     {
419         if (!init(fmt, img.get_width(), img.get_height(), false))
420             return false;
421
422 #if VOGL_SUPPORT_ATI_COMPRESS
423         if (p.m_compressor == cCRNDXTCompressorATI)
424             return init_ati_compress(fmt, img, p);
425 #endif
426
427         task_pool *pPool = p.m_pTask_pool;
428
429         task_pool tmp_pool;
430         if (!pPool)
431         {
432             if (!tmp_pool.init(p.m_num_helper_threads))
433                 return false;
434             pPool = &tmp_pool;
435         }
436
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;
443
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);
446
447         pPool->join();
448
449         if (init_params.m_canceled)
450             return false;
451
452         return true;
453     }
454
455     bool dxt_image::unpack(image_u8 &img) const
456     {
457         if (!m_total_elements)
458             return false;
459
460         img.crop(m_width, m_height);
461
462         color_quad_u8 pixels[cDXTBlockSize * cDXTBlockSize];
463         for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
464             pixels[i].set(0, 0, 0, 255);
465
466         bool all_blocks_valid = true;
467         for (uint block_y = 0; block_y < m_blocks_y; block_y++)
468         {
469             const uint pixel_ofs_y = block_y * cDXTBlockSize;
470             const uint limit_y = math::minimum<uint>(cDXTBlockSize, img.get_height() - pixel_ofs_y);
471
472             for (uint block_x = 0; block_x < m_blocks_x; block_x++)
473             {
474                 if (!get_block_pixels(block_x, block_y, pixels))
475                     all_blocks_valid = false;
476
477                 const uint pixel_ofs_x = block_x * cDXTBlockSize;
478
479                 const uint limit_x = math::minimum<uint>(cDXTBlockSize, img.get_width() - pixel_ofs_x);
480
481                 for (uint y = 0; y < limit_y; y++)
482                 {
483                     const uint iy = pixel_ofs_y + y;
484
485                     for (uint x = 0; x < limit_x; x++)
486                     {
487                         const uint ix = pixel_ofs_x + x;
488
489                         img(ix, iy) = pixels[x + (y << cDXTBlockShift)];
490                     }
491                 }
492             }
493         }
494
495         if (!all_blocks_valid)
496             console::error("dxt_image::unpack: One or more invalid blocks encountered!\n");
497
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++)
503         {
504             if (m_element_component_index[i] < 0)
505             {
506                 img.set_component_valid(0, true);
507                 img.set_component_valid(1, true);
508                 img.set_component_valid(2, true);
509             }
510             else
511                 img.set_component_valid(m_element_component_index[i], true);
512         }
513
514         img.set_component_valid(3, get_dxt_format_has_alpha(m_format));
515
516         return true;
517     }
518
519     void dxt_image::endian_swap()
520     {
521         utils::endian_switch_words(reinterpret_cast<uint16 *>(m_elements.get_ptr()), m_elements.size_in_bytes() / sizeof(uint16));
522     }
523
524     const dxt_image::element &dxt_image::get_element(uint block_x, uint block_y, uint element_index) const
525     {
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];
528     }
529
530     dxt_image::element &dxt_image::get_element(uint block_x, uint block_y, uint element_index)
531     {
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];
534     }
535
536     bool dxt_image::has_alpha() const
537     {
538         switch (m_format)
539         {
540             case cDXT1:
541             {
542                 for (uint i = 0; i < m_total_elements; i++)
543                 {
544                     const dxt1_block &blk = *(dxt1_block *)&m_pElements[i];
545
546                     if (blk.get_low_color() <= blk.get_high_color())
547                     {
548                         for (uint y = 0; y < cDXTBlockSize; y++)
549                             for (uint x = 0; x < cDXTBlockSize; x++)
550                                 if (blk.get_selector(x, y) == 3)
551                                     return true;
552                     }
553                 }
554
555                 break;
556             }
557             case cDXT1A:
558             case cDXT3:
559             case cDXT5:
560             case cDXT5A:
561                 return true;
562             default:
563                 break;
564         }
565
566         return false;
567     }
568
569     color_quad_u8 dxt_image::get_pixel(uint x, uint y) const
570     {
571         VOGL_ASSERT((x < m_width) && (y < m_height));
572
573         const uint block_x = x >> cDXTBlockShift;
574         const uint block_y = y >> cDXTBlockShift;
575
576         const element *pElement = reinterpret_cast<const element *>(&get_element(block_x, block_y, 0));
577
578         color_quad_u8 result(0, 0, 0, 255);
579
580         for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
581         {
582             switch (m_element_type[element_index])
583             {
584                 case cColorETC1:
585                 {
586                     const etc1_block &block = *reinterpret_cast<const etc1_block *>(&get_element(block_x, block_y, element_index));
587
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];
593
594                     if (diff_flag)
595                     {
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);
600                     }
601                     else
602                     {
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);
607                     }
608
609                     const uint bx = x & 3;
610                     const uint by = y & 3;
611
612                     const uint selector_index = block.get_selector(bx, by);
613                     if (flip_flag)
614                     {
615                         if (by <= 2)
616                             result = subblock_colors0[selector_index];
617                         else
618                             result = subblock_colors1[selector_index];
619                     }
620                     else
621                     {
622                         if (bx <= 2)
623                             result = subblock_colors0[selector_index];
624                         else
625                             result = subblock_colors1[selector_index];
626                     }
627
628                     break;
629                 }
630                 case cColorDXT1:
631                 {
632                     const dxt1_block *pBlock = reinterpret_cast<const dxt1_block *>(&get_element(block_x, block_y, element_index));
633
634                     const uint l = pBlock->get_low_color();
635                     const uint h = pBlock->get_high_color();
636
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));
639
640                     const uint s = pBlock->get_selector(x & 3, y & 3);
641
642                     if (l > h)
643                     {
644                         switch (s)
645                         {
646                             case 0:
647                                 result.set_noclamp_rgb(c0.r, c0.g, c0.b);
648                                 break;
649                             case 1:
650                                 result.set_noclamp_rgb(c1.r, c1.g, c1.b);
651                                 break;
652                             case 2:
653                                 result.set_noclamp_rgb((c0.r * 2 + c1.r) / 3, (c0.g * 2 + c1.g) / 3, (c0.b * 2 + c1.b) / 3);
654                                 break;
655                             case 3:
656                                 result.set_noclamp_rgb((c1.r * 2 + c0.r) / 3, (c1.g * 2 + c0.g) / 3, (c1.b * 2 + c0.b) / 3);
657                                 break;
658                         }
659                     }
660                     else
661                     {
662                         switch (s)
663                         {
664                             case 0:
665                                 result.set_noclamp_rgb(c0.r, c0.g, c0.b);
666                                 break;
667                             case 1:
668                                 result.set_noclamp_rgb(c1.r, c1.g, c1.b);
669                                 break;
670                             case 2:
671                                 result.set_noclamp_rgb((c0.r + c1.r) >> 1U, (c0.g + c1.g) >> 1U, (c0.b + c1.b) >> 1U);
672                                 break;
673                             case 3:
674                             {
675                                 if (m_format <= cDXT1A)
676                                     result.set_noclamp_rgba(0, 0, 0, 0);
677                                 else
678                                     result.set_noclamp_rgb(0, 0, 0);
679                                 break;
680                             }
681                         }
682                     }
683
684                     break;
685                 }
686                 case cAlphaDXT5:
687                 {
688                     const int comp_index = m_element_component_index[element_index];
689
690                     const dxt5_block *pBlock = reinterpret_cast<const dxt5_block *>(&get_element(block_x, block_y, element_index));
691
692                     const uint l = pBlock->get_low_alpha();
693                     const uint h = pBlock->get_high_alpha();
694
695                     const uint s = pBlock->get_selector(x & 3, y & 3);
696
697                     if (l > h)
698                     {
699                         switch (s)
700                         {
701                             case 0:
702                                 result[comp_index] = static_cast<uint8>(l);
703                                 break;
704                             case 1:
705                                 result[comp_index] = static_cast<uint8>(h);
706                                 break;
707                             case 2:
708                                 result[comp_index] = static_cast<uint8>((l * 6 + h) / 7);
709                                 break;
710                             case 3:
711                                 result[comp_index] = static_cast<uint8>((l * 5 + h * 2) / 7);
712                                 break;
713                             case 4:
714                                 result[comp_index] = static_cast<uint8>((l * 4 + h * 3) / 7);
715                                 break;
716                             case 5:
717                                 result[comp_index] = static_cast<uint8>((l * 3 + h * 4) / 7);
718                                 break;
719                             case 6:
720                                 result[comp_index] = static_cast<uint8>((l * 2 + h * 5) / 7);
721                                 break;
722                             case 7:
723                                 result[comp_index] = static_cast<uint8>((l + h * 6) / 7);
724                                 break;
725                         }
726                     }
727                     else
728                     {
729                         switch (s)
730                         {
731                             case 0:
732                                 result[comp_index] = static_cast<uint8>(l);
733                                 break;
734                             case 1:
735                                 result[comp_index] = static_cast<uint8>(h);
736                                 break;
737                             case 2:
738                                 result[comp_index] = static_cast<uint8>((l * 4 + h) / 5);
739                                 break;
740                             case 3:
741                                 result[comp_index] = static_cast<uint8>((l * 3 + h * 2) / 5);
742                                 break;
743                             case 4:
744                                 result[comp_index] = static_cast<uint8>((l * 2 + h * 3) / 5);
745                                 break;
746                             case 5:
747                                 result[comp_index] = static_cast<uint8>((l + h * 4) / 5);
748                                 break;
749                             case 6:
750                                 result[comp_index] = 0;
751                                 break;
752                             case 7:
753                                 result[comp_index] = 255;
754                                 break;
755                         }
756                     }
757
758                     break;
759                 }
760                 case cAlphaDXT3:
761                 {
762                     const int comp_index = m_element_component_index[element_index];
763
764                     const dxt3_block *pBlock = reinterpret_cast<const dxt3_block *>(&get_element(block_x, block_y, element_index));
765
766                     result[comp_index] = static_cast<uint8>(pBlock->get_alpha(x & 3, y & 3, true));
767
768                     break;
769                 }
770                 default:
771                     break;
772             }
773         }
774
775         return result;
776     }
777
778     uint dxt_image::get_pixel_alpha(uint x, uint y, uint element_index) const
779     {
780         VOGL_ASSERT((x < m_width) && (y < m_height) && (element_index < m_num_elements_per_block));
781
782         const uint block_x = x >> cDXTBlockShift;
783         const uint block_y = y >> cDXTBlockShift;
784
785         switch (m_element_type[element_index])
786         {
787             case cColorDXT1:
788             {
789                 if (m_format <= cDXT1A)
790                 {
791                     const dxt1_block *pBlock = reinterpret_cast<const dxt1_block *>(&get_element(block_x, block_y, element_index));
792
793                     const uint l = pBlock->get_low_color();
794                     const uint h = pBlock->get_high_color();
795
796                     if (l <= h)
797                     {
798                         uint s = pBlock->get_selector(x & 3, y & 3);
799
800                         return (s == 3) ? 0 : 255;
801                     }
802                     else
803                     {
804                         return 255;
805                     }
806                 }
807
808                 break;
809             }
810             case cAlphaDXT5:
811             {
812                 const dxt5_block *pBlock = reinterpret_cast<const dxt5_block *>(&get_element(block_x, block_y, element_index));
813
814                 const uint l = pBlock->get_low_alpha();
815                 const uint h = pBlock->get_high_alpha();
816
817                 const uint s = pBlock->get_selector(x & 3, y & 3);
818
819                 if (l > h)
820                 {
821                     switch (s)
822                     {
823                         case 0:
824                             return l;
825                         case 1:
826                             return h;
827                         case 2:
828                             return (l * 6 + h) / 7;
829                         case 3:
830                             return (l * 5 + h * 2) / 7;
831                         case 4:
832                             return (l * 4 + h * 3) / 7;
833                         case 5:
834                             return (l * 3 + h * 4) / 7;
835                         case 6:
836                             return (l * 2 + h * 5) / 7;
837                         case 7:
838                             return (l + h * 6) / 7;
839                     }
840                 }
841                 else
842                 {
843                     switch (s)
844                     {
845                         case 0:
846                             return l;
847                         case 1:
848                             return h;
849                         case 2:
850                             return (l * 4 + h) / 5;
851                         case 3:
852                             return (l * 3 + h * 2) / 5;
853                         case 4:
854                             return (l * 2 + h * 3) / 5;
855                         case 5:
856                             return (l + h * 4) / 5;
857                         case 6:
858                             return 0;
859                         case 7:
860                             return 255;
861                     }
862                 }
863             }
864             case cAlphaDXT3:
865             {
866                 const dxt3_block *pBlock = reinterpret_cast<const dxt3_block *>(&get_element(block_x, block_y, element_index));
867
868                 return pBlock->get_alpha(x & 3, y & 3, true);
869             }
870             default:
871                 break;
872         }
873
874         return 255;
875     }
876
877     void dxt_image::set_pixel(uint x, uint y, const color_quad_u8 &c, bool perceptual)
878     {
879         VOGL_ASSERT((x < m_width) && (y < m_height));
880
881         const uint block_x = x >> cDXTBlockShift;
882         const uint block_y = y >> cDXTBlockShift;
883
884         element *pElement = &get_element(block_x, block_y, 0);
885
886         for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
887         {
888             switch (m_element_type[element_index])
889             {
890                 case cColorETC1:
891                 {
892                     etc1_block &block = *reinterpret_cast<etc1_block *>(&get_element(block_x, block_y, element_index));
893
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];
899
900                     if (diff_flag)
901                     {
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);
906                     }
907                     else
908                     {
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);
913                     }
914
915                     const uint bx = x & 3;
916                     const uint by = y & 3;
917
918                     color_quad_u8 *pColors = subblock_colors1;
919                     if (flip_flag)
920                     {
921                         if (by <= 2)
922                             pColors = subblock_colors0;
923                     }
924                     else
925                     {
926                         if (bx <= 2)
927                             pColors = subblock_colors0;
928                     }
929
930                     uint best_error = UINT_MAX;
931                     uint best_selector = 0;
932
933                     for (uint i = 0; i < 4; i++)
934                     {
935                         uint error = color::color_distance(perceptual, pColors[i], c, false);
936                         if (error < best_error)
937                         {
938                             best_error = error;
939                             best_selector = i;
940                         }
941                     }
942
943                     block.set_selector(bx, by, best_selector);
944                     break;
945                 }
946                 case cColorDXT1:
947                 {
948                     dxt1_block *pDXT1_block = reinterpret_cast<dxt1_block *>(pElement);
949
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()));
952
953                     if ((m_format == cDXT1A) && (c.a < 128))
954                         pDXT1_block->set_selector(x & 3, y & 3, 3);
955                     else
956                     {
957                         uint best_error = UINT_MAX;
958                         uint best_selector = 0;
959
960                         for (uint i = 0; i < n; i++)
961                         {
962                             uint error = color::color_distance(perceptual, colors[i], c, false);
963                             if (error < best_error)
964                             {
965                                 best_error = error;
966                                 best_selector = i;
967                             }
968                         }
969
970                         pDXT1_block->set_selector(x & 3, y & 3, best_selector);
971                     }
972
973                     break;
974                 }
975                 case cAlphaDXT5:
976                 {
977                     dxt5_block *pDXT5_block = reinterpret_cast<dxt5_block *>(pElement);
978
979                     uint values[cDXT5SelectorValues];
980                     dxt5_block::get_block_values(values, pDXT5_block->get_low_alpha(), pDXT5_block->get_high_alpha());
981
982                     const int comp_index = m_element_component_index[element_index];
983
984                     uint best_error = UINT_MAX;
985                     uint best_selector = 0;
986
987                     for (uint i = 0; i < cDXT5SelectorValues; i++)
988                     {
989                         uint error = static_cast<uint>(labs(values[i]) - c[comp_index]); // no need to square
990
991                         if (error < best_error)
992                         {
993                             best_error = error;
994                             best_selector = i;
995                         }
996                     }
997
998                     pDXT5_block->set_selector(x & 3, y & 3, best_selector);
999
1000                     break;
1001                 }
1002                 case cAlphaDXT3:
1003                 {
1004                     const int comp_index = m_element_component_index[element_index];
1005
1006                     dxt3_block *pDXT3_block = reinterpret_cast<dxt3_block *>(pElement);
1007
1008                     pDXT3_block->set_alpha(x & 3, y & 3, c[comp_index], true);
1009
1010                     break;
1011                 }
1012                 default:
1013                     break;
1014             }
1015         } // element_index
1016     }
1017
1018     bool dxt_image::get_block_pixels(uint block_x, uint block_y, color_quad_u8 *pPixels) const
1019     {
1020         bool success = true;
1021         const element *pElement = &get_element(block_x, block_y, 0);
1022
1023         for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
1024         {
1025             switch (m_element_type[element_index])
1026             {
1027                 case cColorETC1:
1028                 {
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))
1032                         success = false;
1033
1034                     break;
1035                 }
1036                 case cColorDXT1:
1037                 {
1038                     const dxt1_block *pDXT1_block = reinterpret_cast<const dxt1_block *>(pElement);
1039
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()));
1042
1043                     for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1044                     {
1045                         uint s = pDXT1_block->get_selector(i & 3, i >> 2);
1046
1047                         pPixels[i].r = colors[s].r;
1048                         pPixels[i].g = colors[s].g;
1049                         pPixels[i].b = colors[s].b;
1050
1051                         if (m_format <= cDXT1A)
1052                             pPixels[i].a = colors[s].a;
1053                     }
1054
1055                     break;
1056                 }
1057                 case cAlphaDXT5:
1058                 {
1059                     const dxt5_block *pDXT5_block = reinterpret_cast<const dxt5_block *>(pElement);
1060
1061                     uint values[cDXT5SelectorValues];
1062                     dxt5_block::get_block_values(values, pDXT5_block->get_low_alpha(), pDXT5_block->get_high_alpha());
1063
1064                     const int comp_index = m_element_component_index[element_index];
1065
1066                     for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1067                     {
1068                         uint s = pDXT5_block->get_selector(i & 3, i >> 2);
1069
1070                         pPixels[i][comp_index] = static_cast<uint8>(values[s]);
1071                     }
1072
1073                     break;
1074                 }
1075                 case cAlphaDXT3:
1076                 {
1077                     const dxt3_block *pDXT3_block = reinterpret_cast<const dxt3_block *>(pElement);
1078
1079                     const int comp_index = m_element_component_index[element_index];
1080
1081                     for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1082                     {
1083                         uint a = pDXT3_block->get_alpha(i & 3, i >> 2, true);
1084
1085                         pPixels[i][comp_index] = static_cast<uint8>(a);
1086                     }
1087
1088                     break;
1089                 }
1090                 default:
1091                     break;
1092             }
1093         } // element_index
1094         return success;
1095     }
1096
1097     void dxt_image::set_block_pixels(uint block_x, uint block_y, const color_quad_u8 *pPixels, const pack_params &p)
1098     {
1099         set_block_pixels_context context;
1100         set_block_pixels(block_x, block_y, pPixels, p, context);
1101     }
1102
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)
1106     {
1107         element *pElement = &get_element(block_x, block_y, 0);
1108
1109         if (m_format == cETC1)
1110         {
1111             etc1_block &dst_block = *reinterpret_cast<etc1_block *>(pElement);
1112
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;
1118
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);
1124         }
1125         else
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)))
1128         {
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;
1136             else
1137                 squish_flags = squish::kDxt5;
1138
1139             if (p.m_perceptual)
1140                 squish_flags |= squish::kColourMetricPerceptual;
1141             else
1142                 squish_flags |= squish::kColourMetricUniform;
1143
1144             if (p.m_quality >= cCRNDXTQualityBetter)
1145                 squish_flags |= squish::kColourIterativeClusterFit;
1146             else if (p.m_quality == cCRNDXTQualitySuperFast)
1147                 squish_flags |= squish::kColourRangeFit;
1148
1149             color_quad_u8 pixels[cDXTBlockSize * cDXTBlockSize];
1150
1151             memcpy(pixels, pPixels, sizeof(color_quad_u8) * cDXTBlockSize * cDXTBlockSize);
1152
1153             if (m_format == cDXT1)
1154             {
1155                 for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1156                     pixels[i].a = 255;
1157             }
1158             else if (m_format == cDXT1A)
1159             {
1160                 for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1161                     if (pixels[i].a < p.m_dxt1a_alpha_threshold)
1162                         pixels[i].a = 0;
1163                     else
1164                         pixels[i].a = 255;
1165             }
1166
1167             squish::Compress(reinterpret_cast<const squish::u8 *>(pixels), pElement, squish_flags);
1168         }
1169         else
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)))
1173         {
1174             color_quad_u8 pixels[cDXTBlockSize * cDXTBlockSize];
1175
1176             for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1177             {
1178                 pixels[i].r = pPixels[i].b;
1179                 pixels[i].g = pPixels[i].g;
1180                 pixels[i].b = pPixels[i].r;
1181
1182                 if (m_format == cDXT1)
1183                     pixels[i].a = 255;
1184                 else
1185                     pixels[i].a = pPixels[i].a;
1186             }
1187
1188             if (m_format == cDXT5A)
1189                 ryg_dxt::sCompressDXT5ABlock((sU8 *)pElement, (const sU32 *)pixels, 0);
1190             else
1191                 ryg_dxt::sCompressDXTBlock((sU8 *)pElement, (const sU32 *)pixels, m_format == cDXT5, 0);
1192         }
1193         else if ((p.m_compressor == cCRNDXTCompressorCRNF) && (m_format != cDXT1A))
1194         {
1195             for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
1196             {
1197                 switch (m_element_type[element_index])
1198                 {
1199                     case cColorDXT1:
1200                     {
1201                         dxt1_block *pDXT1_block = reinterpret_cast<dxt1_block *>(pElement);
1202                         dxt_fast::compress_color_block(pDXT1_block, pPixels, p.m_quality >= cCRNDXTQualityNormal);
1203
1204                         break;
1205                     }
1206                     case cAlphaDXT5:
1207                     {
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]);
1210
1211                         break;
1212                     }
1213                     case cAlphaDXT3:
1214                     {
1215                         const int comp_index = m_element_component_index[element_index];
1216
1217                         dxt3_block *pDXT3_block = reinterpret_cast<dxt3_block *>(pElement);
1218
1219                         for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1220                             pDXT3_block->set_alpha(i & 3, i >> 2, pPixels[i][comp_index], true);
1221
1222                         break;
1223                     }
1224                     default:
1225                         break;
1226                 }
1227             }
1228         }
1229         else
1230         {
1231             dxt1_endpoint_optimizer &dxt1_optimizer = context.m_dxt1_optimizer;
1232             dxt5_endpoint_optimizer &dxt5_optimizer = context.m_dxt5_optimizer;
1233
1234             for (uint element_index = 0; element_index < m_num_elements_per_block; element_index++, pElement++)
1235             {
1236                 switch (m_element_type[element_index])
1237                 {
1238                     case cColorDXT1:
1239                     {
1240                         dxt1_block *pDXT1_block = reinterpret_cast<dxt1_block *>(pElement);
1241
1242                         bool pixels_have_alpha = false;
1243                         if (m_format == cDXT1A)
1244                         {
1245                             for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1246                                 if (pPixels[i].a < p.m_dxt1a_alpha_threshold)
1247                                 {
1248                                     pixels_have_alpha = true;
1249                                     break;
1250                                 }
1251                         }
1252
1253                         dxt1_endpoint_optimizer::results results;
1254                         uint8 selectors[cDXTBlockSize * cDXTBlockSize];
1255                         results.m_pSelectors = selectors;
1256
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];
1272
1273                         if ((m_format != cDXT1) && (m_format != cDXT1A))
1274                             params.m_use_alpha_blocks = false;
1275
1276                         if (!dxt1_optimizer.compute(params, results))
1277                         {
1278                             VOGL_ASSERT_ALWAYS;
1279                             break;
1280                         }
1281
1282                         pDXT1_block->set_low_color(results.m_low_color);
1283                         pDXT1_block->set_high_color(results.m_high_color);
1284
1285                         for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1286                             pDXT1_block->set_selector(i & 3, i >> 2, selectors[i]);
1287
1288                         break;
1289                     }
1290                     case cAlphaDXT5:
1291                     {
1292                         dxt5_block *pDXT5_block = reinterpret_cast<dxt5_block *>(pElement);
1293
1294                         dxt5_endpoint_optimizer::results results;
1295
1296                         uint8 selectors[cDXTBlockSize * cDXTBlockSize];
1297                         results.m_pSelectors = selectors;
1298
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;
1306
1307                         if (!dxt5_optimizer.compute(params, results))
1308                         {
1309                             VOGL_ASSERT_ALWAYS;
1310                             break;
1311                         }
1312
1313                         pDXT5_block->set_low_alpha(results.m_first_endpoint);
1314                         pDXT5_block->set_high_alpha(results.m_second_endpoint);
1315
1316                         for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1317                             pDXT5_block->set_selector(i & 3, i >> 2, selectors[i]);
1318
1319                         break;
1320                     }
1321                     case cAlphaDXT3:
1322                     {
1323                         const int comp_index = m_element_component_index[element_index];
1324
1325                         dxt3_block *pDXT3_block = reinterpret_cast<dxt3_block *>(pElement);
1326
1327                         for (uint i = 0; i < cDXTBlockSize * cDXTBlockSize; i++)
1328                             pDXT3_block->set_alpha(i & 3, i >> 2, pPixels[i][comp_index], true);
1329
1330                         break;
1331                     }
1332                     default:
1333                         break;
1334                 }
1335             }
1336         }
1337     }
1338
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
1340     {
1341         const element &block = get_element(block_x, block_y, element_index);
1342
1343         switch (m_element_type[element_index])
1344         {
1345             case cColorETC1:
1346             {
1347                 const etc1_block &src_block = *reinterpret_cast<const etc1_block *>(&block);
1348                 if (src_block.get_diff_bit())
1349                 {
1350                     packed_low_endpoint = src_block.get_base5_color();
1351                     packed_high_endpoint = src_block.get_delta3_color();
1352                 }
1353                 else
1354                 {
1355                     packed_low_endpoint = src_block.get_base4_color(0);
1356                     packed_high_endpoint = src_block.get_base4_color(1);
1357                 }
1358
1359                 break;
1360             }
1361             case cColorDXT1:
1362             {
1363                 const dxt1_block &block1 = *reinterpret_cast<const dxt1_block *>(&block);
1364
1365                 packed_low_endpoint = block1.get_low_color();
1366                 packed_high_endpoint = block1.get_high_color();
1367
1368                 break;
1369             }
1370             case cAlphaDXT5:
1371             {
1372                 const dxt5_block &block5 = *reinterpret_cast<const dxt5_block *>(&block);
1373
1374                 packed_low_endpoint = block5.get_low_alpha();
1375                 packed_high_endpoint = block5.get_high_alpha();
1376
1377                 break;
1378             }
1379             case cAlphaDXT3:
1380             {
1381                 packed_low_endpoint = 0;
1382                 packed_high_endpoint = 255;
1383
1384                 break;
1385             }
1386             default:
1387                 break;
1388         }
1389     }
1390
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
1392     {
1393         uint l = 0, h = 0;
1394         get_block_endpoints(block_x, block_y, element_index, l, h);
1395
1396         switch (m_element_type[element_index])
1397         {
1398             case cColorETC1:
1399             {
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())
1402                 {
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);
1405                 }
1406                 else
1407                 {
1408                     low_endpoint = etc1_block::unpack_color4(static_cast<uint16>(l), scaled);
1409                     high_endpoint = etc1_block::unpack_color4(static_cast<uint16>(h), scaled);
1410                 }
1411
1412                 return -1;
1413             }
1414             case cColorDXT1:
1415             {
1416                 uint r, g, b;
1417
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);
1422
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);
1427
1428                 return -1;
1429             }
1430             case cAlphaDXT5:
1431             {
1432                 const int component = m_element_component_index[element_index];
1433
1434                 low_endpoint[component] = static_cast<uint8>(l);
1435                 high_endpoint[component] = static_cast<uint8>(h);
1436
1437                 return component;
1438             }
1439             case cAlphaDXT3:
1440             {
1441                 const int component = m_element_component_index[element_index];
1442
1443                 low_endpoint[component] = static_cast<uint8>(l);
1444                 high_endpoint[component] = static_cast<uint8>(h);
1445
1446                 return component;
1447             }
1448             default:
1449                 break;
1450         }
1451
1452         return 0;
1453     }
1454
1455     uint dxt_image::get_block_colors(uint block_x, uint block_y, uint element_index, color_quad_u8 *pColors, uint subblock_index)
1456     {
1457         const element &block = get_element(block_x, block_y, element_index);
1458
1459         switch (m_element_type[element_index])
1460         {
1461             case cColorETC1:
1462             {
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())
1467                 {
1468                     const uint16 base_color5 = src_block.get_base5_color();
1469                     const uint16 delta_color3 = src_block.get_delta3_color();
1470                     if (subblock_index)
1471                         etc1_block::get_diff_subblock_colors(pColors, base_color5, delta_color3, table_index1);
1472                     else
1473                         etc1_block::get_diff_subblock_colors(pColors, base_color5, table_index0);
1474                 }
1475                 else
1476                 {
1477                     if (subblock_index)
1478                     {
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);
1481                     }
1482                     else
1483                     {
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);
1486                     }
1487                 }
1488
1489                 break;
1490             }
1491             case cColorDXT1:
1492             {
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()));
1495             }
1496             case cAlphaDXT5:
1497             {
1498                 const dxt5_block &block5 = *reinterpret_cast<const dxt5_block *>(&block);
1499
1500                 uint values[cDXT5SelectorValues];
1501
1502                 const uint n = dxt5_block::get_block_values(values, block5.get_low_alpha(), block5.get_high_alpha());
1503
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]);
1507
1508                 return n;
1509             }
1510             case cAlphaDXT3:
1511             {
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);
1515
1516                 return 16;
1517             }
1518             default:
1519                 break;
1520         }
1521
1522         return 0;
1523     }
1524
1525     uint dxt_image::get_subblock_index(uint x, uint y, uint element_index) const
1526     {
1527         if (m_element_type[element_index] != cColorETC1)
1528             return 0;
1529
1530         const uint block_x = x >> cDXTBlockShift;
1531         const uint block_y = y >> cDXTBlockShift;
1532
1533         const element &block = get_element(block_x, block_y, element_index);
1534
1535         const etc1_block &src_block = *reinterpret_cast<const etc1_block *>(&block);
1536         if (src_block.get_flip_bit())
1537         {
1538             return ((y & 3) >= 2) ? 1 : 0;
1539         }
1540         else
1541         {
1542             return ((x & 3) >= 2) ? 1 : 0;
1543         }
1544     }
1545
1546     uint dxt_image::get_total_subblocks(uint element_index) const
1547     {
1548         return (m_element_type[element_index] == cColorETC1) ? 2 : 0;
1549     }
1550
1551     uint dxt_image::get_selector(uint x, uint y, uint element_index) const
1552     {
1553         VOGL_ASSERT((x < m_width) && (y < m_height));
1554
1555         const uint block_x = x >> cDXTBlockShift;
1556         const uint block_y = y >> cDXTBlockShift;
1557
1558         const element &block = get_element(block_x, block_y, element_index);
1559
1560         switch (m_element_type[element_index])
1561         {
1562             case cColorETC1:
1563             {
1564                 const etc1_block &src_block = *reinterpret_cast<const etc1_block *>(&block);
1565                 return src_block.get_selector(x & 3, y & 3);
1566             }
1567             case cColorDXT1:
1568             {
1569                 const dxt1_block &block1 = *reinterpret_cast<const dxt1_block *>(&block);
1570                 return block1.get_selector(x & 3, y & 3);
1571             }
1572             case cAlphaDXT5:
1573             {
1574                 const dxt5_block &block5 = *reinterpret_cast<const dxt5_block *>(&block);
1575                 return block5.get_selector(x & 3, y & 3);
1576             }
1577             case cAlphaDXT3:
1578             {
1579                 const dxt3_block &block3 = *reinterpret_cast<const dxt3_block *>(&block);
1580                 return block3.get_alpha(x & 3, y & 3, false);
1581             }
1582             default:
1583                 break;
1584         }
1585
1586         return 0;
1587     }
1588
1589     void dxt_image::change_dxt1_to_dxt1a()
1590     {
1591         if (m_format == cDXT1)
1592             m_format = cDXT1A;
1593     }
1594
1595     void dxt_image::flip_col(uint x)
1596     {
1597         const uint other_x = (m_blocks_x - 1) - x;
1598         for (uint y = 0; y < m_blocks_y; y++)
1599         {
1600             for (uint e = 0; e < get_elements_per_block(); e++)
1601             {
1602                 element tmp[2] = { get_element(x, y, e), get_element(other_x, y, e) };
1603
1604                 for (uint i = 0; i < 2; i++)
1605                 {
1606                     switch (get_element_type(e))
1607                     {
1608                         case cColorDXT1:
1609                             reinterpret_cast<dxt1_block *>(&tmp[i])->flip_x();
1610                             break;
1611                         case cAlphaDXT3:
1612                             reinterpret_cast<dxt3_block *>(&tmp[i])->flip_x();
1613                             break;
1614                         case cAlphaDXT5:
1615                             reinterpret_cast<dxt5_block *>(&tmp[i])->flip_x();
1616                             break;
1617                         default:
1618                             VOGL_ASSERT_ALWAYS;
1619                             break;
1620                     }
1621                 }
1622
1623                 get_element(x, y, e) = tmp[1];
1624                 get_element(other_x, y, e) = tmp[0];
1625             }
1626         }
1627     }
1628
1629     void dxt_image::flip_row(uint y)
1630     {
1631         const uint other_y = (m_blocks_y - 1) - y;
1632         for (uint x = 0; x < m_blocks_x; x++)
1633         {
1634             for (uint e = 0; e < get_elements_per_block(); e++)
1635             {
1636                 element tmp[2] = { get_element(x, y, e), get_element(x, other_y, e) };
1637
1638                 for (uint i = 0; i < 2; i++)
1639                 {
1640                     switch (get_element_type(e))
1641                     {
1642                         case cColorDXT1:
1643                             reinterpret_cast<dxt1_block *>(&tmp[i])->flip_y();
1644                             break;
1645                         case cAlphaDXT3:
1646                             reinterpret_cast<dxt3_block *>(&tmp[i])->flip_y();
1647                             break;
1648                         case cAlphaDXT5:
1649                             reinterpret_cast<dxt5_block *>(&tmp[i])->flip_y();
1650                             break;
1651                         default:
1652                             VOGL_ASSERT_ALWAYS;
1653                             break;
1654                     }
1655                 }
1656
1657                 get_element(x, y, e) = tmp[1];
1658                 get_element(x, other_y, e) = tmp[0];
1659             }
1660         }
1661     }
1662
1663     bool dxt_image::can_flip(uint axis_index)
1664     {
1665         if (m_format == cETC1)
1666         {
1667             // Can't reliably flip ETC1 textures (because of asymmetry in the 555/333 differential coding of subblock colors).
1668             return false;
1669         }
1670
1671         uint d;
1672         if (axis_index)
1673             d = m_height;
1674         else
1675             d = m_width;
1676
1677         if (d & 3)
1678         {
1679             if (d > 4)
1680                 return false;
1681         }
1682
1683         return true;
1684     }
1685
1686     bool dxt_image::flip_x()
1687     {
1688         if (m_format == cETC1)
1689         {
1690             // Can't reliably flip ETC1 textures (because of asymmetry in the 555/333 differential coding of subblock colors).
1691             return false;
1692         }
1693
1694         if ((m_width & 3) && (m_width > 4))
1695             return false;
1696
1697         if (m_width == 1)
1698             return true;
1699
1700         const uint mid_x = m_blocks_x / 2;
1701
1702         for (uint x = 0; x < mid_x; x++)
1703             flip_col(x);
1704
1705         if (m_blocks_x & 1)
1706         {
1707             const uint w = math::minimum(m_width, 4U);
1708             for (uint y = 0; y < m_blocks_y; y++)
1709             {
1710                 for (uint e = 0; e < get_elements_per_block(); e++)
1711                 {
1712                     element tmp(get_element(mid_x, y, e));
1713                     switch (get_element_type(e))
1714                     {
1715                         case cColorDXT1:
1716                             reinterpret_cast<dxt1_block *>(&tmp)->flip_x(w, 4);
1717                             break;
1718                         case cAlphaDXT3:
1719                             reinterpret_cast<dxt3_block *>(&tmp)->flip_x(w, 4);
1720                             break;
1721                         case cAlphaDXT5:
1722                             reinterpret_cast<dxt5_block *>(&tmp)->flip_x(w, 4);
1723                             break;
1724                         default:
1725                             VOGL_ASSERT_ALWAYS;
1726                             break;
1727                     }
1728                     get_element(mid_x, y, e) = tmp;
1729                 }
1730             }
1731         }
1732
1733         return true;
1734     }
1735
1736     bool dxt_image::flip_y()
1737     {
1738         if (m_format == cETC1)
1739         {
1740             // Can't reliably flip ETC1 textures (because of asymmetry in the 555/333 differential coding of subblock colors).
1741             return false;
1742         }
1743
1744         if ((m_height & 3) && (m_height > 4))
1745             return false;
1746
1747         if (m_height == 1)
1748             return true;
1749
1750         const uint mid_y = m_blocks_y / 2;
1751
1752         for (uint y = 0; y < mid_y; y++)
1753             flip_row(y);
1754
1755         if (m_blocks_y & 1)
1756         {
1757             const uint h = math::minimum(m_height, 4U);
1758             for (uint x = 0; x < m_blocks_x; x++)
1759             {
1760                 for (uint e = 0; e < get_elements_per_block(); e++)
1761                 {
1762                     element tmp(get_element(x, mid_y, e));
1763                     switch (get_element_type(e))
1764                     {
1765                         case cColorDXT1:
1766                             reinterpret_cast<dxt1_block *>(&tmp)->flip_y(4, h);
1767                             break;
1768                         case cAlphaDXT3:
1769                             reinterpret_cast<dxt3_block *>(&tmp)->flip_y(4, h);
1770                             break;
1771                         case cAlphaDXT5:
1772                             reinterpret_cast<dxt5_block *>(&tmp)->flip_y(4, h);
1773                             break;
1774                         default:
1775                             VOGL_ASSERT_ALWAYS;
1776                             break;
1777                     }
1778                     get_element(x, mid_y, e) = tmp;
1779                 }
1780             }
1781         }
1782
1783         return true;
1784     }
1785
1786 } // namespace vogl