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 */
14int
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
84int
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
154int
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
191unsigned 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*/
200int
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*/
259int
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
312float
313find_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
339int
340RGBE_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
388int
389RGBE_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