1 /**************************************************************************
3 * Copyright 2013-2014 RAD Game Tools and Valve Software
4 * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
7 * Permission is hereby granted, free of charge, to any person obtaining a copy
8 * of this software and associated documentation files (the "Software"), to deal
9 * in the Software without restriction, including without limitation the rights
10 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 * copies of the Software, and to permit persons to whom the Software is
12 * furnished to do so, subject to the following conditions:
14 * The above copyright notice and this permission notice shall be included in
15 * all copies or substantial portions of the Software.
17 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
25 **************************************************************************/
27 // File: vogl_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"
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) };
40 mip_level::mip_level()
43 m_comp_flags(pixel_format_helpers::cDefaultCompFlags),
44 m_format(PIXEL_FMT_INVALID),
47 m_orient_flags(cDefaultOrientationFlags)
51 mip_level::mip_level(const mip_level &other)
54 m_comp_flags(pixel_format_helpers::cDefaultCompFlags),
55 m_format(PIXEL_FMT_INVALID),
58 m_orient_flags(cDefaultOrientationFlags)
63 mip_level &mip_level::operator=(const mip_level &rhs)
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;
74 m_pImage = vogl_new(image_u8, *rhs.m_pImage);
77 m_pDXTImage = vogl_new(dxt_image, *rhs.m_pDXTImage);
82 mip_level::~mip_level()
84 vogl_delete(m_pImage);
85 vogl_delete(m_pDXTImage);
88 void mip_level::clear()
92 m_comp_flags = pixel_format_helpers::cDefaultCompFlags;
93 m_format = PIXEL_FMT_INVALID;
94 m_orient_flags = cDefaultOrientationFlags;
98 vogl_delete(m_pImage);
104 vogl_delete(m_pDXTImage);
109 void mip_level::assign(image_u8 *p, pixel_format fmt, orientation_flags_t orient_flags)
117 m_width = p->get_width();
118 m_height = p->get_height();
119 m_orient_flags = orient_flags;
121 if (fmt != PIXEL_FMT_INVALID)
125 if (p->is_grayscale())
126 m_format = p->is_component_valid(3) ? PIXEL_FMT_A8L8 : PIXEL_FMT_L8;
128 m_format = p->is_component_valid(3) ? PIXEL_FMT_A8R8G8B8 : PIXEL_FMT_R8G8B8;
131 m_comp_flags = p->get_comp_flags(); //pixel_format_helpers::get_component_flags(m_format);
134 void mip_level::assign(dxt_image *p, pixel_format fmt, orientation_flags_t orient_flags)
142 m_width = p->get_width();
143 m_height = p->get_height();
144 m_orient_flags = orient_flags;
146 if (fmt != PIXEL_FMT_INVALID)
149 m_format = pixel_format_helpers::from_dxt_format(p->get_format());
151 m_comp_flags = pixel_format_helpers::get_component_flags(m_format);
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)
156 VOGL_ASSERT(pixel_format_helpers::is_dxt(fmt));
157 if (!pixel_format_helpers::is_dxt(fmt))
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))
163 // Disable perceptual colorspace metrics when packing to swizzled or non-RGB pixel formats.
164 p.m_perceptual = false;
167 image_u8 tmp_img(img);
176 if ((pixel_format_helpers::is_alpha_only(fmt)) && (!tmp_img.has_alpha()))
177 tmp_img.set_alpha_to_luma();
179 dxt_format dxt_fmt = pixel_format_helpers::get_dxt_format(fmt);
181 dxt_image *pDXT_image = vogl_new(dxt_image);
182 if (!pDXT_image->init(dxt_fmt, tmp_img, p))
188 assign(pDXT_image, fmt, orient_flags);
193 bool mip_level::pack_to_dxt(pixel_format fmt, bool cook, const dxt_image::pack_params &p)
195 VOGL_ASSERT(pixel_format_helpers::is_dxt(fmt));
196 if (!pixel_format_helpers::is_dxt(fmt))
200 image_u8 *pImage = get_unpacked_image(tmp_img, cUnpackFlagUncook);
202 return pack_to_dxt(*pImage, fmt, cook, p, m_orient_flags);
205 bool mip_level::unpack_from_dxt(bool uncook)
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);
214 VOGL_ASSERT(pImg == pNew_img);
216 assign(pNew_img, PIXEL_FMT_INVALID, m_orient_flags);
220 bool mip_level::is_flipped() const
222 return ((m_orient_flags & (cOrientationFlagXFlipped | cOrientationFlagYFlipped)) != 0);
225 bool mip_level::is_x_flipped() const
227 return ((m_orient_flags & cOrientationFlagXFlipped) != 0);
230 bool mip_level::is_y_flipped() const
232 return ((m_orient_flags & cOrientationFlagYFlipped) != 0);
235 bool mip_level::can_unflip_without_unpacking() const
243 bool can_unflip = true;
244 if (m_orient_flags & cOrientationFlagXFlipped)
246 if (!m_pDXTImage->can_flip(0))
249 if (m_orient_flags & cOrientationFlagYFlipped)
251 if (!m_pDXTImage->can_flip(1))
258 bool mip_level::unflip(bool allow_unpacking_to_flip, bool uncook_if_necessary_to_unpack)
268 if (can_unflip_without_unpacking())
270 if (m_orient_flags & cOrientationFlagXFlipped)
272 m_pDXTImage->flip_x();
273 m_orient_flags = static_cast<orientation_flags_t>(m_orient_flags & ~cOrientationFlagXFlipped);
276 if (m_orient_flags & cOrientationFlagYFlipped)
278 m_pDXTImage->flip_y();
279 m_orient_flags = static_cast<orientation_flags_t>(m_orient_flags & ~cOrientationFlagYFlipped);
285 if (!allow_unpacking_to_flip)
289 unpack_from_dxt(uncook_if_necessary_to_unpack);
291 if (m_orient_flags & cOrientationFlagXFlipped)
294 m_orient_flags = static_cast<orientation_flags_t>(m_orient_flags & ~cOrientationFlagXFlipped);
297 if (m_orient_flags & cOrientationFlagYFlipped)
300 m_orient_flags = static_cast<orientation_flags_t>(m_orient_flags & ~cOrientationFlagYFlipped);
306 bool mip_level::set_alpha_to_luma()
309 unpack_from_dxt(true);
311 m_pImage->set_alpha_to_luma();
313 m_comp_flags = m_pImage->get_comp_flags();
315 if (m_pImage->is_grayscale())
316 m_format = PIXEL_FMT_A8L8;
318 m_format = PIXEL_FMT_A8R8G8B8;
323 bool mip_level::convert(image_utils::conversion_type conv_type)
326 unpack_from_dxt(true);
328 image_utils::convert_image(*m_pImage, conv_type);
330 m_comp_flags = m_pImage->get_comp_flags();
332 if (m_pImage->is_grayscale())
333 m_format = m_pImage->has_alpha() ? PIXEL_FMT_A8L8 : PIXEL_FMT_L8;
335 m_format = m_pImage->has_alpha() ? PIXEL_FMT_A8R8G8B8 : PIXEL_FMT_R8G8B8;
340 bool mip_level::convert(pixel_format fmt, bool cook, const dxt_image::pack_params &p)
342 if (pixel_format_helpers::is_dxt(fmt))
343 return pack_to_dxt(fmt, cook, p);
346 image_u8 *pImg = get_unpacked_image(tmp_img, cUnpackFlagUncook);
348 image_u8 *pImage = vogl_new(image_u8);
349 pImage->set_comp_flags(pixel_format_helpers::get_component_flags(fmt));
351 if (!pImage->crop(pImg->get_width(), pImg->get_height()))
354 for (uint y = 0; y < pImg->get_height(); y++)
356 for (uint x = 0; x < pImg->get_width(); x++)
358 color_quad_u8 c((*pImg)(x, y));
360 if ((pixel_format_helpers::is_alpha_only(fmt)) && (!pImg->has_alpha()))
362 c.a = static_cast<uint8>(c.get_luma());
366 if (pImage->is_grayscale())
368 uint8 g = static_cast<uint8>(c.get_luma());
374 if (!pImage->is_component_valid(3))
382 assign(pImage, fmt, m_orient_flags);
387 void mip_level::cook_image(image_u8 &img) const
389 image_utils::conversion_type conv_type = image_utils::get_conversion_type(true, m_format);
391 if (conv_type != image_utils::cConversion_Invalid)
392 image_utils::convert_image(img, conv_type);
395 void mip_level::uncook_image(image_u8 &img) const
397 image_utils::conversion_type conv_type = image_utils::get_conversion_type(false, m_format);
399 if (conv_type != image_utils::cConversion_Invalid)
400 image_utils::convert_image(img, conv_type);
403 image_u8 *mip_level::get_unpacked_image(image_u8 &tmp, uint unpack_flags) const
410 m_pDXTImage->unpack(tmp);
412 tmp.set_comp_flags(m_comp_flags);
414 if (unpack_flags & cUnpackFlagUncook)
417 else if ((unpack_flags & cUnpackFlagUnflip) && (m_orient_flags & (cOrientationFlagXFlipped | cOrientationFlagYFlipped)))
422 if (unpack_flags & cUnpackFlagUnflip)
424 if (m_orient_flags & cOrientationFlagXFlipped)
426 if (m_orient_flags & cOrientationFlagYFlipped)
433 bool mip_level::flip_x()
439 return m_pDXTImage->flip_x();
449 bool mip_level::flip_y()
455 return m_pDXTImage->flip_y();
465 // -------------------------------------------------------------------------
467 mipmapped_texture::mipmapped_texture()
470 m_comp_flags(pixel_format_helpers::cDefaultCompFlags),
471 m_format(PIXEL_FMT_INVALID),
472 m_source_file_type(texture_file_types::cFormatInvalid)
476 mipmapped_texture::~mipmapped_texture()
481 void mipmapped_texture::clear()
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();
494 void mipmapped_texture::free_all_mips()
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]);
503 mipmapped_texture::mipmapped_texture(const mipmapped_texture &other)
506 m_comp_flags(pixel_format_helpers::cDefaultCompFlags),
507 m_format(PIXEL_FMT_INVALID)
512 mipmapped_texture &mipmapped_texture::operator=(const mipmapped_texture &rhs)
520 m_width = rhs.m_width;
521 m_height = rhs.m_height;
523 m_comp_flags = rhs.m_comp_flags;
524 m_format = rhs.m_format;
526 m_faces.resize(rhs.m_faces.size());
527 for (uint i = 0; i < m_faces.size(); i++)
529 m_faces[i].resize(rhs.m_faces[i].size());
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]);
535 VOGL_ASSERT((!is_valid()) || check());
540 bool mipmapped_texture::read_dds(data_stream_serializer &serializer)
542 if (!read_dds_internal(serializer))
551 bool mipmapped_texture::read_dds_internal(data_stream_serializer &serializer)
553 VOGL_ASSERT(serializer.get_little_endian());
557 set_last_error("Not a DDS file");
560 if (!serializer.read(hdr, sizeof(hdr)))
563 if (memcmp(hdr, "DDS ", 4) != 0)
567 if (!serializer.read(&desc, sizeof(desc)))
570 if (!c_vogl_little_endian_platform)
571 utils::endian_switch_dwords(reinterpret_cast<uint32 *>(&desc), sizeof(desc) / sizeof(uint32));
573 if (desc.dwSize != sizeof(desc))
576 if ((!desc.dwHeight) || (!desc.dwWidth) || (desc.dwHeight > cDDSMaxImageDimensions) || (desc.dwWidth > cDDSMaxImageDimensions))
579 m_width = desc.dwWidth;
580 m_height = desc.dwHeight;
582 uint num_mip_levels = 1;
584 if ((desc.dwFlags & DDSD_MIPMAPCOUNT) && (desc.ddsCaps.dwCaps & DDSCAPS_MIPMAP) && (desc.dwMipMapCount))
586 num_mip_levels = desc.dwMipMapCount;
587 if (num_mip_levels > utils::compute_max_mips(desc.dwWidth, desc.dwHeight))
593 if (desc.ddsCaps.dwCaps & DDSCAPS_COMPLEX)
595 if (desc.ddsCaps.dwCaps2 & DDSCAPS2_CUBEMAP)
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)
600 set_last_error("Incomplete cubemaps unsupported");
606 else if (desc.ddsCaps.dwCaps2 & DDSCAPS2_VOLUME)
608 set_last_error("Volume textures unsupported");
613 if (desc.ddpfPixelFormat.dwFlags & DDPF_PALETTEINDEXED8)
615 // It's difficult to even make P8 textures with existing tools:
617 // dxtex.exe just makes all-white textures
619 set_last_error("Palettized textures unsupported");
623 dxt_format dxt_fmt = cDXTInvalid;
625 if (desc.ddpfPixelFormat.dwFlags & DDPF_FOURCC)
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
632 switch (desc.ddpfPixelFormat.dwFourCC)
636 m_format = PIXEL_FMT_DXT1;
643 m_format = PIXEL_FMT_DXT3;
650 switch (desc.ddpfPixelFormat.dwRGBBitCount)
652 case PIXEL_FMT_DXT5_CCxY:
653 m_format = PIXEL_FMT_DXT5_CCxY;
655 case PIXEL_FMT_DXT5_xGxR:
656 m_format = PIXEL_FMT_DXT5_xGxR;
658 case PIXEL_FMT_DXT5_xGBR:
659 m_format = PIXEL_FMT_DXT5_xGBR;
661 case PIXEL_FMT_DXT5_AGBR:
662 m_format = PIXEL_FMT_DXT5_AGBR;
665 m_format = PIXEL_FMT_DXT5;
674 if (desc.ddpfPixelFormat.dwRGBBitCount == VOGL_PIXEL_FMT_FOURCC('A', '2', 'X', 'Y'))
677 m_format = PIXEL_FMT_DXN;
681 dxt_fmt = cDXN_YX; // aka ATI2
682 m_format = PIXEL_FMT_3DC;
687 case PIXEL_FMT_DXT5A:
689 m_format = PIXEL_FMT_DXT5A;
695 m_format = PIXEL_FMT_ETC1;
701 dynamic_string err_msg(cVarArg, "Unsupported DDS FOURCC format: 0x%08X", desc.ddpfPixelFormat.dwFourCC);
702 set_last_error(err_msg.get_ptr());
707 else if ((desc.ddpfPixelFormat.dwRGBBitCount < 8) || (desc.ddpfPixelFormat.dwRGBBitCount > 32) || (desc.ddpfPixelFormat.dwRGBBitCount & 7))
709 set_last_error("Unsupported bit count");
712 else if (desc.ddpfPixelFormat.dwFlags & DDPF_RGB)
714 if (desc.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE)
716 if (desc.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS)
717 m_format = PIXEL_FMT_A8L8;
719 m_format = PIXEL_FMT_L8;
721 else if (desc.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS)
722 m_format = PIXEL_FMT_A8R8G8B8;
724 m_format = PIXEL_FMT_R8G8B8;
726 else if (desc.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS)
728 if (desc.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE)
729 m_format = PIXEL_FMT_A8L8;
731 m_format = PIXEL_FMT_A8;
733 else if (desc.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE)
735 m_format = PIXEL_FMT_L8;
737 else if (desc.ddpfPixelFormat.dwFlags & DDPF_ALPHA)
739 m_format = PIXEL_FMT_A8;
743 set_last_error("Unsupported format");
747 m_comp_flags = pixel_format_helpers::get_component_flags(m_format);
749 uint bits_per_pixel = desc.ddpfPixelFormat.dwRGBBitCount;
751 if (desc.ddpfPixelFormat.dwFlags & DDPF_FOURCC)
752 bits_per_pixel = pixel_format_helpers::get_bpp(m_format);
754 set_last_error("Load failed");
757 if (desc.ddpfPixelFormat.dwFlags & DDPF_FOURCC)
758 default_pitch = (((desc.dwWidth + 3) & ~3) * ((desc.dwHeight + 3) & ~3) * bits_per_pixel) >> 3;
760 default_pitch = (desc.dwWidth * bits_per_pixel) >> 3;
763 if ((desc.dwFlags & DDSD_PITCH) && (!(desc.dwFlags & DDSD_LINEARSIZE)))
769 pitch = default_pitch;
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");
779 // Check for obviously wacky source pitches (probably a corrupted/invalid file).
780 else if (pitch > default_pitch * 8)
782 set_last_error("Invalid pitch");
786 vogl::vector<uint8> load_buf;
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);
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);
800 if ((desc.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE) && (!mask_size[0]))
802 mask_size[0] = desc.ddpfPixelFormat.dwRGBBitCount >> 3;
803 if (desc.ddpfPixelFormat.dwFlags & DDPF_ALPHAPIXELS)
807 m_faces.resize(num_faces);
809 bool dxt1_alpha = false;
811 for (uint face_index = 0; face_index < num_faces; face_index++)
813 m_faces[face_index].resize(num_mip_levels);
815 for (uint level_index = 0; level_index < num_mip_levels; level_index++)
817 const uint width = math::maximum<uint>(desc.dwWidth >> level_index, 1U);
818 const uint height = math::maximum<uint>(desc.dwHeight >> level_index, 1U);
820 mip_level *pMip = vogl_new(mip_level);
821 m_faces[face_index][level_index] = pMip;
823 if (desc.ddpfPixelFormat.dwFlags & DDPF_FOURCC)
825 const uint bytes_per_block = pixel_format_helpers::get_dxt_bytes_per_block(m_format);
827 const uint num_blocks_x = (width + 3) >> 2;
828 const uint num_blocks_y = (height + 3) >> 2;
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;
833 dxt_image *pDXTImage = vogl_new(dxt_image);
834 if (!pDXTImage->init(dxt_fmt, width, height, false))
836 vogl_delete(pDXTImage);
842 VOGL_ASSERT(pDXTImage->get_element_vec().size() * sizeof(dxt_image::element) == actual_level_pitch);
844 if (!serializer.read(&pDXTImage->get_element_vec()[0], actual_level_pitch))
846 vogl_delete(pDXTImage);
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));
855 if (level_pitch > actual_level_pitch)
857 if (!serializer.skip(level_pitch - actual_level_pitch))
859 vogl_delete(pDXTImage);
865 if ((m_format == PIXEL_FMT_DXT1) && (!dxt1_alpha))
866 dxt1_alpha = pDXTImage->has_alpha();
868 pMip->assign(pDXTImage, m_format);
872 image_u8 *pImage = vogl_new(image_u8, width, height);
874 pImage->set_comp_flags(m_comp_flags);
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;
880 if (load_buf.size() < line_pitch)
881 load_buf.resize(line_pitch);
883 color_quad_u8 q(0, 0, 0, 255);
885 for (uint y = 0; y < height; y++)
887 if (!serializer.read(&load_buf[0], line_pitch))
893 color_quad_u8 *pDst = pImage->get_scanline(y);
895 for (uint x = 0; x < width; x++)
897 const uint8 *pPixel = &load_buf[x * bytes_per_pixel];
900 // Assumes DDS is always little endian.
901 for (uint l = 0; l < bytes_per_pixel; l++)
902 c |= (pPixel[l] << (l * 8U));
904 for (uint i = 0; i < 4; i++)
909 uint mask = (1U << mask_size[i]) - 1U;
910 uint bits = (c >> mask_ofs[i]) & mask;
912 uint v = (bits * 255 + (mask >> 1)) / mask;
914 q.set_component(i, v);
917 if (desc.ddpfPixelFormat.dwFlags & DDPF_LUMINANCE)
927 pMip->assign(pImage, m_format);
929 VOGL_ASSERT(pMip->get_comp_flags() == m_comp_flags);
937 change_dxt1_to_dxt1a();
942 void mipmapped_texture::change_dxt1_to_dxt1a()
944 if (m_format != PIXEL_FMT_DXT1)
947 m_format = PIXEL_FMT_DXT1A;
949 m_comp_flags = pixel_format_helpers::get_component_flags(m_format);
951 for (uint f = 0; f < m_faces.size(); f++)
953 for (uint l = 0; l < m_faces[f].size(); l++)
955 if (m_faces[f][l]->get_dxt_image())
957 m_faces[f][l]->set_format(m_format);
958 m_faces[f][l]->set_comp_flags(m_comp_flags);
960 m_faces[f][l]->get_dxt_image()->change_dxt1_to_dxt1a();
966 bool mipmapped_texture::check() const
969 orientation_flags_t orient_flags = cDefaultOrientationFlags;
970 for (uint f = 0; f < m_faces.size(); f++)
974 levels = m_faces[f].size();
975 if ((levels) && (m_faces[f][0]))
976 orient_flags = m_faces[f][0]->get_orientation_flags();
978 else if (m_faces[f].size() != levels)
981 for (uint l = 0; l < m_faces[f].size(); l++)
983 mip_level *p = m_faces[f][l];
990 if (p->get_orientation_flags() != orient_flags)
995 if (m_width != p->get_width())
997 if (m_height != p->get_height())
1001 if (p->get_comp_flags() != m_comp_flags)
1004 if (p->get_format() != m_format)
1009 if (pixel_format_helpers::is_dxt(p->get_format()))
1012 if (p->get_image()->get_width() != p->get_width())
1014 if (p->get_image()->get_height() != p->get_height())
1016 if (p->get_image()->get_comp_flags() != m_comp_flags)
1019 else if (!pixel_format_helpers::is_dxt(p->get_format()))
1027 bool mipmapped_texture::write_dds(data_stream_serializer &serializer) const
1031 set_last_error("Nothing to write");
1035 set_last_error("write_dds() failed");
1037 if (!serializer.write("DDS ", sizeof(uint32)))
1040 DDSURFACEDESC2 desc;
1041 utils::zero_object(desc);
1043 desc.dwSize = sizeof(desc);
1044 desc.dwFlags = DDSD_WIDTH | DDSD_HEIGHT | DDSD_PIXELFORMAT | DDSD_CAPS;
1046 desc.dwWidth = m_width;
1047 desc.dwHeight = m_height;
1049 desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE;
1050 desc.ddpfPixelFormat.dwSize = sizeof(desc.ddpfPixelFormat);
1052 if (get_num_levels() > 1)
1054 desc.dwMipMapCount = get_num_levels();
1055 desc.dwFlags |= DDSD_MIPMAPCOUNT;
1056 desc.ddsCaps.dwCaps |= (DDSCAPS_MIPMAP | DDSCAPS_COMPLEX);
1059 if (get_num_faces() > 1)
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;
1066 bool dxt_format = false;
1067 if (pixel_format_helpers::is_dxt(m_format))
1071 desc.ddpfPixelFormat.dwFlags |= DDPF_FOURCC;
1075 case PIXEL_FMT_ETC1:
1077 desc.ddpfPixelFormat.dwFourCC = (uint32)PIXEL_FMT_ETC1;
1078 desc.ddpfPixelFormat.dwRGBBitCount = 0;
1083 desc.ddpfPixelFormat.dwFourCC = (uint32)PIXEL_FMT_3DC;
1084 desc.ddpfPixelFormat.dwRGBBitCount = PIXEL_FMT_DXN;
1087 case PIXEL_FMT_DXT1A:
1089 desc.ddpfPixelFormat.dwFourCC = (uint32)PIXEL_FMT_DXT1;
1090 desc.ddpfPixelFormat.dwRGBBitCount = 0;
1093 case PIXEL_FMT_DXT5_CCxY:
1095 desc.ddpfPixelFormat.dwFourCC = (uint32)PIXEL_FMT_DXT5;
1096 desc.ddpfPixelFormat.dwRGBBitCount = (uint32)PIXEL_FMT_DXT5_CCxY;
1099 case PIXEL_FMT_DXT5_xGxR:
1101 desc.ddpfPixelFormat.dwFourCC = (uint32)PIXEL_FMT_DXT5;
1102 desc.ddpfPixelFormat.dwRGBBitCount = (uint32)PIXEL_FMT_DXT5_xGxR;
1105 case PIXEL_FMT_DXT5_xGBR:
1107 desc.ddpfPixelFormat.dwFourCC = (uint32)PIXEL_FMT_DXT5;
1108 desc.ddpfPixelFormat.dwRGBBitCount = (uint32)PIXEL_FMT_DXT5_xGBR;
1111 case PIXEL_FMT_DXT5_AGBR:
1113 desc.ddpfPixelFormat.dwFourCC = (uint32)PIXEL_FMT_DXT5;
1114 desc.ddpfPixelFormat.dwRGBBitCount = (uint32)PIXEL_FMT_DXT5_AGBR;
1119 desc.ddpfPixelFormat.dwFourCC = (uint32)m_format;
1120 desc.ddpfPixelFormat.dwRGBBitCount = 0;
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;
1133 case PIXEL_FMT_A8R8G8B8:
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;
1143 case PIXEL_FMT_R8G8B8:
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;
1154 desc.ddpfPixelFormat.dwFlags |= DDPF_ALPHA;
1155 desc.ddpfPixelFormat.dwRGBBitCount = 8;
1156 desc.ddpfPixelFormat.dwRGBAlphaBitMask = 0xFF;
1161 desc.ddpfPixelFormat.dwFlags |= DDPF_LUMINANCE;
1162 desc.ddpfPixelFormat.dwRGBBitCount = 8;
1163 desc.ddpfPixelFormat.dwRBitMask = 0xFF;
1166 case PIXEL_FMT_A8L8:
1168 desc.ddpfPixelFormat.dwFlags |= DDPF_ALPHAPIXELS | DDPF_LUMINANCE;
1169 desc.ddpfPixelFormat.dwRGBBitCount = 16;
1170 desc.ddpfPixelFormat.dwRBitMask = 0xFF;
1171 desc.ddpfPixelFormat.dwRGBAlphaBitMask = 0xFF00;
1181 uint bits_per_pixel = desc.ddpfPixelFormat.dwRGBBitCount;
1182 desc.lPitch = (desc.dwWidth * bits_per_pixel) >> 3;
1183 desc.dwFlags |= DDSD_LINEARSIZE;
1186 if (!c_vogl_little_endian_platform)
1187 utils::endian_switch_dwords(reinterpret_cast<uint32 *>(&desc), sizeof(desc) / sizeof(uint32));
1189 if (!serializer.write(&desc, sizeof(desc)))
1192 if (!c_vogl_little_endian_platform)
1193 utils::endian_switch_dwords(reinterpret_cast<uint32 *>(&desc), sizeof(desc) / sizeof(uint32));
1195 vogl::vector<uint8> write_buf;
1197 const bool can_unflip_packed_texture = can_unflip_without_unpacking();
1198 if ((is_packed()) && (is_flipped()) && (!can_unflip_without_unpacking()))
1200 console::warning("mipmapped_texture::write_dds: One or more faces/miplevels cannot be unflipped without unpacking. Writing flipped .DDS texture.\n");
1203 for (uint face = 0; face < get_num_faces(); face++)
1205 for (uint level = 0; level < get_num_levels(); level++)
1207 const mip_level *pLevel = get_level(face, level);
1211 const uint width = pLevel->get_width();
1212 const uint height = pLevel->get_height();
1214 VOGL_ASSERT(width == math::maximum<uint>(1, m_width >> level));
1215 VOGL_ASSERT(height == math::maximum<uint>(1, m_height >> level));
1217 const dxt_image *p = pLevel->get_dxt_image();
1219 if ((can_unflip_packed_texture) && (pLevel->get_orientation_flags() & (cOrientationFlagXFlipped | cOrientationFlagYFlipped)))
1222 if (pLevel->get_orientation_flags() & cOrientationFlagXFlipped)
1225 console::warning("mipmapped_texture::write_dds: Unable to unflip compressed texture on X axis\n");
1228 if (pLevel->get_orientation_flags() & cOrientationFlagYFlipped)
1231 console::warning("mipmapped_texture::write_dds: Unable to unflip compressed texture on Y axis\n");
1236 const uint num_blocks_x = (width + 3) >> 2;
1237 const uint num_blocks_y = (height + 3) >> 2;
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);
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);
1249 memcpy(&write_buf[0], p->get_element_ptr(), size_in_bytes);
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));
1256 if (!serializer.write(&write_buf[0], size_in_bytes))
1261 const uint width = pLevel->get_width();
1262 const uint height = pLevel->get_height();
1264 const image_u8 *p = pLevel->get_image();
1266 if (pLevel->get_orientation_flags() & (cOrientationFlagXFlipped | cOrientationFlagYFlipped))
1268 p = pLevel->get_unpacked_image(tmp, cUnpackFlagUnflip);
1271 const uint bits_per_pixel = desc.ddpfPixelFormat.dwRGBBitCount;
1272 const uint bytes_per_pixel = bits_per_pixel >> 3;
1274 const uint pitch = width * bytes_per_pixel;
1275 if (pitch > write_buf.size())
1276 write_buf.resize(pitch);
1278 for (uint y = 0; y < height; y++)
1280 const color_quad_u8 *pSrc = p->get_scanline(y);
1281 const color_quad_u8 *pEnd = pSrc + width;
1283 uint8 *pDst = &write_buf[0];
1287 const color_quad_u8 &c = *pSrc;
1292 case PIXEL_FMT_A8R8G8B8:
1294 x = (c.a << 24) | (c.r << 16) | (c.g << 8) | c.b;
1297 case PIXEL_FMT_R8G8B8:
1299 x = (c.r << 16) | (c.g << 8) | c.b;
1307 case PIXEL_FMT_A8L8:
1309 x = (c.a << 8) | c.get_luma();
1321 pDst[0] = static_cast<uint8>(x);
1322 if (bytes_per_pixel > 1)
1324 pDst[1] = static_cast<uint8>(x >> 8);
1326 if (bytes_per_pixel > 2)
1328 pDst[2] = static_cast<uint8>(x >> 16);
1330 if (bytes_per_pixel > 3)
1331 pDst[3] = static_cast<uint8>(x >> 24);
1336 pDst += bytes_per_pixel;
1338 } while (pSrc != pEnd);
1340 if (!serializer.write(&write_buf[0], pitch))
1352 bool mipmapped_texture::read_ktx(data_stream_serializer &serializer)
1356 set_last_error("Unable to read KTX file");
1359 if (!kt.read_from_stream(serializer))
1362 if ((kt.get_depth() > 1) || (kt.get_array_size() > 1))
1364 set_last_error("read_ktx: 3D and array textures are not supported");
1368 return read_ktx(kt);
1371 bool mipmapped_texture::read_ktx(const vogl::ktx_texture &ktx)
1373 // Must be 1D, 2D, or a cubemap, with or without mipmaps.
1374 m_width = ktx.get_width();
1375 m_height = ktx.get_height();
1377 uint num_mip_levels = ktx.get_num_mips();
1378 uint num_faces = ktx.get_num_faces();
1380 uint32 vogl_fourcc = 0;
1381 dynamic_string vogl_fourcc_str;
1382 if (ktx.get_key_value_as_string("VOGL_FOURCC", vogl_fourcc_str))
1384 if (vogl_fourcc_str.get_len() == 4)
1386 for (int i = 3; i >= 0; i--)
1387 vogl_fourcc = (vogl_fourcc << 8) | vogl_fourcc_str[i];
1391 const bool is_compressed_texture = ktx.is_compressed();
1392 dxt_format dxt_fmt = cDXTInvalid;
1394 pixel_packer unpacker;
1395 if (is_compressed_texture)
1397 switch (ktx.get_ogl_internal_fmt())
1399 case KTX_ETC1_RGB8_OES:
1404 case KTX_COMPRESSED_RGB_S3TC_DXT1_EXT:
1405 case KTX_COMPRESSED_SRGB_S3TC_DXT1_EXT:
1408 case KTX_COMPRESSED_RGBA_S3TC_DXT1_EXT:
1409 case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
1413 case KTX_RGBA4_S3TC:
1414 case KTX_COMPRESSED_RGBA_S3TC_DXT3_EXT:
1415 case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
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:
1424 case KTX_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
1426 if (vogl_fourcc == PIXEL_FMT_DXN)
1431 case KTX_COMPRESSED_LUMINANCE_LATC1_EXT:
1435 set_last_error("Unsupported KTX internal format");
1439 m_format = pixel_format_helpers::from_dxt_format(dxt_fmt);
1440 if (m_format == PIXEL_FMT_INVALID)
1442 set_last_error("Unsupported KTX internal compressed format");
1446 if (vogl_fourcc != 0)
1448 switch (vogl_fourcc)
1450 case PIXEL_FMT_DXT5_CCxY:
1451 case PIXEL_FMT_DXT5_xGxR:
1452 case PIXEL_FMT_DXT5_xGBR:
1453 case PIXEL_FMT_DXT5_AGBR:
1455 if (dxt_fmt == cDXT5)
1457 m_format = static_cast<pixel_format>(vogl_fourcc);
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;
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()))
1475 switch (ktx.get_ogl_type())
1477 // 24bpp packed formats
1478 case KTX_UNSIGNED_BYTE_3_3_2:
1479 unpacker.init("B2G3R3");
1480 m_format = PIXEL_FMT_R8G8B8;
1482 case KTX_UNSIGNED_BYTE_2_3_3_REV:
1483 unpacker.init("R3G3B2");
1484 m_format = PIXEL_FMT_R8G8B8;
1486 case KTX_UNSIGNED_SHORT_5_6_5:
1487 unpacker.init("B5G6R5");
1488 m_format = PIXEL_FMT_R8G8B8;
1490 case KTX_UNSIGNED_SHORT_5_6_5_REV:
1491 unpacker.init("R5G6B5");
1492 m_format = PIXEL_FMT_R8G8B8;
1494 // 32bpp packed formats
1495 case KTX_UNSIGNED_SHORT_4_4_4_4:
1496 unpacker.init("A4B4G4R4");
1498 case KTX_UNSIGNED_SHORT_4_4_4_4_REV:
1499 unpacker.init("R4G4B4A4");
1501 case KTX_UNSIGNED_SHORT_5_5_5_1:
1502 unpacker.init("A1B5G5R5");
1504 case KTX_UNSIGNED_SHORT_1_5_5_5_REV:
1505 unpacker.init("R5G5B5A1");
1507 case KTX_UNSIGNED_INT_8_8_8_8:
1508 unpacker.init("A8B8G8R8");
1510 case KTX_UNSIGNED_INT_8_8_8_8_REV:
1511 unpacker.init("R8G8B8A8");
1513 case KTX_UNSIGNED_INT_10_10_10_2:
1514 unpacker.init("A2B10G10R10");
1516 case KTX_UNSIGNED_INT_2_10_10_10_REV:
1517 unpacker.init("R10G10B10A2");
1519 case KTX_UNSIGNED_INT_5_9_9_9_REV:
1520 unpacker.init("R9G9B9A5");
1522 case KTX_UNSIGNED_INT_24_8:
1523 unpacker.init("A8B8G8R8"); // A=stencil,RGB=depth (R is MSB)
1525 case KTX_FLOAT_32_UNSIGNED_INT_24_8_REV:
1526 unpacker.init("R8G8B8A8"); // A=stencil,RGB=depth (R is MSB)
1529 set_last_error("Unsupported KTX packed pixel type");
1533 unpacker.set_pixel_stride(ktx_get_ogl_type_size(ktx.get_ogl_type()));
1537 if ((ktx.get_ogl_fmt() == KTX_STENCIL_INDEX) || (ktx.get_ogl_fmt() == KTX_DEPTH_COMPONENT) || (ktx.get_ogl_fmt() == KTX_DEPTH_STENCIL))
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);
1547 unpacker.init("A8B8G8R8", type_size);
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,
1552 ktx.get_ogl_internal_fmt(),
1553 ktx.get_ogl_base_fmt());
1557 switch (ktx.get_ogl_fmt())
1561 case KTX_RED_INTEGER:
1565 unpacker.init("R", -1, type_bits);
1566 m_format = PIXEL_FMT_R8G8B8;
1570 case KTX_GREEN_INTEGER:
1572 unpacker.init("G", -1, type_bits);
1573 m_format = PIXEL_FMT_R8G8B8;
1577 case KTX_BLUE_INTEGER:
1579 unpacker.init("B", -1, type_bits);
1580 m_format = PIXEL_FMT_R8G8B8;
1585 unpacker.init("A", -1, type_bits);
1586 m_format = PIXEL_FMT_A8;
1591 unpacker.init("Y", -1, type_bits);
1592 m_format = PIXEL_FMT_L8;
1598 case KTX_RG_INTEGER:
1600 unpacker.init("RG", -1, type_bits);
1601 m_format = PIXEL_FMT_A8L8;
1604 case KTX_LUMINANCE_ALPHA:
1606 unpacker.init("YA", -1, type_bits);
1607 m_format = PIXEL_FMT_A8L8;
1613 case KTX_RGB_INTEGER:
1617 unpacker.init("RGB", -1, type_bits);
1618 m_format = PIXEL_FMT_R8G8B8;
1622 case KTX_BGR_INTEGER:
1624 unpacker.init("BGR", -1, type_bits);
1625 m_format = PIXEL_FMT_R8G8B8;
1629 case KTX_RGBA_INTEGER:
1631 case KTX_SRGB_ALPHA:
1632 case KTX_SRGB8_ALPHA8:
1635 unpacker.init("RGBA", -1, type_bits);
1639 case KTX_BGRA_INTEGER:
1641 unpacker.init("BGRA", -1, type_bits);
1645 set_last_error("Unsupported KTX pixel format");
1649 unpacker.set_pixel_stride(unpacker.get_num_comps() * ktx_get_ogl_type_size(ktx.get_ogl_type()));
1653 VOGL_ASSERT(unpacker.is_valid());
1656 m_comp_flags = pixel_format_helpers::get_component_flags(m_format);
1658 m_faces.resize(num_faces);
1660 bool x_flipped = false;
1661 bool y_flipped = true;
1663 dynamic_string orient;
1664 if ((ktx.get_key_value_as_string("KTXorientation", orient)) && (orient.get_len() >= 7))
1668 if ((orient[0] == 'S') && (orient[1] == '=') && (orient[3] == ',') &&
1669 (orient[4] == 'T') && (orient[5] == '='))
1671 if (vogl_tolower(orient[2]) == 'l')
1673 else if (vogl_tolower(orient[2]) == 'r')
1676 if (vogl_tolower(orient[6]) == 'u')
1678 else if (vogl_tolower(orient[6]) == 'd')
1683 orientation_flags_t orient_flags = cDefaultOrientationFlags;
1685 orient_flags = static_cast<orientation_flags_t>(orient_flags | cOrientationFlagXFlipped);
1687 orient_flags = static_cast<orientation_flags_t>(orient_flags | cOrientationFlagYFlipped);
1689 bool dxt1_alpha = false;
1691 for (uint face_index = 0; face_index < num_faces; face_index++)
1693 m_faces[face_index].resize(num_mip_levels);
1695 for (uint level_index = 0; level_index < num_mip_levels; level_index++)
1697 const uint width = math::maximum<uint>(m_width >> level_index, 1U);
1698 const uint height = math::maximum<uint>(m_height >> level_index, 1U);
1700 mip_level *pMip = vogl_new(mip_level);
1701 m_faces[face_index][level_index] = pMip;
1703 const vogl::vector<uint8> &image_data = ktx.get_image_data(level_index, 0, face_index, 0);
1705 if (is_compressed_texture)
1707 const uint bytes_per_block = pixel_format_helpers::get_dxt_bytes_per_block(m_format);
1709 const uint num_blocks_x = (width + 3) >> 2;
1710 const uint num_blocks_y = (height + 3) >> 2;
1712 const uint level_pitch = num_blocks_x * num_blocks_y * bytes_per_block;
1713 if (image_data.size() != level_pitch)
1716 dxt_image *pDXTImage = vogl_new(dxt_image);
1717 if (!pDXTImage->init(dxt_fmt, width, height, false))
1719 vogl_delete(pDXTImage);
1725 VOGL_ASSERT(pDXTImage->get_element_vec().size() * sizeof(dxt_image::element) == level_pitch);
1727 memcpy(&pDXTImage->get_element_vec()[0], image_data.get_ptr(), image_data.size());
1729 if ((m_format == PIXEL_FMT_DXT1) && (!dxt1_alpha))
1730 dxt1_alpha = pDXTImage->has_alpha();
1732 pMip->assign(pDXTImage, m_format, orient_flags);
1736 if (image_data.size() != (width * height * unpacker.get_pixel_stride()))
1739 image_u8 *pImage = vogl_new(image_u8, width, height);
1741 pImage->set_comp_flags(m_comp_flags);
1743 const uint8 *pSrc = image_data.get_ptr();
1745 color_quad_u8 q(0, 0, 0, 255);
1747 for (uint y = 0; y < height; y++)
1749 for (uint x = 0; x < width; x++)
1752 pSrc = static_cast<const uint8 *>(unpacker.unpack(pSrc, c));
1753 pImage->set_pixel_unclipped(x, y, c);
1757 pMip->assign(pImage, m_format, orient_flags);
1759 VOGL_ASSERT(pMip->get_comp_flags() == m_comp_flags);
1767 change_dxt1_to_dxt1a();
1772 bool mipmapped_texture::write_ktx(data_stream_serializer &serializer) const
1776 set_last_error("Nothing to write");
1780 set_last_error("write_ktx() failed");
1782 uint32 ogl_internal_fmt = 0, ogl_fmt = 0, ogl_type = 0;
1784 pixel_packer packer;
1788 switch (get_format())
1790 case PIXEL_FMT_DXT1:
1792 ogl_internal_fmt = KTX_COMPRESSED_RGB_S3TC_DXT1_EXT;
1795 case PIXEL_FMT_DXT1A:
1797 ogl_internal_fmt = KTX_COMPRESSED_RGBA_S3TC_DXT1_EXT;
1800 case PIXEL_FMT_DXT2:
1801 case PIXEL_FMT_DXT3:
1803 ogl_internal_fmt = KTX_COMPRESSED_RGBA_S3TC_DXT3_EXT;
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:
1813 ogl_internal_fmt = KTX_COMPRESSED_RGBA_S3TC_DXT5_EXT;
1819 ogl_internal_fmt = KTX_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT;
1822 case PIXEL_FMT_DXT5A:
1824 ogl_internal_fmt = KTX_COMPRESSED_LUMINANCE_LATC1_EXT;
1827 case PIXEL_FMT_ETC1:
1829 ogl_internal_fmt = KTX_ETC1_RGB8_OES;
1841 ogl_type = KTX_UNSIGNED_BYTE;
1843 switch (get_format())
1845 case PIXEL_FMT_R8G8B8:
1846 ogl_internal_fmt = KTX_RGB8;
1848 packer.init("R8G8B8");
1851 ogl_internal_fmt = KTX_LUMINANCE8;
1852 ogl_fmt = KTX_LUMINANCE;
1856 ogl_internal_fmt = KTX_ALPHA8;
1857 ogl_fmt = KTX_ALPHA;
1860 case PIXEL_FMT_A8L8:
1861 ogl_internal_fmt = KTX_LUMINANCE8_ALPHA8;
1862 ogl_fmt = KTX_LUMINANCE_ALPHA;
1863 packer.init("Y8A8");
1865 case PIXEL_FMT_A8R8G8B8:
1866 ogl_internal_fmt = KTX_RGBA8;
1868 packer.init("R8G8B8A8");
1880 if (determine_texture_type() == cTextureTypeCubemap)
1881 success = kt.init_cubemap(get_width(), get_num_levels(), ogl_internal_fmt, ogl_fmt, ogl_type);
1883 success = kt.init_2D(get_width(), get_height(), get_num_levels(), ogl_internal_fmt, ogl_fmt, ogl_type);
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());
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());
1894 for (uint face_index = 0; face_index < get_num_faces(); face_index++)
1896 for (uint level_index = 0; level_index < get_num_levels(); level_index++)
1898 const mip_level *pLevel = get_level(face_index, level_index);
1900 const uint mip_width = pLevel->get_width();
1901 const uint mip_height = pLevel->get_height();
1905 const dxt_image *p = pLevel->get_dxt_image();
1907 kt.add_image(face_index, level_index, p->get_element_ptr(), p->get_size_in_bytes());
1911 const image_u8 *p = pLevel->get_image();
1913 vogl::vector<uint8> tmp(mip_width * mip_height * packer.get_pixel_stride());
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);
1920 kt.add_image(face_index, level_index, tmp.get_ptr(), tmp.size_in_bytes());
1925 if (!kt.write_to_stream(serializer))
1932 void mipmapped_texture::assign(face_vec &faces)
1934 VOGL_ASSERT(!faces.is_empty());
1935 if (faces.is_empty())
1940 #ifdef VOGL_BUILD_DEBUG
1941 for (uint i = 1; i < faces.size(); i++)
1942 VOGL_ASSERT(faces[i].size() == faces[0].size());
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();
1951 m_faces.swap(faces);
1953 VOGL_ASSERT(check());
1956 void mipmapped_texture::assign(mip_level *pLevel)
1958 face_vec faces(1, mip_ptr_vec(1, pLevel));
1962 void mipmapped_texture::assign(image_u8 *p, pixel_format fmt, orientation_flags_t orient_flags)
1964 mip_level *pLevel = vogl_new(mip_level);
1965 pLevel->assign(p, fmt, orient_flags);
1969 void mipmapped_texture::assign(dxt_image *p, pixel_format fmt, orientation_flags_t orient_flags)
1971 mip_level *pLevel = vogl_new(mip_level);
1972 pLevel->assign(p, fmt, orient_flags);
1976 void mipmapped_texture::set(texture_file_types::format source_file_type, const mipmapped_texture &mipmapped_texture)
1980 *this = mipmapped_texture;
1981 m_source_file_type = source_file_type;
1984 image_u8 *mipmapped_texture::get_level_image(uint face, uint level, image_u8 &img, uint unpack_flags) const
1989 const mip_level *pLevel = get_level(face, level);
1991 return pLevel->get_unpacked_image(img, unpack_flags);
1994 void mipmapped_texture::swap(mipmapped_texture &img)
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);
2004 VOGL_ASSERT(check());
2007 texture_type mipmapped_texture::determine_texture_type() const
2010 return cTextureTypeUnknown;
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;
2019 return cTextureTypeRegularMap;
2022 void mipmapped_texture::discard_mips()
2024 for (uint f = 0; f < m_faces.size(); f++)
2026 if (m_faces[f].size() > 1)
2028 for (uint l = 1; l < m_faces[f].size(); l++)
2029 vogl_delete(m_faces[f][l]);
2031 m_faces[f].resize(1);
2035 VOGL_ASSERT(check());
2038 void mipmapped_texture::init(uint width, uint height, uint levels, uint faces, pixel_format fmt, const char *pName, orientation_flags_t orient_flags)
2042 VOGL_ASSERT((width > 0) && (height > 0) && (levels > 0));
2043 VOGL_ASSERT((faces == 1) || (faces == 6));
2047 m_comp_flags = pixel_format_helpers::get_component_flags(fmt);
2052 m_faces.resize(faces);
2053 for (uint f = 0; f < faces; f++)
2055 m_faces[f].resize(levels);
2056 for (uint l = 0; l < levels; l++)
2058 m_faces[f][l] = vogl_new(mip_level);
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))
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);
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);
2077 VOGL_ASSERT(check());
2080 void mipmapped_texture::discard_mipmaps()
2088 bool mipmapped_texture::convert(pixel_format fmt, bool cook, const dxt_image::pack_params &p)
2093 if (fmt == get_format())
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();
2101 uint num_pixels_processed = 0;
2103 uint progress_start = p.m_progress_start;
2105 for (uint f = 0; f < m_faces.size(); f++)
2107 for (uint l = 0; l < m_faces[f].size(); l++)
2109 const uint num_pixels = m_faces[f][l]->get_total_pixels();
2111 uint progress_range = (num_pixels * p.m_progress_range) / total_pixels;
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);
2117 progress_start += tmp_params.m_progress_range;
2119 if (!m_faces[f][l]->convert(fmt, cook, tmp_params))
2125 num_pixels_processed += num_pixels;
2129 m_format = get_level(0, 0)->get_format();
2130 m_comp_flags = get_level(0, 0)->get_comp_flags();
2132 VOGL_ASSERT(check());
2134 if (p.m_pProgress_callback)
2136 if (!p.m_pProgress_callback(p.m_progress_start + p.m_progress_range, p.m_pProgress_callback_user_data_ptr))
2143 bool mipmapped_texture::convert(pixel_format fmt, const dxt_image::pack_params &p)
2145 return convert(fmt, true, p);
2148 bool mipmapped_texture::is_packed() const
2150 VOGL_ASSERT(is_valid());
2154 return get_level(0, 0)->is_packed();
2157 bool mipmapped_texture::set_alpha_to_luma()
2159 VOGL_ASSERT(is_valid());
2164 unpack_from_dxt(true);
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();
2170 m_format = get_level(0, 0)->get_format();
2171 m_comp_flags = get_level(0, 0)->get_comp_flags();
2173 VOGL_ASSERT(check());
2178 bool mipmapped_texture::convert(image_utils::conversion_type conv_type)
2180 VOGL_ASSERT(is_valid());
2185 unpack_from_dxt(true);
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);
2191 m_format = get_level(0, 0)->get_format();
2192 m_comp_flags = get_level(0, 0)->get_comp_flags();
2194 VOGL_ASSERT(check());
2199 bool mipmapped_texture::unpack_from_dxt(bool uncook)
2201 VOGL_ASSERT(is_valid());
2205 VOGL_ASSERT(pixel_format_helpers::is_dxt(m_format));
2206 if (!pixel_format_helpers::is_dxt(m_format))
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))
2214 m_format = get_level(0, 0)->get_format();
2215 m_comp_flags = get_level(0, 0)->get_comp_flags();
2217 VOGL_ASSERT(check());
2222 bool mipmapped_texture::has_alpha() const
2224 VOGL_ASSERT(is_valid());
2228 if (pixel_format_helpers::has_alpha(m_format))
2231 if ((m_format == PIXEL_FMT_DXT1) && (get_level(0, 0)->get_dxt_image()))
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())
2242 bool mipmapped_texture::is_normal_map() const
2244 VOGL_ASSERT(is_valid());
2248 if (pixel_format_helpers::is_normal_map(get_format()))
2251 const mip_level *pLevel = get_level(0, 0);
2253 if (pLevel->get_image())
2254 return image_utils::is_normal_map(*pLevel->get_image(), m_name.get_ptr());
2257 pLevel->get_dxt_image()->unpack(tmp);
2258 return image_utils::is_normal_map(tmp, m_name.get_ptr());
2261 bool mipmapped_texture::is_vertical_cross() const
2263 VOGL_ASSERT(is_valid());
2267 if (get_num_faces() > 1)
2270 if (!((math::is_power_of_2(m_height)) && (!math::is_power_of_2(m_width)) && (m_height / 4U == m_width / 3U)))
2276 bool mipmapped_texture::resize(uint new_width, uint new_height, const resample_params ¶ms)
2278 VOGL_ASSERT(is_valid());
2282 VOGL_ASSERT((new_width >= 1) && (new_height >= 1));
2284 face_vec faces(get_num_faces());
2285 for (uint f = 0; f < faces.size(); f++)
2288 faces[f][0] = vogl_new(mip_level);
2291 for (uint f = 0; f < faces.size(); f++)
2294 image_u8 *pImg = get_level(f, 0)->get_unpacked_image(tmp, cUnpackFlagUncook);
2296 image_u8 *pMip = vogl_new(image_u8);
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;
2309 if (!image_utils::resample(*pImg, *pMip, rparams))
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]);
2320 if (params.m_renormalize)
2321 image_utils::renorm_normal_map(*pMip);
2323 pMip->set_comp_flags(pImg->get_comp_flags());
2325 faces[f][0]->assign(pMip, PIXEL_FMT_INVALID, get_level(f, 0)->get_orientation_flags());
2330 VOGL_ASSERT(check());
2335 bool mipmapped_texture::generate_mipmaps(const generate_mipmap_params ¶ms, bool force)
2337 VOGL_ASSERT(is_valid());
2341 uint num_levels = 1;
2343 uint width = get_width();
2344 uint height = get_height();
2345 while ((width > params.m_min_mip_size) || (height > params.m_min_mip_size))
2353 if ((params.m_max_mips > 0) && (num_levels > params.m_max_mips))
2354 num_levels = params.m_max_mips;
2356 if ((force) && (get_num_levels() > 1))
2359 if (num_levels == get_num_levels())
2362 face_vec faces(get_num_faces());
2363 for (uint f = 0; f < faces.size(); f++)
2365 faces[f].resize(num_levels);
2366 for (uint l = 0; l < num_levels; l++)
2367 faces[f][l] = vogl_new(mip_level);
2370 for (uint f = 0; f < faces.size(); f++)
2373 image_u8 *pImg = get_level(f, 0)->get_unpacked_image(tmp, cUnpackFlagUncook);
2375 for (uint l = 0; l < num_levels; l++)
2377 const uint mip_width = math::maximum<uint>(1U, get_width() >> l);
2378 const uint mip_height = math::maximum<uint>(1U, get_height() >> l);
2380 image_u8 *pMip = vogl_new(image_u8);
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;
2397 if (!image_utils::resample(*pImg, *pMip, rparams))
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]);
2408 if (params.m_renormalize)
2409 image_utils::renorm_normal_map(*pMip);
2411 pMip->set_comp_flags(pImg->get_comp_flags());
2414 faces[f][l]->assign(pMip, PIXEL_FMT_INVALID, get_level(f, 0)->get_orientation_flags());
2420 VOGL_ASSERT(check());
2425 bool mipmapped_texture::crop(uint x, uint y, uint width, uint height)
2427 VOGL_ASSERT(is_valid());
2430 if (get_num_faces() > 1)
2433 if ((width < 1) || (height < 1))
2437 image_u8 *pImg = get_level(0, 0)->get_unpacked_image(tmp, cUnpackFlagUncook | cUnpackFlagUnflip);
2439 image_u8 *pMip = vogl_new(image_u8, width, height);
2441 if (!pImg->extract_block_clamped(pMip->get_ptr(), x, y, width, height))
2446 faces[0][0] = vogl_new(mip_level);
2448 pMip->set_comp_flags(pImg->get_comp_flags());
2450 faces[0][0]->assign(pMip);
2454 VOGL_ASSERT(check());
2459 bool mipmapped_texture::vertical_cross_to_cubemap()
2461 if (!is_vertical_cross())
2464 const uint face_width = get_height() / 4;
2466 bool alpha_is_valid = has_alpha();
2468 mipmapped_texture cubemap;
2470 pixel_format fmt = alpha_is_valid ? PIXEL_FMT_A8R8G8B8 : PIXEL_FMT_R8G8B8;
2472 cubemap.init(face_width, face_width, 1, 6, fmt, m_name.get_ptr(), cDefaultOrientationFlags);
2474 // +x -x +y -y +z -z
2481 for (uint face_index = 0; face_index < 6; face_index++)
2483 const mip_level *pSrc = get_level(0, 0);
2486 image_u8 *pSrc_image = pSrc->get_unpacked_image(tmp_img, cUnpackFlagUncook | cUnpackFlagUnflip);
2488 const mip_level *pDst = get_level(face_index, 0);
2489 image_u8 *pDst_image = pDst->get_image();
2490 VOGL_ASSERT(pDst_image);
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;
2496 for (uint y = 0; y < face_width; y++)
2498 for (uint x = 0; x < face_width; x++)
2500 const color_quad_u8 &c = (*pSrc_image)(x_ofs + x, y_ofs + y);
2503 (*pDst_image)(x, y) = c;
2505 (*pDst_image)(face_width - 1 - x, face_width - 1 - y) = c;
2512 VOGL_ASSERT(check());
2517 bool mipmapped_texture::read_from_file(const char *pFilename, texture_file_types::format file_format)
2521 set_last_error("Can't open file");
2523 bool success = false;
2525 cfile_stream in_stream;
2526 if (in_stream.open(pFilename))
2528 data_stream_serializer serializer(in_stream);
2529 success = read_from_stream(serializer, file_format);
2535 bool mipmapped_texture::read_from_stream(data_stream_serializer &serializer, texture_file_types::format file_format)
2539 if (!serializer.get_stream())
2541 set_last_error("Invalid stream");
2545 if (file_format == texture_file_types::cFormatInvalid)
2546 file_format = texture_file_types::determine_file_format(serializer.get_name().get_ptr());
2548 if (file_format == texture_file_types::cFormatInvalid)
2550 set_last_error("Unsupported file format");
2554 set_last_error("Image file load failed");
2556 bool success = false;
2558 if (!texture_file_types::supports_mipmaps(file_format))
2560 success = read_regular_image(serializer, file_format);
2564 switch (file_format)
2566 case texture_file_types::cFormatDDS:
2568 success = read_dds(serializer);
2571 case texture_file_types::cFormatCRN:
2575 case texture_file_types::cFormatKTX:
2577 success = read_ktx(serializer);
2590 VOGL_ASSERT(check());
2592 m_source_file_type = file_format;
2593 set_name(serializer.get_name());
2600 bool mipmapped_texture::read_regular_image(data_stream_serializer &serializer, texture_file_types::format file_format)
2602 VOGL_NOTE_UNUSED(file_format);
2604 image_u8 *pImg = vogl_new(image_u8);
2605 bool status = image_utils::read_from_stream(*pImg, serializer, 0);
2610 set_last_error("Failed loading image file");
2614 mip_level *pLevel = vogl_new(mip_level);
2615 pLevel->assign(pImg);
2618 set_name(serializer.get_name());
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)
2630 if (pActual_quality_level)
2631 *pActual_quality_level = 0;
2632 if (pActual_bitrate)
2633 *pActual_bitrate = 0.0f;
2637 set_last_error("Unable to write empty texture");
2641 if (file_format == texture_file_types::cFormatInvalid)
2642 file_format = texture_file_types::determine_file_format(pFilename);
2644 if (file_format == texture_file_types::cFormatInvalid)
2646 set_last_error("Unknown file format");
2650 bool success = false;
2652 if (((pComp_params) && (file_format == texture_file_types::cFormatDDS)) ||
2653 (file_format == texture_file_types::cFormatCRN))
2655 set_last_error("Functionality removed");
2658 else if (!texture_file_types::supports_mipmaps(file_format))
2660 success = write_regular_image(pFilename, image_write_flags);
2666 console::warning("mipmapped_texture::write_to_file: Ignoring CRN compression parameters (currently unsupported for this file type).\n");
2669 cfile_stream write_stream;
2670 if (!write_stream.open(pFilename, cDataStreamWritable | cDataStreamSeekable))
2672 set_last_error(dynamic_string(cVarArg, "Failed creating output file \"%s\"", pFilename).get_ptr());
2675 data_stream_serializer serializer(write_stream);
2677 switch (file_format)
2679 case texture_file_types::cFormatDDS:
2681 success = write_dds(serializer);
2684 case texture_file_types::cFormatKTX:
2686 success = write_ktx(serializer);
2699 bool mipmapped_texture::write_regular_image(const char *pFilename, uint32 image_write_flags)
2702 image_u8 *pLevel_image = get_level_image(0, 0, tmp);
2704 if (!image_utils::write_to_file(pFilename, *pLevel_image, image_write_flags))
2706 set_last_error("File write failed");
2713 uint mipmapped_texture::get_total_pixels_in_all_faces_and_mips() const
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();
2720 return total_pixels;
2723 void mipmapped_texture::set_orientation_flags(orientation_flags_t flags)
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);
2730 bool mipmapped_texture::is_flipped() const
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())
2740 bool mipmapped_texture::is_x_flipped() const
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())
2750 bool mipmapped_texture::is_y_flipped() const
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())
2760 bool mipmapped_texture::can_unflip_without_unpacking() const
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())
2776 bool mipmapped_texture::unflip(bool allow_unpacking_to_flip, bool uncook_if_necessary_to_unpack)
2781 if (!is_y_flipped())
2786 // The texture is packed - make sure all faces/miplevels can be consistently unflipped.
2787 bool can_do_packed_unflip = can_unflip_without_unpacking();
2789 if ((!can_do_packed_unflip) && (!allow_unpacking_to_flip))
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);
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))
2802 VOGL_VERIFY(check());
2808 bool mipmapped_texture::flip_x()
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())
2819 bool mipmapped_texture::flip_y_helper()
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())
2829 bool mipmapped_texture::flip_y(bool update_orientation_flags)
2831 mipmapped_texture temp_tex(*this);
2832 if (!temp_tex.flip_y_helper())
2835 temp_tex.unpack_from_dxt(true);
2836 if (!temp_tex.flip_y_helper())
2841 if (update_orientation_flags)
2843 for (uint f = 0; f < get_num_faces(); f++)
2845 for (uint m = 0; m < get_face(f).size(); m++)
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));
2854 VOGL_ASSERT(check());