| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920 |
- // File: crn_ktx_texture.cpp
- #include "crn_core.h"
- #include "crn_ktx_texture.h"
- #include "crn_console.h"
- // Set #if CRNLIB_KTX_PVRTEX_WORKAROUNDS to 1 to enable various workarounds for oddball KTX files written by PVRTexTool.
- #define CRNLIB_KTX_PVRTEX_WORKAROUNDS 1
- namespace crnlib
- {
- const uint8 s_ktx_file_id[12] = { 0xAB, 0x4B, 0x54, 0x58, 0x20, 0x31, 0x31, 0xBB, 0x0D, 0x0A, 0x1A, 0x0A };
- bool is_packed_pixel_ogl_type(uint32 ogl_type)
- {
- switch (ogl_type)
- {
- case KTX_UNSIGNED_BYTE_3_3_2:
- case KTX_UNSIGNED_BYTE_2_3_3_REV:
- case KTX_UNSIGNED_SHORT_5_6_5:
- case KTX_UNSIGNED_SHORT_5_6_5_REV:
- case KTX_UNSIGNED_SHORT_4_4_4_4:
- case KTX_UNSIGNED_SHORT_4_4_4_4_REV:
- case KTX_UNSIGNED_SHORT_5_5_5_1:
- case KTX_UNSIGNED_SHORT_1_5_5_5_REV:
- case KTX_UNSIGNED_INT_8_8_8_8:
- case KTX_UNSIGNED_INT_8_8_8_8_REV:
- case KTX_UNSIGNED_INT_10_10_10_2:
- case KTX_UNSIGNED_INT_2_10_10_10_REV:
- case KTX_UNSIGNED_INT_24_8:
- case KTX_UNSIGNED_INT_10F_11F_11F_REV:
- case KTX_UNSIGNED_INT_5_9_9_9_REV:
- return true;
- }
- return false;
- }
- uint get_ogl_type_size(uint32 ogl_type)
- {
- switch (ogl_type)
- {
- case KTX_UNSIGNED_BYTE:
- case KTX_BYTE:
- return 1;
- case KTX_HALF_FLOAT:
- case KTX_UNSIGNED_SHORT:
- case KTX_SHORT:
- return 2;
- case KTX_FLOAT:
- case KTX_UNSIGNED_INT:
- case KTX_INT:
- return 4;
- case KTX_UNSIGNED_BYTE_3_3_2:
- case KTX_UNSIGNED_BYTE_2_3_3_REV:
- return 1;
- case KTX_UNSIGNED_SHORT_5_6_5:
- case KTX_UNSIGNED_SHORT_5_6_5_REV:
- case KTX_UNSIGNED_SHORT_4_4_4_4:
- case KTX_UNSIGNED_SHORT_4_4_4_4_REV:
- case KTX_UNSIGNED_SHORT_5_5_5_1:
- case KTX_UNSIGNED_SHORT_1_5_5_5_REV:
- return 2;
- case KTX_UNSIGNED_INT_8_8_8_8:
- case KTX_UNSIGNED_INT_8_8_8_8_REV:
- case KTX_UNSIGNED_INT_10_10_10_2:
- case KTX_UNSIGNED_INT_2_10_10_10_REV:
- case KTX_UNSIGNED_INT_24_8:
- case KTX_UNSIGNED_INT_10F_11F_11F_REV:
- case KTX_UNSIGNED_INT_5_9_9_9_REV:
- return 4;
- }
- return 0;
- }
- uint32 get_ogl_base_internal_fmt(uint32 ogl_fmt)
- {
- switch (ogl_fmt)
- {
- case KTX_ETC1_RGB8_OES:
- case KTX_RGB_S3TC:
- case KTX_RGB4_S3TC:
- case KTX_COMPRESSED_RGB_S3TC_DXT1_EXT:
- case KTX_COMPRESSED_SRGB_S3TC_DXT1_EXT:
- return KTX_RGB;
- case KTX_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
- case KTX_RGBA_S3TC:
- case KTX_RGBA4_S3TC:
- case KTX_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
- case KTX_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
- case KTX_RGBA_DXT5_S3TC:
- case KTX_RGBA4_DXT5_S3TC:
- return KTX_RGBA;
- case 1:
- case KTX_RED:
- case KTX_RED_INTEGER:
- case KTX_GREEN:
- case KTX_GREEN_INTEGER:
- case KTX_BLUE:
- case KTX_BLUE_INTEGER:
- case KTX_R8:
- case KTX_R8UI:
- case KTX_LUMINANCE8:
- case KTX_ALPHA:
- case KTX_LUMINANCE:
- case KTX_COMPRESSED_RED_RGTC1_EXT:
- case KTX_COMPRESSED_SIGNED_RED_RGTC1_EXT:
- case KTX_COMPRESSED_LUMINANCE_LATC1_EXT:
- case KTX_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT:
- return KTX_RED;
- case 2:
- case KTX_RG:
- case KTX_RG8:
- case KTX_RG_INTEGER:
- case KTX_LUMINANCE_ALPHA:
- case KTX_COMPRESSED_RED_GREEN_RGTC2_EXT:
- case KTX_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
- case KTX_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:
- case KTX_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT:
- return KTX_RG;
- case 3:
- case KTX_SRGB:
- case KTX_RGB:
- case KTX_RGB_INTEGER:
- case KTX_BGR:
- case KTX_BGR_INTEGER:
- case KTX_RGB8:
- case KTX_SRGB8:
- return KTX_RGB;
- case 4:
- case KTX_RGBA:
- case KTX_BGRA:
- case KTX_RGBA_INTEGER:
- case KTX_BGRA_INTEGER:
- case KTX_SRGB_ALPHA:
- case KTX_SRGB8_ALPHA8:
- case KTX_RGBA8:
- return KTX_RGBA;
- }
- return 0;
- }
- bool get_ogl_fmt_desc(uint32 ogl_fmt, uint32 ogl_type, uint& block_dim, uint& bytes_per_block)
- {
- uint ogl_type_size = get_ogl_type_size(ogl_type);
- block_dim = 1;
- bytes_per_block = 0;
- switch (ogl_fmt)
- {
- case KTX_COMPRESSED_RED_RGTC1_EXT:
- case KTX_COMPRESSED_SIGNED_RED_RGTC1_EXT:
- case KTX_COMPRESSED_LUMINANCE_LATC1_EXT:
- case KTX_COMPRESSED_SIGNED_LUMINANCE_LATC1_EXT:
- case KTX_ETC1_RGB8_OES:
- case KTX_RGB_S3TC:
- case KTX_RGB4_S3TC:
- case KTX_COMPRESSED_RGB_S3TC_DXT1_EXT:
- case KTX_COMPRESSED_RGBA_S3TC_DXT1_EXT:
- case KTX_COMPRESSED_SRGB_S3TC_DXT1_EXT:
- case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT1_EXT:
- {
- block_dim = 4;
- bytes_per_block = 8;
- break;
- }
- case KTX_COMPRESSED_LUMINANCE_ALPHA_LATC2_EXT:
- case KTX_COMPRESSED_SIGNED_LUMINANCE_ALPHA_LATC2_EXT:
- case KTX_COMPRESSED_RED_GREEN_RGTC2_EXT:
- case KTX_COMPRESSED_SIGNED_RED_GREEN_RGTC2_EXT:
- case KTX_RGBA_S3TC:
- case KTX_RGBA4_S3TC:
- case KTX_COMPRESSED_RGBA_S3TC_DXT3_EXT:
- case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT3_EXT:
- case KTX_COMPRESSED_RGBA_S3TC_DXT5_EXT:
- case KTX_COMPRESSED_SRGB_ALPHA_S3TC_DXT5_EXT:
- case KTX_RGBA_DXT5_S3TC:
- case KTX_RGBA4_DXT5_S3TC:
- {
- block_dim = 4;
- bytes_per_block = 16;
- break;
- }
- case 1:
- case KTX_ALPHA:
- case KTX_RED:
- case KTX_GREEN:
- case KTX_BLUE:
- case KTX_RED_INTEGER:
- case KTX_GREEN_INTEGER:
- case KTX_BLUE_INTEGER:
- case KTX_LUMINANCE:
- {
- bytes_per_block = ogl_type_size;
- break;
- }
- case KTX_R8:
- case KTX_R8UI:
- case KTX_ALPHA8:
- case KTX_LUMINANCE8:
- {
- bytes_per_block = 1;
- break;
- }
- case 2:
- case KTX_RG:
- case KTX_RG_INTEGER:
- case KTX_LUMINANCE_ALPHA:
- {
- bytes_per_block = 2 * ogl_type_size;
- break;
- }
- case KTX_RG8:
- case KTX_LUMINANCE8_ALPHA8:
- {
- bytes_per_block = 2;
- break;
- }
- case 3:
- case KTX_SRGB:
- case KTX_RGB:
- case KTX_BGR:
- case KTX_RGB_INTEGER:
- case KTX_BGR_INTEGER:
- {
- bytes_per_block = is_packed_pixel_ogl_type(ogl_type) ? ogl_type_size : (3 * ogl_type_size);
- break;
- }
- case KTX_RGB8:
- case KTX_SRGB8:
- {
- bytes_per_block = 3;
- break;
- }
- case 4:
- case KTX_RGBA:
- case KTX_BGRA:
- case KTX_RGBA_INTEGER:
- case KTX_BGRA_INTEGER:
- case KTX_SRGB_ALPHA:
- {
- bytes_per_block = is_packed_pixel_ogl_type(ogl_type) ? ogl_type_size : (4 * ogl_type_size);
- break;
- }
- case KTX_SRGB8_ALPHA8:
- case KTX_RGBA8:
- {
- bytes_per_block = 4;
- break;
- }
- default:
- return false;
- }
- return true;
- }
- bool ktx_texture::compute_pixel_info()
- {
- if ((!m_header.m_glType) || (!m_header.m_glFormat))
- {
- if ((m_header.m_glType) || (m_header.m_glFormat))
- return false;
- // Must be a compressed format.
- if (!get_ogl_fmt_desc(m_header.m_glInternalFormat, m_header.m_glType, m_block_dim, m_bytes_per_block))
- {
- #if CRNLIB_KTX_PVRTEX_WORKAROUNDS
- if ((!m_header.m_glInternalFormat) && (!m_header.m_glType) && (!m_header.m_glTypeSize) && (!m_header.m_glBaseInternalFormat))
- {
- // PVRTexTool writes bogus headers when outputting ETC1.
- console::warning("ktx_texture::compute_pixel_info: Header doesn't specify any format, assuming ETC1 and hoping for the best");
- m_header.m_glBaseInternalFormat = KTX_RGB;
- m_header.m_glInternalFormat = KTX_ETC1_RGB8_OES;
- m_header.m_glTypeSize = 1;
- m_block_dim = 4;
- m_bytes_per_block = 8;
- return true;
- }
- #endif
- return false;
- }
- if (m_block_dim == 1)
- return false;
- }
- else
- {
- // Must be an uncompressed format.
- if (!get_ogl_fmt_desc(m_header.m_glFormat, m_header.m_glType, m_block_dim, m_bytes_per_block))
- return false;
- if (m_block_dim > 1)
- return false;
- }
- return true;
- }
- bool ktx_texture::read_from_stream(data_stream_serializer& serializer)
- {
- clear();
- // Read header
- if (serializer.read(&m_header, 1, sizeof(m_header)) != sizeof(ktx_header))
- return false;
- // Check header
- if (memcmp(s_ktx_file_id, m_header.m_identifier, sizeof(m_header.m_identifier)))
- return false;
- if ((m_header.m_endianness != KTX_OPPOSITE_ENDIAN) && (m_header.m_endianness != KTX_ENDIAN))
- return false;
- m_opposite_endianness = (m_header.m_endianness == KTX_OPPOSITE_ENDIAN);
- if (m_opposite_endianness)
- {
- m_header.endian_swap();
- if ((m_header.m_glTypeSize != sizeof(uint8)) && (m_header.m_glTypeSize != sizeof(uint16)) && (m_header.m_glTypeSize != sizeof(uint32)))
- return false;
- }
- if (!check_header())
- return false;
- if (!compute_pixel_info())
- return false;
- uint8 pad_bytes[3];
- // Read the key value entries
- uint num_key_value_bytes_remaining = m_header.m_bytesOfKeyValueData;
- while (num_key_value_bytes_remaining)
- {
- if (num_key_value_bytes_remaining < sizeof(uint32))
- return false;
- uint32 key_value_byte_size;
- if (serializer.read(&key_value_byte_size, 1, sizeof(uint32)) != sizeof(uint32))
- return false;
- num_key_value_bytes_remaining -= sizeof(uint32);
- if (m_opposite_endianness)
- key_value_byte_size = utils::swap32(key_value_byte_size);
- if (key_value_byte_size > num_key_value_bytes_remaining)
- return false;
- uint8_vec key_value_data;
- if (key_value_byte_size)
- {
- key_value_data.resize(key_value_byte_size);
- if (serializer.read(&key_value_data[0], 1, key_value_byte_size) != key_value_byte_size)
- return false;
- }
- m_key_values.push_back(key_value_data);
- uint padding = 3 - ((key_value_byte_size + 3) % 4);
- if (padding)
- {
- if (serializer.read(pad_bytes, 1, padding) != padding)
- return false;
- }
- num_key_value_bytes_remaining -= key_value_byte_size;
- if (num_key_value_bytes_remaining < padding)
- return false;
- num_key_value_bytes_remaining -= padding;
- }
- // Now read the mip levels
- uint total_faces = get_num_mips() * get_array_size() * get_num_faces() * get_depth();
- if ((!total_faces) || (total_faces > 65535))
- return false;
- // See Section 2.8 of KTX file format: No rounding to block sizes should be applied for block compressed textures.
- // OK, I'm going to break that rule otherwise KTX can only store a subset of textures that DDS can handle for no good reason.
- #if 0
- const uint mip0_row_blocks = m_header.m_pixelWidth / m_block_dim;
- const uint mip0_col_blocks = CRNLIB_MAX(1, m_header.m_pixelHeight) / m_block_dim;
- #else
- const uint mip0_row_blocks = (m_header.m_pixelWidth + m_block_dim - 1) / m_block_dim;
- const uint mip0_col_blocks = (CRNLIB_MAX(1, m_header.m_pixelHeight) + m_block_dim - 1) / m_block_dim;
- #endif
- if ((!mip0_row_blocks) || (!mip0_col_blocks))
- return false;
- const uint mip0_depth = CRNLIB_MAX(1, m_header.m_pixelDepth); mip0_depth;
- bool has_valid_image_size_fields = true;
- bool disable_mip_and_cubemap_padding = false;
- #if CRNLIB_KTX_PVRTEX_WORKAROUNDS
- {
- // PVRTexTool has a bogus KTX writer that doesn't write any imageSize fields. Nice.
- size_t expected_bytes_remaining = 0;
- for (uint mip_level = 0; mip_level < get_num_mips(); mip_level++)
- {
- uint mip_width, mip_height, mip_depth;
- get_mip_dim(mip_level, mip_width, mip_height, mip_depth);
- const uint mip_row_blocks = (mip_width + m_block_dim - 1) / m_block_dim;
- const uint mip_col_blocks = (mip_height + m_block_dim - 1) / m_block_dim;
- if ((!mip_row_blocks) || (!mip_col_blocks))
- return false;
- expected_bytes_remaining += sizeof(uint32);
- if ((!m_header.m_numberOfArrayElements) && (get_num_faces() == 6))
- {
- for (uint face = 0; face < get_num_faces(); face++)
- {
- uint slice_size = mip_row_blocks * mip_col_blocks * m_bytes_per_block;
- expected_bytes_remaining += slice_size;
- uint num_cube_pad_bytes = 3 - ((slice_size + 3) % 4);
- expected_bytes_remaining += num_cube_pad_bytes;
- }
- }
- else
- {
- uint total_mip_size = 0;
- for (uint array_element = 0; array_element < get_array_size(); array_element++)
- {
- for (uint face = 0; face < get_num_faces(); face++)
- {
- for (uint zslice = 0; zslice < mip_depth; zslice++)
- {
- uint slice_size = mip_row_blocks * mip_col_blocks * m_bytes_per_block;
- total_mip_size += slice_size;
- }
- }
- }
- expected_bytes_remaining += total_mip_size;
- uint num_mip_pad_bytes = 3 - ((total_mip_size + 3) % 4);
- expected_bytes_remaining += num_mip_pad_bytes;
- }
- }
- if (serializer.get_stream()->get_remaining() < expected_bytes_remaining)
- {
- has_valid_image_size_fields = false;
- disable_mip_and_cubemap_padding = true;
- console::warning("ktx_texture::read_from_stream: KTX file size is smaller than expected - trying to read anyway without imageSize fields");
- }
- }
- #endif
- for (uint mip_level = 0; mip_level < get_num_mips(); mip_level++)
- {
- uint mip_width, mip_height, mip_depth;
- get_mip_dim(mip_level, mip_width, mip_height, mip_depth);
- const uint mip_row_blocks = (mip_width + m_block_dim - 1) / m_block_dim;
- const uint mip_col_blocks = (mip_height + m_block_dim - 1) / m_block_dim;
- if ((!mip_row_blocks) || (!mip_col_blocks))
- return false;
- uint32 image_size = 0;
- if (!has_valid_image_size_fields)
- image_size = mip_depth * mip_row_blocks * mip_col_blocks * m_bytes_per_block * get_array_size() * get_num_faces();
- else
- {
- if (serializer.read(&image_size, 1, sizeof(image_size)) != sizeof(image_size))
- return false;
- if (m_opposite_endianness)
- image_size = utils::swap32(image_size);
- }
- if (!image_size)
- return false;
- uint total_mip_size = 0;
- if ((!m_header.m_numberOfArrayElements) && (get_num_faces() == 6))
- {
- // plain non-array cubemap
- for (uint face = 0; face < get_num_faces(); face++)
- {
- CRNLIB_ASSERT(m_image_data.size() == get_image_index(mip_level, 0, face, 0));
- m_image_data.push_back(uint8_vec());
- uint8_vec& image_data = m_image_data.back();
- image_data.resize(image_size);
- if (serializer.read(&image_data[0], 1, image_size) != image_size)
- return false;
- if (m_opposite_endianness)
- utils::endian_swap_mem(&image_data[0], image_size, m_header.m_glTypeSize);
- uint num_cube_pad_bytes = disable_mip_and_cubemap_padding ? 0 : (3 - ((image_size + 3) % 4));
- if (serializer.read(pad_bytes, 1, num_cube_pad_bytes) != num_cube_pad_bytes)
- return false;
- total_mip_size += image_size + num_cube_pad_bytes;
- }
- }
- else
- {
- // 1D, 2D, 3D (normal or array texture), or array cubemap
- uint num_image_bytes_remaining = image_size;
- for (uint array_element = 0; array_element < get_array_size(); array_element++)
- {
- for (uint face = 0; face < get_num_faces(); face++)
- {
- for (uint zslice = 0; zslice < mip_depth; zslice++)
- {
- CRNLIB_ASSERT(m_image_data.size() == get_image_index(mip_level, array_element, face, zslice));
- uint slice_size = mip_row_blocks * mip_col_blocks * m_bytes_per_block;
- if ((!slice_size) || (slice_size > num_image_bytes_remaining))
- return false;
- m_image_data.push_back(uint8_vec());
- uint8_vec& image_data = m_image_data.back();
- image_data.resize(slice_size);
- if (serializer.read(&image_data[0], 1, slice_size) != slice_size)
- return false;
- if (m_opposite_endianness)
- utils::endian_swap_mem(&image_data[0], slice_size, m_header.m_glTypeSize);
- num_image_bytes_remaining -= slice_size;
- total_mip_size += slice_size;
- }
- }
- }
- if (num_image_bytes_remaining)
- return false;
- }
- uint num_mip_pad_bytes = disable_mip_and_cubemap_padding ? 0 : (3 - ((total_mip_size + 3) % 4));
- if (serializer.read(pad_bytes, 1, num_mip_pad_bytes) != num_mip_pad_bytes)
- return false;
- }
- return true;
- }
- bool ktx_texture::write_to_stream(data_stream_serializer& serializer, bool no_keyvalue_data)
- {
- if (!consistency_check())
- {
- CRNLIB_ASSERT(0);
- return false;
- }
- memcpy(m_header.m_identifier, s_ktx_file_id, sizeof(m_header.m_identifier));
- m_header.m_endianness = m_opposite_endianness ? KTX_OPPOSITE_ENDIAN : KTX_ENDIAN;
- if (m_block_dim == 1)
- {
- m_header.m_glTypeSize = get_ogl_type_size(m_header.m_glType);
- m_header.m_glBaseInternalFormat = m_header.m_glFormat;
- }
- else
- {
- m_header.m_glBaseInternalFormat = get_ogl_base_internal_fmt(m_header.m_glInternalFormat);
- }
- m_header.m_bytesOfKeyValueData = 0;
- if (!no_keyvalue_data)
- {
- for (uint i = 0; i < m_key_values.size(); i++)
- m_header.m_bytesOfKeyValueData += sizeof(uint32) + ((m_key_values[i].size() + 3) & ~3);
- }
- if (m_opposite_endianness)
- m_header.endian_swap();
- bool success = (serializer.write(&m_header, sizeof(m_header), 1) == 1);
- if (m_opposite_endianness)
- m_header.endian_swap();
- if (!success)
- return success;
- uint total_key_value_bytes = 0;
- const uint8 padding[3] = { 0, 0, 0 };
- if (!no_keyvalue_data)
- {
- for (uint i = 0; i < m_key_values.size(); i++)
- {
- uint32 key_value_size = m_key_values[i].size();
- if (m_opposite_endianness)
- key_value_size = utils::swap32(key_value_size);
- success = (serializer.write(&key_value_size, sizeof(key_value_size), 1) == 1);
- total_key_value_bytes += sizeof(key_value_size);
- if (m_opposite_endianness)
- key_value_size = utils::swap32(key_value_size);
- if (!success)
- return false;
- if (key_value_size)
- {
- if (serializer.write(&m_key_values[i][0], key_value_size, 1) != 1)
- return false;
- total_key_value_bytes += key_value_size;
- uint num_padding = 3 - ((key_value_size + 3) % 4);
- if ((num_padding) && (serializer.write(padding, num_padding, 1) != 1))
- return false;
- total_key_value_bytes += num_padding;
- }
- }
- (void)total_key_value_bytes;
- }
- CRNLIB_ASSERT(total_key_value_bytes == m_header.m_bytesOfKeyValueData);
- for (uint mip_level = 0; mip_level < get_num_mips(); mip_level++)
- {
- uint mip_width, mip_height, mip_depth;
- get_mip_dim(mip_level, mip_width, mip_height, mip_depth);
- const uint mip_row_blocks = (mip_width + m_block_dim - 1) / m_block_dim;
- const uint mip_col_blocks = (mip_height + m_block_dim - 1) / m_block_dim;
- if ((!mip_row_blocks) || (!mip_col_blocks))
- return false;
- uint32 image_size = mip_row_blocks * mip_col_blocks * m_bytes_per_block;
- if ((m_header.m_numberOfArrayElements) || (get_num_faces() == 1))
- image_size *= (get_array_size() * get_num_faces() * get_depth());
- if (!image_size)
- return false;
- if (m_opposite_endianness)
- image_size = utils::swap32(image_size);
- success = (serializer.write(&image_size, sizeof(image_size), 1) == 1);
- if (m_opposite_endianness)
- image_size = utils::swap32(image_size);
- if (!success)
- return false;
- uint total_mip_size = 0;
- if ((!m_header.m_numberOfArrayElements) && (get_num_faces() == 6))
- {
- // plain non-array cubemap
- for (uint face = 0; face < get_num_faces(); face++)
- {
- const uint8_vec& image_data = get_image_data(get_image_index(mip_level, 0, face, 0));
- if ((!image_data.size()) || (image_data.size() != image_size))
- return false;
- if (m_opposite_endianness)
- {
- uint8_vec tmp_image_data(image_data);
- utils::endian_swap_mem(&tmp_image_data[0], tmp_image_data.size(), m_header.m_glTypeSize);
- if (serializer.write(&tmp_image_data[0], tmp_image_data.size(), 1) != 1)
- return false;
- }
- else if (serializer.write(&image_data[0], image_data.size(), 1) != 1)
- return false;
- uint num_cube_pad_bytes = 3 - ((image_data.size() + 3) % 4);
- if ((num_cube_pad_bytes) && (serializer.write(padding, num_cube_pad_bytes, 1) != 1))
- return false;
- total_mip_size += image_size + num_cube_pad_bytes;
- }
- }
- else
- {
- // 1D, 2D, 3D (normal or array texture), or array cubemap
- for (uint array_element = 0; array_element < get_array_size(); array_element++)
- {
- for (uint face = 0; face < get_num_faces(); face++)
- {
- for (uint zslice = 0; zslice < mip_depth; zslice++)
- {
- const uint8_vec& image_data = get_image_data(get_image_index(mip_level, array_element, face, zslice));
- if (!image_data.size())
- return false;
- if (m_opposite_endianness)
- {
- uint8_vec tmp_image_data(image_data);
- utils::endian_swap_mem(&tmp_image_data[0], tmp_image_data.size(), m_header.m_glTypeSize);
- if (serializer.write(&tmp_image_data[0], tmp_image_data.size(), 1) != 1)
- return false;
- }
- else if (serializer.write(&image_data[0], image_data.size(), 1) != 1)
- return false;
- total_mip_size += image_data.size();
- }
- }
- }
- uint num_mip_pad_bytes = 3 - ((total_mip_size + 3) % 4);
- if ((num_mip_pad_bytes) && (serializer.write(padding, num_mip_pad_bytes, 1) != 1))
- return false;
- total_mip_size += num_mip_pad_bytes;
- }
- CRNLIB_ASSERT((total_mip_size & 3) == 0);
- }
- return true;
- }
- bool ktx_texture::init_2D(uint width, uint height, uint num_mips, uint32 ogl_internal_fmt, uint32 ogl_fmt, uint32 ogl_type)
- {
- clear();
- m_header.m_pixelWidth = width;
- m_header.m_pixelHeight = height;
- m_header.m_numberOfMipmapLevels = num_mips;
- m_header.m_glInternalFormat = ogl_internal_fmt;
- m_header.m_glFormat = ogl_fmt;
- m_header.m_glType = ogl_type;
- m_header.m_numberOfFaces = 1;
- if (!compute_pixel_info())
- return false;
- return true;
- }
- bool ktx_texture::init_2D_array(uint width, uint height, uint num_mips, uint array_size, uint32 ogl_internal_fmt, uint32 ogl_fmt, uint32 ogl_type)
- {
- clear();
- m_header.m_pixelWidth = width;
- m_header.m_pixelHeight = height;
- m_header.m_numberOfMipmapLevels = num_mips;
- m_header.m_numberOfArrayElements = array_size;
- m_header.m_glInternalFormat = ogl_internal_fmt;
- m_header.m_glFormat = ogl_fmt;
- m_header.m_glType = ogl_type;
- m_header.m_numberOfFaces = 1;
- if (!compute_pixel_info())
- return false;
- return true;
- }
- bool ktx_texture::init_3D(uint width, uint height, uint depth, uint num_mips, uint32 ogl_internal_fmt, uint32 ogl_fmt, uint32 ogl_type)
- {
- clear();
- m_header.m_pixelWidth = width;
- m_header.m_pixelHeight = height;
- m_header.m_pixelDepth = depth;
- m_header.m_numberOfMipmapLevels = num_mips;
- m_header.m_glInternalFormat = ogl_internal_fmt;
- m_header.m_glFormat = ogl_fmt;
- m_header.m_glType = ogl_type;
- m_header.m_numberOfFaces = 1;
- if (!compute_pixel_info())
- return false;
- return true;
- }
- bool ktx_texture::init_cubemap(uint dim, uint num_mips, uint32 ogl_internal_fmt, uint32 ogl_fmt, uint32 ogl_type)
- {
- clear();
- m_header.m_pixelWidth = dim;
- m_header.m_pixelHeight = dim;
- m_header.m_numberOfMipmapLevels = num_mips;
- m_header.m_glInternalFormat = ogl_internal_fmt;
- m_header.m_glFormat = ogl_fmt;
- m_header.m_glType = ogl_type;
- m_header.m_numberOfFaces = 6;
- if (!compute_pixel_info())
- return false;
- return true;
- }
- bool ktx_texture::check_header() const
- {
- if (((get_num_faces() != 1) && (get_num_faces() != 6)) || (!m_header.m_pixelWidth))
- return false;
- if ((!m_header.m_pixelHeight) && (m_header.m_pixelDepth))
- return false;
- if ((get_num_faces() == 6) && ((m_header.m_pixelDepth) || (!m_header.m_pixelHeight)))
- return false;
- if (m_header.m_numberOfMipmapLevels)
- {
- const uint max_mipmap_dimension = 1U << (m_header.m_numberOfMipmapLevels - 1U);
- if (max_mipmap_dimension > (CRNLIB_MAX(CRNLIB_MAX(m_header.m_pixelWidth, m_header.m_pixelHeight), m_header.m_pixelDepth)))
- return false;
- }
- return true;
- }
- bool ktx_texture::consistency_check() const
- {
- if (!check_header())
- return false;
- uint block_dim = 0, bytes_per_block = 0;
- if ((!m_header.m_glType) || (!m_header.m_glFormat))
- {
- if ((m_header.m_glType) || (m_header.m_glFormat))
- return false;
- if (!get_ogl_fmt_desc(m_header.m_glInternalFormat, m_header.m_glType, block_dim, bytes_per_block))
- return false;
- if (block_dim == 1)
- return false;
- //if ((get_width() % block_dim) || (get_height() % block_dim))
- // return false;
- }
- else
- {
- if (!get_ogl_fmt_desc(m_header.m_glFormat, m_header.m_glType, block_dim, bytes_per_block))
- return false;
- if (block_dim > 1)
- return false;
- }
- if ((m_block_dim != block_dim) || (m_bytes_per_block != bytes_per_block))
- return false;
- if (m_image_data.size() != get_total_images())
- return false;
- for (uint mip_level = 0; mip_level < get_num_mips(); mip_level++)
- {
- uint mip_width, mip_height, mip_depth;
- get_mip_dim(mip_level, mip_width, mip_height, mip_depth);
- const uint mip_row_blocks = (mip_width + m_block_dim - 1) / m_block_dim;
- const uint mip_col_blocks = (mip_height + m_block_dim - 1) / m_block_dim;
- if ((!mip_row_blocks) || (!mip_col_blocks))
- return false;
- for (uint array_element = 0; array_element < get_array_size(); array_element++)
- {
- for (uint face = 0; face < get_num_faces(); face++)
- {
- for (uint zslice = 0; zslice < mip_depth; zslice++)
- {
- const uint8_vec& image_data = get_image_data(get_image_index(mip_level, array_element, face, zslice));
- uint expected_image_size = mip_row_blocks * mip_col_blocks * m_bytes_per_block;
- if (image_data.size() != expected_image_size)
- return false;
- }
- }
- }
- }
- return true;
- }
- const uint8_vec* ktx_texture::find_key(const char* pKey) const
- {
- const size_t n = strlen(pKey) + 1;
- for (uint i = 0; i < m_key_values.size(); i++)
- {
- const uint8_vec& v = m_key_values[i];
- if ((v.size() >= n) && (!memcmp(&v[0], pKey, n)))
- return &v;
- }
- return NULL;
- }
- bool ktx_texture::get_key_value_as_string(const char* pKey, dynamic_string& str) const
- {
- const uint8_vec* p = find_key(pKey);
- if (!p)
- {
- str.clear();
- return false;
- }
- const uint ofs = (static_cast<uint>(strlen(pKey)) + 1);
- const uint8* pValue = p->get_ptr() + ofs;
- const uint n = p->size() - ofs;
- uint i;
- for (i = 0; i < n; i++)
- if (!pValue[i])
- break;
- str.set_from_buf(pValue, i);
- return true;
- }
- uint ktx_texture::add_key_value(const char* pKey, const void* pVal, uint val_size)
- {
- const uint idx = m_key_values.size();
- m_key_values.resize(idx + 1);
- uint8_vec& v = m_key_values.back();
- v.append(reinterpret_cast<const uint8*>(pKey), static_cast<uint>(strlen(pKey)) + 1);
- v.append(static_cast<const uint8*>(pVal), val_size);
- return idx;
- }
- } // namespace crnlib
|