// File: crn_image_utils.h // See Copyright Notice and license at the end of inc/crnlib.h #pragma once #include "crn_image.h" #include "crn_data_stream_serializer.h" namespace crnlib { enum pixel_format; namespace image_utils { enum read_flags_t { cReadFlagForceSTB = 1, cReadFlagsAllFlags = 1 }; bool read_from_stream_stb(data_stream_serializer& serializer, image_u8& img); bool read_from_stream_jpgd(data_stream_serializer& serializer, image_u8& img); bool read_from_stream(image_u8& dest, data_stream_serializer& serializer, uint read_flags = 0); bool read_from_file(image_u8& dest, const char* pFilename, uint read_flags = 0); // Reads texture from memory, results returned stb_image.c style. // *pActual_comps is set to 1, 3, or 4. req_comps must range from 1-4. uint8* read_from_memory(const uint8* pImage, int nSize, int* pWidth, int* pHeight, int* pActualComps, int req_comps, const char* pFilename); enum { cWriteFlagIgnoreAlpha = 0x00000001, cWriteFlagGrayscale = 0x00000002, cWriteFlagJPEGH1V1 = 0x00010000, cWriteFlagJPEGH2V1 = 0x00020000, cWriteFlagJPEGH2V2 = 0x00040000, cWriteFlagJPEGTwoPass = 0x00080000, cWriteFlagJPEGNoChromaDiscrim = 0x00100000, cWriteFlagJPEGQualityLevelMask = 0xFF000000, cWriteFlagJPEGQualityLevelShift = 24, }; const int cLumaComponentIndex = -1; inline uint create_jpeg_write_flags(uint base_flags, uint quality_level) { CRNLIB_ASSERT(quality_level <= 100); return base_flags | ((quality_level << cWriteFlagJPEGQualityLevelShift) & cWriteFlagJPEGQualityLevelMask); } bool write_to_file(const char* pFilename, const image_u8& img, uint write_flags = 0, int grayscale_comp_index = cLumaComponentIndex); bool has_alpha(const image_u8& img); bool is_normal_map(const image_u8& img, const char* pFilename = NULL); void renorm_normal_map(image_u8& img); struct resample_params { resample_params() : m_dst_width(0), m_dst_height(0), m_pFilter("lanczos4"), m_filter_scale(1.0f), m_srgb(true), m_wrapping(false), m_first_comp(0), m_num_comps(4), m_source_gamma(2.2f), // 1.75f m_multithreaded(true) { } uint m_dst_width; uint m_dst_height; const char* m_pFilter; float m_filter_scale; bool m_srgb; bool m_wrapping; uint m_first_comp; uint m_num_comps; float m_source_gamma; bool m_multithreaded; }; bool resample_single_thread(const image_u8& src, image_u8& dst, const resample_params& params); bool resample_multithreaded(const image_u8& src, image_u8& dst, const resample_params& params); bool resample(const image_u8& src, image_u8& dst, const resample_params& params); bool compute_delta(image_u8& dest, image_u8& a, image_u8& b, uint scale = 2); class error_metrics { public: error_metrics() { utils::zero_this(this); } void print(const char* pName) const; // If num_channels==0, luma error is computed. // If pHist != NULL, it must point to a 256 entry array. bool compute(const image_u8& a, const image_u8& b, uint first_channel, uint num_channels, bool average_component_error = true); uint mMax; double mMean; double mMeanSquared; double mRootMeanSquared; double mPeakSNR; inline bool operator== (const error_metrics& other) const { return mPeakSNR == other.mPeakSNR; } inline bool operator< (const error_metrics& other) const { return mPeakSNR < other.mPeakSNR; } inline bool operator> (const error_metrics& other) const { return mPeakSNR > other.mPeakSNR; } }; void print_image_metrics(const image_u8& src_img, const image_u8& dst_img); double compute_block_ssim(uint n, const uint8* pX, const uint8* pY); double compute_ssim(const image_u8& a, const image_u8& b, int channel_index); void print_ssim(const image_u8& src_img, const image_u8& dst_img); enum conversion_type { cConversion_Invalid = -1, cConversion_To_CCxY, cConversion_From_CCxY, cConversion_To_xGxR, cConversion_From_xGxR, cConversion_To_xGBR, cConversion_From_xGBR, cConversion_To_AGBR, cConversion_From_AGBR, cConversion_XY_to_XYZ, cConversion_Y_To_A, cConversion_A_To_RGBA, cConversion_Y_To_RGB, cConversion_To_Y, cConversionTotal }; void convert_image(image_u8& img, conversion_type conv_type); template inline uint8* pack_image(const image_type& img, const pixel_packer& packer, uint& n) { n = 0; if (!packer.is_valid()) return NULL; const uint width = img.get_width(), height = img.get_height(); uint dst_pixel_stride = packer.get_pixel_stride(); uint dst_pitch = width * dst_pixel_stride; n = dst_pitch * height; uint8* pImage = static_cast(crnlib_malloc(n)); uint8* pDst = pImage; for (uint y = 0; y < height; y++) { const typename image_type::color_t* pSrc = img.get_scanline(y); for (uint x = 0; x < width; x++) pDst = (uint8*)packer.pack(*pSrc++, pDst); } return pImage; } image_utils::conversion_type get_conversion_type(bool cooking, pixel_format fmt); image_utils::conversion_type get_image_conversion_type_from_crn_format(crn_format fmt); double compute_std_dev(uint n, const color_quad_u8* pPixels, uint first_channel, uint num_channels); uint8* read_image_from_memory(const uint8* pImage, int nSize, int* pWidth, int* pHeight, int* pActualComps, int req_comps, const char* pFilename); } // namespace image_utils } // namespace crnlib