]> git.cworth.org Git - vogl/blob - src/voglcore/vogl_mipmapped_texture.cpp
Initial vogl checkin
[vogl] / src / voglcore / vogl_mipmapped_texture.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_dds_texture.cpp - Actually supports both .DDS and .KTX. Probably will rename this eventually.
28 #include "vogl_core.h"
29 #include "vogl_mipmapped_texture.h"
30 #include "vogl_cfile_stream.h"
31 #include "vogl_image_utils.h"
32 #include "vogl_console.h"
33 #include "vogl_ktx_texture.h"
34 #include "vogl_strutils.h"
35
36 namespace vogl
37 {
38     const vec2I g_vertical_cross_image_offsets[6] = { vec2I(2, 1), vec2I(0, 1), vec2I(1, 0), vec2I(1, 2), vec2I(1, 1), vec2I(1, 3) };
39
40     mip_level::mip_level()
41         : m_width(0),
42           m_height(0),
43           m_comp_flags(pixel_format_helpers::cDefaultCompFlags),
44           m_format(PIXEL_FMT_INVALID),
45           m_pImage(NULL),
46           m_pDXTImage(NULL),
47           m_orient_flags(cDefaultOrientationFlags)
48     {
49     }
50
51     mip_level::mip_level(const mip_level &other)
52         : m_width(0),
53           m_height(0),
54           m_comp_flags(pixel_format_helpers::cDefaultCompFlags),
55           m_format(PIXEL_FMT_INVALID),
56           m_pImage(NULL),
57           m_pDXTImage(NULL),
58           m_orient_flags(cDefaultOrientationFlags)
59     {
60         *this = other;
61     }
62
63     mip_level &mip_level::operator=(const mip_level &rhs)
64     {
65         clear();
66
67         m_width = rhs.m_width;
68         m_height = rhs.m_height;
69         m_comp_flags = rhs.m_comp_flags;
70         m_format = rhs.m_format;
71         m_orient_flags = rhs.m_orient_flags;
72
73         if (rhs.m_pImage)
74             m_pImage = vogl_new(image_u8, *rhs.m_pImage);
75
76         if (rhs.m_pDXTImage)
77             m_pDXTImage = vogl_new(dxt_image, *rhs.m_pDXTImage);
78
79         return *this;
80     }
81
82     mip_level::~mip_level()
83     {
84         vogl_delete(m_pImage);
85         vogl_delete(m_pDXTImage);
86     }
87
88     void mip_level::clear()
89     {
90         m_width = 0;
91         m_height = 0;
92         m_comp_flags = pixel_format_helpers::cDefaultCompFlags;
93         m_format = PIXEL_FMT_INVALID;
94         m_orient_flags = cDefaultOrientationFlags;
95
96         if (m_pImage)
97         {
98             vogl_delete(m_pImage);
99             m_pImage = NULL;
100         }
101
102         if (m_pDXTImage)
103         {
104             vogl_delete(m_pDXTImage);
105             m_pDXTImage = NULL;
106         }
107     }
108
109     void mip_level::assign(image_u8 *p, pixel_format fmt, orientation_flags_t orient_flags)
110     {
111         VOGL_ASSERT(p);
112
113         clear();
114
115         m_pImage = p;
116
117         m_width = p->get_width();
118         m_height = p->get_height();
119         m_orient_flags = orient_flags;
120
121         if (fmt != PIXEL_FMT_INVALID)
122             m_format = fmt;
123         else
124         {
125             if (p->is_grayscale())
126                 m_format = p->is_component_valid(3) ? PIXEL_FMT_A8L8 : PIXEL_FMT_L8;
127             else
128                 m_format = p->is_component_valid(3) ? PIXEL_FMT_A8R8G8B8 : PIXEL_FMT_R8G8B8;
129         }
130
131         m_comp_flags = p->get_comp_flags(); //pixel_format_helpers::get_component_flags(m_format);
132     }
133
134     void mip_level::assign(dxt_image *p, pixel_format fmt, orientation_flags_t orient_flags)
135     {
136         VOGL_ASSERT(p);
137
138         clear();
139
140         m_pDXTImage = p;
141
142         m_width = p->get_width();
143         m_height = p->get_height();
144         m_orient_flags = orient_flags;
145
146         if (fmt != PIXEL_FMT_INVALID)
147             m_format = fmt;
148         else
149             m_format = pixel_format_helpers::from_dxt_format(p->get_format());
150
151         m_comp_flags = pixel_format_helpers::get_component_flags(m_format);
152     }
153
154     bool mip_level::pack_to_dxt(const image_u8 &img, pixel_format fmt, bool cook, const dxt_image::pack_params &orig_params, orientation_flags_t orient_flags)
155     {
156         VOGL_ASSERT(pixel_format_helpers::is_dxt(fmt));
157         if (!pixel_format_helpers::is_dxt(fmt))
158             return false;
159
160         dxt_image::pack_params p(orig_params);
161         if (pixel_format_helpers::is_pixel_format_non_srgb(fmt) || (img.get_comp_flags() & pixel_format_helpers::cCompFlagNormalMap) || (img.get_comp_flags() & pixel_format_helpers::cCompFlagLumaChroma))
162         {
163             // Disable perceptual colorspace metrics when packing to swizzled or non-RGB pixel formats.
164             p.m_perceptual = false;
165         }
166
167         image_u8 tmp_img(img);
168
169         clear();
170
171         m_format = fmt;
172
173         if (cook)
174             cook_image(tmp_img);
175
176         if ((pixel_format_helpers::is_alpha_only(fmt)) && (!tmp_img.has_alpha()))
177             tmp_img.set_alpha_to_luma();
178
179         dxt_format dxt_fmt = pixel_format_helpers::get_dxt_format(fmt);
180
181         dxt_image *pDXT_image = vogl_new(dxt_image);
182         if (!pDXT_image->init(dxt_fmt, tmp_img, p))
183         {
184             clear();
185             return false;
186         }
187
188         assign(pDXT_image, fmt, orient_flags);
189
190         return true;
191     }
192
193     bool mip_level::pack_to_dxt(pixel_format fmt, bool cook, const dxt_image::pack_params &p)
194     {
195         VOGL_ASSERT(pixel_format_helpers::is_dxt(fmt));
196         if (!pixel_format_helpers::is_dxt(fmt))
197             return false;
198
199         image_u8 tmp_img;
200         image_u8 *pImage = get_unpacked_image(tmp_img, cUnpackFlagUncook);
201
202         return pack_to_dxt(*pImage, fmt, cook, p, m_orient_flags);
203     }
204
205     bool mip_level::unpack_from_dxt(bool uncook)
206     {
207         if (!m_pDXTImage)
208             return false;
209
210         image_u8 *pNew_img = vogl_new(image_u8);
211         image_u8 *pImg = get_unpacked_image(*pNew_img, uncook ? cUnpackFlagUncook : 0);
212         VOGL_NOTE_UNUSED(pImg);
213
214         VOGL_ASSERT(pImg == pNew_img);
215
216         assign(pNew_img, PIXEL_FMT_INVALID, m_orient_flags);
217         return true;
218     }
219
220     bool mip_level::is_flipped() const
221     {
222         return ((m_orient_flags & (cOrientationFlagXFlipped | cOrientationFlagYFlipped)) != 0);
223     }
224
225     bool mip_level::is_x_flipped() const
226     {
227         return ((m_orient_flags & cOrientationFlagXFlipped) != 0);
228     }
229
230     bool mip_level::is_y_flipped() const
231     {
232         return ((m_orient_flags & cOrientationFlagYFlipped) != 0);
233     }
234
235     bool mip_level::can_unflip_without_unpacking() const
236     {
237         if (!is_valid())
238             return false;
239
240         if (!is_packed())
241             return true;
242
243         bool can_unflip = true;
244         if (m_orient_flags & cOrientationFlagXFlipped)
245         {
246             if (!m_pDXTImage->can_flip(0))
247                 can_unflip = false;
248         }
249         if (m_orient_flags & cOrientationFlagYFlipped)
250         {
251             if (!m_pDXTImage->can_flip(1))
252                 can_unflip = false;
253         }
254
255         return can_unflip;
256     }
257
258     bool mip_level::unflip(bool allow_unpacking_to_flip, bool uncook_if_necessary_to_unpack)
259     {
260         if (!is_valid())
261             return false;
262
263         if (!is_flipped())
264             return false;
265
266         if (is_packed())
267         {
268             if (can_unflip_without_unpacking())
269             {
270                 if (m_orient_flags & cOrientationFlagXFlipped)
271                 {
272                     m_pDXTImage->flip_x();
273                     m_orient_flags = static_cast<orientation_flags_t>(m_orient_flags & ~cOrientationFlagXFlipped);
274                 }
275
276                 if (m_orient_flags & cOrientationFlagYFlipped)
277                 {
278                     m_pDXTImage->flip_y();
279                     m_orient_flags = static_cast<orientation_flags_t>(m_orient_flags & ~cOrientationFlagYFlipped);
280                 }
281
282                 return true;
283             }
284
285             if (!allow_unpacking_to_flip)
286                 return false;
287         }
288
289         unpack_from_dxt(uncook_if_necessary_to_unpack);
290
291         if (m_orient_flags & cOrientationFlagXFlipped)
292         {
293             m_pImage->flip_x();
294             m_orient_flags = static_cast<orientation_flags_t>(m_orient_flags & ~cOrientationFlagXFlipped);
295         }
296
297         if (m_orient_flags & cOrientationFlagYFlipped)
298         {
299             m_pImage->flip_y();
300             m_orient_flags = static_cast<orientation_flags_t>(m_orient_flags & ~cOrientationFlagYFlipped);
301         }
302
303         return true;
304     }
305
306     bool mip_level::set_alpha_to_luma()
307     {
308         if (m_pDXTImage)
309             unpack_from_dxt(true);
310
311         m_pImage->set_alpha_to_luma();
312
313         m_comp_flags = m_pImage->get_comp_flags();
314
315         if (m_pImage->is_grayscale())
316             m_format = PIXEL_FMT_A8L8;
317         else
318             m_format = PIXEL_FMT_A8R8G8B8;
319
320         return true;
321     }
322
323     bool mip_level::convert(image_utils::conversion_type conv_type)
324     {
325         if (m_pDXTImage)
326             unpack_from_dxt(true);
327
328         image_utils::convert_image(*m_pImage, conv_type);
329
330         m_comp_flags = m_pImage->get_comp_flags();
331
332         if (m_pImage->is_grayscale())
333             m_format = m_pImage->has_alpha() ? PIXEL_FMT_A8L8 : PIXEL_FMT_L8;
334         else
335             m_format = m_pImage->has_alpha() ? PIXEL_FMT_A8R8G8B8 : PIXEL_FMT_R8G8B8;
336
337         return true;
338     }
339
340     bool mip_level::convert(pixel_format fmt, bool cook, const dxt_image::pack_params &p)
341     {
342         if (pixel_format_helpers::is_dxt(fmt))
343             return pack_to_dxt(fmt, cook, p);
344
345         image_u8 tmp_img;
346         image_u8 *pImg = get_unpacked_image(tmp_img, cUnpackFlagUncook);
347
348         image_u8 *pImage = vogl_new(image_u8);
349         pImage->set_comp_flags(pixel_format_helpers::get_component_flags(fmt));
350
351         if (!pImage->crop(pImg->get_width(), pImg->get_height()))
352             return false;
353
354         for (uint y = 0; y < pImg->get_height(); y++)
355         {
356             for (uint x = 0; x < pImg->get_width(); x++)
357             {
358                 color_quad_u8 c((*pImg)(x, y));
359
360                 if ((pixel_format_helpers::is_alpha_only(fmt)) && (!pImg->has_alpha()))
361                 {
362                     c.a = static_cast<uint8>(c.get_luma());
363                 }
364                 else
365                 {
366                     if (pImage->is_grayscale())
367                     {
368                         uint8 g = static_cast<uint8>(c.get_luma());
369                         c.r = g;
370                         c.g = g;
371                         c.b = g;
372                     }
373
374                     if (!pImage->is_component_valid(3))
375                         c.a = 255;
376                 }
377
378                 (*pImage)(x, y) = c;
379             }
380         }
381
382         assign(pImage, fmt, m_orient_flags);
383
384         return true;
385     }
386
387     void mip_level::cook_image(image_u8 &img) const
388     {
389         image_utils::conversion_type conv_type = image_utils::get_conversion_type(true, m_format);
390
391         if (conv_type != image_utils::cConversion_Invalid)
392             image_utils::convert_image(img, conv_type);
393     }
394
395     void mip_level::uncook_image(image_u8 &img) const
396     {
397         image_utils::conversion_type conv_type = image_utils::get_conversion_type(false, m_format);
398
399         if (conv_type != image_utils::cConversion_Invalid)
400             image_utils::convert_image(img, conv_type);
401     }
402
403     image_u8 *mip_level::get_unpacked_image(image_u8 &tmp, uint unpack_flags) const
404     {
405         if (!is_valid())
406             return NULL;
407
408         if (m_pDXTImage)
409         {
410             m_pDXTImage->unpack(tmp);
411
412             tmp.set_comp_flags(m_comp_flags);
413
414             if (unpack_flags & cUnpackFlagUncook)
415                 uncook_image(tmp);
416         }
417         else if ((unpack_flags & cUnpackFlagUnflip) && (m_orient_flags & (cOrientationFlagXFlipped | cOrientationFlagYFlipped)))
418             tmp = *m_pImage;
419         else
420             return m_pImage;
421
422         if (unpack_flags & cUnpackFlagUnflip)
423         {
424             if (m_orient_flags & cOrientationFlagXFlipped)
425                 tmp.flip_x();
426             if (m_orient_flags & cOrientationFlagYFlipped)
427                 tmp.flip_y();
428         }
429
430         return &tmp;
431     }
432
433     bool mip_level::flip_x()
434     {
435         if (!is_valid())
436             return false;
437
438         if (m_pDXTImage)
439             return m_pDXTImage->flip_x();
440         else if (m_pImage)
441         {
442             m_pImage->flip_x();
443             return true;
444         }
445
446         return false;
447     }
448
449     bool mip_level::flip_y()
450     {
451         if (!is_valid())
452             return false;
453
454         if (m_pDXTImage)
455             return m_pDXTImage->flip_y();
456         else if (m_pImage)
457         {
458             m_pImage->flip_y();
459             return true;
460         }
461
462         return false;
463     }
464
465     // -------------------------------------------------------------------------
466
467     mipmapped_texture::mipmapped_texture()
468         : m_width(0),
469           m_height(0),
470           m_comp_flags(pixel_format_helpers::cDefaultCompFlags),
471           m_format(PIXEL_FMT_INVALID),
472           m_source_file_type(texture_file_types::cFormatInvalid)
473     {
474     }
475
476     mipmapped_texture::~mipmapped_texture()
477     {
478         free_all_mips();
479     }
480
481     void mipmapped_texture::clear()
482     {
483         free_all_mips();
484
485         m_name.clear();
486         m_width = 0;
487         m_height = 0;
488         m_comp_flags = pixel_format_helpers::cDefaultCompFlags;
489         m_format = PIXEL_FMT_INVALID;
490         m_source_file_type = texture_file_types::cFormatInvalid;
491         m_last_error.clear();
492     }
493
494     void mipmapped_texture::free_all_mips()
495     {
496         for (uint i = 0; i < m_faces.size(); i++)
497             for (uint j = 0; j < m_faces[i].size(); j++)
498                 vogl_delete(m_faces[i][j]);
499
500         m_faces.clear();
501     }
502
503     mipmapped_texture::mipmapped_texture(const mipmapped_texture &other)
504         : m_width(0),
505           m_height(0),
506           m_comp_flags(pixel_format_helpers::cDefaultCompFlags),
507           m_format(PIXEL_FMT_INVALID)
508     {
509         *this = other;
510     }
511
512     mipmapped_texture &mipmapped_texture::operator=(const mipmapped_texture &rhs)
513     {
514         if (this == &rhs)
515             return *this;
516
517         clear();
518
519         m_name = rhs.m_name;
520         m_width = rhs.m_width;
521         m_height = rhs.m_height;
522
523         m_comp_flags = rhs.m_comp_flags;
524         m_format = rhs.m_format;
525
526         m_faces.resize(rhs.m_faces.size());
527         for (uint i = 0; i < m_faces.size(); i++)
528         {
529             m_faces[i].resize(rhs.m_faces[i].size());
530
531             for (uint j = 0; j < rhs.m_faces[i].size(); j++)
532                 m_faces[i][j] = vogl_new(mip_level, *rhs.m_faces[i][j]);
533         }
534
535         VOGL_ASSERT((!is_valid()) || check());
536
537         return *this;
538     }
539
540     bool mipmapped_texture::read_dds(data_stream_serializer &serializer)
541     {
542         if (!read_dds_internal(serializer))
543         {
544             clear();
545             return false;
546         }
547
548         return true;
549     }
550
551     bool mipmapped_texture::read_dds_internal(data_stream_serializer &serializer)
552     {
553         VOGL_ASSERT(serializer.get_little_endian());
554
555         clear();
556
557         set_last_error("Not a DDS file");
558
559         uint8 hdr[4];
560         if (!serializer.read(hdr, sizeof(hdr)))
561             return false;
562
563         if (memcmp(hdr, "DDS ", 4) != 0)
564             return false;
565
566         DDSURFACEDESC2 desc;
567         if (!serializer.read(&desc, sizeof(desc)))
568             return false;
569
570         if (!c_vogl_little_endian_platform)
571             utils::endian_switch_dwords(reinterpret_cast<uint32 *>(&desc), sizeof(desc) / sizeof(uint32));
572
573         if (desc.dwSize != sizeof(desc))
574             return false;
575
576         if ((!desc.dwHeight) || (!desc.dwWidth) || (desc.dwHeight > cDDSMaxImageDimensions) || (desc.dwWidth > cDDSMaxImageDimensions))
577             return false;
578
579         m_width = desc.dwWidth;
580         m_height = desc.dwHeight;
581
582         uint num_mip_levels = 1;
583
584         if ((desc.dwFlags & DDSD_MIPMAPCOUNT) && (desc.ddsCaps.dwCaps & DDSCAPS_MIPMAP) && (desc.dwMipMapCount))
585         {
586             num_mip_levels = desc.dwMipMapCount;
587             if (num_mip_levels > utils::compute_max_mips(desc.dwWidth, desc.dwHeight))
588                 return false;
589         }
590
591         uint num_faces = 1;
592
593         if (desc.ddsCaps.dwCaps & DDSCAPS_COMPLEX)
594         {
595             if (desc.ddsCaps.dwCaps2 & DDSCAPS2_CUBEMAP)
596             {
597                 const uint all_faces_mask = DDSCAPS2_CUBEMAP_POSITIVEX | DDSCAPS2_CUBEMAP_NEGATIVEX | DDSCAPS2_CUBEMAP_POSITIVEY | DDSCAPS2_CUBEMAP_NEGATIVEY | DDSCAPS2_CUBEMAP_POSITIVEZ | DDSCAPS2_CUBEMAP_NEGATIVEZ;
598                 if ((desc.ddsCaps.dwCaps2 & all_faces_mask) != all_faces_mask)
599                 {
600                     set_last_error("Incomplete cubemaps unsupported");
601                     return false;
602                 }
603
604                 num_faces = 6;
605             }
606             else if (desc.ddsCaps.dwCaps2 & DDSCAPS2_VOLUME)
607             {
608                 set_last_error("Volume textures unsupported");
609                 return false;
610             }
611         }
612
613         if (desc.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8)
614         {
615             // It's difficult to even make P8 textures with existing tools:
616             // nvdxt just hangs
617             // dxtex.exe just makes all-white textures
618             // So screw it.
619             set_last_error("Palettized textures unsupported");
620             return false;
621         }
622
623         dxt_format dxt_fmt = cDXTInvalid;
624
625         if (desc.ddpfPixelFormat.dwFlags & DDPF_FOURCC)
626         {
627             // http://code.google.com/p/nvidia-texture-tools/issues/detail?id=41
628             // ATI2 YX:            0 (0x00000000)
629             // ATI2 XY:   1498952257 (0x59583241) (BC5)
630             // ATI Compressonator obeys this stuff, nvidia's tools (like readdxt) don't - oh great
631
632             switch (desc.ddpfPixelFormat.dwFourCC)
633             {
634                 case PIXEL_FMT_DXT1:
635                 {
636                     m_format = PIXEL_FMT_DXT1;
637                     dxt_fmt = cDXT1;
638                     break;
639                 }
640                 case PIXEL_FMT_DXT2:
641                 case PIXEL_FMT_DXT3:
642                 {
643                     m_format = PIXEL_FMT_DXT3;
644                     dxt_fmt = cDXT3;
645                     break;
646                 }
647                 case PIXEL_FMT_DXT4:
648                 case PIXEL_FMT_DXT5:
649                 {
650                     switch (desc.ddpfPixelFormat.dwRGBBitCount)
651                     {
652                         case PIXEL_FMT_DXT5_CCxY:
653                             m_format = PIXEL_FMT_DXT5_CCxY;
654                             break;
655                         case PIXEL_FMT_DXT5_xGxR:
656                             m_format = PIXEL_FMT_DXT5_xGxR;
657                             break;
658                         case PIXEL_FMT_DXT5_xGBR:
659                             m_format = PIXEL_FMT_DXT5_xGBR;
660                             break;
661                         case PIXEL_FMT_DXT5_AGBR:
662                             m_format = PIXEL_FMT_DXT5_AGBR;
663                             break;
664                         default:
665                             m_format = PIXEL_FMT_DXT5;
666                             break;
667                     }
668
669                     dxt_fmt = cDXT5;
670                     break;
671                 }
672                 case PIXEL_FMT_3DC:
673                 {
674                     if (desc.ddpfPixelFormat.dwRGBBitCount == VOGL_PIXEL_FMT_FOURCC('A', '2', 'X', 'Y'))
675                     {
676                         dxt_fmt = cDXN_XY;
677                         m_format = PIXEL_FMT_DXN;
678                     }
679                     else
680                     {
681                         dxt_fmt = cDXN_YX; // aka ATI2
682                         m_format = PIXEL_FMT_3DC;
683                     }
684
685                     break;
686                 }
687                 case PIXEL_FMT_DXT5A:
688                 {
689                     m_format = PIXEL_FMT_DXT5A;
690                     dxt_fmt = cDXT5A;
691                     break;
692                 }
693                 case PIXEL_FMT_ETC1:
694                 {
695                     m_format = PIXEL_FMT_ETC1;
696                     dxt_fmt = cETC1;
697                     break;
698                 }
699                 default:
700                 {
701                     dynamic_string err_msg(cVarArg, "Unsupported DDS FOURCC format: 0x%08X", desc.ddpfPixelFormat.dwFourCC);
702                     set_last_error(err_msg.get_ptr());
703                     return false;
704                 }
705             }
706         }
707         else if ((desc.ddpfPixelFormat.dwRGBBitCount < 8) || (desc.ddpfPixelFormat.dwRGBBitCount > 32) || (desc.ddpfPixelFormat.dwRGBBitCount & 7))
708         {
709             set_last_error("Unsupported bit count");
710             return false;
711         }
712         else if (desc.ddpfPixelFormat.dwFlags & DDPF_RGB)
713         {
714             if (desc.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE)
715             {
716                 if (desc.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS)
717                     m_format = PIXEL_FMT_A8L8;
718                 else
719                     m_format = PIXEL_FMT_L8;
720             }
721             else if (desc.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS)
722                 m_format = PIXEL_FMT_A8R8G8B8;
723             else
724                 m_format = PIXEL_FMT_R8G8B8;
725         }
726         else if (desc.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS)
727         {
728             if (desc.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE)
729                 m_format = PIXEL_FMT_A8L8;
730             else
731                 m_format = PIXEL_FMT_A8;
732         }
733         else if (desc.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE)
734         {
735             m_format = PIXEL_FMT_L8;
736         }
737         else if (desc.ddpfPixelFormat.dwFlags & DDPF_ALPHA)
738         {
739             m_format = PIXEL_FMT_A8;
740         }
741         else
742         {
743             set_last_error("Unsupported format");
744             return false;
745         }
746
747         m_comp_flags = pixel_format_helpers::get_component_flags(m_format);
748
749         uint bits_per_pixel = desc.ddpfPixelFormat.dwRGBBitCount;
750
751         if (desc.ddpfPixelFormat.dwFlags & DDPF_FOURCC)
752             bits_per_pixel = pixel_format_helpers::get_bpp(m_format);
753
754         set_last_error("Load failed");
755
756         uint default_pitch;
757         if (desc.ddpfPixelFormat.dwFlags & DDPF_FOURCC)
758             default_pitch = (((desc.dwWidth + 3) & ~3) * ((desc.dwHeight + 3) & ~3) * bits_per_pixel) >> 3;
759         else
760             default_pitch = (desc.dwWidth * bits_per_pixel) >> 3;
761
762         uint pitch = 0;
763         if ((desc.dwFlags & DDSD_PITCH) && (!(desc.dwFlags & DDSD_LINEARSIZE)))
764         {
765             pitch = desc.lPitch;
766         }
767
768         if (!pitch)
769             pitch = default_pitch;
770 #if 0
771         else if (pitch & 3)
772         {
773             // MS's DDS docs say the pitch must be DWORD aligned - but this isn't always the case.
774             // ATI Compressonator writes images with non-DWORD aligned pitches, and the DDSWithoutD3DX sample from MS doesn't compute the proper DWORD aligned pitch when reading DDS
775             // files, so the docs must be wrong/outdated.
776             console::warning("DDS file's pitch is not divisible by 4 - trying to load anyway.\n");
777         }
778 #endif
779         // Check for obviously wacky source pitches (probably a corrupted/invalid file).
780         else if (pitch > default_pitch * 8)
781         {
782             set_last_error("Invalid pitch");
783             return false;
784         }
785
786         vogl::vector<uint8> load_buf;
787
788         uint mask_size[4];
789         mask_size[0] = math::bitmask_size(desc.ddpfPixelFormat.dwRBitMask);
790         mask_size[1] = math::bitmask_size(desc.ddpfPixelFormat.dwGBitMask);
791         mask_size[2] = math::bitmask_size(desc.ddpfPixelFormat.dwBBitMask);
792         mask_size[3] = math::bitmask_size(desc.ddpfPixelFormat.dwRGBAlphaBitMask);
793
794         uint mask_ofs[4];
795         mask_ofs[0] = math::bitmask_ofs(desc.ddpfPixelFormat.dwRBitMask);
796         mask_ofs[1] = math::bitmask_ofs(desc.ddpfPixelFormat.dwGBitMask);
797         mask_ofs[2] = math::bitmask_ofs(desc.ddpfPixelFormat.dwBBitMask);
798         mask_ofs[3] = math::bitmask_ofs(desc.ddpfPixelFormat.dwRGBAlphaBitMask);
799
800         if ((desc.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE) && (!mask_size[0]))
801         {
802             mask_size[0] = desc.ddpfPixelFormat.dwRGBBitCount >> 3;
803             if (desc.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS)
804                 mask_size[0] /= 2;
805         }
806
807         m_faces.resize(num_faces);
808
809         bool dxt1_alpha = false;
810
811         for (uint face_index = 0; face_index < num_faces; face_index++)
812         {
813             m_faces[face_index].resize(num_mip_levels);
814
815             for (uint level_index = 0; level_index < num_mip_levels; level_index++)
816             {
817                 const uint width = math::maximum<uint>(desc.dwWidth >> level_index, 1U);
818                 const uint height = math::maximum<uint>(desc.dwHeight >> level_index, 1U);
819
820                 mip_level *pMip = vogl_new(mip_level);
821                 m_faces[face_index][level_index] = pMip;
822
823                 if (desc.ddpfPixelFormat.dwFlags & DDPF_FOURCC)
824                 {
825                     const uint bytes_per_block = pixel_format_helpers::get_dxt_bytes_per_block(m_format);
826
827                     const uint num_blocks_x = (width + 3) >> 2;
828                     const uint num_blocks_y = (height + 3) >> 2;
829
830                     const uint actual_level_pitch = num_blocks_x * num_blocks_y * bytes_per_block;
831                     const uint level_pitch = level_index ? actual_level_pitch : pitch;
832
833                     dxt_image *pDXTImage = vogl_new(dxt_image);
834                     if (!pDXTImage->init(dxt_fmt, width, height, false))
835                     {
836                         vogl_delete(pDXTImage);
837
838                         VOGL_ASSERT_ALWAYS;
839                         return false;
840                     }
841
842                     VOGL_ASSERT(pDXTImage->get_element_vec().size() * sizeof(dxt_image::element) == actual_level_pitch);
843
844                     if (!serializer.read(&pDXTImage->get_element_vec()[0], actual_level_pitch))
845                     {
846                         vogl_delete(pDXTImage);
847
848                         return false;
849                     }
850
851                     // DDS image in memory are always assumed to be little endian - the same as DDS itself.
852                     //if (c_vogl_big_endian_platform)
853                     //   utils::endian_switch_words(reinterpret_cast<uint16*>(&pDXTImage->get_element_vec()[0]), actual_level_pitch / sizeof(uint16));
854
855                     if (level_pitch > actual_level_pitch)
856                     {
857                         if (!serializer.skip(level_pitch - actual_level_pitch))
858                         {
859                             vogl_delete(pDXTImage);
860
861                             return false;
862                         }
863                     }
864
865                     if ((m_format == PIXEL_FMT_DXT1) && (!dxt1_alpha))
866                         dxt1_alpha = pDXTImage->has_alpha();
867
868                     pMip->assign(pDXTImage, m_format);
869                 }
870                 else
871                 {
872                     image_u8 *pImage = vogl_new(image_u8, width, height);
873
874                     pImage->set_comp_flags(m_comp_flags);
875
876                     const uint bytes_per_pixel = desc.ddpfPixelFormat.dwRGBBitCount >> 3;
877                     const uint actual_line_pitch = width * bytes_per_pixel;
878                     const uint line_pitch = level_index ? actual_line_pitch : pitch;
879
880                     if (load_buf.size() < line_pitch)
881                         load_buf.resize(line_pitch);
882
883                     color_quad_u8 q(0, 0, 0, 255);
884
885                     for (uint y = 0; y < height; y++)
886                     {
887                         if (!serializer.read(&load_buf[0], line_pitch))
888                         {
889                             vogl_delete(pImage);
890                             return false;
891                         }
892
893                         color_quad_u8 *pDst = pImage->get_scanline(y);
894
895                         for (uint x = 0; x < width; x++)
896                         {
897                             const uint8 *pPixel = &load_buf[x * bytes_per_pixel];
898
899                             uint c = 0;
900                             // Assumes DDS is always little endian.
901                             for (uint l = 0; l < bytes_per_pixel; l++)
902                                 c |= (pPixel[l] << (l * 8U));
903
904                             for (uint i = 0; i < 4; i++)
905                             {
906                                 if (!mask_size[i])
907                                     continue;
908
909                                 uint mask = (1U << mask_size[i]) - 1U;
910                                 uint bits = (c >> mask_ofs[i]) & mask;
911
912                                 uint v = (bits * 255 + (mask >> 1)) / mask;
913
914                                 q.set_component(i, v);
915                             }
916
917                             if (desc.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE)
918                             {
919                                 q.g = q.r;
920                                 q.b = q.r;
921                             }
922
923                             *pDst++ = q;
924                         }
925                     }
926
927                     pMip->assign(pImage, m_format);
928
929                     VOGL_ASSERT(pMip->get_comp_flags() == m_comp_flags);
930                 }
931             }
932         }
933
934         clear_last_error();
935
936         if (dxt1_alpha)
937             change_dxt1_to_dxt1a();
938
939         return true;
940     }
941
942     void mipmapped_texture::change_dxt1_to_dxt1a()
943     {
944         if (m_format != PIXEL_FMT_DXT1)
945             return;
946
947         m_format = PIXEL_FMT_DXT1A;
948
949         m_comp_flags = pixel_format_helpers::get_component_flags(m_format);
950
951         for (uint f = 0; f < m_faces.size(); f++)
952         {
953             for (uint l = 0; l < m_faces[f].size(); l++)
954             {
955                 if (m_faces[f][l]->get_dxt_image())
956                 {
957                     m_faces[f][l]->set_format(m_format);
958                     m_faces[f][l]->set_comp_flags(m_comp_flags);
959
960                     m_faces[f][l]->get_dxt_image()->change_dxt1_to_dxt1a();
961                 }
962             }
963         }
964     }
965
966     bool mipmapped_texture::check() const
967     {
968         uint levels = 0;
969         orientation_flags_t orient_flags = cDefaultOrientationFlags;
970         for (uint f = 0; f < m_faces.size(); f++)
971         {
972             if (!f)
973             {
974                 levels = m_faces[f].size();
975                 if ((levels) && (m_faces[f][0]))
976                     orient_flags = m_faces[f][0]->get_orientation_flags();
977             }
978             else if (m_faces[f].size() != levels)
979                 return false;
980
981             for (uint l = 0; l < m_faces[f].size(); l++)
982             {
983                 mip_level *p = m_faces[f][l];
984                 if (!p)
985                     return false;
986
987                 if (!p->is_valid())
988                     return false;
989
990                 if (p->get_orientation_flags() != orient_flags)
991                     return false;
992
993                 if (!l)
994                 {
995                     if (m_width != p->get_width())
996                         return false;
997                     if (m_height != p->get_height())
998                         return false;
999                 }
1000
1001                 if (p->get_comp_flags() != m_comp_flags)
1002                     return false;
1003
1004                 if (p->get_format() != m_format)
1005                     return false;
1006
1007                 if (p->get_image())
1008                 {
1009                     if (pixel_format_helpers::is_dxt(p->get_format()))
1010                         return false;
1011
1012                     if (p->get_image()->get_width() != p->get_width())
1013                         return false;
1014                     if (p->get_image()->get_height() != p->get_height())
1015                         return false;
1016                     if (p->get_image()->get_comp_flags() != m_comp_flags)
1017                         return false;
1018                 }
1019                 else if (!pixel_format_helpers::is_dxt(p->get_format()))
1020                     return false;
1021             }
1022         }
1023
1024         return true;
1025     }
1026
1027     bool mipmapped_texture::write_dds(data_stream_serializer &serializer) const
1028     {
1029         if (!m_width)
1030         {
1031             set_last_error("Nothing to write");
1032             return false;
1033         }
1034
1035         set_last_error("write_dds() failed");
1036
1037         if (!serializer.write("DDS ", sizeof(uint32)))
1038             return false;
1039
1040         DDSURFACEDESC2 desc;
1041         utils::zero_object(desc);
1042
1043         desc.dwSize = sizeof(desc);
1044         desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS;
1045
1046         desc.dwWidth = m_width;
1047         desc.dwHeight = m_height;
1048
1049         desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE;
1050         desc.ddpfPixelFormat.dwSize = sizeof(desc.ddpfPixelFormat);
1051
1052         if (get_num_levels() > 1)
1053         {
1054             desc.dwMipMapCount = get_num_levels();
1055             desc.dwFlags |= DDSD_MIPMAPCOUNT;
1056             desc.ddsCaps.dwCaps |= (DDSCAPS_MIPMAP | DDSCAPS_COMPLEX);
1057         }
1058
1059         if (get_num_faces() > 1)
1060         {
1061             desc.ddsCaps.dwCaps |= DDSCAPS_COMPLEX;
1062             desc.ddsCaps.dwCaps2 |= DDSCAPS2_CUBEMAP;
1063             desc.ddsCaps.dwCaps2 |= DDSCAPS2_CUBEMAP_POSITIVEX | DDSCAPS2_CUBEMAP_NEGATIVEX | DDSCAPS2_CUBEMAP_POSITIVEY | DDSCAPS2_CUBEMAP_NEGATIVEY | DDSCAPS2_CUBEMAP_POSITIVEZ | DDSCAPS2_CUBEMAP_NEGATIVEZ;
1064         }
1065
1066         bool dxt_format = false;
1067         if (pixel_format_helpers::is_dxt(m_format))
1068         {
1069             dxt_format = true;
1070
1071             desc.ddpfPixelFormat.dwFlags |= DDPF_FOURCC;
1072
1073             switch (m_format)
1074             {
1075                 case PIXEL_FMT_ETC1:
1076                 {
1077                     desc.ddpfPixelFormat.dwFourCC = (uint32)PIXEL_FMT_ETC1;
1078                     desc.ddpfPixelFormat.dwRGBBitCount = 0;
1079                     break;
1080                 }
1081                 case PIXEL_FMT_DXN:
1082                 {
1083                     desc.ddpfPixelFormat.dwFourCC = (uint32)PIXEL_FMT_3DC;
1084                     desc.ddpfPixelFormat.dwRGBBitCount = PIXEL_FMT_DXN;
1085                     break;
1086                 }
1087                 case PIXEL_FMT_DXT1A:
1088                 {
1089                     desc.ddpfPixelFormat.dwFourCC = (uint32)PIXEL_FMT_DXT1;
1090                     desc.ddpfPixelFormat.dwRGBBitCount = 0;
1091                     break;
1092                 }
1093                 case PIXEL_FMT_DXT5_CCxY:
1094                 {
1095                     desc.ddpfPixelFormat.dwFourCC = (uint32)PIXEL_FMT_DXT5;
1096                     desc.ddpfPixelFormat.dwRGBBitCount = (uint32)PIXEL_FMT_DXT5_CCxY;
1097                     break;
1098                 }
1099                 case PIXEL_FMT_DXT5_xGxR:
1100                 {
1101                     desc.ddpfPixelFormat.dwFourCC = (uint32)PIXEL_FMT_DXT5;
1102                     desc.ddpfPixelFormat.dwRGBBitCount = (uint32)PIXEL_FMT_DXT5_xGxR;
1103                     break;
1104                 }
1105                 case PIXEL_FMT_DXT5_xGBR:
1106                 {
1107                     desc.ddpfPixelFormat.dwFourCC = (uint32)PIXEL_FMT_DXT5;
1108                     desc.ddpfPixelFormat.dwRGBBitCount = (uint32)PIXEL_FMT_DXT5_xGBR;
1109                     break;
1110                 }
1111                 case PIXEL_FMT_DXT5_AGBR:
1112                 {
1113                     desc.ddpfPixelFormat.dwFourCC = (uint32)PIXEL_FMT_DXT5;
1114                     desc.ddpfPixelFormat.dwRGBBitCount = (uint32)PIXEL_FMT_DXT5_AGBR;
1115                     break;
1116                 }
1117                 default:
1118                 {
1119                     desc.ddpfPixelFormat.dwFourCC = (uint32)m_format;
1120                     desc.ddpfPixelFormat.dwRGBBitCount = 0;
1121                     break;
1122                 }
1123             }
1124
1125             uint bits_per_pixel = pixel_format_helpers::get_bpp(m_format);
1126             desc.lPitch = (((desc.dwWidth + 3) & ~3) * ((desc.dwHeight + 3) & ~3) * bits_per_pixel) >> 3;
1127             desc.dwFlags |= DDSD_LINEARSIZE;
1128         }
1129         else
1130         {
1131             switch (m_format)
1132             {
1133                 case PIXEL_FMT_A8R8G8B8:
1134                 {
1135                     desc.ddpfPixelFormat.dwFlags |= (DDPF_RGB | DDPF_ALPHAPIXELS);
1136                     desc.ddpfPixelFormat.dwRGBBitCount = 32;
1137                     desc.ddpfPixelFormat.dwRBitMask = 0xFF0000;
1138                     desc.ddpfPixelFormat.dwGBitMask = 0x00FF00;
1139                     desc.ddpfPixelFormat.dwBBitMask = 0x0000FF;
1140                     desc.ddpfPixelFormat.dwRGBAlphaBitMask = 0xFF000000;
1141                     break;
1142                 }
1143                 case PIXEL_FMT_R8G8B8:
1144                 {
1145                     desc.ddpfPixelFormat.dwFlags |= DDPF_RGB;
1146                     desc.ddpfPixelFormat.dwRGBBitCount = 24;
1147                     desc.ddpfPixelFormat.dwRBitMask = 0xFF0000;
1148                     desc.ddpfPixelFormat.dwGBitMask = 0x00FF00;
1149                     desc.ddpfPixelFormat.dwBBitMask = 0x0000FF;
1150                     break;
1151                 }
1152                 case PIXEL_FMT_A8:
1153                 {
1154                     desc.ddpfPixelFormat.dwFlags |= DDPF_ALPHA;
1155                     desc.ddpfPixelFormat.dwRGBBitCount = 8;
1156                     desc.ddpfPixelFormat.dwRGBAlphaBitMask = 0xFF;
1157                     break;
1158                 }
1159                 case PIXEL_FMT_L8:
1160                 {
1161                     desc.ddpfPixelFormat.dwFlags |= DDPF_LUMINANCE;
1162                     desc.ddpfPixelFormat.dwRGBBitCount = 8;
1163                     desc.ddpfPixelFormat.dwRBitMask = 0xFF;
1164                     break;
1165                 }
1166                 case PIXEL_FMT_A8L8:
1167                 {
1168                     desc.ddpfPixelFormat.dwFlags |= DDPF_ALPHAPIXELS | DDPF_LUMINANCE;
1169                     desc.ddpfPixelFormat.dwRGBBitCount = 16;
1170                     desc.ddpfPixelFormat.dwRBitMask = 0xFF;
1171                     desc.ddpfPixelFormat.dwRGBAlphaBitMask = 0xFF00;
1172                     break;
1173                 }
1174                 default:
1175                 {
1176                     VOGL_ASSERT(false);
1177                     return false;
1178                 }
1179             }
1180
1181             uint bits_per_pixel = desc.ddpfPixelFormat.dwRGBBitCount;
1182             desc.lPitch = (desc.dwWidth * bits_per_pixel) >> 3;
1183             desc.dwFlags |= DDSD_LINEARSIZE;
1184         }
1185
1186         if (!c_vogl_little_endian_platform)
1187             utils::endian_switch_dwords(reinterpret_cast<uint32 *>(&desc), sizeof(desc) / sizeof(uint32));
1188
1189         if (!serializer.write(&desc, sizeof(desc)))
1190             return false;
1191
1192         if (!c_vogl_little_endian_platform)
1193             utils::endian_switch_dwords(reinterpret_cast<uint32 *>(&desc), sizeof(desc) / sizeof(uint32));
1194
1195         vogl::vector<uint8> write_buf;
1196
1197         const bool can_unflip_packed_texture = can_unflip_without_unpacking();
1198         if ((is_packed()) && (is_flipped()) && (!can_unflip_without_unpacking()))
1199         {
1200             console::warning("mipmapped_texture::write_dds: One or more faces/miplevels cannot be unflipped without unpacking. Writing flipped .DDS texture.\n");
1201         }
1202
1203         for (uint face = 0; face < get_num_faces(); face++)
1204         {
1205             for (uint level = 0; level < get_num_levels(); level++)
1206             {
1207                 const mip_level *pLevel = get_level(face, level);
1208
1209                 if (dxt_format)
1210                 {
1211                     const uint width = pLevel->get_width();
1212                     const uint height = pLevel->get_height();
1213
1214                     VOGL_ASSERT(width == math::maximum<uint>(1, m_width >> level));
1215                     VOGL_ASSERT(height == math::maximum<uint>(1, m_height >> level));
1216
1217                     const dxt_image *p = pLevel->get_dxt_image();
1218                     dxt_image tmp;
1219                     if ((can_unflip_packed_texture) && (pLevel->get_orientation_flags() & (cOrientationFlagXFlipped | cOrientationFlagYFlipped)))
1220                     {
1221                         tmp = *p;
1222                         if (pLevel->get_orientation_flags() & cOrientationFlagXFlipped)
1223                         {
1224                             if (!tmp.flip_x())
1225                                 console::warning("mipmapped_texture::write_dds: Unable to unflip compressed texture on X axis\n");
1226                         }
1227
1228                         if (pLevel->get_orientation_flags() & cOrientationFlagYFlipped)
1229                         {
1230                             if (!tmp.flip_y())
1231                                 console::warning("mipmapped_texture::write_dds: Unable to unflip compressed texture on Y axis\n");
1232                         }
1233                         p = &tmp;
1234                     }
1235
1236                     const uint num_blocks_x = (width + 3) >> 2;
1237                     const uint num_blocks_y = (height + 3) >> 2;
1238
1239                     VOGL_ASSERT(num_blocks_x * num_blocks_y * p->get_elements_per_block() == p->get_total_elements());
1240                     VOGL_NOTE_UNUSED(width);
1241                     VOGL_NOTE_UNUSED(height);
1242                     VOGL_NOTE_UNUSED(num_blocks_x);
1243                     VOGL_NOTE_UNUSED(num_blocks_y);
1244
1245                     const uint size_in_bytes = p->get_total_elements() * sizeof(dxt_image::element);
1246                     if (size_in_bytes > write_buf.size())
1247                         write_buf.resize(size_in_bytes);
1248
1249                     memcpy(&write_buf[0], p->get_element_ptr(), size_in_bytes);
1250
1251                     // DXT data is always little endian in memory, just like the DDS format.
1252                     // (Except for ETC1, which contains big endian 64-bit QWORD's).
1253                     //if (!c_vogl_little_endian_platform)
1254                     //   utils::endian_switch_words(reinterpret_cast<WORD*>(&write_buf[0]), size_in_bytes / sizeof(WORD));
1255
1256                     if (!serializer.write(&write_buf[0], size_in_bytes))
1257                         return false;
1258                 }
1259                 else
1260                 {
1261                     const uint width = pLevel->get_width();
1262                     const uint height = pLevel->get_height();
1263
1264                     const image_u8 *p = pLevel->get_image();
1265                     image_u8 tmp;
1266                     if (pLevel->get_orientation_flags() & (cOrientationFlagXFlipped | cOrientationFlagYFlipped))
1267                     {
1268                         p = pLevel->get_unpacked_image(tmp, cUnpackFlagUnflip);
1269                     }
1270
1271                     const uint bits_per_pixel = desc.ddpfPixelFormat.dwRGBBitCount;
1272                     const uint bytes_per_pixel = bits_per_pixel >> 3;
1273
1274                     const uint pitch = width * bytes_per_pixel;
1275                     if (pitch > write_buf.size())
1276                         write_buf.resize(pitch);
1277
1278                     for (uint y = 0; y < height; y++)
1279                     {
1280                         const color_quad_u8 *pSrc = p->get_scanline(y);
1281                         const color_quad_u8 *pEnd = pSrc + width;
1282
1283                         uint8 *pDst = &write_buf[0];
1284
1285                         do
1286                         {
1287                             const color_quad_u8 &c = *pSrc;
1288
1289                             uint x = 0;
1290                             switch (m_format)
1291                             {
1292                                 case PIXEL_FMT_A8R8G8B8:
1293                                 {
1294                                     x = (c.a << 24) | (c.r << 16) | (c.g << 8) | c.b;
1295                                     break;
1296                                 }
1297                                 case PIXEL_FMT_R8G8B8:
1298                                 {
1299                                     x = (c.r << 16) | (c.g << 8) | c.b;
1300                                     break;
1301                                 }
1302                                 case PIXEL_FMT_A8:
1303                                 {
1304                                     x = c.a;
1305                                     break;
1306                                 }
1307                                 case PIXEL_FMT_A8L8:
1308                                 {
1309                                     x = (c.a << 8) | c.get_luma();
1310                                     break;
1311                                 }
1312                                 case PIXEL_FMT_L8:
1313                                 {
1314                                     x = c.get_luma();
1315                                     break;
1316                                 }
1317                                 default:
1318                                     break;
1319                             }
1320
1321                             pDst[0] = static_cast<uint8>(x);
1322                             if (bytes_per_pixel > 1)
1323                             {
1324                                 pDst[1] = static_cast<uint8>(x >> 8);
1325
1326                                 if (bytes_per_pixel > 2)
1327                                 {
1328                                     pDst[2] = static_cast<uint8>(x >> 16);
1329
1330                                     if (bytes_per_pixel > 3)
1331                                         pDst[3] = static_cast<uint8>(x >> 24);
1332                                 }
1333                             }
1334
1335                             pSrc++;
1336                             pDst += bytes_per_pixel;
1337
1338                         } while (pSrc != pEnd);
1339
1340                         if (!serializer.write(&write_buf[0], pitch))
1341                             return false;
1342                     }
1343                 }
1344             }
1345         }
1346
1347         clear_last_error();
1348
1349         return true;
1350     }
1351
1352     bool mipmapped_texture::read_ktx(data_stream_serializer &serializer)
1353     {
1354         clear();
1355
1356         set_last_error("Unable to read KTX file");
1357
1358         ktx_texture kt;
1359         if (!kt.read_from_stream(serializer))
1360             return false;
1361
1362         if ((kt.get_depth() > 1) || (kt.get_array_size() > 1))
1363         {
1364             set_last_error("read_ktx: 3D and array textures are not supported");
1365             return false;
1366         }
1367
1368         return read_ktx(kt);
1369     }
1370
1371     bool mipmapped_texture::read_ktx(const vogl::ktx_texture &ktx)
1372     {
1373         // Must be 1D, 2D, or a cubemap, with or without mipmaps.
1374         m_width = ktx.get_width();
1375         m_height = ktx.get_height();
1376
1377         uint num_mip_levels = ktx.get_num_mips();
1378         uint num_faces = ktx.get_num_faces();
1379
1380         uint32 vogl_fourcc = 0;
1381         dynamic_string vogl_fourcc_str;
1382         if (ktx.get_key_value_as_string("VOGL_FOURCC", vogl_fourcc_str))
1383         {
1384             if (vogl_fourcc_str.get_len() == 4)
1385             {
1386                 for (int i = 3; i >= 0; i--)
1387                     vogl_fourcc = (vogl_fourcc << 8) | vogl_fourcc_str[i];
1388             }
1389         }
1390
1391         const bool is_compressed_texture = ktx.is_compressed();
1392         dxt_format dxt_fmt = cDXTInvalid;
1393
1394         pixel_packer unpacker;
1395         if (is_compressed_texture)
1396         {
1397             switch (ktx.get_ogl_internal_fmt())
1398             {
1399                 case KTX_ETC1_RGB8_OES:
1400                     dxt_fmt = cETC1;
1401                     break;
1402                 case KTX_RGB_S3TC:
1403                 case KTX_RGB4_S3TC:
1404                 case KTX_COMPRESSED_RGB_S3TC_DXT1_EXT:
1405                 case KTX_COMPRESSED_SRGB_S3TC_DXT1_EXT:
1406                     dxt_fmt = cDXT1;
1407                     break;
1408                 case KTX_COMPRESSED_RGBA_S3TC_DXT1_EXT:
1409                 case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
1410                     dxt_fmt = cDXT1A;
1411                     break;
1412                 case KTX_RGBA_S3TC:
1413                 case KTX_RGBA4_S3TC:
1414                 case KTX_COMPRESSED_RGBA_S3TC_DXT3_EXT:
1415                 case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
1416                     dxt_fmt = cDXT3;
1417                     break;
1418                 case KTX_COMPRESSED_RGBA_S3TC_DXT5_EXT:
1419                 case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
1420                 case KTX_RGBA_DXT5_S3TC:
1421                 case KTX_RGBA4_DXT5_S3TC:
1422                     dxt_fmt = cDXT5;
1423                     break;
1424                 case KTX_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
1425                     dxt_fmt = cDXN_YX;
1426                     if (vogl_fourcc == PIXEL_FMT_DXN)
1427                     {
1428                         dxt_fmt = cDXN_XY;
1429                     }
1430                     break;
1431                 case KTX_COMPRESSED_LUMINANCE_LATC1_EXT:
1432                     dxt_fmt = cDXT5A;
1433                     break;
1434                 default:
1435                     set_last_error("Unsupported KTX internal format");
1436                     return false;
1437             }
1438
1439             m_format = pixel_format_helpers::from_dxt_format(dxt_fmt);
1440             if (m_format == PIXEL_FMT_INVALID)
1441             {
1442                 set_last_error("Unsupported KTX internal compressed format");
1443                 return false;
1444             }
1445
1446             if (vogl_fourcc != 0)
1447             {
1448                 switch (vogl_fourcc)
1449                 {
1450                     case PIXEL_FMT_DXT5_CCxY:
1451                     case PIXEL_FMT_DXT5_xGxR:
1452                     case PIXEL_FMT_DXT5_xGBR:
1453                     case PIXEL_FMT_DXT5_AGBR:
1454                     {
1455                         if (dxt_fmt == cDXT5)
1456                         {
1457                             m_format = static_cast<pixel_format>(vogl_fourcc);
1458                         }
1459                         break;
1460                     }
1461                 }
1462             }
1463         }
1464         else
1465         {
1466             m_format = PIXEL_FMT_A8R8G8B8;
1467             const uint type_size = ktx_get_ogl_type_size(ktx.get_ogl_type());
1468             const uint type_bits = type_size * 8;
1469
1470             // In GL the component order is specified as R_G_B_A, and is normally read/stored RIGHT to LEFT (last comp is first in memory), unless it's a REV format where it's read/stored LEFT to RIGHT (first comp is first in memory).
1471             // Normal component order:   1,2,3,4 (*last* component packed into LSB of output type)
1472             // Reversed component order: 4,3,2,1 (*first* component packed into LSB of output type)
1473             if (ktx_is_packed_pixel_ogl_type(ktx.get_ogl_type()))
1474             {
1475                 switch (ktx.get_ogl_type())
1476                 {
1477                     // 24bpp packed formats
1478                     case KTX_UNSIGNED_BYTE_3_3_2:
1479                         unpacker.init("B2G3R3");
1480                         m_format = PIXEL_FMT_R8G8B8;
1481                         break;
1482                     case KTX_UNSIGNED_BYTE_2_3_3_REV:
1483                         unpacker.init("R3G3B2");
1484                         m_format = PIXEL_FMT_R8G8B8;
1485                         break;
1486                     case KTX_UNSIGNED_SHORT_5_6_5:
1487                         unpacker.init("B5G6R5");
1488                         m_format = PIXEL_FMT_R8G8B8;
1489                         break;
1490                     case KTX_UNSIGNED_SHORT_5_6_5_REV:
1491                         unpacker.init("R5G6B5");
1492                         m_format = PIXEL_FMT_R8G8B8;
1493                         break;
1494                     // 32bpp packed formats
1495                     case KTX_UNSIGNED_SHORT_4_4_4_4:
1496                         unpacker.init("A4B4G4R4");
1497                         break;
1498                     case KTX_UNSIGNED_SHORT_4_4_4_4_REV:
1499                         unpacker.init("R4G4B4A4");
1500                         break;
1501                     case KTX_UNSIGNED_SHORT_5_5_5_1:
1502                         unpacker.init("A1B5G5R5");
1503                         break;
1504                     case KTX_UNSIGNED_SHORT_1_5_5_5_REV:
1505                         unpacker.init("R5G5B5A1");
1506                         break;
1507                     case KTX_UNSIGNED_INT_8_8_8_8:
1508                         unpacker.init("A8B8G8R8");
1509                         break;
1510                     case KTX_UNSIGNED_INT_8_8_8_8_REV:
1511                         unpacker.init("R8G8B8A8");
1512                         break;
1513                     case KTX_UNSIGNED_INT_10_10_10_2:
1514                         unpacker.init("A2B10G10R10");
1515                         break;
1516                     case KTX_UNSIGNED_INT_2_10_10_10_REV:
1517                         unpacker.init("R10G10B10A2");
1518                         break;
1519                     case KTX_UNSIGNED_INT_5_9_9_9_REV:
1520                         unpacker.init("R9G9B9A5");
1521                         break;
1522                     case KTX_UNSIGNED_INT_24_8:
1523                         unpacker.init("A8B8G8R8");  // A=stencil,RGB=depth (R is MSB)
1524                         break;
1525                     case KTX_FLOAT_32_UNSIGNED_INT_24_8_REV:
1526                         unpacker.init("R8G8B8A8"); // A=stencil,RGB=depth (R is MSB)
1527                         break;
1528                     default:
1529                         set_last_error("Unsupported KTX packed pixel type");
1530                         return false;
1531                 }
1532
1533                 unpacker.set_pixel_stride(ktx_get_ogl_type_size(ktx.get_ogl_type()));
1534             }
1535             else
1536             {
1537                 if ((ktx.get_ogl_fmt() == KTX_STENCIL_INDEX) || (ktx.get_ogl_fmt() == KTX_DEPTH_COMPONENT) || (ktx.get_ogl_fmt() == KTX_DEPTH_STENCIL))
1538                 {
1539                     // HACK HACK
1540                     if (type_size == 1)
1541                         unpacker.init("R8", type_size);
1542                     else if (type_size == 2)
1543                         unpacker.init("G8R8", type_size);
1544                     else if (type_size == 3)
1545                         unpacker.init("B8G8R8", type_size);
1546                     else
1547                         unpacker.init("A8B8G8R8", type_size);
1548
1549                     console::debug("%s: Hacked pixel format decoding for OGL type 0x%X fmt 0x%X internal fmt 0x%X base internal fmt 0x%X\n", VOGL_METHOD_NAME,
1550                                    ktx.get_ogl_type(),
1551                                    ktx.get_ogl_fmt(),
1552                                    ktx.get_ogl_internal_fmt(),
1553                                    ktx.get_ogl_base_fmt());
1554                 }
1555                 else
1556                 {
1557                     switch (ktx.get_ogl_fmt())
1558                     {
1559                         case 1:
1560                         case KTX_RED:
1561                         case KTX_RED_INTEGER:
1562                         case KTX_R8:
1563                         case KTX_R8UI:
1564                         {
1565                             unpacker.init("R", -1, type_bits);
1566                             m_format = PIXEL_FMT_R8G8B8;
1567                             break;
1568                         }
1569                         case KTX_GREEN:
1570                         case KTX_GREEN_INTEGER:
1571                         {
1572                             unpacker.init("G", -1, type_bits);
1573                             m_format = PIXEL_FMT_R8G8B8;
1574                             break;
1575                         }
1576                         case KTX_BLUE:
1577                         case KTX_BLUE_INTEGER:
1578                         {
1579                             unpacker.init("B", -1, type_bits);
1580                             m_format = PIXEL_FMT_R8G8B8;
1581                             break;
1582                         }
1583                         case KTX_ALPHA:
1584                         {
1585                             unpacker.init("A", -1, type_bits);
1586                             m_format = PIXEL_FMT_A8;
1587                             break;
1588                         }
1589                         case KTX_LUMINANCE:
1590                         {
1591                             unpacker.init("Y", -1, type_bits);
1592                             m_format = PIXEL_FMT_L8;
1593                             break;
1594                         }
1595                         case 2:
1596                         case KTX_RG:
1597                         case KTX_RG8:
1598                         case KTX_RG_INTEGER:
1599                         {
1600                             unpacker.init("RG", -1, type_bits);
1601                             m_format = PIXEL_FMT_A8L8;
1602                             break;
1603                         }
1604                         case KTX_LUMINANCE_ALPHA:
1605                         {
1606                             unpacker.init("YA", -1, type_bits);
1607                             m_format = PIXEL_FMT_A8L8;
1608                             break;
1609                         }
1610                         case 3:
1611                         case KTX_SRGB:
1612                         case KTX_RGB:
1613                         case KTX_RGB_INTEGER:
1614                         case KTX_RGB8:
1615                         case KTX_SRGB8:
1616                         {
1617                             unpacker.init("RGB", -1, type_bits);
1618                             m_format = PIXEL_FMT_R8G8B8;
1619                             break;
1620                         }
1621                         case KTX_BGR:
1622                         case KTX_BGR_INTEGER:
1623                         {
1624                             unpacker.init("BGR", -1, type_bits);
1625                             m_format = PIXEL_FMT_R8G8B8;
1626                             break;
1627                         }
1628                         case 4:
1629                         case KTX_RGBA_INTEGER:
1630                         case KTX_RGBA:
1631                         case KTX_SRGB_ALPHA:
1632                         case KTX_SRGB8_ALPHA8:
1633                         case KTX_RGBA8:
1634                         {
1635                             unpacker.init("RGBA", -1, type_bits);
1636                             break;
1637                         }
1638                         case KTX_BGRA:
1639                         case KTX_BGRA_INTEGER:
1640                         {
1641                             unpacker.init("BGRA", -1, type_bits);
1642                             break;
1643                         }
1644                         default:
1645                             set_last_error("Unsupported KTX pixel format");
1646                             return false;
1647                     }
1648
1649                     unpacker.set_pixel_stride(unpacker.get_num_comps() * ktx_get_ogl_type_size(ktx.get_ogl_type()));
1650                 }
1651             }
1652
1653             VOGL_ASSERT(unpacker.is_valid());
1654         }
1655
1656         m_comp_flags = pixel_format_helpers::get_component_flags(m_format);
1657
1658         m_faces.resize(num_faces);
1659
1660         bool x_flipped = false;
1661         bool y_flipped = true;
1662
1663         dynamic_string orient;
1664         if ((ktx.get_key_value_as_string("KTXorientation", orient)) && (orient.get_len() >= 7))
1665         {
1666             //  0123456
1667             // "S=r,T=d"
1668             if ((orient[0] == 'S') && (orient[1] == '=') && (orient[3] == ',') &&
1669                 (orient[4] == 'T') && (orient[5] == '='))
1670             {
1671                 if (vogl_tolower(orient[2]) == 'l')
1672                     x_flipped = true;
1673                 else if (vogl_tolower(orient[2]) == 'r')
1674                     x_flipped = false;
1675
1676                 if (vogl_tolower(orient[6]) == 'u')
1677                     y_flipped = true;
1678                 else if (vogl_tolower(orient[6]) == 'd')
1679                     y_flipped = false;
1680             }
1681         }
1682
1683         orientation_flags_t orient_flags = cDefaultOrientationFlags;
1684         if (x_flipped)
1685             orient_flags = static_cast<orientation_flags_t>(orient_flags | cOrientationFlagXFlipped);
1686         if (y_flipped)
1687             orient_flags = static_cast<orientation_flags_t>(orient_flags | cOrientationFlagYFlipped);
1688
1689         bool dxt1_alpha = false;
1690
1691         for (uint face_index = 0; face_index < num_faces; face_index++)
1692         {
1693             m_faces[face_index].resize(num_mip_levels);
1694
1695             for (uint level_index = 0; level_index < num_mip_levels; level_index++)
1696             {
1697                 const uint width = math::maximum<uint>(m_width >> level_index, 1U);
1698                 const uint height = math::maximum<uint>(m_height >> level_index, 1U);
1699
1700                 mip_level *pMip = vogl_new(mip_level);
1701                 m_faces[face_index][level_index] = pMip;
1702
1703                 const vogl::vector<uint8> &image_data = ktx.get_image_data(level_index, 0, face_index, 0);
1704
1705                 if (is_compressed_texture)
1706                 {
1707                     const uint bytes_per_block = pixel_format_helpers::get_dxt_bytes_per_block(m_format);
1708
1709                     const uint num_blocks_x = (width + 3) >> 2;
1710                     const uint num_blocks_y = (height + 3) >> 2;
1711
1712                     const uint level_pitch = num_blocks_x * num_blocks_y * bytes_per_block;
1713                     if (image_data.size() != level_pitch)
1714                         return false;
1715
1716                     dxt_image *pDXTImage = vogl_new(dxt_image);
1717                     if (!pDXTImage->init(dxt_fmt, width, height, false))
1718                     {
1719                         vogl_delete(pDXTImage);
1720
1721                         VOGL_ASSERT_ALWAYS;
1722                         return false;
1723                     }
1724
1725                     VOGL_ASSERT(pDXTImage->get_element_vec().size() * sizeof(dxt_image::element) == level_pitch);
1726
1727                     memcpy(&pDXTImage->get_element_vec()[0], image_data.get_ptr(), image_data.size());
1728
1729                     if ((m_format == PIXEL_FMT_DXT1) && (!dxt1_alpha))
1730                         dxt1_alpha = pDXTImage->has_alpha();
1731
1732                     pMip->assign(pDXTImage, m_format, orient_flags);
1733                 }
1734                 else
1735                 {
1736                     if (image_data.size() != (width * height * unpacker.get_pixel_stride()))
1737                         return false;
1738
1739                     image_u8 *pImage = vogl_new(image_u8, width, height);
1740
1741                     pImage->set_comp_flags(m_comp_flags);
1742
1743                     const uint8 *pSrc = image_data.get_ptr();
1744
1745                     color_quad_u8 q(0, 0, 0, 255);
1746
1747                     for (uint y = 0; y < height; y++)
1748                     {
1749                         for (uint x = 0; x < width; x++)
1750                         {
1751                             color_quad_u8 c;
1752                             pSrc = static_cast<const uint8 *>(unpacker.unpack(pSrc, c));
1753                             pImage->set_pixel_unclipped(x, y, c);
1754                         }
1755                     }
1756
1757                     pMip->assign(pImage, m_format, orient_flags);
1758
1759                     VOGL_ASSERT(pMip->get_comp_flags() == m_comp_flags);
1760                 }
1761             }
1762         }
1763
1764         clear_last_error();
1765
1766         if (dxt1_alpha)
1767             change_dxt1_to_dxt1a();
1768
1769         return true;
1770     }
1771
1772     bool mipmapped_texture::write_ktx(data_stream_serializer &serializer) const
1773     {
1774         if (!m_width)
1775         {
1776             set_last_error("Nothing to write");
1777             return false;
1778         }
1779
1780         set_last_error("write_ktx() failed");
1781
1782         uint32 ogl_internal_fmt = 0, ogl_fmt = 0, ogl_type = 0;
1783
1784         pixel_packer packer;
1785
1786         if (is_packed())
1787         {
1788             switch (get_format())
1789             {
1790                 case PIXEL_FMT_DXT1:
1791                 {
1792                     ogl_internal_fmt = KTX_COMPRESSED_RGB_S3TC_DXT1_EXT;
1793                     break;
1794                 }
1795                 case PIXEL_FMT_DXT1A:
1796                 {
1797                     ogl_internal_fmt = KTX_COMPRESSED_RGBA_S3TC_DXT1_EXT;
1798                     break;
1799                 }
1800                 case PIXEL_FMT_DXT2:
1801                 case PIXEL_FMT_DXT3:
1802                 {
1803                     ogl_internal_fmt = KTX_COMPRESSED_RGBA_S3TC_DXT3_EXT;
1804                     break;
1805                 }
1806                 case PIXEL_FMT_DXT4:
1807                 case PIXEL_FMT_DXT5:
1808                 case PIXEL_FMT_DXT5_CCxY:
1809                 case PIXEL_FMT_DXT5_xGxR:
1810                 case PIXEL_FMT_DXT5_xGBR:
1811                 case PIXEL_FMT_DXT5_AGBR:
1812                 {
1813                     ogl_internal_fmt = KTX_COMPRESSED_RGBA_S3TC_DXT5_EXT;
1814                     break;
1815                 }
1816                 case PIXEL_FMT_3DC:
1817                 case PIXEL_FMT_DXN:
1818                 {
1819                     ogl_internal_fmt = KTX_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT;
1820                     break;
1821                 }
1822                 case PIXEL_FMT_DXT5A:
1823                 {
1824                     ogl_internal_fmt = KTX_COMPRESSED_LUMINANCE_LATC1_EXT;
1825                     break;
1826                 }
1827                 case PIXEL_FMT_ETC1:
1828                 {
1829                     ogl_internal_fmt = KTX_ETC1_RGB8_OES;
1830                     break;
1831                 }
1832                 default:
1833                 {
1834                     VOGL_ASSERT_ALWAYS;
1835                     return false;
1836                 }
1837             }
1838         }
1839         else
1840         {
1841             ogl_type = KTX_UNSIGNED_BYTE;
1842
1843             switch (get_format())
1844             {
1845                 case PIXEL_FMT_R8G8B8:
1846                     ogl_internal_fmt = KTX_RGB8;
1847                     ogl_fmt = KTX_RGB;
1848                     packer.init("R8G8B8");
1849                     break;
1850                 case PIXEL_FMT_L8:
1851                     ogl_internal_fmt = KTX_LUMINANCE8;
1852                     ogl_fmt = KTX_LUMINANCE;
1853                     packer.init("G8");
1854                     break;
1855                 case PIXEL_FMT_A8:
1856                     ogl_internal_fmt = KTX_ALPHA8;
1857                     ogl_fmt = KTX_ALPHA;
1858                     packer.init("A8");
1859                     break;
1860                 case PIXEL_FMT_A8L8:
1861                     ogl_internal_fmt = KTX_LUMINANCE8_ALPHA8;
1862                     ogl_fmt = KTX_LUMINANCE_ALPHA;
1863                     packer.init("Y8A8");
1864                     break;
1865                 case PIXEL_FMT_A8R8G8B8:
1866                     ogl_internal_fmt = KTX_RGBA8;
1867                     ogl_fmt = KTX_RGBA;
1868                     packer.init("R8G8B8A8");
1869                     break;
1870                 default:
1871                 {
1872                     VOGL_ASSERT_ALWAYS;
1873                     return false;
1874                 }
1875             }
1876         }
1877
1878         ktx_texture kt;
1879         bool success;
1880         if (determine_texture_type() == cTextureTypeCubemap)
1881             success = kt.init_cubemap(get_width(), get_num_levels(), ogl_internal_fmt, ogl_fmt, ogl_type);
1882         else
1883             success = kt.init_2D(get_width(), get_height(), get_num_levels(), ogl_internal_fmt, ogl_fmt, ogl_type);
1884         if (!success)
1885             return false;
1886
1887         dynamic_string fourcc_str(cVarArg, "%c%c%c%c", m_format & 0xFF, (m_format >> 8) & 0xFF, (m_format >> 16) & 0xFF, (m_format >> 24) & 0xFF);
1888         kt.add_key_value("VOGL_FOURCC", fourcc_str.get_ptr());
1889
1890         const mip_level *pLevel0 = get_level(0, 0);
1891         dynamic_string ktx_orient_str(cVarArg, "S=%c,T=%c", (pLevel0->get_orientation_flags() & cOrientationFlagXFlipped) ? 'l' : 'r', (pLevel0->get_orientation_flags() & cOrientationFlagYFlipped) ? 'u' : 'd');
1892         kt.add_key_value("KTXorientation", ktx_orient_str.get_ptr());
1893
1894         for (uint face_index = 0; face_index < get_num_faces(); face_index++)
1895         {
1896             for (uint level_index = 0; level_index < get_num_levels(); level_index++)
1897             {
1898                 const mip_level *pLevel = get_level(face_index, level_index);
1899
1900                 const uint mip_width = pLevel->get_width();
1901                 const uint mip_height = pLevel->get_height();
1902
1903                 if (is_packed())
1904                 {
1905                     const dxt_image *p = pLevel->get_dxt_image();
1906
1907                     kt.add_image(face_index, level_index, p->get_element_ptr(), p->get_size_in_bytes());
1908                 }
1909                 else
1910                 {
1911                     const image_u8 *p = pLevel->get_image();
1912
1913                     vogl::vector<uint8> tmp(mip_width * mip_height * packer.get_pixel_stride());
1914
1915                     uint8 *pDst = tmp.get_ptr();
1916                     for (uint y = 0; y < mip_height; y++)
1917                         for (uint x = 0; x < mip_width; x++)
1918                             pDst = (uint8 *)packer.pack(p->get_unclamped(x, y), pDst);
1919
1920                     kt.add_image(face_index, level_index, tmp.get_ptr(), tmp.size_in_bytes());
1921                 }
1922             }
1923         }
1924
1925         if (!kt.write_to_stream(serializer))
1926             return false;
1927
1928         clear_last_error();
1929         return true;
1930     }
1931
1932     void mipmapped_texture::assign(face_vec &faces)
1933     {
1934         VOGL_ASSERT(!faces.is_empty());
1935         if (faces.is_empty())
1936             return;
1937
1938         free_all_mips();
1939
1940 #ifdef VOGL_BUILD_DEBUG
1941         for (uint i = 1; i < faces.size(); i++)
1942             VOGL_ASSERT(faces[i].size() == faces[0].size());
1943 #endif
1944
1945         mip_level *p = faces[0][0];
1946         m_width = p->get_width();
1947         m_height = p->get_height();
1948         m_comp_flags = p->get_comp_flags();
1949         m_format = p->get_format();
1950
1951         m_faces.swap(faces);
1952
1953         VOGL_ASSERT(check());
1954     }
1955
1956     void mipmapped_texture::assign(mip_level *pLevel)
1957     {
1958         face_vec faces(1, mip_ptr_vec(1, pLevel));
1959         assign(faces);
1960     }
1961
1962     void mipmapped_texture::assign(image_u8 *p, pixel_format fmt, orientation_flags_t orient_flags)
1963     {
1964         mip_level *pLevel = vogl_new(mip_level);
1965         pLevel->assign(p, fmt, orient_flags);
1966         assign(pLevel);
1967     }
1968
1969     void mipmapped_texture::assign(dxt_image *p, pixel_format fmt, orientation_flags_t orient_flags)
1970     {
1971         mip_level *pLevel = vogl_new(mip_level);
1972         pLevel->assign(p, fmt, orient_flags);
1973         assign(pLevel);
1974     }
1975
1976     void mipmapped_texture::set(texture_file_types::format source_file_type, const mipmapped_texture &mipmapped_texture)
1977     {
1978         clear();
1979
1980         *this = mipmapped_texture;
1981         m_source_file_type = source_file_type;
1982     }
1983
1984     image_u8 *mipmapped_texture::get_level_image(uint face, uint level, image_u8 &img, uint unpack_flags) const
1985     {
1986         if (!is_valid())
1987             return NULL;
1988
1989         const mip_level *pLevel = get_level(face, level);
1990
1991         return pLevel->get_unpacked_image(img, unpack_flags);
1992     }
1993
1994     void mipmapped_texture::swap(mipmapped_texture &img)
1995     {
1996         utils::swap(m_width, img.m_width);
1997         utils::swap(m_height, img.m_height);
1998         utils::swap(m_comp_flags, img.m_comp_flags);
1999         utils::swap(m_format, img.m_format);
2000         m_faces.swap(img.m_faces);
2001         m_last_error.swap(img.m_last_error);
2002         utils::swap(m_source_file_type, img.m_source_file_type);
2003
2004         VOGL_ASSERT(check());
2005     }
2006
2007     texture_type mipmapped_texture::determine_texture_type() const
2008     {
2009         if (!is_valid())
2010             return cTextureTypeUnknown;
2011
2012         if (get_num_faces() == 6)
2013             return cTextureTypeCubemap;
2014         else if (is_vertical_cross())
2015             return cTextureTypeVerticalCrossCubemap;
2016         else if (is_normal_map())
2017             return cTextureTypeNormalMap;
2018
2019         return cTextureTypeRegularMap;
2020     }
2021
2022     void mipmapped_texture::discard_mips()
2023     {
2024         for (uint f = 0; f < m_faces.size(); f++)
2025         {
2026             if (m_faces[f].size() > 1)
2027             {
2028                 for (uint l = 1; l < m_faces[f].size(); l++)
2029                     vogl_delete(m_faces[f][l]);
2030
2031                 m_faces[f].resize(1);
2032             }
2033         }
2034
2035         VOGL_ASSERT(check());
2036     }
2037
2038     void mipmapped_texture::init(uint width, uint height, uint levels, uint faces, pixel_format fmt, const char *pName, orientation_flags_t orient_flags)
2039     {
2040         clear();
2041
2042         VOGL_ASSERT((width > 0) && (height > 0) && (levels > 0));
2043         VOGL_ASSERT((faces == 1) || (faces == 6));
2044
2045         m_width = width;
2046         m_height = height;
2047         m_comp_flags = pixel_format_helpers::get_component_flags(fmt);
2048         m_format = fmt;
2049         if (pName)
2050             m_name.set(pName);
2051
2052         m_faces.resize(faces);
2053         for (uint f = 0; f < faces; f++)
2054         {
2055             m_faces[f].resize(levels);
2056             for (uint l = 0; l < levels; l++)
2057             {
2058                 m_faces[f][l] = vogl_new(mip_level);
2059
2060                 const uint mip_width = math::maximum(1U, width >> l);
2061                 const uint mip_height = math::maximum(1U, height >> l);
2062                 if (pixel_format_helpers::is_dxt(fmt))
2063                 {
2064                     dxt_image *p = vogl_new(dxt_image);
2065                     p->init(pixel_format_helpers::get_dxt_format(fmt), mip_width, mip_height, true);
2066                     m_faces[f][l]->assign(p, m_format, orient_flags);
2067                 }
2068                 else
2069                 {
2070                     image_u8 *p = vogl_new(image_u8, mip_width, mip_height);
2071                     p->set_comp_flags(m_comp_flags);
2072                     m_faces[f][l]->assign(p, m_format, orient_flags);
2073                 }
2074             }
2075         }
2076
2077         VOGL_ASSERT(check());
2078     }
2079
2080     void mipmapped_texture::discard_mipmaps()
2081     {
2082         if (!is_valid())
2083             return;
2084
2085         discard_mips();
2086     }
2087
2088     bool mipmapped_texture::convert(pixel_format fmt, bool cook, const dxt_image::pack_params &p)
2089     {
2090         if (!is_valid())
2091             return false;
2092
2093         if (fmt == get_format())
2094             return true;
2095
2096         uint total_pixels = 0;
2097         for (uint f = 0; f < m_faces.size(); f++)
2098             for (uint l = 0; l < m_faces[f].size(); l++)
2099                 total_pixels += m_faces[f][l]->get_total_pixels();
2100
2101         uint num_pixels_processed = 0;
2102
2103         uint progress_start = p.m_progress_start;
2104
2105         for (uint f = 0; f < m_faces.size(); f++)
2106         {
2107             for (uint l = 0; l < m_faces[f].size(); l++)
2108             {
2109                 const uint num_pixels = m_faces[f][l]->get_total_pixels();
2110
2111                 uint progress_range = (num_pixels * p.m_progress_range) / total_pixels;
2112
2113                 dxt_image::pack_params tmp_params(p);
2114                 tmp_params.m_progress_start = math::clamp<uint>(progress_start, 0, p.m_progress_range);
2115                 tmp_params.m_progress_range = math::clamp<uint>(progress_range, 0, p.m_progress_range - tmp_params.m_progress_start);
2116
2117                 progress_start += tmp_params.m_progress_range;
2118
2119                 if (!m_faces[f][l]->convert(fmt, cook, tmp_params))
2120                 {
2121                     clear();
2122                     return false;
2123                 }
2124
2125                 num_pixels_processed += num_pixels;
2126             }
2127         }
2128
2129         m_format = get_level(0, 0)->get_format();
2130         m_comp_flags = get_level(0, 0)->get_comp_flags();
2131
2132         VOGL_ASSERT(check());
2133
2134         if (p.m_pProgress_callback)
2135         {
2136             if (!p.m_pProgress_callback(p.m_progress_start + p.m_progress_range, p.m_pProgress_callback_user_data_ptr))
2137                 return false;
2138         }
2139
2140         return true;
2141     }
2142
2143     bool mipmapped_texture::convert(pixel_format fmt, const dxt_image::pack_params &p)
2144     {
2145         return convert(fmt, true, p);
2146     }
2147
2148     bool mipmapped_texture::is_packed() const
2149     {
2150         VOGL_ASSERT(is_valid());
2151         if (!is_valid())
2152             return false;
2153
2154         return get_level(0, 0)->is_packed();
2155     }
2156
2157     bool mipmapped_texture::set_alpha_to_luma()
2158     {
2159         VOGL_ASSERT(is_valid());
2160         if (!is_valid())
2161             return false;
2162
2163         if (is_packed())
2164             unpack_from_dxt(true);
2165
2166         for (uint f = 0; f < m_faces.size(); f++)
2167             for (uint l = 0; l < get_num_levels(); l++)
2168                 get_level(f, l)->set_alpha_to_luma();
2169
2170         m_format = get_level(0, 0)->get_format();
2171         m_comp_flags = get_level(0, 0)->get_comp_flags();
2172
2173         VOGL_ASSERT(check());
2174
2175         return true;
2176     }
2177
2178     bool mipmapped_texture::convert(image_utils::conversion_type conv_type)
2179     {
2180         VOGL_ASSERT(is_valid());
2181         if (!is_valid())
2182             return false;
2183
2184         if (is_packed())
2185             unpack_from_dxt(true);
2186
2187         for (uint f = 0; f < m_faces.size(); f++)
2188             for (uint l = 0; l < get_num_levels(); l++)
2189                 get_level(f, l)->convert(conv_type);
2190
2191         m_format = get_level(0, 0)->get_format();
2192         m_comp_flags = get_level(0, 0)->get_comp_flags();
2193
2194         VOGL_ASSERT(check());
2195
2196         return true;
2197     }
2198
2199     bool mipmapped_texture::unpack_from_dxt(bool uncook)
2200     {
2201         VOGL_ASSERT(is_valid());
2202         if (!is_valid())
2203             return false;
2204
2205         VOGL_ASSERT(pixel_format_helpers::is_dxt(m_format));
2206         if (!pixel_format_helpers::is_dxt(m_format))
2207             return false;
2208
2209         for (uint f = 0; f < m_faces.size(); f++)
2210             for (uint l = 0; l < get_num_levels(); l++)
2211                 if (!get_level(f, l)->unpack_from_dxt(uncook))
2212                     return false;
2213
2214         m_format = get_level(0, 0)->get_format();
2215         m_comp_flags = get_level(0, 0)->get_comp_flags();
2216
2217         VOGL_ASSERT(check());
2218
2219         return true;
2220     }
2221
2222     bool mipmapped_texture::has_alpha() const
2223     {
2224         VOGL_ASSERT(is_valid());
2225         if (!is_valid())
2226             return false;
2227
2228         if (pixel_format_helpers::has_alpha(m_format))
2229             return true;
2230
2231         if ((m_format == PIXEL_FMT_DXT1) && (get_level(0, 0)->get_dxt_image()))
2232         {
2233             // Try scanning DXT1 mip levels to find blocks with transparent pixels.
2234             for (uint f = 0; f < get_num_faces(); f++)
2235                 if (get_level(f, 0)->get_dxt_image()->has_alpha())
2236                     return true;
2237         }
2238
2239         return false;
2240     }
2241
2242     bool mipmapped_texture::is_normal_map() const
2243     {
2244         VOGL_ASSERT(is_valid());
2245         if (!is_valid())
2246             return false;
2247
2248         if (pixel_format_helpers::is_normal_map(get_format()))
2249             return true;
2250
2251         const mip_level *pLevel = get_level(0, 0);
2252
2253         if (pLevel->get_image())
2254             return image_utils::is_normal_map(*pLevel->get_image(), m_name.get_ptr());
2255
2256         image_u8 tmp;
2257         pLevel->get_dxt_image()->unpack(tmp);
2258         return image_utils::is_normal_map(tmp, m_name.get_ptr());
2259     }
2260
2261     bool mipmapped_texture::is_vertical_cross() const
2262     {
2263         VOGL_ASSERT(is_valid());
2264         if (!is_valid())
2265             return false;
2266
2267         if (get_num_faces() > 1)
2268             return false;
2269
2270         if (!((math::is_power_of_2(m_height)) && (!math::is_power_of_2(m_width)) && (m_height / 4U == m_width / 3U)))
2271             return false;
2272
2273         return true;
2274     }
2275
2276     bool mipmapped_texture::resize(uint new_width, uint new_height, const resample_params &params)
2277     {
2278         VOGL_ASSERT(is_valid());
2279         if (!is_valid())
2280             return false;
2281
2282         VOGL_ASSERT((new_width >= 1) && (new_height >= 1));
2283
2284         face_vec faces(get_num_faces());
2285         for (uint f = 0; f < faces.size(); f++)
2286         {
2287             faces[f].resize(1);
2288             faces[f][0] = vogl_new(mip_level);
2289         }
2290
2291         for (uint f = 0; f < faces.size(); f++)
2292         {
2293             image_u8 tmp;
2294             image_u8 *pImg = get_level(f, 0)->get_unpacked_image(tmp, cUnpackFlagUncook);
2295
2296             image_u8 *pMip = vogl_new(image_u8);
2297
2298             image_utils::resample_params rparams;
2299             rparams.m_dst_width = new_width;
2300             rparams.m_dst_height = new_height;
2301             rparams.m_filter_scale = params.m_filter_scale;
2302             rparams.m_first_comp = 0;
2303             rparams.m_num_comps = pImg->is_component_valid(3) ? 4 : 3;
2304             rparams.m_srgb = params.m_srgb;
2305             rparams.m_wrapping = params.m_wrapping;
2306             rparams.m_pFilter = params.m_pFilter;
2307             rparams.m_multithreaded = params.m_multithreaded;
2308
2309             if (!image_utils::resample(*pImg, *pMip, rparams))
2310             {
2311                 vogl_delete(pMip);
2312
2313                 for (uint f = 0; f < faces.size(); f++)
2314                     for (uint l = 0; l < faces[f].size(); l++)
2315                         vogl_delete(faces[f][l]);
2316
2317                 return false;
2318             }
2319
2320             if (params.m_renormalize)
2321                 image_utils::renorm_normal_map(*pMip);
2322
2323             pMip->set_comp_flags(pImg->get_comp_flags());
2324
2325             faces[f][0]->assign(pMip, PIXEL_FMT_INVALID, get_level(f, 0)->get_orientation_flags());
2326         }
2327
2328         assign(faces);
2329
2330         VOGL_ASSERT(check());
2331
2332         return true;
2333     }
2334
2335     bool mipmapped_texture::generate_mipmaps(const generate_mipmap_params &params, bool force)
2336     {
2337         VOGL_ASSERT(is_valid());
2338         if (!is_valid())
2339             return false;
2340
2341         uint num_levels = 1;
2342         {
2343             uint width = get_width();
2344             uint height = get_height();
2345             while ((width > params.m_min_mip_size) || (height > params.m_min_mip_size))
2346             {
2347                 width >>= 1U;
2348                 height >>= 1U;
2349                 num_levels++;
2350             }
2351         }
2352
2353         if ((params.m_max_mips > 0) && (num_levels > params.m_max_mips))
2354             num_levels = params.m_max_mips;
2355
2356         if ((force) && (get_num_levels() > 1))
2357             discard_mipmaps();
2358
2359         if (num_levels == get_num_levels())
2360             return true;
2361
2362         face_vec faces(get_num_faces());
2363         for (uint f = 0; f < faces.size(); f++)
2364         {
2365             faces[f].resize(num_levels);
2366             for (uint l = 0; l < num_levels; l++)
2367                 faces[f][l] = vogl_new(mip_level);
2368         }
2369
2370         for (uint f = 0; f < faces.size(); f++)
2371         {
2372             image_u8 tmp;
2373             image_u8 *pImg = get_level(f, 0)->get_unpacked_image(tmp, cUnpackFlagUncook);
2374
2375             for (uint l = 0; l < num_levels; l++)
2376             {
2377                 const uint mip_width = math::maximum<uint>(1U, get_width() >> l);
2378                 const uint mip_height = math::maximum<uint>(1U, get_height() >> l);
2379
2380                 image_u8 *pMip = vogl_new(image_u8);
2381
2382                 if (!l)
2383                     *pMip = *pImg;
2384                 else
2385                 {
2386                     image_utils::resample_params rparams;
2387                     rparams.m_dst_width = mip_width;
2388                     rparams.m_dst_height = mip_height;
2389                     rparams.m_filter_scale = params.m_filter_scale;
2390                     rparams.m_first_comp = 0;
2391                     rparams.m_num_comps = pImg->is_component_valid(3) ? 4 : 3;
2392                     rparams.m_srgb = params.m_srgb;
2393                     rparams.m_wrapping = params.m_wrapping;
2394                     rparams.m_pFilter = params.m_pFilter;
2395                     rparams.m_multithreaded = params.m_multithreaded;
2396
2397                     if (!image_utils::resample(*pImg, *pMip, rparams))
2398                     {
2399                         vogl_delete(pMip);
2400
2401                         for (uint f = 0; f < faces.size(); f++)
2402                             for (uint l = 0; l < faces[f].size(); l++)
2403                                 vogl_delete(faces[f][l]);
2404
2405                         return false;
2406                     }
2407
2408                     if (params.m_renormalize)
2409                         image_utils::renorm_normal_map(*pMip);
2410
2411                     pMip->set_comp_flags(pImg->get_comp_flags());
2412                 }
2413
2414                 faces[f][l]->assign(pMip, PIXEL_FMT_INVALID, get_level(f, 0)->get_orientation_flags());
2415             }
2416         }
2417
2418         assign(faces);
2419
2420         VOGL_ASSERT(check());
2421
2422         return true;
2423     }
2424
2425     bool mipmapped_texture::crop(uint x, uint y, uint width, uint height)
2426     {
2427         VOGL_ASSERT(is_valid());
2428         if (!is_valid())
2429             return false;
2430         if (get_num_faces() > 1)
2431             return false;
2432
2433         if ((width < 1) || (height < 1))
2434             return false;
2435
2436         image_u8 tmp;
2437         image_u8 *pImg = get_level(0, 0)->get_unpacked_image(tmp, cUnpackFlagUncook | cUnpackFlagUnflip);
2438
2439         image_u8 *pMip = vogl_new(image_u8, width, height);
2440
2441         if (!pImg->extract_block_clamped(pMip->get_ptr(), x, y, width, height))
2442             return false;
2443
2444         face_vec faces(1);
2445         faces[0].resize(1);
2446         faces[0][0] = vogl_new(mip_level);
2447
2448         pMip->set_comp_flags(pImg->get_comp_flags());
2449
2450         faces[0][0]->assign(pMip);
2451
2452         assign(faces);
2453
2454         VOGL_ASSERT(check());
2455
2456         return true;
2457     }
2458
2459     bool mipmapped_texture::vertical_cross_to_cubemap()
2460     {
2461         if (!is_vertical_cross())
2462             return false;
2463
2464         const uint face_width = get_height() / 4;
2465
2466         bool alpha_is_valid = has_alpha();
2467
2468         mipmapped_texture cubemap;
2469
2470         pixel_format fmt = alpha_is_valid ? PIXEL_FMT_A8R8G8B8 : PIXEL_FMT_R8G8B8;
2471
2472         cubemap.init(face_width, face_width, 1, 6, fmt, m_name.get_ptr(), cDefaultOrientationFlags);
2473
2474         // +x -x +y -y +z -z
2475         //     0  1  2
2476         // 0     +y
2477         // 1  -x +z +x
2478         // 2     -y
2479         // 3     -z
2480
2481         for (uint face_index = 0; face_index < 6; face_index++)
2482         {
2483             const mip_level *pSrc = get_level(0, 0);
2484
2485             image_u8 tmp_img;
2486             image_u8 *pSrc_image = pSrc->get_unpacked_image(tmp_img, cUnpackFlagUncook | cUnpackFlagUnflip);
2487
2488             const mip_level *pDst = get_level(face_index, 0);
2489             image_u8 *pDst_image = pDst->get_image();
2490             VOGL_ASSERT(pDst_image);
2491
2492             const bool flipped = (face_index == 5);
2493             const uint x_ofs = g_vertical_cross_image_offsets[face_index][0] * face_width;
2494             const uint y_ofs = g_vertical_cross_image_offsets[face_index][1] * face_width;
2495
2496             for (uint y = 0; y < face_width; y++)
2497             {
2498                 for (uint x = 0; x < face_width; x++)
2499                 {
2500                     const color_quad_u8 &c = (*pSrc_image)(x_ofs + x, y_ofs + y);
2501
2502                     if (!flipped)
2503                         (*pDst_image)(x, y) = c;
2504                     else
2505                         (*pDst_image)(face_width - 1 - x, face_width - 1 - y) = c;
2506                 }
2507             }
2508         }
2509
2510         swap(cubemap);
2511
2512         VOGL_ASSERT(check());
2513
2514         return true;
2515     }
2516
2517     bool mipmapped_texture::read_from_file(const char *pFilename, texture_file_types::format file_format)
2518     {
2519         clear();
2520
2521         set_last_error("Can't open file");
2522
2523         bool success = false;
2524
2525         cfile_stream in_stream;
2526         if (in_stream.open(pFilename))
2527         {
2528             data_stream_serializer serializer(in_stream);
2529             success = read_from_stream(serializer, file_format);
2530         }
2531
2532         return success;
2533     }
2534
2535     bool mipmapped_texture::read_from_stream(data_stream_serializer &serializer, texture_file_types::format file_format)
2536     {
2537         clear();
2538
2539         if (!serializer.get_stream())
2540         {
2541             set_last_error("Invalid stream");
2542             return false;
2543         }
2544
2545         if (file_format == texture_file_types::cFormatInvalid)
2546             file_format = texture_file_types::determine_file_format(serializer.get_name().get_ptr());
2547
2548         if (file_format == texture_file_types::cFormatInvalid)
2549         {
2550             set_last_error("Unsupported file format");
2551             return false;
2552         }
2553
2554         set_last_error("Image file load failed");
2555
2556         bool success = false;
2557
2558         if (!texture_file_types::supports_mipmaps(file_format))
2559         {
2560             success = read_regular_image(serializer, file_format);
2561         }
2562         else
2563         {
2564             switch (file_format)
2565             {
2566                 case texture_file_types::cFormatDDS:
2567                 {
2568                     success = read_dds(serializer);
2569                     break;
2570                 }
2571                 case texture_file_types::cFormatCRN:
2572                 {
2573                     break;
2574                 }
2575                 case texture_file_types::cFormatKTX:
2576                 {
2577                     success = read_ktx(serializer);
2578                     break;
2579                 }
2580                 default:
2581                 {
2582                     VOGL_ASSERT_ALWAYS;
2583                     break;
2584                 }
2585             }
2586         }
2587
2588         if (success)
2589         {
2590             VOGL_ASSERT(check());
2591
2592             m_source_file_type = file_format;
2593             set_name(serializer.get_name());
2594             clear_last_error();
2595         }
2596
2597         return success;
2598     }
2599
2600     bool mipmapped_texture::read_regular_image(data_stream_serializer &serializer, texture_file_types::format file_format)
2601     {
2602         VOGL_NOTE_UNUSED(file_format);
2603
2604         image_u8 *pImg = vogl_new(image_u8);
2605         bool status = image_utils::read_from_stream(*pImg, serializer, 0);
2606         if (!status)
2607         {
2608             vogl_delete(pImg);
2609
2610             set_last_error("Failed loading image file");
2611             return false;
2612         }
2613
2614         mip_level *pLevel = vogl_new(mip_level);
2615         pLevel->assign(pImg);
2616
2617         assign(pLevel);
2618         set_name(serializer.get_name());
2619
2620         return true;
2621     }
2622
2623     bool mipmapped_texture::write_to_file(
2624         const char *pFilename,
2625         texture_file_types::format file_format,
2626         vogl_comp_params *pComp_params,
2627         uint32 *pActual_quality_level, float *pActual_bitrate,
2628         uint32 image_write_flags)
2629     {
2630         if (pActual_quality_level)
2631             *pActual_quality_level = 0;
2632         if (pActual_bitrate)
2633             *pActual_bitrate = 0.0f;
2634
2635         if (!is_valid())
2636         {
2637             set_last_error("Unable to write empty texture");
2638             return false;
2639         }
2640
2641         if (file_format == texture_file_types::cFormatInvalid)
2642             file_format = texture_file_types::determine_file_format(pFilename);
2643
2644         if (file_format == texture_file_types::cFormatInvalid)
2645         {
2646             set_last_error("Unknown file format");
2647             return false;
2648         }
2649
2650         bool success = false;
2651
2652         if (((pComp_params) && (file_format == texture_file_types::cFormatDDS)) ||
2653             (file_format == texture_file_types::cFormatCRN))
2654         {
2655             set_last_error("Functionality removed");
2656             return false;
2657         }
2658         else if (!texture_file_types::supports_mipmaps(file_format))
2659         {
2660             success = write_regular_image(pFilename, image_write_flags);
2661         }
2662         else
2663         {
2664             if (pComp_params)
2665             {
2666                 console::warning("mipmapped_texture::write_to_file: Ignoring CRN compression parameters (currently unsupported for this file type).\n");
2667             }
2668
2669             cfile_stream write_stream;
2670             if (!write_stream.open(pFilename, cDataStreamWritable | cDataStreamSeekable))
2671             {
2672                 set_last_error(dynamic_string(cVarArg, "Failed creating output file \"%s\"", pFilename).get_ptr());
2673                 return false;
2674             }
2675             data_stream_serializer serializer(write_stream);
2676
2677             switch (file_format)
2678             {
2679                 case texture_file_types::cFormatDDS:
2680                 {
2681                     success = write_dds(serializer);
2682                     break;
2683                 }
2684                 case texture_file_types::cFormatKTX:
2685                 {
2686                     success = write_ktx(serializer);
2687                     break;
2688                 }
2689                 default:
2690                 {
2691                     break;
2692                 }
2693             }
2694         }
2695
2696         return success;
2697     }
2698
2699     bool mipmapped_texture::write_regular_image(const char *pFilename, uint32 image_write_flags)
2700     {
2701         image_u8 tmp;
2702         image_u8 *pLevel_image = get_level_image(0, 0, tmp);
2703
2704         if (!image_utils::write_to_file(pFilename, *pLevel_image, image_write_flags))
2705         {
2706             set_last_error("File write failed");
2707             return false;
2708         }
2709
2710         return true;
2711     }
2712
2713     uint mipmapped_texture::get_total_pixels_in_all_faces_and_mips() const
2714     {
2715         uint total_pixels = 0;
2716         for (uint l = 0; l < m_faces.size(); l++)
2717             for (uint m = 0; m < m_faces[l].size(); m++)
2718                 total_pixels += m_faces[l][m]->get_total_pixels();
2719
2720         return total_pixels;
2721     }
2722
2723     void mipmapped_texture::set_orientation_flags(orientation_flags_t flags)
2724     {
2725         for (uint l = 0; l < m_faces.size(); l++)
2726             for (uint m = 0; m < m_faces[l].size(); m++)
2727                 m_faces[l][m]->set_orientation_flags(flags);
2728     }
2729
2730     bool mipmapped_texture::is_flipped() const
2731     {
2732         for (uint l = 0; l < m_faces.size(); l++)
2733             for (uint m = 0; m < m_faces[l].size(); m++)
2734                 if (m_faces[l][m]->is_flipped())
2735                     return true;
2736
2737         return false;
2738     }
2739
2740     bool mipmapped_texture::is_x_flipped() const
2741     {
2742         for (uint l = 0; l < m_faces.size(); l++)
2743             for (uint m = 0; m < m_faces[l].size(); m++)
2744                 if (m_faces[l][m]->is_x_flipped())
2745                     return true;
2746
2747         return false;
2748     }
2749
2750     bool mipmapped_texture::is_y_flipped() const
2751     {
2752         for (uint l = 0; l < m_faces.size(); l++)
2753             for (uint m = 0; m < m_faces[l].size(); m++)
2754                 if (m_faces[l][m]->is_y_flipped())
2755                     return true;
2756
2757         return false;
2758     }
2759
2760     bool mipmapped_texture::can_unflip_without_unpacking() const
2761     {
2762         if (!is_valid())
2763             return false;
2764
2765         if (!is_packed())
2766             return true;
2767
2768         for (uint l = 0; l < m_faces.size(); l++)
2769             for (uint m = 0; m < m_faces[l].size(); m++)
2770                 if (!m_faces[l][m]->can_unflip_without_unpacking())
2771                     return false;
2772
2773         return true;
2774     }
2775
2776     bool mipmapped_texture::unflip(bool allow_unpacking_to_flip, bool uncook_if_necessary_to_unpack)
2777     {
2778         if (!is_valid())
2779             return false;
2780
2781         if (!is_y_flipped())
2782             return true;
2783
2784         if (is_packed())
2785         {
2786             // The texture is packed - make sure all faces/miplevels can be consistently unflipped.
2787             bool can_do_packed_unflip = can_unflip_without_unpacking();
2788
2789             if ((!can_do_packed_unflip) && (!allow_unpacking_to_flip))
2790                 return false;
2791
2792             // If any face/miplevel can't unflip the packed bits, then just unpack the whole texture.
2793             if (!can_do_packed_unflip)
2794                 unpack_from_dxt(uncook_if_necessary_to_unpack);
2795         }
2796
2797         for (uint l = 0; l < m_faces.size(); l++)
2798             for (uint m = 0; m < m_faces[l].size(); m++)
2799                 if (!m_faces[l][m]->unflip(true, false))
2800                     return false;
2801
2802         VOGL_VERIFY(check());
2803
2804         return true;
2805     }
2806
2807 #if 0
2808 bool mipmapped_texture::flip_x()
2809 {
2810         for (uint l = 0; l < m_faces.size(); l++)
2811                 for (uint m = 0; m < m_faces[l].size(); m++)
2812                         if (!m_faces[l][m]->flip_x())
2813                                 return false;
2814
2815         return true;
2816 }
2817 #endif
2818
2819     bool mipmapped_texture::flip_y_helper()
2820     {
2821         for (uint l = 0; l < m_faces.size(); l++)
2822             for (uint m = 0; m < m_faces[l].size(); m++)
2823                 if (!m_faces[l][m]->flip_y())
2824                     return false;
2825
2826         return true;
2827     }
2828
2829     bool mipmapped_texture::flip_y(bool update_orientation_flags)
2830     {
2831         mipmapped_texture temp_tex(*this);
2832         if (!temp_tex.flip_y_helper())
2833         {
2834             temp_tex = *this;
2835             temp_tex.unpack_from_dxt(true);
2836             if (!temp_tex.flip_y_helper())
2837                 return false;
2838         }
2839         swap(temp_tex);
2840
2841         if (update_orientation_flags)
2842         {
2843             for (uint f = 0; f < get_num_faces(); f++)
2844             {
2845                 for (uint m = 0; m < get_face(f).size(); m++)
2846                 {
2847                     uint orient_flags = get_face(f)[m]->get_orientation_flags();
2848                     orient_flags ^= cOrientationFlagYFlipped;
2849                     get_face(f)[m]->set_orientation_flags(static_cast<orientation_flags_t>(orient_flags));
2850                 }
2851             }
2852         }
2853
2854         VOGL_ASSERT(check());
2855
2856         return true;
2857     }
2858
2859 } // namespace vogl