340 lines
12 KiB
C++
340 lines
12 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, texture_file_types::format file_format);
|
|
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
|