]> git.cworth.org Git - vogl/blob - src/voglcore/vogl_image_utils.h
Initial vogl checkin
[vogl] / src / voglcore / vogl_image_utils.h
1 /**************************************************************************
2  *
3  * Copyright 2013-2014 RAD Game Tools and Valve Software
4  * Copyright 2010-2014 Rich Geldreich and Tenacious Software LLC
5  * All Rights Reserved.
6  *
7  * Permission is hereby granted, free of charge, to any person obtaining a copy
8  * of this software and associated documentation files (the "Software"), to deal
9  * in the Software without restriction, including without limitation the rights
10  * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11  * copies of the Software, and to permit persons to whom the Software is
12  * furnished to do so, subject to the following conditions:
13  *
14  * The above copyright notice and this permission notice shall be included in
15  * all copies or substantial portions of the Software.
16  *
17  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18  * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19  * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20  * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21  * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22  * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
23  * THE SOFTWARE.
24  *
25  **************************************************************************/
26
27 // File: vogl_image_utils.h
28 #pragma once
29
30 #include "vogl_core.h"
31 #include "vogl_image.h"
32 #include "vogl_data_stream_serializer.h"
33 #include "vogl_matrix.h"
34
35 namespace vogl
36 {
37     enum pixel_format;
38
39     namespace image_utils
40     {
41         enum read_flags_t
42         {
43             cReadFlagForceSTB = 1,
44             cReadFlagsAllFlags = 1
45         };
46
47         bool read_from_stream_stb(data_stream_serializer &serializer, image_u8 &img);
48         bool read_from_stream_jpgd(data_stream_serializer &serializer, image_u8 &img);
49         bool read_from_stream(image_u8 &dest, data_stream_serializer &serializer, uint read_flags = 0);
50         bool read_from_file(image_u8 &dest, const char *pFilename, uint read_flags = 0);
51
52         // Reads texture from memory, results returned stb_image.c style.
53         // *pActual_comps is set to 1, 3, or 4. req_comps must range from 1-4.
54         uint8 *read_from_memory(const uint8 *pImage, int nSize, int *pWidth, int *pHeight, int *pActualComps, int req_comps, const char *pFilename);
55
56         enum
57         {
58             cWriteFlagIgnoreAlpha = 0x00000001,
59             cWriteFlagGrayscale = 0x00000002,
60             cWriteFlagJPEGH1V1 = 0x00010000,
61             cWriteFlagJPEGH2V1 = 0x00020000,
62             cWriteFlagJPEGH2V2 = 0x00040000,
63             cWriteFlagJPEGTwoPass = 0x00080000,
64             cWriteFlagJPEGNoChromaDiscrim = 0x00100000,
65             cWriteFlagJPEGQualityLevelMask = 0xFF000000,
66             cWriteFlagJPEGQualityLevelShift = 24,
67         };
68
69         enum
70         {
71             cJPEGMinQualityLevel = 1,
72             cJPEGMaxQualityLevel = 100
73         };
74
75         inline uint create_jpeg_write_flags(uint base_flags, uint quality_level)
76         {
77             VOGL_ASSERT(quality_level <= 100);
78             return base_flags | ((quality_level << cWriteFlagJPEGQualityLevelShift) & cWriteFlagJPEGQualityLevelMask);
79         }
80
81         const int cLumaComponentIndex = -1;
82
83         bool write_to_file(const char *pFilename, const image_u8 &img, uint write_flags = 0, int grayscale_comp_index = cLumaComponentIndex);
84         bool write_to_file(const char *pFilename, const image_f &img, const vec4F &l, const vec4F &h, uint write_flags = 0, int grayscale_comp_index = cLumaComponentIndex);
85
86         bool has_alpha(const image_u8 &img);
87         bool is_normal_map(const image_u8 &img, const char *pFilename = NULL);
88         void renorm_normal_map(image_u8 &img);
89
90         struct resample_params
91         {
92             resample_params()
93                 : m_dst_width(0),
94                   m_dst_height(0),
95                   m_pFilter("lanczos4"),
96                   m_filter_scale(1.0f),
97                   m_srgb(true),
98                   m_wrapping(false),
99                   m_first_comp(0),
100                   m_num_comps(4),
101                   m_source_gamma(2.2f), // 1.75f
102                   m_multithreaded(true),
103                   m_x_ofs(0.0f),
104                   m_y_ofs(0.0f)
105             {
106             }
107
108             uint m_dst_width;
109             uint m_dst_height;
110             const char *m_pFilter;
111             float m_filter_scale;
112             bool m_srgb;
113             bool m_wrapping;
114             uint m_first_comp;
115             uint m_num_comps;
116             float m_source_gamma;
117             bool m_multithreaded;
118             float m_x_ofs;
119             float m_y_ofs;
120         };
121
122         bool resample_single_thread(const image_u8 &src, image_u8 &dst, const resample_params &params);
123         bool resample_multithreaded(const image_u8 &src, image_u8 &dst, const resample_params &params);
124         bool resample(const image_u8 &src, image_u8 &dst, const resample_params &params);
125         bool resample(const image_f &src, image_f &dst, const resample_params &params);
126
127         bool compute_delta(image_u8 &dest, const image_u8 &a, const image_u8 &b, uint scale = 2);
128
129         class error_metrics
130         {
131         public:
132             error_metrics()
133             {
134                 utils::zero_this(this);
135             }
136
137             void print(const char *pName) const;
138
139             // If num_channels==0, luma error is computed.
140             // If pHist != NULL, it must point to a 256 entry array.
141             bool compute(const image_u8 &a, const image_u8 &b, uint first_channel, uint num_channels, bool average_component_error = true);
142
143             uint mMax;
144             double mMean;
145             double mMeanSquared;
146             double mRootMeanSquared;
147             double mPeakSNR;
148
149             double mSSIM;
150
151             inline bool operator==(const error_metrics &other) const
152             {
153                 return mPeakSNR == other.mPeakSNR;
154             }
155
156             inline bool operator<(const error_metrics &other) const
157             {
158                 return mPeakSNR < other.mPeakSNR;
159             }
160
161             inline bool operator>(const error_metrics &other) const
162             {
163                 return mPeakSNR > other.mPeakSNR;
164             }
165         };
166
167         void print_image_metrics(const image_u8 &src_img, const image_u8 &dst_img);
168
169         double compute_block_ssim(uint n, const uint8 *pX, const uint8 *pY);
170         double compute_ssim(const image_u8 &a, const image_u8 &b, int channel_index);
171         void print_ssim(const image_u8 &src_img, const image_u8 &dst_img);
172
173         enum conversion_type
174         {
175             cConversion_Invalid = -1,
176             cConversion_To_CCxY,
177             cConversion_From_CCxY,
178             cConversion_To_xGxR,
179             cConversion_From_xGxR,
180             cConversion_To_xGBR,
181             cConversion_From_xGBR,
182             cConversion_To_AGBR,
183             cConversion_From_AGBR,
184             cConversion_XY_to_XYZ,
185             cConversion_Y_To_A,
186             cConversion_A_To_RGBA,
187             cConversion_Y_To_RGB,
188             cConversion_To_Y,
189             cConversionTotal
190         };
191
192         void convert_image(image_u8 &img, conversion_type conv_type);
193
194         template <typename image_type>
195         inline uint8 *pack_image(const image_type &img, const pixel_packer &packer, uint &n)
196         {
197             n = 0;
198
199             if (!packer.is_valid())
200                 return NULL;
201
202             const uint width = img.get_width(), height = img.get_height();
203             uint dst_pixel_stride = packer.get_pixel_stride();
204             uint dst_pitch = width * dst_pixel_stride;
205
206             n = dst_pitch * height;
207
208             uint8 *pImage = static_cast<uint8 *>(vogl_malloc(n));
209
210             uint8 *pDst = pImage;
211             for (uint y = 0; y < height; y++)
212             {
213                 const typename image_type::color_t *pSrc = img.get_scanline(y);
214                 for (uint x = 0; x < width; x++)
215                     pDst = (uint8 *)packer.pack(*pSrc++, pDst);
216             }
217
218             return pImage;
219         }
220
221         image_utils::conversion_type get_conversion_type(bool cooking, pixel_format fmt);
222
223         image_utils::conversion_type get_image_conversion_type_from_vogl_format(vogl_format fmt);
224
225         double compute_std_dev(uint n, const color_quad_u8 *pPixels, uint first_channel, uint num_channels);
226
227         uint8 *read_image_from_memory(const uint8 *pImage, int nSize, int *pWidth, int *pHeight, int *pActualComps, int req_comps, const char *pFilename);
228
229         template <uint M>
230         void create_normalized_convolution_matrix(matrix<M, M, float> &weights, const float *pSrc_weights)
231         {
232             if ((M & 1) == 0)
233             {
234                 VOGL_ASSERT_ALWAYS;
235                 return;
236             }
237
238             const int HM = M / 2;
239             VOGL_ASSERT(HM * 2 + 1 == M);
240
241             float tw = 0.0f;
242             for (int yd = -HM; yd <= HM; yd++)
243             {
244                 float yw = pSrc_weights[yd + HM];
245                 for (int xd = -HM; xd <= HM; xd++)
246                 {
247                     float xw = pSrc_weights[xd + HM];
248                     float w = xw * yw;
249                     weights[yd + HM][xd + HM] = w;
250                     tw += w;
251                 }
252             }
253
254             VOGL_ASSERT(tw > 0.0f);
255             weights /= tw;
256         }
257
258         inline void convolution_filter(image_u8 &dst, const image_u8 &src, const float *pWeights, uint M, uint N, bool wrapping);
259
260         // M = num rows (Y), N = num cols (X)
261         template <uint M, uint N>
262         void convolution_filter(image_u8 &dst, const image_u8 &src, const matrix<M, N, float> &weights, bool wrapping)
263         {
264             convolution_filter(dst, src, &weights[0][0], M, N, wrapping);
265         }
266
267         void convolution_filter(image_f &dst, const image_f &src, const float *pWeights, uint M, uint N, bool wrapping);
268
269         // M = num rows (Y), N = num cols (X)
270         template <uint M, uint N>
271         inline void convolution_filter(image_f &dst, const image_f &src, const matrix<M, N, float> &weights, bool wrapping)
272         {
273             convolution_filter(dst, src, &weights[0][0], M, N, wrapping);
274         }
275
276         bool forward_fourier_transform(const image_u8 &src, image_u8 &dst, uint comp_mask = 0xF);
277
278         void gaussian_filter(image_u8 &dst, const image_u8 &orig_img, uint width_divisor, uint height_divisor, uint odd_filter_width, float sigma_sqr, bool wrapping);
279
280     } // namespace image_utils
281
282 } // namespace vogl