Files
unity/crnlib/crn_mipmapped_texture.h
T
Alexander Suvorov 3e12aff909 Fix miscellaneous compiler warnings
DXT Testing:

The modified algorithm has been tested on the Kodak test set using 64-bit build with default settings (running on Windows 10, i7-4790, 3.6GHz). All the decompressed test images are identical to the images being compressed and decompressed using original version of Crunch (revision ea9b8d8).

[Compressing Kodak set without mipmaps using DXT1 encoding]
Original: 1582222 bytes / 28.866 sec
Modified: 1468204 bytes / 11.858 sec
Improvement: 7.21% (compression ratio) / 58.92% (compression time)

[Compressing Kodak set with mipmaps using DXT1 encoding]
Original: 2065243 bytes / 36.878 sec
Modified: 1914805 bytes / 15.625 sec
Improvement: 7.28% (compression ratio) / 57.63% (compression time)

ETC Testing:

The modified algorithm has been tested on the Kodak test set using 64-bit build with default settings (running on Windows 10, i7-4790, 3.6GHz). The ETC1 quantization parameters have been selected in such a way, so that ETC1 compression gives approximately the same average Luma PSNR as the corresponding DXT1 compression (which is equal to 34.044 dB for the Kodak test set compressed without mipmaps using DXT1 encoding and default quality settings).

[Compressing Kodak set without mipmaps using ETC1 encoding]
Total size: 1607858 bytes
Total time: 17.181 sec
Average bitrate: 1.363 bpp
Average Luma PSNR: 34.050 dB
2017-09-11 13:52:21 +02:00

338 lines
11 KiB
C++

// File: crn_mipmapped_texture.h
// See Copyright Notice and license at the end of inc/crnlib.h
#pragma once
#include "crn_dxt_image.h"
#include "../inc/dds_defs.h"
#include "crn_pixel_format.h"
#include "crn_image.h"
#include "crn_resampler.h"
#include "crn_data_stream_serializer.h"
#include "crn_qdxt1.h"
#include "crn_qdxt5.h"
#include "crn_texture_file_types.h"
#include "crn_image_utils.h"
namespace crnlib {
extern const vec2I g_vertical_cross_image_offsets[6];
enum orientation_flags_t {
cOrientationFlagXFlipped = 1,
cOrientationFlagYFlipped = 2,
cDefaultOrientationFlags = 0
};
enum unpack_flags_t {
cUnpackFlagUncook = 1,
cUnpackFlagUnflip = 2
};
class mip_level {
friend class mipmapped_texture;
public:
mip_level();
~mip_level();
mip_level(const mip_level& other);
mip_level& operator=(const mip_level& rhs);
// Assumes ownership.
void assign(image_u8* p, pixel_format fmt = PIXEL_FMT_INVALID, orientation_flags_t orient_flags = cDefaultOrientationFlags);
void assign(dxt_image* p, pixel_format fmt = PIXEL_FMT_INVALID, orientation_flags_t orient_flags = cDefaultOrientationFlags);
void clear();
inline uint get_width() const { return m_width; }
inline uint get_height() const { return m_height; }
inline uint get_total_pixels() const { return m_width * m_height; }
orientation_flags_t get_orientation_flags() const { return m_orient_flags; }
void set_orientation_flags(orientation_flags_t flags) { m_orient_flags = flags; }
inline image_u8* get_image() const { return m_pImage; }
inline dxt_image* get_dxt_image() const { return m_pDXTImage; }
image_u8* get_unpacked_image(image_u8& tmp, uint unpack_flags) const;
inline bool is_packed() const { return m_pDXTImage != NULL; }
inline bool is_valid() const { return (m_pImage != NULL) || (m_pDXTImage != NULL); }
inline pixel_format_helpers::component_flags get_comp_flags() const { return m_comp_flags; }
inline void set_comp_flags(pixel_format_helpers::component_flags comp_flags) { m_comp_flags = comp_flags; }
inline pixel_format get_format() const { return m_format; }
inline void set_format(pixel_format fmt) { m_format = fmt; }
bool convert(pixel_format fmt, bool cook, const dxt_image::pack_params& p);
bool pack_to_dxt(const image_u8& img, pixel_format fmt, bool cook, const dxt_image::pack_params& p, orientation_flags_t orient_flags = cDefaultOrientationFlags);
bool pack_to_dxt(pixel_format fmt, bool cook, const dxt_image::pack_params& p);
bool unpack_from_dxt(bool uncook = true);
// Returns true if flipped on either axis.
bool is_flipped() const;
bool is_x_flipped() const;
bool is_y_flipped() const;
bool can_unflip_without_unpacking() const;
// Returns true if unflipped on either axis.
// Will try to flip packed (DXT/ETC) data in-place, if this isn't possible it'll unpack/uncook the mip level then unflip.
bool unflip(bool allow_unpacking_to_flip, bool uncook_during_unpack);
bool set_alpha_to_luma();
bool convert(image_utils::conversion_type conv_type);
bool flip_x();
bool flip_y();
private:
uint m_width;
uint m_height;
pixel_format_helpers::component_flags m_comp_flags;
pixel_format m_format;
image_u8* m_pImage;
dxt_image* m_pDXTImage;
orientation_flags_t m_orient_flags;
void cook_image(image_u8& img) const;
void uncook_image(image_u8& img) const;
};
// A face is an array of mip_level ptr's.
typedef crnlib::vector<mip_level*> mip_ptr_vec;
// And an array of one, six, or N faces make up a texture.
typedef crnlib::vector<mip_ptr_vec> face_vec;
class mipmapped_texture {
public:
// Construction/destruction
mipmapped_texture();
~mipmapped_texture();
mipmapped_texture(const mipmapped_texture& other);
mipmapped_texture& operator=(const mipmapped_texture& rhs);
void clear();
void init(uint width, uint height, uint levels, uint faces, pixel_format fmt, const char* pName, orientation_flags_t orient_flags);
// Assumes ownership.
void assign(face_vec& faces);
void assign(mip_level* pLevel);
void assign(image_u8* p, pixel_format fmt = PIXEL_FMT_INVALID, orientation_flags_t orient_flags = cDefaultOrientationFlags);
void assign(dxt_image* p, pixel_format fmt = PIXEL_FMT_INVALID, orientation_flags_t orient_flags = cDefaultOrientationFlags);
void set(texture_file_types::format source_file_type, const mipmapped_texture& mipmapped_texture);
// Accessors
image_u8* get_level_image(uint face, uint level, image_u8& img, uint unpack_flags = cUnpackFlagUncook | cUnpackFlagUnflip) const;
inline bool is_valid() const { return m_faces.size() > 0; }
const dynamic_string& get_name() const { return m_name; }
void set_name(const dynamic_string& name) { m_name = name; }
const dynamic_string& get_source_filename() const { return get_name(); }
texture_file_types::format get_source_file_type() const { return m_source_file_type; }
inline uint get_width() const { return m_width; }
inline uint get_height() const { return m_height; }
inline uint get_total_pixels() const { return m_width * m_height; }
uint get_total_pixels_in_all_faces_and_mips() const;
inline uint get_num_faces() const { return m_faces.size(); }
inline uint get_num_levels() const {
if (m_faces.empty())
return 0;
else
return m_faces[0].size();
}
inline pixel_format_helpers::component_flags get_comp_flags() const { return m_comp_flags; }
inline pixel_format get_format() const { return m_format; }
inline bool is_unpacked() const {
if (get_num_faces()) {
return get_level(0, 0)->get_image() != NULL;
}
return false;
}
inline const mip_ptr_vec& get_face(uint face) const { return m_faces[face]; }
inline mip_ptr_vec& get_face(uint face) { return m_faces[face]; }
inline const mip_level* get_level(uint face, uint mip) const { return m_faces[face][mip]; }
inline mip_level* get_level(uint face, uint mip) { return m_faces[face][mip]; }
bool has_alpha() const;
bool is_normal_map() const;
bool is_vertical_cross() const;
bool is_packed() const;
texture_type determine_texture_type() const;
const dynamic_string& get_last_error() const { return m_last_error; }
void clear_last_error() { m_last_error.clear(); }
// Reading/writing
bool read_dds(data_stream_serializer& serializer);
bool write_dds(data_stream_serializer& serializer) const;
bool read_ktx(data_stream_serializer& serializer);
bool write_ktx(data_stream_serializer& serializer) const;
bool read_crn(data_stream_serializer& serializer);
bool read_crn_from_memory(const void* pData, uint data_size, const char* pFilename);
// If file_format is texture_file_types::cFormatInvalid, the format will be determined from the filename's extension.
bool read_from_file(const char* pFilename, texture_file_types::format file_format = texture_file_types::cFormatInvalid);
bool read_from_stream(data_stream_serializer& serializer, texture_file_types::format file_format = texture_file_types::cFormatInvalid);
bool write_to_file(
const char* pFilename,
texture_file_types::format file_format = texture_file_types::cFormatInvalid,
crn_comp_params* pComp_params = NULL,
uint32* pActual_quality_level = NULL, float* pActual_bitrate = NULL,
uint32 image_write_flags = 0);
// Conversion
bool convert(pixel_format fmt, bool cook, const dxt_image::pack_params& p);
bool convert(pixel_format fmt, const dxt_image::pack_params& p);
bool convert(pixel_format fmt, bool cook, const dxt_image::pack_params& p, int qdxt_quality, bool hierarchical = true);
bool convert(image_utils::conversion_type conv_type);
bool unpack_from_dxt(bool uncook = true);
bool set_alpha_to_luma();
void discard_mipmaps();
void discard_mips();
struct resample_params {
resample_params()
: m_pFilter("kaiser"),
m_wrapping(false),
m_srgb(false),
m_renormalize(false),
m_filter_scale(.9f),
m_gamma(1.75f), // or 2.2f
m_multithreaded(true) {
}
const char* m_pFilter;
bool m_wrapping;
bool m_srgb;
bool m_renormalize;
float m_filter_scale;
float m_gamma;
bool m_multithreaded;
};
bool resize(uint new_width, uint new_height, const resample_params& params);
struct generate_mipmap_params : public resample_params {
generate_mipmap_params()
: resample_params(),
m_min_mip_size(1),
m_max_mips(0) {
}
uint m_min_mip_size;
uint m_max_mips; // actually the max # of total levels
};
bool generate_mipmaps(const generate_mipmap_params& params, bool force);
bool crop(uint x, uint y, uint width, uint height);
bool vertical_cross_to_cubemap();
// Low-level clustered DXT (QDXT) compression
struct qdxt_state {
qdxt_state(task_pool& tp)
: m_fmt(PIXEL_FMT_INVALID), m_qdxt1(tp), m_qdxt5a(tp), m_qdxt5b(tp) {
}
pixel_format m_fmt;
qdxt1 m_qdxt1;
qdxt5 m_qdxt5a;
qdxt5 m_qdxt5b;
crnlib::vector<dxt_pixel_block> m_pixel_blocks;
qdxt1_params m_qdxt1_params;
qdxt5_params m_qdxt5_params[2];
bool m_has_blocks[3];
void clear() {
m_fmt = PIXEL_FMT_INVALID;
m_qdxt1.clear();
m_qdxt5a.clear();
m_qdxt5b.clear();
m_pixel_blocks.clear();
m_qdxt1_params.clear();
m_qdxt5_params[0].clear();
m_qdxt5_params[1].clear();
utils::zero_object(m_has_blocks);
}
};
bool qdxt_pack_init(qdxt_state& state, mipmapped_texture& dst_tex, const qdxt1_params& dxt1_params, const qdxt5_params& dxt5_params, pixel_format fmt, bool cook);
bool qdxt_pack(qdxt_state& state, mipmapped_texture& dst_tex, const qdxt1_params& dxt1_params, const qdxt5_params& dxt5_params);
void swap(mipmapped_texture& img);
bool check() const;
void set_orientation_flags(orientation_flags_t flags);
// Returns true if any face/miplevel is flipped.
bool is_flipped() const;
bool is_x_flipped() const;
bool is_y_flipped() const;
bool can_unflip_without_unpacking() const;
bool unflip(bool allow_unpacking_to_flip, bool uncook_if_necessary_to_unpack);
bool flip_y(bool update_orientation_flags);
private:
dynamic_string m_name;
uint m_width;
uint m_height;
pixel_format_helpers::component_flags m_comp_flags;
pixel_format m_format;
face_vec m_faces;
texture_file_types::format m_source_file_type;
mutable dynamic_string m_last_error;
inline void clear_last_error() const { m_last_error.clear(); }
inline void set_last_error(const char* p) const { m_last_error = p; }
void free_all_mips();
bool read_regular_image(data_stream_serializer& serializer);
bool write_regular_image(const char* pFilename, uint32 image_write_flags);
bool read_dds_internal(data_stream_serializer& serializer);
void print_crn_comp_params(const crn_comp_params& p);
bool write_comp_texture(const char* pFilename, const crn_comp_params& comp_params, uint32* pActual_quality_level, float* pActual_bitrate);
void change_dxt1_to_dxt1a();
bool flip_y_helper();
};
inline void swap(mipmapped_texture& a, mipmapped_texture& b) {
a.swap(b);
}
} // namespace crnlib