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= clamp(image(i) * 255, 0, 255);
228 tmp[offset ]= pixel.r;
229 tmp[offset +1]= pixel.g;
230 tmp[offset +2]= pixel.b;
231 tmp[offset +3]= pixel.a;
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= clamp(image(i) * 255, 0, 255);
252 tmp[offset ]= pixel.r;
253 tmp[offset +1]= pixel.g;
254 tmp[offset +2]= pixel.b;
255 tmp[offset +3]= pixel.a;
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 r= 0;
311 unsigned g= 0;
312 unsigned 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
358unsigned read_image_data_size( const char *filename )
359{
360 int w, h, channels;
361 if(!stbi_info(filename, &w, &h, &channels))
362 return 0;
363
364 return w*h * channels;
365}
366
367
368int write_image_data( const ImageData& image, const char *filename, const bool flipY )
369{
370 if(image.size != 1)
371 {
372 printf("[error] writing color image '%s'... not an 8 bits image.\n", filename);
373 return -1;
374 }
375
376 stbi_flip_vertically_on_write(flipY);
377
378 int code= 0;
379 if(std::string(filename).rfind(".png") != std::string::npos)
380 code= stbi_write_png(filename, image.width, image.height, image.channels, image.pixels.data(), image.width * image.channels) != 0;
381 else if(std::string(filename).rfind(".bmp") != std::string::npos)
382 code= stbi_write_bmp(filename, image.width, image.height, image.channels, image.pixels.data()) != 0;
383
384 else
385 {
386 printf("[error] writing color image '%s'... not a .png / .bmp image.\n", filename);
387 return -1;
388 }
389
390 if(code > 0)
391 return 0;
392
393 printf("[error] writing color image '%s'...\n", filename);
394 return -1;
395}
396
397
398
399ImageData srgb( const ImageData& image )
400{
401 ImageData tmp(image.width, image.height, image.channels);
402
403 for(unsigned i= 0; i < image.pixels.size(); i+= image.channels)
404 {
405 Color pixel= srgb( Color( image.pixels[i], image.pixels[i+1], image.pixels[i+2], (image.channels > 3) ? image.pixels[i+3] : 255 ) / 255 );
406
407 pixel= clamp(pixel * 255, 0, 255);
408 tmp.pixels[i]= pixel.r;
409 tmp.pixels[i +1]= pixel.g;
410 tmp.pixels[i +2]= pixel.b;
411 if(image.channels > 3)
412 tmp.pixels[i +3]= pixel.a;
413 }
414
415 return tmp;
416}
417
419{
420 ImageData tmp(image.width, image.height, image.channels);
421
422 for(unsigned i= 0; i < image.pixels.size(); i+= image.channels)
423 {
424 Color pixel= linear( Color( image.pixels[i], image.pixels[i+1], image.pixels[i+2], (image.channels > 3) ? image.pixels[i+3] : 255 ) / 255 );
425
426 pixel= clamp(pixel * 255, 0, 255);
427 tmp.pixels[i]= pixel.r;
428 tmp.pixels[i +1]= pixel.g;
429 tmp.pixels[i +2]= pixel.b;
430 if(image.channels > 3)
431 tmp.pixels[i +3]= pixel.a;
432 }
433
434 return tmp;
435}
436
437ImageData flipY( const ImageData& image )
438{
439 // flip de l'image : origine en haut a gauche
440 ImageData flip(image.width, image.height, image.channels);
441
442 for(unsigned y= 0; y < image.height; y++)
443 for(unsigned x= 0; x < image.width; x++)
444 {
445 unsigned s= image.offset(x, y);
446 unsigned d= flip.offset(x, flip.height - y -1);
447
448 for(unsigned i= 0; i < image.channels; i++)
449 flip.pixels[d +i]= image.pixels[s +i];
450 }
451
452 return flip;
453}
454
455ImageData flipX( const ImageData& image )
456{
457 ImageData flip(image.width, image.height, image.channels);
458
459 for(unsigned y= 0; y < image.height; y++)
460 for(unsigned x= 0; x < image.width; x++)
461 {
462 unsigned s= image.offset(x, y);
463 unsigned d= flip.offset(flip.width -x -1, y);
464
465 for(unsigned i= 0; i < image.channels; i++)
466 flip.pixels[d +i]= image.pixels[s +i];
467 }
468
469 return flip;
470}
471
472ImageData copy( const ImageData& image, const unsigned xmin, const unsigned ymin, const unsigned width, const unsigned height )
473{
474 ImageData copy(width, height, image.channels);
475
476 for(unsigned y= 0; y < height; y++)
477 for(unsigned x= 0; x < width; x++)
478 {
479 unsigned s= image.offset(xmin +x, ymin +y);
480 unsigned d= copy.offset(x, y);
481
482 for(unsigned i= 0; i < image.channels; i++)
483 copy.pixels[d +i]= image.pixels[s +i];
484 }
485
486 return copy;
487}
488
489
490ImageData& blit( ImageData& image, const unsigned xmin, const unsigned ymin, const ImageData& pixels )
491{
492 if(image.pixels.size() == 0)
493 return image;
494 if(pixels.pixels.size() == 0)
495 return image;
496 if(image.channels != pixels.channels)
497 return image;
498
499 unsigned xmax= std::min( image.width, xmin + pixels.width );
500 unsigned ymax= std::min( image.height, ymin + pixels.height );
501 unsigned w= xmax - xmin;
502 unsigned h= ymax - ymin;
503
504 for(unsigned y= 0; y < h; y++)
505 for(unsigned x= 0; x < w; x++)
506 {
507 unsigned char *dst= image.pixels.data() + image.offset(x + xmin, y + ymin);
508 const unsigned char *src= pixels.pixels.data() + pixels.offset(x, y);
509 for(unsigned i= 0; i < pixels.channels; i++)
510 dst[i]= src[i];
511 }
512
513 return image;
514}
515
517{
518 unsigned w= std::max(unsigned(1), image.width / 2);
519 unsigned h= std::max(unsigned(1), image.height / 2);
520
521 ImageData tmp(w, h, image.channels);
522
523 for(unsigned py= 0; py < h; py++)
524 for(unsigned px= 0; px < w; px++)
525 {
526 unsigned x= 2*px;
527 unsigned y= 2*py;
528 unsigned d= tmp.offset(px, py);
529
530 for(unsigned i= 0; i < image.channels; i++)
531 tmp.pixels[d +i]= (
532 image.pixels[image.offset(x, y) +i]
533 + image.pixels[image.offset(x+1, y) +i]
534 + image.pixels[image.offset(x, y+1) +i]
535 + image.pixels[image.offset(x+1, y+1) +i] ) / 4;
536 }
537
538 return tmp;
539}
540
541int miplevels( const int width, const int height )
542{
543 int w= width;
544 int h= height;
545 int levels= 1;
546 while(w > 1 || h > 1)
547 {
548 w= std::max(1, w / 2);
549 h= std::max(1, h / 2);
550 levels= levels + 1;
551 }
552
553 return levels;
554}
555
557{
558 if(data.pixels.empty())
559 return {};
560
561 assert(data.channels >= 3);
562 Image tmp( data.width, data.height );
563
564 for(unsigned i= 0; i < tmp.size(); i++)
565 {
566 unsigned offset= i * data.channels;
567 Color pixel= Color( data.pixels[offset], data.pixels[offset+1], data.pixels[offset+2], (data.channels > 3) ? data.pixels[offset+3] : 255 ) / 255;
568 tmp(i)= pixel;
569 }
570
571 return tmp;
572}
573
575{
576 if(image.size() == 0)
577 return {};
578
579 ImageData tmp( image.width(), image.height(), 4 );
580 for(unsigned i= 0; i < image.size(); i++)
581 {
582 Color pixel= clamp(image(i) * 255, 0, 255);
583
584 unsigned offset= i * tmp.channels;
585 tmp.pixels[offset]= pixel.r;
586 tmp.pixels[offset +1]= pixel.g;
587 tmp.pixels[offset +2]= pixel.b;
588 tmp.pixels[offset +3]= pixel.a;
589 }
590
591 return tmp;
592}
representation d'une image.
Definition image.h:21
unsigned width() const
renvoie la largeur de l'image.
Definition image.h:100
unsigned size() const
renvoie le nombre de pixels de l'image.
Definition image.h:104
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
ImageData make_image_data(const Image &image)
cree une image data à partir d'une image.
Definition image_io.cpp:574
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
Image make_image(const ImageData &data)
cree une image à partir d'une image data.
Definition image_io.cpp:556
int write_image_data(const ImageData &image, const char *filename, const bool flipY)
enregistre des donnees dans un fichier png.
Definition image_io.cpp:368
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:541
Image srgb(const Image &image)
transformation couleur : rgb lineaire vers srgb
Definition image_io.cpp:15
unsigned read_image_data_size(const char *filename)
renvoie la taille en octets de l'image data chargee. ou 0 si erreur.
Definition image_io.cpp:358
representation d'une couleur (rgba) transparente ou opaque.
Definition color.h:14
stockage temporaire des donnees d'une image.
Definition image_io.h:55