1 | /* |
2 | Jonathan Dummer |
3 | |
4 | image helper functions |
5 | |
6 | MIT license |
7 | */ |
8 | |
9 | #include "image_helper.h" |
10 | #include <stdlib.h> |
11 | #include <math.h> |
12 | |
13 | /* Upscaling the image uses simple bilinear interpolation */ |
14 | int |
15 | up_scale_image |
16 | ( |
17 | const unsigned char* const orig, |
18 | int width, int height, int channels, |
19 | unsigned char* resampled, |
20 | int resampled_width, int resampled_height |
21 | ) |
22 | { |
23 | float dx, dy; |
24 | int x, y, c; |
25 | |
26 | /* error(s) check */ |
27 | if ( (width < 1) || (height < 1) || |
28 | (resampled_width < 2) || (resampled_height < 2) || |
29 | (channels < 1) || |
30 | (NULL == orig) || (NULL == resampled) ) |
31 | { |
32 | /* signify badness */ |
33 | return 0; |
34 | } |
35 | /* |
36 | for each given pixel in the new map, find the exact location |
37 | from the original map which would contribute to this guy |
38 | */ |
39 | dx = (width - 1.0f) / (resampled_width - 1.0f); |
40 | dy = (height - 1.0f) / (resampled_height - 1.0f); |
41 | for ( y = 0; y < resampled_height; ++y ) |
42 | { |
43 | /* find the base y index and fractional offset from that */ |
44 | float sampley = y * dy; |
45 | int inty = (int)sampley; |
46 | /* if( inty < 0 ) { inty = 0; } else */ |
47 | if( inty > height - 2 ) { inty = height - 2; } |
48 | sampley -= inty; |
49 | for ( x = 0; x < resampled_width; ++x ) |
50 | { |
51 | float samplex = x * dx; |
52 | int intx = (int)samplex; |
53 | int base_index; |
54 | /* find the base x index and fractional offset from that */ |
55 | /* if( intx < 0 ) { intx = 0; } else */ |
56 | if( intx > width - 2 ) { intx = width - 2; } |
57 | samplex -= intx; |
58 | /* base index into the original image */ |
59 | base_index = (inty * width + intx) * channels; |
60 | for ( c = 0; c < channels; ++c ) |
61 | { |
62 | /* do the sampling */ |
63 | float value = 0.5f; |
64 | value += orig[base_index] |
65 | *(1.0f-samplex)*(1.0f-sampley); |
66 | value += orig[base_index+channels] |
67 | *(samplex)*(1.0f-sampley); |
68 | value += orig[base_index+width*channels] |
69 | *(1.0f-samplex)*(sampley); |
70 | value += orig[base_index+width*channels+channels] |
71 | *(samplex)*(sampley); |
72 | /* move to the next channel */ |
73 | ++base_index; |
74 | /* save the new value */ |
75 | resampled[y*resampled_width*channels+x*channels+c] = |
76 | (unsigned char)(value); |
77 | } |
78 | } |
79 | } |
80 | /* done */ |
81 | return 1; |
82 | } |
83 | |
84 | int |
85 | mipmap_image |
86 | ( |
87 | const unsigned char* const orig, |
88 | int width, int height, int channels, |
89 | unsigned char* resampled, |
90 | int block_size_x, int block_size_y |
91 | ) |
92 | { |
93 | int mip_width, mip_height; |
94 | int i, j, c; |
95 | |
96 | /* error check */ |
97 | if( (width < 1) || (height < 1) || |
98 | (channels < 1) || (orig == NULL) || |
99 | (resampled == NULL) || |
100 | (block_size_x < 1) || (block_size_y < 1) ) |
101 | { |
102 | /* nothing to do */ |
103 | return 0; |
104 | } |
105 | mip_width = width / block_size_x; |
106 | mip_height = height / block_size_y; |
107 | if( mip_width < 1 ) |
108 | { |
109 | mip_width = 1; |
110 | } |
111 | if( mip_height < 1 ) |
112 | { |
113 | mip_height = 1; |
114 | } |
115 | for( j = 0; j < mip_height; ++j ) |
116 | { |
117 | for( i = 0; i < mip_width; ++i ) |
118 | { |
119 | for( c = 0; c < channels; ++c ) |
120 | { |
121 | const int index = (j*block_size_y)*width*channels + (i*block_size_x)*channels + c; |
122 | int sum_value; |
123 | int u,v; |
124 | int u_block = block_size_x; |
125 | int v_block = block_size_y; |
126 | int block_area; |
127 | /* do a bit of checking so we don't over-run the boundaries |
128 | (necessary for non-square textures!) */ |
129 | if( block_size_x * (i+1) > width ) |
130 | { |
131 | u_block = width - i*block_size_y; |
132 | } |
133 | if( block_size_y * (j+1) > height ) |
134 | { |
135 | v_block = height - j*block_size_y; |
136 | } |
137 | block_area = u_block*v_block; |
138 | /* for this pixel, see what the average |
139 | of all the values in the block are. |
140 | note: start the sum at the rounding value, not at 0 */ |
141 | sum_value = block_area >> 1; |
142 | for( v = 0; v < v_block; ++v ) |
143 | for( u = 0; u < u_block; ++u ) |
144 | { |
145 | sum_value += orig[index + v*width*channels + u*channels]; |
146 | } |
147 | resampled[j*mip_width*channels + i*channels + c] = sum_value / block_area; |
148 | } |
149 | } |
150 | } |
151 | return 1; |
152 | } |
153 | |
154 | int |
155 | scale_image_RGB_to_NTSC_safe |
156 | ( |
157 | unsigned char* orig, |
158 | int width, int height, int channels |
159 | ) |
160 | { |
161 | const float scale_lo = 16.0f - 0.499f; |
162 | const float scale_hi = 235.0f + 0.499f; |
163 | int i, j; |
164 | int nc = channels; |
165 | unsigned char scale_LUT[256]; |
166 | /* error check */ |
167 | if( (width < 1) || (height < 1) || |
168 | (channels < 1) || (orig == NULL) ) |
169 | { |
170 | /* nothing to do */ |
171 | return 0; |
172 | } |
173 | /* set up the scaling Look Up Table */ |
174 | for( i = 0; i < 256; ++i ) |
175 | { |
176 | scale_LUT[i] = (unsigned char)((scale_hi - scale_lo) * i / 255.0f + scale_lo); |
177 | } |
178 | /* for channels = 2 or 4, ignore the alpha component */ |
179 | nc -= 1 - (channels & 1); |
180 | /* OK, go through the image and scale any non-alpha components */ |
181 | for( i = 0; i < width*height*channels; i += channels ) |
182 | { |
183 | for( j = 0; j < nc; ++j ) |
184 | { |
185 | orig[i+j] = scale_LUT[orig[i+j]]; |
186 | } |
187 | } |
188 | return 1; |
189 | } |
190 | |
191 | unsigned char clamp_byte( int x ) { return ( (x) < 0 ? (0) : ( (x) > 255 ? 255 : (x) ) ); } |
192 | |
193 | /* |
194 | This function takes the RGB components of the image |
195 | and converts them into YCoCg. 3 components will be |
196 | re-ordered to CoYCg (for optimum DXT1 compression), |
197 | while 4 components will be ordered CoCgAY (for DXT5 |
198 | compression). |
199 | */ |
200 | int |
201 | convert_RGB_to_YCoCg |
202 | ( |
203 | unsigned char* orig, |
204 | int width, int height, int channels |
205 | ) |
206 | { |
207 | int i; |
208 | /* error check */ |
209 | if( (width < 1) || (height < 1) || |
210 | (channels < 3) || (channels > 4) || |
211 | (orig == NULL) ) |
212 | { |
213 | /* nothing to do */ |
214 | return -1; |
215 | } |
216 | /* do the conversion */ |
217 | if( channels == 3 ) |
218 | { |
219 | for( i = 0; i < width*height*3; i += 3 ) |
220 | { |
221 | int r = orig[i+0]; |
222 | int g = (orig[i+1] + 1) >> 1; |
223 | int b = orig[i+2]; |
224 | int tmp = (2 + r + b) >> 2; |
225 | /* Co */ |
226 | orig[i+0] = clamp_byte( 128 + ((r - b + 1) >> 1) ); |
227 | /* Y */ |
228 | orig[i+1] = clamp_byte( g + tmp ); |
229 | /* Cg */ |
230 | orig[i+2] = clamp_byte( 128 + g - tmp ); |
231 | } |
232 | } else |
233 | { |
234 | for( i = 0; i < width*height*4; i += 4 ) |
235 | { |
236 | int r = orig[i+0]; |
237 | int g = (orig[i+1] + 1) >> 1; |
238 | int b = orig[i+2]; |
239 | unsigned char a = orig[i+3]; |
240 | int tmp = (2 + r + b) >> 2; |
241 | /* Co */ |
242 | orig[i+0] = clamp_byte( 128 + ((r - b + 1) >> 1) ); |
243 | /* Cg */ |
244 | orig[i+1] = clamp_byte( 128 + g - tmp ); |
245 | /* Alpha */ |
246 | orig[i+2] = a; |
247 | /* Y */ |
248 | orig[i+3] = clamp_byte( g + tmp ); |
249 | } |
250 | } |
251 | /* done */ |
252 | return 0; |
253 | } |
254 | |
255 | /* |
256 | This function takes the YCoCg components of the image |
257 | and converts them into RGB. See above. |
258 | */ |
259 | int |
260 | convert_YCoCg_to_RGB |
261 | ( |
262 | unsigned char* orig, |
263 | int width, int height, int channels |
264 | ) |
265 | { |
266 | int i; |
267 | /* error check */ |
268 | if( (width < 1) || (height < 1) || |
269 | (channels < 3) || (channels > 4) || |
270 | (orig == NULL) ) |
271 | { |
272 | /* nothing to do */ |
273 | return -1; |
274 | } |
275 | /* do the conversion */ |
276 | if( channels == 3 ) |
277 | { |
278 | for( i = 0; i < width*height*3; i += 3 ) |
279 | { |
280 | int co = orig[i+0] - 128; |
281 | int y = orig[i+1]; |
282 | int cg = orig[i+2] - 128; |
283 | /* R */ |
284 | orig[i+0] = clamp_byte( y + co - cg ); |
285 | /* G */ |
286 | orig[i+1] = clamp_byte( y + cg ); |
287 | /* B */ |
288 | orig[i+2] = clamp_byte( y - co - cg ); |
289 | } |
290 | } else |
291 | { |
292 | for( i = 0; i < width*height*4; i += 4 ) |
293 | { |
294 | int co = orig[i+0] - 128; |
295 | int cg = orig[i+1] - 128; |
296 | unsigned char a = orig[i+2]; |
297 | int y = orig[i+3]; |
298 | /* R */ |
299 | orig[i+0] = clamp_byte( y + co - cg ); |
300 | /* G */ |
301 | orig[i+1] = clamp_byte( y + cg ); |
302 | /* B */ |
303 | orig[i+2] = clamp_byte( y - co - cg ); |
304 | /* A */ |
305 | orig[i+3] = a; |
306 | } |
307 | } |
308 | /* done */ |
309 | return 0; |
310 | } |
311 | |
312 | float |
313 | find_max_RGBE |
314 | ( |
315 | unsigned char *image, |
316 | int width, int height |
317 | ) |
318 | { |
319 | float max_val = 0.0f; |
320 | unsigned char *img = image; |
321 | int i, j; |
322 | for( i = width * height; i > 0; --i ) |
323 | { |
324 | /* float scale = powf( 2.0f, img[3] - 128.0f ) / 255.0f; */ |
325 | float scale = (float)ldexp( 1.0f / 255.0f, (int)(img[3]) - 128 ); |
326 | for( j = 0; j < 3; ++j ) |
327 | { |
328 | if( img[j] * scale > max_val ) |
329 | { |
330 | max_val = img[j] * scale; |
331 | } |
332 | } |
333 | /* next pixel */ |
334 | img += 4; |
335 | } |
336 | return max_val; |
337 | } |
338 | |
339 | int |
340 | RGBE_to_RGBdivA |
341 | ( |
342 | unsigned char *image, |
343 | int width, int height, |
344 | int rescale_to_max |
345 | ) |
346 | { |
347 | /* local variables */ |
348 | int i, iv; |
349 | unsigned char *img = image; |
350 | float scale = 1.0f; |
351 | /* error check */ |
352 | if( (!image) || (width < 1) || (height < 1) ) |
353 | { |
354 | return 0; |
355 | } |
356 | /* convert (note: no negative numbers, but 0.0 is possible) */ |
357 | if( rescale_to_max ) |
358 | { |
359 | scale = 255.0f / find_max_RGBE( image, width, height ); |
360 | } |
361 | for( i = width * height; i > 0; --i ) |
362 | { |
363 | /* decode this pixel, and find the max */ |
364 | float r,g,b,e, m; |
365 | /* e = scale * powf( 2.0f, img[3] - 128.0f ) / 255.0f; */ |
366 | e = (float)(scale * ldexp( 1.0f / 255.0f, (int)(img[3]) - 128 )); |
367 | r = e * img[0]; |
368 | g = e * img[1]; |
369 | b = e * img[2]; |
370 | m = (r > g) ? r : g; |
371 | m = (b > m) ? b : m; |
372 | /* and encode it into RGBdivA */ |
373 | iv = (m != 0.0f) ? (int)(255.0f / m) : 1; |
374 | iv = (iv < 1) ? 1 : iv; |
375 | img[3] = (iv > 255) ? 255 : iv; |
376 | iv = (int)(img[3] * r + 0.5f); |
377 | img[0] = (iv > 255) ? 255 : iv; |
378 | iv = (int)(img[3] * g + 0.5f); |
379 | img[1] = (iv > 255) ? 255 : iv; |
380 | iv = (int)(img[3] * b + 0.5f); |
381 | img[2] = (iv > 255) ? 255 : iv; |
382 | /* and on to the next pixel */ |
383 | img += 4; |
384 | } |
385 | return 1; |
386 | } |
387 | |
388 | int |
389 | RGBE_to_RGBdivA2 |
390 | ( |
391 | unsigned char *image, |
392 | int width, int height, |
393 | int rescale_to_max |
394 | ) |
395 | { |
396 | /* local variables */ |
397 | int i, iv; |
398 | unsigned char *img = image; |
399 | float scale = 1.0f; |
400 | /* error check */ |
401 | if( (!image) || (width < 1) || (height < 1) ) |
402 | { |
403 | return 0; |
404 | } |
405 | /* convert (note: no negative numbers, but 0.0 is possible) */ |
406 | if( rescale_to_max ) |
407 | { |
408 | scale = 255.0f * 255.0f / find_max_RGBE( image, width, height ); |
409 | } |
410 | for( i = width * height; i > 0; --i ) |
411 | { |
412 | /* decode this pixel, and find the max */ |
413 | float r,g,b,e, m; |
414 | /* e = scale * powf( 2.0f, img[3] - 128.0f ) / 255.0f; */ |
415 | e = (float)(scale * ldexp( 1.0f / 255.0f, (int)(img[3]) - 128 )); |
416 | r = e * img[0]; |
417 | g = e * img[1]; |
418 | b = e * img[2]; |
419 | m = (r > g) ? r : g; |
420 | m = (b > m) ? b : m; |
421 | /* and encode it into RGBdivA */ |
422 | iv = (m != 0.0f) ? (int)sqrtf( 255.0f * 255.0f / m ) : 1; |
423 | iv = (iv < 1) ? 1 : iv; |
424 | img[3] = (iv > 255) ? 255 : iv; |
425 | iv = (int)(img[3] * img[3] * r / 255.0f + 0.5f); |
426 | img[0] = (iv > 255) ? 255 : iv; |
427 | iv = (int)(img[3] * img[3] * g / 255.0f + 0.5f); |
428 | img[1] = (iv > 255) ? 255 : iv; |
429 | iv = (int)(img[3] * img[3] * b / 255.0f + 0.5f); |
430 | img[2] = (iv > 255) ? 255 : iv; |
431 | /* and on to the next pixel */ |
432 | img += 4; |
433 | } |
434 | return 1; |
435 | } |
436 | |