gKit2 light
Loading...
Searching...
No Matches
image_io.cpp
1
2#include <cfloat>
3#include <string>
4
5#include "color.h"
6#include "image.h"
7#include "image_io.h"
8
9#define STB_IMAGE_IMPLEMENTATION
10#include "stb_image.h"
11
12#define STB_IMAGE_WRITE_IMPLEMENTATION
13#include "stb_image_write.h"
14
15Image srgb( const Image& image )
16{
17 Image tmp(image.width(), image.height());
18
19 for(unsigned i= 0; i < image.size(); i++)
20 tmp(i)= srgb(image(i));
21
22 return tmp;
23}
24
25Image linear( const Image& image )
26{
27 Image tmp(image.width(), image.height());
28
29 for(unsigned i= 0; i < image.size(); i++)
30 tmp(i)= linear(image(i));
31
32 return tmp;
33}
34
35float range( const Image& image )
36{
37 float gmin= FLT_MAX;
38 float gmax= 0;
39 for(unsigned i= 0; i < image.size(); i++)
40 {
41 Color color= image(i);
42 float g= color.r + color.g + color.b;
43
44 if(g < gmin) gmin= g;
45 if(g > gmax) gmax= g;
46 }
47
49 int bins[100] = {};
50 for(unsigned i= 0; i < image.size(); i++)
51 {
52 Color color= image(i);
53 float g= color.r + color.g + color.b;
54
55 int b= (g - gmin) * 100 / (gmax - gmin);
56 if(b >= 99) b= 99;
57 if(b < 0) b= 0;
58 bins[b]++;
59 }
60
61 float saturation= 0;
62 float qbins= 0;
63 for(unsigned i= 0; i < 100; i++)
64 {
65 qbins= qbins + float(bins[i]) / float(image.size());
66 if(qbins > .75f)
67 return gmin + float(i+1) / 100 * (gmax - gmin);
68 }
69
70 return gmax;
71}
72
73Image tone( const Image& image, const float saturation )
74{
75 Image tmp(image.width(), image.height());
76
77 float k= 1 / std::pow(saturation, 1 / 2.2f);
78 for(unsigned i= 0; i < image.size(); i++)
79 {
80 Color color= image(i);
81 if(std::isnan(color.r) || std::isnan(color.g) || std::isnan(color.b))
82 // marque les pixels pourris avec une couleur improbable...
83 color= Color(1, 0, 1);
84 else
85 // sinon transformation rgb -> srgb
86 color= k * srgb(color);
87
88 tmp(i)= Color(color, 1);
89 }
90
91 return tmp;
92}
93
94
95Image mipmap( const Image& image )
96{
97 unsigned w= std::max(unsigned(1), image.width() / 2);
98 unsigned h= std::max(unsigned(1), image.height() / 2);
99
100 Image tmp(w, h);
101
102 for(unsigned py= 0; py < h; py++)
103 for(unsigned px= 0; px < w; px++)
104 {
105 unsigned x= 2*px;
106 unsigned y= 2*py;
107 tmp(px, py)= (image(x, y) + image(x +1, y) + image(x +1, y +1) + image(x, y +1)) / 4;
108 }
109
110 return tmp;
111}
112
113Image flipY( const Image& image )
114{
115 // flip de l'image : origine en haut a gauche
116 Image flip(image.width(), image.height());
117
118 for(unsigned y= 0; y < image.height(); y++)
119 for(unsigned x= 0; x < image.width(); x++)
120 {
121 unsigned s= image.offset(x, y);
122 unsigned d= flip.offset(x, flip.height() - y -1);
123
124 flip(d)= image(s);
125 }
126
127 return flip;
128}
129
130Image flipX( const Image& image )
131{
132 Image flip(image.width(), image.height());
133
134 for(unsigned y= 0; y < image.height(); y++)
135 for(unsigned x= 0; x < image.width(); x++)
136 {
137 unsigned s= image.offset(x, y);
138 unsigned d= flip.offset(flip.width() -x -1, y);
139
140 flip(d)= image(s);
141 }
142
143 return flip;
144}
145
146Image copy( const Image& image, const unsigned xmin, const unsigned ymin, const unsigned width, const unsigned height )
147{
148 Image copy(width, height);
149
150 for(unsigned y= 0; y < height; y++)
151 for(unsigned x= 0; x < width; x++)
152 {
153 unsigned s= image.offset(xmin+x, ymin+y);
154 unsigned d= copy.offset(x, y);
155
156 copy(d)= image(s);
157 }
158
159 return copy;
160}
161
162Image& blit( Image& image, const unsigned xmin, const unsigned ymin, const Image& pixels )
163{
164 if(image.size() == 0)
165 return image;
166 if(pixels.size() == 0)
167 return image;
168
169 unsigned xmax= std::min( image.width(), xmin + pixels.width() );
170 unsigned ymax= std::min( image.height(), ymin + pixels.height() );
171 unsigned w= xmax - xmin;
172 unsigned h= ymax - ymin;
173
174 for(unsigned y= 0; y < h; y++)
175 for(unsigned x= 0; x < w; x++)
176 image(x + xmin, y + ymin)= pixels(x, y);
177
178 return image;
179}
180
181
182unsigned read_image_size( const char *filename )
183{
184 int w, h, c;
185 if(!stbi_info(filename, &w, &h, &c))
186 return 0;
187
188 return sizeof(Color)*w*h;
189}
190
191Image read_image( const char *filename, const bool flipY )
192{
193 stbi_ldr_to_hdr_scale(1);
194 stbi_ldr_to_hdr_gamma(1);
195 stbi_set_flip_vertically_on_load(flipY);
196
197 int width, height, channels;
198 float *data= stbi_loadf(filename, &width, &height, &channels, 4);
199 if(!data)
200 {
201 printf("[error] loading '%s'...\n", filename);
202 return {};
203 }
204
205 Image image(width, height);
206 for(unsigned i= 0, offset= 0; i < image.size(); i++, offset+= 4)
207 image(i)= Color( data[offset], data[offset + 1], data[offset + 2], data[offset + 3]);
208
209 stbi_image_free(data);
210 return image;
211}
212
213Image read_image_hdr( const char *filename, const bool flipY )
214{
215 return read_image(filename, flipY );
216}
217
218
219bool write_image_png( const Image& image, const char *filename, const bool flipY )
220{
221 if(image.size() == 0)
222 return false;
223
224 std::vector<unsigned char> tmp(image.width()*image.height()*4);
225 for(unsigned i= 0, offset= 0; i < image.size(); i++, offset+= 4)
226 {
227 Color pixel= image(i) * 255;
228 tmp[offset ]= clamp(pixel.r, 0, 255);
229 tmp[offset +1]= clamp(pixel.g, 0, 255);
230 tmp[offset +2]= clamp(pixel.b, 0, 255);
231 tmp[offset +3]= clamp(pixel.a, 0, 255);
232 }
233
234 stbi_flip_vertically_on_write(flipY);
235 return stbi_write_png(filename, image.width(), image.height(), 4, tmp.data(), image.width() * 4) != 0;
236}
237
238bool write_image( const Image& image, const char *filename, const bool flipY )
239{
240 return write_image_png(image, filename, flipY );
241}
242
243bool write_image_bmp( const Image& image, const char *filename, const bool flipY )
244{
245 if(image.size() == 0)
246 return false;
247
248 std::vector<unsigned char> tmp(image.width()*image.height()*4);
249 for(unsigned i= 0, offset= 0; i < image.size(); i++, offset+= 4)
250 {
251 Color pixel= image(i) * 255;
252 tmp[offset ]= clamp(pixel.r, 0, 255);
253 tmp[offset +1]= clamp(pixel.g, 0, 255);
254 tmp[offset +2]= clamp(pixel.b, 0, 255);
255 tmp[offset +3]= clamp(pixel.a, 0, 255);
256 }
257
258 stbi_flip_vertically_on_write(flipY);
259 return stbi_write_bmp(filename, image.width(), image.height(), 4, tmp.data()) != 0;
260}
261
262bool write_image_hdr( const Image& image, const char *filename, const bool flipY )
263{
264 if(image.size() == 0)
265 return false;
266
267 stbi_flip_vertically_on_write(flipY);
268 return stbi_write_hdr(filename, image.width(), image.height(), 4, image.data()) != 0;
269}
270
271bool write_image_preview( const Image& image, const char *filename, const bool flipY )
272{
273 if(image.size() == 0)
274 return false;
275
276 Image tmp= tone(image, range(image));
277 return write_image_png(tmp, filename, flipY);
278}
279
280
281ImageData image_data( unsigned char *data,const int width, const int height, const int channels )
282{
283 int n= std::max(3, channels);
284 ImageData image(width, height, n);
285
286 if(channels == 4)
287 {
288 for(int i= 0; i < width*height*4; i+= 4)
289 {
290 image.pixels[i]= data[i];
291 image.pixels[i+1]= data[i+1];
292 image.pixels[i+2]= data[i+2];
293 image.pixels[i+3]= data[i+3];
294 }
295 }
296 else if(channels == 3)
297 {
298 for(int i= 0; i < width*height*3; i+= 3)
299 {
300 image.pixels[i]= data[i];
301 image.pixels[i+1]= data[i+1];
302 image.pixels[i+2]= data[i+2];
303 }
304 }
305 else
306 {
307 int k= 0;
308 for(int i= 0; i < width*height*3; i+= 3)
309 {
310 unsigned char r= 0;
311 unsigned char g= 0;
312 unsigned char b= 0;
313
314 if(n >= 1) { r= data[k++]; g= r; b= r; } // rgb= rrr
315 if(n >= 2) { g= data[k++]; b= 0; } // rgb= rg0
316 if(n >= 3) { b= data[k++]; } // rgb
317
318 image.pixels[i]= r;
319 image.pixels[i+1]= g;
320 image.pixels[i+2]= b;
321 }
322 }
323
324 stbi_image_free(data);
325 return image;
326}
327
328ImageData read_image_data( const void *buffer, const unsigned size, const bool flipY )
329{
330 stbi_set_flip_vertically_on_load(flipY);
331
332 int width, height, channels;
333 unsigned char *data= stbi_load_from_memory((const unsigned char *) buffer, size, &width, &height, &channels, 0);
334 if(!data)
335 {
336 printf("[error] reading buffer image...\n");
337 return {};
338 }
339
340 return image_data(data, width, height, channels);
341}
342
343ImageData read_image_data( const char *filename, const bool flipY )
344{
345 stbi_set_flip_vertically_on_load(flipY);
346
347 int width, height, channels;
348 unsigned char *data= stbi_load(filename, &width, &height, &channels, 0);
349 if(!data)
350 {
351 printf("[error] loading '%s'...\n", filename);
352 return {};
353 }
354
355 return image_data(data, width, height, channels);
356}
357
358
359int write_image_data( const ImageData& image, const char *filename, const bool flipY )
360{
361 if(image.size != 1)
362 {
363 printf("[error] writing color image '%s'... not an 8 bits image.\n", filename);
364 return -1;
365 }
366
367 stbi_flip_vertically_on_write(flipY);
368
369 int code= 0;
370 if(std::string(filename).rfind(".png") != std::string::npos)
371 code= stbi_write_png(filename, image.width, image.height, image.channels, image.pixels.data(), image.width * image.channels) != 0;
372 else if(std::string(filename).rfind(".bmp") != std::string::npos)
373 code= stbi_write_bmp(filename, image.width, image.height, image.channels, image.pixels.data()) != 0;
374
375 else
376 {
377 printf("[error] writing color image '%s'... not a .png / .bmp image.\n", filename);
378 return -1;
379 }
380
381 if(code > 0)
382 return 0;
383
384 printf("[error] writing color image '%s'...\n", filename);
385 return -1;
386}
387
388
389
390ImageData srgb( const ImageData& image )
391{
392 ImageData tmp(image.width, image.height, image.channels);
393
394 for(unsigned i= 0; i < image.pixels.size(); i+= image.channels)
395 {
396 Color pixel= srgb( Color( image.pixels[i], image.pixels[i+1], image.pixels[i+2], (image.channels > 3) ? image.pixels[i+3] : 255 ) / 255 );
397
398 tmp.pixels[i]= clamp(pixel.r * 255, 0, 255);
399 tmp.pixels[i +1]= clamp(pixel.g * 255, 0, 255);
400 tmp.pixels[i +2]= clamp(pixel.b * 255, 0, 255);
401 if(image.channels > 3)
402 tmp.pixels[i +3]= clamp(pixel.a * 255, 0, 255);
403 }
404
405 return tmp;
406}
407
409{
410 ImageData tmp(image.width, image.height, image.channels);
411
412 for(unsigned i= 0; i < image.pixels.size(); i+= image.channels)
413 {
414 Color pixel= linear( Color( image.pixels[i], image.pixels[i+1], image.pixels[i+2], (image.channels > 3) ? image.pixels[i+3] : 255 ) / 255 );
415
416 tmp.pixels[i]= clamp(pixel.r * 255, 0, 255);
417 tmp.pixels[i +1]= clamp(pixel.g * 255, 0, 255);
418 tmp.pixels[i +2]= clamp(pixel.b * 255, 0, 255);
419 if(image.channels > 3)
420 tmp.pixels[i +3]= clamp(pixel.a * 255, 0, 255);
421 }
422
423 return tmp;
424}
425
426ImageData flipY( const ImageData& image )
427{
428 // flip de l'image : origine en haut a gauche
429 ImageData flip(image.width, image.height, image.channels);
430
431 for(unsigned y= 0; y < image.height; y++)
432 for(unsigned x= 0; x < image.width; x++)
433 {
434 unsigned s= image.offset(x, y);
435 unsigned d= flip.offset(x, flip.height - y -1);
436
437 for(unsigned i= 0; i < image.channels; i++)
438 flip.pixels[d +i]= image.pixels[s +i];
439 }
440
441 return flip;
442}
443
444ImageData flipX( const ImageData& image )
445{
446 ImageData flip(image.width, image.height, image.channels);
447
448 for(unsigned y= 0; y < image.height; y++)
449 for(unsigned x= 0; x < image.width; x++)
450 {
451 unsigned s= image.offset(x, y);
452 unsigned d= flip.offset(flip.width -x -1, y);
453
454 for(unsigned i= 0; i < image.channels; i++)
455 flip.pixels[d +i]= image.pixels[s +i];
456 }
457
458 return flip;
459}
460
461ImageData copy( const ImageData& image, const unsigned xmin, const unsigned ymin, const unsigned width, const unsigned height )
462{
463 ImageData copy(width, height, image.channels);
464
465 for(unsigned y= 0; y < height; y++)
466 for(unsigned x= 0; x < width; x++)
467 {
468 unsigned s= image.offset(xmin +x, ymin +y);
469 unsigned d= copy.offset(x, y);
470
471 for(unsigned i= 0; i < image.channels; i++)
472 copy.pixels[d +i]= image.pixels[s +i];
473 }
474
475 return copy;
476}
477
478
479ImageData& blit( ImageData& image, const unsigned xmin, const unsigned ymin, const ImageData& pixels )
480{
481 if(image.pixels.size() == 0)
482 return image;
483 if(pixels.pixels.size() == 0)
484 return image;
485 if(image.channels != pixels.channels)
486 return image;
487
488 unsigned xmax= std::min( image.width, xmin + pixels.width );
489 unsigned ymax= std::min( image.height, ymin + pixels.height );
490 unsigned w= xmax - xmin;
491 unsigned h= ymax - ymin;
492
493 for(unsigned y= 0; y < h; y++)
494 for(unsigned x= 0; x < w; x++)
495 {
496 unsigned char *dst= image.pixels.data() + image.offset(x + xmin, y + ymin);
497 const unsigned char *src= pixels.pixels.data() + pixels.offset(x, y);
498 for(unsigned i= 0; i < pixels.channels; i++)
499 dst[i]= src[i];
500 }
501
502 return image;
503}
504
506{
507 unsigned w= std::max(unsigned(1), image.width / 2);
508 unsigned h= std::max(unsigned(1), image.height / 2);
509
510 ImageData tmp(w, h, image.channels);
511
512 for(unsigned py= 0; py < h; py++)
513 for(unsigned px= 0; px < w; px++)
514 {
515 unsigned x= 2*px;
516 unsigned y= 2*py;
517 unsigned d= tmp.offset(px, py);
518
519 for(unsigned i= 0; i < image.channels; i++)
520 tmp.pixels[d +i]= (
521 image.pixels[image.offset(x, y) +i]
522 + image.pixels[image.offset(x+1, y) +i]
523 + image.pixels[image.offset(x, y+1) +i]
524 + image.pixels[image.offset(x+1, y+1) +i] ) / 4;
525 }
526
527 return tmp;
528}
529
530int miplevels( const int width, const int height )
531{
532 int w= width;
533 int h= height;
534 int levels= 1;
535 while(w > 1 || h > 1)
536 {
537 w= std::max(1, w / 2);
538 h= std::max(1, h / 2);
539 levels= levels + 1;
540 }
541
542 return levels;
543}
representation d'une image.
Definition image.h:21
unsigned width() const
renvoie la largeur de l'image.
Definition image.h:100
unsigned height() const
renvoie la hauteur de l'image.
Definition image.h:102
unsigned offset(const int x, const int y) const
Definition image.h:108
void printf(Text &text, const int px, const int py, const char *format,...)
affiche un texte a la position x, y. meme utilisation que printf().
Definition text.cpp:140
bool write_image_png(const Image &image, const char *filename, const bool flipY)
enregistre une image au format .png
Definition image_io.cpp:219
bool write_image_preview(const Image &image, const char *filename, const bool flipY)
raccourci pour write_image_png(tone(image, range(image)), "image.png")
Definition image_io.cpp:271
bool write_image_hdr(const Image &image, const char *filename, const bool flipY)
enregistre une image au format .hdr
Definition image_io.cpp:262
unsigned read_image_size(const char *filename)
renvoie la taille en octets de l'image chargee. ou 0 si erreur.
Definition image_io.cpp:182
bool write_image_bmp(const Image &image, const char *filename, const bool flipY)
enregistre une image au format .bmp
Definition image_io.cpp:243
Image & blit(Image &image, const unsigned xmin, const unsigned ymin, const Image &pixels)
remplace un bout d'image par une autre. copie l'image pixels dans image [xmin, xmin+pixels....
Definition image_io.cpp:162
Image mipmap(const Image &image)
reduit une image.
Definition image_io.cpp:95
Image flipY(const Image &image)
retourne l'image
Definition image_io.cpp:113
Image flipX(const Image &image)
retourne l'image
Definition image_io.cpp:130
Image read_image(const char *filename, const bool flipY)
Definition image_io.cpp:191
int write_image_data(const ImageData &image, const char *filename, const bool flipY)
enregistre des donnees dans un fichier png.
Definition image_io.cpp:359
Image linear(const Image &image)
transformation couleur : srgb vers rgb lineaire
Definition image_io.cpp:25
ImageData read_image_data(const void *buffer, const unsigned size, const bool flipY)
charge les donnees d'un fichier png stocke en memoire. renvoie une image initialisee par defaut en ca...
Definition image_io.cpp:328
bool write_image(const Image &image, const char *filename, const bool flipY)
enregistre une image au format .png
Definition image_io.cpp:238
Image copy(const Image &image, const unsigned xmin, const unsigned ymin, const unsigned width, const unsigned height)
renvoie un bloc de l'image
Definition image_io.cpp:146
int miplevels(const int width, const int height)
renvoie le nombre de mipmap d'une image width x height.
Definition image_io.cpp:530
Image srgb(const Image &image)
transformation couleur : rgb lineaire vers srgb
Definition image_io.cpp:15
representation d'une couleur (rgba) transparente ou opaque.
Definition color.h:14
stockage temporaire des donnees d'une image.
Definition image_io.h:55