- Fixing DDS reader so it doesn't require the source pitch/linear size field to be divisible by 4

- Fixing DDS writer so it writes a non-zero pitch/linearsize field (a few DDS readers in the wild require this field to be set)
- Fixing example2.cpp and example3.cpp so they write non-zero pitch/linearsize fields.
- Misc merges from the ddsexport branch
- Adding -usesourceformat command line option, useful when reprocessing a lot of existing DDS files.
This commit is contained in:
richgel99@gmail.com
2012-04-16 00:58:38 +00:00
parent d96d9807b1
commit 45901c935c
9 changed files with 179 additions and 16 deletions
+1 -1
View File
@@ -3,5 +3,5 @@
#include "crn_core.h" #include "crn_core.h"
#include "crn_winhdr.h" #include "crn_winhdr.h"
char *g_copyright_str = "Copyright (c) 2010-2011 Tenacious Software LLC"; char *g_copyright_str = "Copyright (c) 2010-2012 Tenacious Software LLC";
char *g_sig_str = "C8cfRlaorj0wLtnMSxrBJxTC85rho2L9hUZKHcBL"; char *g_sig_str = "C8cfRlaorj0wLtnMSxrBJxTC85rho2L9hUZKHcBL";
+21 -2
View File
@@ -620,7 +620,17 @@ namespace crnlib
uint pitch = desc.lPitch; uint pitch = desc.lPitch;
if (!pitch) if (!pitch)
pitch = default_pitch; pitch = default_pitch;
else if ((pitch > default_pitch * 8) || (pitch & 3)) #if 0
else if (pitch & 3)
{
// MS's DDS docs say the pitch must be DWORD aligned - but this isn't always the case.
// 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
// files, so the docs must be wrong/outdated.
console::warning(L"DDS file's pitch is not divisible by 4 - trying to load anyway.");
}
#endif
// Check for obviously wacky source pitches (probably a corrupted/invalid file).
else if (pitch > default_pitch * 8)
{ {
set_last_error(L"Invalid pitch"); set_last_error(L"Invalid pitch");
return false; return false;
@@ -946,6 +956,10 @@ namespace crnlib
break; break;
} }
} }
uint bits_per_pixel = pixel_format_helpers::get_bpp(m_format);
desc.lPitch = (((desc.dwWidth + 3) & ~3) * ((desc.dwHeight + 3) & ~3) * bits_per_pixel) >> 3;
desc.dwFlags |= DDSD_LINEARSIZE;
} }
else else
{ {
@@ -998,8 +1012,12 @@ namespace crnlib
return false; return false;
} }
} }
}
uint bits_per_pixel = desc.ddpfPixelFormat.dwRGBBitCount;
desc.lPitch = (desc.dwWidth * bits_per_pixel) >> 3;
desc.dwFlags |= DDSD_LINEARSIZE;
}
if (!c_crnlib_little_endian_platform) if (!c_crnlib_little_endian_platform)
utils::endian_switch_dwords(reinterpret_cast<uint32*>(&desc), sizeof(desc) / sizeof(uint32)); utils::endian_switch_dwords(reinterpret_cast<uint32*>(&desc), sizeof(desc) / sizeof(uint32));
@@ -1272,6 +1290,7 @@ namespace crnlib
else else
{ {
image_u8* p = crnlib_new<image_u8>(mip_width, mip_height); image_u8* p = crnlib_new<image_u8>(mip_width, mip_height);
p->set_comp_flags(m_comp_flags);
m_faces[f][l]->assign(p, m_format); m_faces[f][l]->assign(p, m_format);
} }
} }
+3
View File
@@ -35,6 +35,8 @@ namespace crnlib
return (fmt == PIXEL_FMT_DXT1) || (fmt == PIXEL_FMT_DXT1A); return (fmt == PIXEL_FMT_DXT1) || (fmt == PIXEL_FMT_DXT1A);
} }
// has_alpha() should probably be called "has_opacity()" - it indicates if the format encodes opacity
// because some swizzled DXT5 formats do not encode opacity.
inline bool has_alpha(pixel_format fmt) inline bool has_alpha(pixel_format fmt)
{ {
switch (fmt) switch (fmt)
@@ -48,6 +50,7 @@ namespace crnlib
case PIXEL_FMT_A8R8G8B8: case PIXEL_FMT_A8R8G8B8:
case PIXEL_FMT_A8: case PIXEL_FMT_A8:
case PIXEL_FMT_A8L8: case PIXEL_FMT_A8L8:
case PIXEL_FMT_DXT5_AGBR:
return true; return true;
default: break; default: break;
} }
+104
View File
@@ -34,6 +34,24 @@ namespace crnlib
m_corner[0] = point; m_corner[0] = point;
m_corner[1].set(point[0] + 1, point[1] + 1); m_corner[1].set(point[0] + 1, point[1] + 1);
} }
inline bool operator== (const rect& r) const
{
return (m_corner[0] == r.m_corner[0]) && (m_corner[1] == r.m_corner[1]);
}
inline bool operator< (const rect& r) const
{
for (uint i = 0; i < 2; i++)
{
if (m_corner[i] < r.m_corner[i])
return true;
else if (!(m_corner[i] == r.m_corner[i]))
return false;
}
return false;
}
inline void clear() inline void clear()
{ {
@@ -70,10 +88,96 @@ namespace crnlib
inline bool is_empty() const { return (m_corner[1][0] <= m_corner[0][0]) || (m_corner[1][1] <= m_corner[0][1]); } inline bool is_empty() const { return (m_corner[1][0] <= m_corner[0][0]) || (m_corner[1][1] <= m_corner[0][1]); }
inline uint get_dimension(uint axis) const { return m_corner[1][axis] - m_corner[0][axis]; } inline uint get_dimension(uint axis) const { return m_corner[1][axis] - m_corner[0][axis]; }
inline uint get_area() const { return get_dimension(0) * get_dimension(1); }
inline const vec2I& operator[] (uint i) const { CRNLIB_ASSERT(i < 2); return m_corner[i]; } inline const vec2I& operator[] (uint i) const { CRNLIB_ASSERT(i < 2); return m_corner[i]; }
inline vec2I& operator[] (uint i) { CRNLIB_ASSERT(i < 2); return m_corner[i]; } inline vec2I& operator[] (uint i) { CRNLIB_ASSERT(i < 2); return m_corner[i]; }
inline rect& translate(int x_ofs, int y_ofs)
{
m_corner[0][0] += x_ofs;
m_corner[0][1] += y_ofs;
m_corner[1][0] += x_ofs;
m_corner[1][1] += y_ofs;
return *this;
}
inline rect& init_expand()
{
m_corner[0].set(INT_MAX);
m_corner[1].set(INT_MIN);
return *this;
}
inline rect& expand(int x, int y)
{
m_corner[0][0] = math::minimum(m_corner[0][0], x);
m_corner[0][1] = math::minimum(m_corner[0][1], y);
m_corner[1][0] = math::maximum(m_corner[1][0], x + 1);
m_corner[1][1] = math::maximum(m_corner[1][1], y + 1);
return *this;
}
inline rect& expand(const rect& r)
{
m_corner[0][0] = math::minimum(m_corner[0][0], r[0][0]);
m_corner[0][1] = math::minimum(m_corner[0][1], r[0][1]);
m_corner[1][0] = math::maximum(m_corner[1][0], r[1][0]);
m_corner[1][1] = math::maximum(m_corner[1][1], r[1][1]);
return *this;
}
inline bool touches(const rect& r) const
{
for (uint i = 0; i < 2; i++)
{
if (r[1][i] <= m_corner[0][i])
return false;
else if (r[0][i] >= m_corner[1][i])
return false;
}
return true;
}
inline bool within(const rect& r) const
{
for (uint i = 0; i < 2; i++)
{
if (m_corner[0][i] < r[0][i])
return false;
else if (m_corner[1][i] > r[1][i])
return false;
}
return true;
}
inline bool intersect(const rect& r)
{
if (!touches(r))
{
clear();
return false;
}
for (uint i = 0; i < 2; i++)
{
m_corner[0][i] = math::maximum<int>(m_corner[0][i], r[0][i]);
m_corner[1][i] = math::minimum<int>(m_corner[1][i], r[1][i]);
}
return true;
}
inline bool contains(int x, int y) const
{
return (x >= m_corner[0][0]) && (x < m_corner[1][0]) &&
(y >= m_corner[0][1]) && (y < m_corner[1][1]);
}
inline bool contains(const vec2I& p) const { return contains(p[0], p[1]); }
private: private:
vec2I m_corner[2]; vec2I m_corner[2];
}; };
+9 -5
View File
@@ -295,6 +295,9 @@ namespace crnlib
static pixel_format choose_pixel_format(convert_params& params, const crn_comp_params &comp_params, const dds_texture& src_tex, texture_type tex_type) static pixel_format choose_pixel_format(convert_params& params, const crn_comp_params &comp_params, const dds_texture& src_tex, texture_type tex_type)
{ {
if (params.m_use_source_format)
return src_tex.get_format();
const bool is_normal_map = (tex_type == cTextureTypeNormalMap); const bool is_normal_map = (tex_type == cTextureTypeNormalMap);
if (params.m_dst_file_type == texture_file_types::cFormatCRN) if (params.m_dst_file_type == texture_file_types::cFormatCRN)
@@ -409,11 +412,12 @@ namespace crnlib
m_pInput_texture->get_num_levels(), m_pInput_texture->get_num_levels(),
pixel_format_helpers::get_pixel_format_string(m_pInput_texture->get_format())); pixel_format_helpers::get_pixel_format_string(m_pInput_texture->get_format()));
console::debug(L" texture_type: %s", get_texture_type_desc(m_texture_type)); console::debug(L" texture_type: %s", get_texture_type_desc(m_texture_type));
console::debug(L" dst_filename: %s", m_dst_filename.get_ptr()); console::debug(L" dst_filename: %s", m_dst_filename.get_ptr());
console::debug(L"dst_file_type: %s", texture_file_types::get_extension(m_dst_file_type)); console::debug(L" dst_file_type: %s", texture_file_types::get_extension(m_dst_file_type));
console::debug(L" dst_format: %s", pixel_format_helpers::get_pixel_format_string(m_dst_format)); console::debug(L" dst_format: %s", pixel_format_helpers::get_pixel_format_string(m_dst_format));
console::debug(L" quick: %u", m_quick); console::debug(L" quick: %u", m_quick);
console::debug(L" use_source_format: %u", m_use_source_format);
} }
static bool write_compressed_texture( static bool write_compressed_texture(
+3 -1
View File
@@ -60,7 +60,8 @@ namespace crnlib
m_no_stats(false), m_no_stats(false),
m_lzma_stats(false), m_lzma_stats(false),
m_status(false), m_status(false),
m_canceled(false) m_canceled(false),
m_use_source_format(false)
{ {
} }
@@ -96,6 +97,7 @@ namespace crnlib
bool m_debugging; bool m_debugging;
bool m_param_debugging; bool m_param_debugging;
bool m_no_stats; bool m_no_stats;
bool m_use_source_format;
bool m_lzma_stats; bool m_lzma_stats;
mutable bool m_status; mutable bool m_status;
+25 -6
View File
@@ -65,6 +65,7 @@ public:
static void print_usage() static void print_usage()
{ {
// -------------------------------------------------------------------------------
console::message(L"\nCommand line usage:"); console::message(L"\nCommand line usage:");
console::printf(L"crunch [options] -file filename"); console::printf(L"crunch [options] -file filename");
console::printf(L"-file filename - Required input filename, wildcards, multiple /file params OK."); console::printf(L"-file filename - Required input filename, wildcards, multiple /file params OK.");
@@ -104,7 +105,7 @@ public:
console::printf(L"/mipstats - Print statistics for each mipmap, not just the top mip"); console::printf(L"/mipstats - Print statistics for each mipmap, not just the top mip");
console::printf(L"/lzmastats - Print size of output file compressed with LZMA codec"); console::printf(L"/lzmastats - Print size of output file compressed with LZMA codec");
console::printf(L"/split - Write faces/mip levels to multiple separate output files"); console::printf(L"/split - Write faces/mip levels to multiple separate output files");
console::message(L"\nImage rescaling (mutually exclusive options)"); console::message(L"\nImage rescaling (mutually exclusive options)");
console::printf(L"/rescale <int> <int> - Rescale image to specified resolution"); console::printf(L"/rescale <int> <int> - Rescale image to specified resolution");
console::printf(L"/relscale <float> <float> - Rescale image to specified relative resolution"); console::printf(L"/relscale <float> <float> - Rescale image to specified relative resolution");
@@ -125,9 +126,14 @@ public:
console::printf(L"/ca # - Alpha endpoint palette size, 32-8192, default=3072"); console::printf(L"/ca # - Alpha endpoint palette size, 32-8192, default=3072");
console::printf(L"/sa # - Alpha selector palette size, 32-8192, default=3072"); console::printf(L"/sa # - Alpha selector palette size, 32-8192, default=3072");
// -------------------------------------------------------------------------------
console::message(L"\nMipmap filtering options:"); console::message(L"\nMipmap filtering options:");
console::printf(L"/mipMode [UseSourceOrGenerate,UseSource,Generate,None]"); console::printf(L"/mipMode [UseSourceOrGenerate,UseSource,Generate,None]");
console::printf(L" Default mipMode is UseSourceOrGenerate"); console::printf(L" Default mipMode is UseSourceOrGenerate");
console::printf(L" UseSourceOrGenerate: Use source mipmaps if possible, or create new mipmaps.");
console::printf(L" UseSource: Always use source mipmaps, if any (never generate new mipmaps)");
console::printf(L" Generate: Always generate a new mipmap chain (ignore source mipmaps)");
console::printf(L" None: Do not output any mipmaps");
console::printf(L"/mipFilter [box,tent,lanczos4,mitchell,kaiser], default=kaiser"); console::printf(L"/mipFilter [box,tent,lanczos4,mitchell,kaiser], default=kaiser");
console::printf(L"/gamma # - Mipmap gamma correction value, default=2.2, use 1.0 for linear"); console::printf(L"/gamma # - Mipmap gamma correction value, default=2.2, use 1.0 for linear");
console::printf(L"/blurriness # - Scale filter kernel, >1=blur, <1=sharpen, .01-8, default=.9"); console::printf(L"/blurriness # - Scale filter kernel, >1=blur, <1=sharpen, .01-8, default=.9");
@@ -150,12 +156,16 @@ public:
console::printf(L"/forceprimaryencoding - Only use DXT1 color4 and DXT5 alpha8 block encodings."); console::printf(L"/forceprimaryencoding - Only use DXT1 color4 and DXT5 alpha8 block encodings.");
console::printf(L"/usetransparentindicesforblack - Try DXT1 transparent indices for dark pixels."); console::printf(L"/usetransparentindicesforblack - Try DXT1 transparent indices for dark pixels.");
console::message(L"\nOuptut pixel format options:");
console::printf(L"/usesourceformat - Use input file's format for output format (when possible).");
console::message(L"\nAll supported texture formats (Note: .CRN only supports DXTn pixel formats):"); console::message(L"\nAll supported texture formats (Note: .CRN only supports DXTn pixel formats):");
for (uint i = 0; i < pixel_format_helpers::get_num_formats(); i++) for (uint i = 0; i < pixel_format_helpers::get_num_formats(); i++)
{ {
pixel_format fmt = pixel_format_helpers::get_pixel_format_by_index(i); pixel_format fmt = pixel_format_helpers::get_pixel_format_by_index(i);
console::printf(L"/%s", pixel_format_helpers::get_pixel_format_string(fmt)); console::printf(L"/%s", pixel_format_helpers::get_pixel_format_string(fmt));
} }
console::printf(L"\nFor bugs, support, or feedback: richgel99@gmail.com");
} }
bool convert(const wchar_t* pCommand_line) bool convert(const wchar_t* pCommand_line)
@@ -221,6 +231,7 @@ public:
{ L"info" }, { L"info" },
{ L"forceprimaryencoding" }, { L"forceprimaryencoding" },
{ L"usetransparentindicesforblack" }, { L"usetransparentindicesforblack" },
{ L"usesourceformat" },
{ L"rescalemode", 1 }, { L"rescalemode", 1 },
{ L"rescale", 2 }, { L"rescale", 2 },
@@ -475,10 +486,17 @@ private:
if (!m_params.has_key(L"fileformat")) if (!m_params.has_key(L"fileformat"))
{ {
texture_file_types::format input_file_type = texture_file_types::determine_file_format(in_filename.get_ptr()); if (m_params.has_key(L"split"))
if (input_file_type == texture_file_types::cFormatCRN)
{ {
out_file_type = texture_file_types::cFormatDDS; out_file_type = texture_file_types::cFormatTGA;
}
else
{
texture_file_types::format input_file_type = texture_file_types::determine_file_format(in_filename.get_ptr());
if (input_file_type == texture_file_types::cFormatCRN)
{
out_file_type = texture_file_types::cFormatDDS;
}
} }
} }
@@ -1106,6 +1124,7 @@ private:
params.m_dst_file_type = out_file_type; params.m_dst_file_type = out_file_type;
params.m_lzma_stats = m_params.has_key(L"lzmastats"); params.m_lzma_stats = m_params.has_key(L"lzmastats");
params.m_write_mipmaps_to_multiple_files = m_params.has_key(L"split"); params.m_write_mipmaps_to_multiple_files = m_params.has_key(L"split");
params.m_use_source_format = m_params.has_key(L"usesourceformat");
if ((!m_params.get_value_as_bool(L"noprogress")) && (!m_params.get_value_as_bool(L"quiet"))) if ((!m_params.get_value_as_bool(L"noprogress")) && (!m_params.get_value_as_bool(L"quiet")))
params.m_pProgress_func = progress_callback_func; params.m_pProgress_func = progress_callback_func;
@@ -1203,8 +1222,8 @@ static bool check_for_option(int argc, wchar_t *argv[], const wchar_t *pOption)
static void print_title() static void print_title()
{ {
console::printf(L"crunch: Advanced DXTn Texture Compressor"); console::printf(L"crunch: Advanced DXTn Texture Compressor - http://code.google.com/p/crunch");
console::printf(L"Copyright (c) 2010-2011 Tenacious Software LLC"); console::printf(L"Copyright (c) 2010-2012 Rich Geldreich and Tenacious Software LLC");
console::printf(L"crnlib version v%u.%02u %s Built %s, %s", CRNLIB_VERSION / 100U, CRNLIB_VERSION % 100U, crnlib_is_x64() ? L"x64" : L"x86", U(__DATE__), U(__TIME__)); console::printf(L"crnlib version v%u.%02u %s Built %s, %s", CRNLIB_VERSION / 100U, CRNLIB_VERSION % 100U, crnlib_is_x64() ? L"x64" : L"x86", U(__DATE__), U(__TIME__));
console::printf(L""); console::printf(L"");
} }
+8 -1
View File
@@ -3,7 +3,8 @@
// This tool does NOT depend on the crnlib library at all. It only needs the low-level // This tool does NOT depend on the crnlib library at all. It only needs the low-level
// decompression/transcoding functionality defined in inc/crn_decomp.h. // decompression/transcoding functionality defined in inc/crn_decomp.h.
// This is the basic functionality a game engine would need to employ at runtime to utilize // This is the basic functionality a game engine would need to employ at runtime to utilize
// .CRN textures. // .CRN textures (excluding writing the output DDS file - instead you would provide the DXTn
// bits directly to OpenGL/D3D).
// See Copyright Notice and license at the end of inc/crnlib.h // See Copyright Notice and license at the end of inc/crnlib.h
#include <stdlib.h> #include <stdlib.h>
#include <stdio.h> #include <stdio.h>
@@ -170,6 +171,7 @@ int main(int argc, char *argv[])
dds_desc.ddpfPixelFormat.dwFourCC = crnd::crnd_crn_format_to_fourcc(fundamental_fmt); dds_desc.ddpfPixelFormat.dwFourCC = crnd::crnd_crn_format_to_fourcc(fundamental_fmt);
if (fundamental_fmt != tex_info.m_format) if (fundamental_fmt != tex_info.m_format)
{ {
// It's a funky swizzled DXTn format - write its FOURCC to dwRGBBitCount.
dds_desc.ddpfPixelFormat.dwRGBBitCount = crnd::crnd_crn_format_to_fourcc(tex_info.m_format); dds_desc.ddpfPixelFormat.dwRGBBitCount = crnd::crnd_crn_format_to_fourcc(tex_info.m_format);
} }
@@ -186,6 +188,11 @@ int main(int argc, char *argv[])
DDSCAPS2_CUBEMAP_NEGATIVEY | DDSCAPS2_CUBEMAP_POSITIVEZ | DDSCAPS2_CUBEMAP_NEGATIVEZ; DDSCAPS2_CUBEMAP_NEGATIVEY | DDSCAPS2_CUBEMAP_POSITIVEZ | DDSCAPS2_CUBEMAP_NEGATIVEZ;
} }
// Set pitch/linearsize field (some DDS readers require this field to be non-zero).
int bits_per_pixel = crnd::crnd_get_crn_format_bits_per_texel(tex_info.m_format);
dds_desc.lPitch = (((dds_desc.dwWidth + 3) & ~3) * ((dds_desc.dwHeight + 3) & ~3) * bits_per_pixel) >> 3;
dds_desc.dwFlags |= DDSD_LINEARSIZE;
// Write the DDS header to the output file. // Write the DDS header to the output file.
fwrite(&dds_desc, sizeof(dds_desc), 1, pDDS_file); fwrite(&dds_desc, sizeof(dds_desc), 1, pDDS_file);
+5
View File
@@ -260,6 +260,11 @@ int main(int argc, char *argv[])
dds_desc.ddpfPixelFormat.dwFourCC = crn_get_format_fourcc(fmt); dds_desc.ddpfPixelFormat.dwFourCC = crn_get_format_fourcc(fmt);
dds_desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE; dds_desc.ddsCaps.dwCaps = DDSCAPS_TEXTURE;
// Set pitch/linearsize field (some DDS readers require this field to be non-zero).
uint bits_per_pixel = crn_get_format_bits_per_texel(fmt);
dds_desc.lPitch = (((dds_desc.dwWidth + 3) & ~3) * ((dds_desc.dwHeight + 3) & ~3) * bits_per_pixel) >> 3;
dds_desc.dwFlags |= DDSD_LINEARSIZE;
// Write the DDS header to the output file. // Write the DDS header to the output file.
fwrite(&dds_desc, sizeof(dds_desc), 1, pDDS_file); fwrite(&dds_desc, sizeof(dds_desc), 1, pDDS_file);