// 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_ptr_vec; // And an array of one, six, or N faces make up a texture. typedef crnlib::vector 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 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