c343f47f5b44d21880d17b43c6a711023a410cef
[folded-ctf.git] / rgb_image.cc
1
2 ///////////////////////////////////////////////////////////////////////////
3 // This program is free software: you can redistribute it and/or modify  //
4 // it under the terms of the version 3 of the GNU General Public License //
5 // as published by the Free Software Foundation.                         //
6 //                                                                       //
7 // This program is distributed in the hope that it will be useful, but   //
8 // WITHOUT ANY WARRANTY; without even the implied warranty of            //
9 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU      //
10 // General Public License for more details.                              //
11 //                                                                       //
12 // You should have received a copy of the GNU General Public License     //
13 // along with this program. If not, see <http://www.gnu.org/licenses/>.  //
14 //                                                                       //
15 // Written by Francois Fleuret                                           //
16 // (C) Idiap Research Institute                                          //
17 //                                                                       //
18 // Contact <francois.fleuret@idiap.ch> for comments & bug reports        //
19 ///////////////////////////////////////////////////////////////////////////
20
21 #include <iostream>
22 #include <stdio.h>
23
24 #include <libpng/png.h>
25 #include "jpeg_misc.h"
26
27 #include "rgb_image.h"
28
29 void RGBImage::allocate() {
30   _bit_plans = new unsigned char **[RGB_DEPTH];
31   _bit_lines = new unsigned char *[RGB_DEPTH * _height];
32   _bit_map = new unsigned char [_width * _height * RGB_DEPTH];
33   for(int k = 0; k < RGB_DEPTH; k++) _bit_plans[k] = _bit_lines + k * _height;
34   for(int k = 0; k < RGB_DEPTH * _height; k++) _bit_lines[k] = _bit_map + k * _width;
35 }
36
37 void RGBImage::deallocate() {
38   delete[] _bit_plans;
39   delete[] _bit_lines;
40   delete[] _bit_map;
41 }
42
43 RGBImage::RGBImage() : _bit_plans(0), _bit_lines(0), _bit_map(0) { }
44
45 RGBImage::RGBImage(int width, int height) : _width(width), _height(height) {
46   allocate();
47   memset(_bit_map, 0, _width * _height * RGB_DEPTH * sizeof(unsigned char));
48 }
49
50 RGBImage::RGBImage(RGBImage *image, scalar_t scale) {
51   _width = int(scale * image->_width);
52   _height = int(scale * image->_height);
53
54   allocate();
55
56   for(int y = 0; y < _height; y++) {
57     for(int x = 0; x < _width; x++) {
58
59       const int delta = 10;
60       int sr = 0, sg = 0, sb = 0, t = 0;
61       int xo, yo;
62
63       for(int yy = y * delta; yy < (y + 1) * delta; yy++) {
64         for(int xx = x * delta; xx < (x + 1) * delta; xx++) {
65           xo = (image->_width * xx)/(_width * delta);
66           yo = (image->_height * yy)/(_height * delta);
67           if(xo >= 0 && xo < image->_width && yo >= 0 && yo < image->_height) {
68             sr += image->_bit_plans[RED][yo][xo];
69             sg += image->_bit_plans[GREEN][yo][xo];
70             sb += image->_bit_plans[BLUE][yo][xo];
71             t++;
72           }
73         }
74       }
75
76       if(t > 0) {
77         _bit_plans[RED][y][x] = sr / t;
78         _bit_plans[GREEN][y][x] = sg / t;
79         _bit_plans[BLUE][y][x] = sb / t;
80       } else {
81         _bit_plans[RED][y][x] = 0;
82         _bit_plans[GREEN][y][x] = 0;
83         _bit_plans[BLUE][y][x] = 0;
84       }
85
86     }
87   }
88 }
89
90 RGBImage::~RGBImage() {
91   deallocate();
92 }
93
94 void RGBImage::write_ppm(const char *filename) {
95   FILE *outfile;
96
97   if ((outfile = fopen (filename, "wb")) == 0) {
98     fprintf (stderr, "Can't open %s for reading\n", filename);
99     exit(1);
100   }
101
102   fprintf(outfile, "P6\n%d %d\n255\n", _width, _height);
103
104   char *raw = new char[_width * _height * 3];
105
106   int k = 0;
107   for(int y = 0; y < _height; y++) for(int x = 0; x < _width; x++) {
108     raw[k++] = _bit_map[x + _width * (y + _height * RED)];
109     raw[k++] = _bit_map[x + _width * (y + _height * GREEN)];
110     raw[k++] = _bit_map[x + _width * (y + _height * BLUE)];
111   }
112
113   fwrite((void *) raw, sizeof(unsigned char), _width * _height * 3, outfile);
114   fclose(outfile);
115
116   delete[] raw;
117 }
118
119 void RGBImage::read_ppm(const char *filename) {
120   const int buffer_size = 1024;
121   FILE *infile;
122   char buffer[buffer_size];
123   int max;
124
125   deallocate();
126
127   if((infile = fopen (filename, "r")) == 0) {
128     fprintf (stderr, "Can't open %s for reading\n", filename);
129     exit(1);
130   }
131
132   fgets(buffer, buffer_size, infile);
133
134   if(strncmp(buffer, "P6", 2) == 0) {
135
136     do {
137       fgets(buffer, buffer_size, infile);
138     } while((buffer[0] < '0') || (buffer[0] > '9'));
139     sscanf(buffer, "%d %d", &_width, &_height);
140     fgets(buffer, buffer_size, infile);
141     sscanf(buffer, "%d", &max);
142
143     allocate();
144
145     unsigned char *raw = new unsigned char[_width * _height * RGB_DEPTH];
146     fread(raw, sizeof(unsigned char), _width * _height * RGB_DEPTH, infile);
147
148     int k = 0;
149     for(int y = 0; y < _height; y++) for(int x = 0; x < _width; x++) {
150       _bit_plans[RED][y][x] = raw[k++];
151       _bit_plans[GREEN][y][x] = raw[k++];
152       _bit_plans[BLUE][y][x] = raw[k++];
153     }
154
155     delete[] raw;
156
157   } else if(strncmp(buffer, "P5", 2) == 0) {
158
159     do {
160       fgets(buffer, buffer_size, infile);
161     } while((buffer[0] < '0') || (buffer[0] > '9'));
162     sscanf(buffer, "%d %d", &_width, &_height);
163     fgets(buffer, buffer_size, infile);
164     sscanf(buffer, "%d", &max);
165
166     allocate();
167
168     unsigned char *pixbuf = new unsigned char[_width * _height];
169     fread(buffer, sizeof(unsigned char), _width * _height, infile);
170
171     int k = 0, l = 0;
172     for(int y = 0; y < _height; y++) for(int x = 0; x < _width; x++) {
173       unsigned char c = pixbuf[k++];
174       _bit_map[l++] = c;
175       _bit_map[l++] = c;
176       _bit_map[l++] = c;
177     }
178
179     delete[] pixbuf;
180
181   } else {
182     cerr << "Can not read ppm of type [" << buffer << "] from " << filename << ".\n";
183     exit(1);
184   }
185 }
186
187 void RGBImage::read_png(const char *name) {
188   // This is the number of bytes the read_png routine will read to
189   // decide if the file is a PNG or not. According to the png
190   // documentation, it can be 1 to 8 bytes, 8 being the max and the
191   // best.
192
193   const int header_size = 8;
194
195   png_byte header[header_size];
196   png_bytep *row_pointers;
197
198   deallocate();
199
200   // open file
201   FILE *fp = fopen(name, "rb");
202   if (!fp) {
203     cerr << "Unable to open file " << name << " for reading.\n";
204     exit(1);
205   }
206
207   // read header
208   fread(header, 1, header_size, fp);
209   if (png_sig_cmp(header, 0, header_size)) {
210     cerr << "File " << name << " does not look like PNG.\n";
211     fclose(fp);
212     exit(1);
213   }
214
215   // create png pointer
216   png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
217   if (!png_ptr) {
218     cerr << "png_create_read_struct failed\n";
219     fclose(fp);
220     exit(1);
221   }
222
223   // create png info struct
224   png_infop info_ptr = png_create_info_struct(png_ptr);
225   if (!info_ptr) {
226     png_destroy_read_struct(&png_ptr, (png_infopp) 0, (png_infopp) 0);
227     cerr << "png_create_info_struct failed\n";
228     fclose(fp);
229     exit(1);
230   }
231
232   // get image info
233   png_init_io(png_ptr, fp);
234   png_set_sig_bytes(png_ptr, header_size);
235   png_read_info(png_ptr, info_ptr);
236
237   _width = info_ptr->width;
238   _height = info_ptr->height;
239
240   png_byte bit_depth, color_type, channels;
241   color_type = info_ptr->color_type;
242   bit_depth = info_ptr->bit_depth;
243   channels = info_ptr->channels;
244
245   if(bit_depth != 8) {
246     cerr << "Can only read 8-bits PNG images." << endl;
247     exit(1);
248   }
249
250   // allocate image pointer
251   row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * _height);
252   for (int y = 0; y < _height; y++)
253     row_pointers[y] = (png_byte*) malloc(info_ptr->rowbytes);
254
255   allocate();
256
257   // read image
258   png_read_image(png_ptr, row_pointers);
259
260   // send image to red, green and blue buffers
261   switch (color_type) {
262   case PNG_COLOR_TYPE_GRAY:
263     {
264       unsigned char pixel = 0;
265       for (int y = 0; y < _height; y++) for (int x = 0; x < _width; x++) {
266         pixel = row_pointers[y][x];
267         _bit_plans[RED][y][x] = pixel;
268         _bit_plans[GREEN][y][x] = pixel;
269         _bit_plans[BLUE][y][x] = pixel;
270       }
271     }
272     break;
273
274   case PNG_COLOR_TYPE_GRAY_ALPHA:
275     cerr << "PNG type GRAY_ALPHA not supported.\n";
276     exit(1);
277     break;
278
279   case PNG_COLOR_TYPE_PALETTE:
280     cerr << "PNG type PALETTE not supported.\n";
281     exit(1);
282     break;
283
284   case PNG_COLOR_TYPE_RGB:
285     {
286       if(channels != RGB_DEPTH) {
287         cerr << "Unsupported number of channels for RGB type\n";
288         break;
289       }
290       int k;
291       for (int y = 0; y < _height; y++) {
292         k = 0;
293         for (int x = 0; x < _width; x++) {
294           _bit_plans[RED][y][x] = row_pointers[y][k++];
295           _bit_plans[GREEN][y][x] = row_pointers[y][k++];
296           _bit_plans[BLUE][y][x] = row_pointers[y][k++];
297         }
298       }
299     }
300     break;
301
302   case PNG_COLOR_TYPE_RGB_ALPHA:
303     cerr << "PNG type RGB_ALPHA not supported.\n";
304     exit(1);
305     break;
306
307   default:
308     cerr << "Unknown PNG type\n";
309     exit(1);
310   }
311
312   // release memory
313   png_destroy_read_struct(&png_ptr, &info_ptr, 0);
314
315   for (int y = 0; y < _height; y++) free(row_pointers[y]);
316   free(row_pointers);
317
318   fclose(fp);
319 }
320
321 void RGBImage::write_png(const char *name) {
322   png_bytep *row_pointers;
323
324   // create file
325   FILE *fp = fopen(name, "wb");
326
327   if (!fp) {
328     cerr << "Unable to create image '" << name << "'\n";
329     exit(1);
330   }
331
332   png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
333
334   if (!png_ptr) {
335     cerr << "png_create_write_struct failed\n";
336     fclose(fp);
337     exit(1);
338   }
339
340   png_infop info_ptr = png_create_info_struct(png_ptr);
341   if (!info_ptr) {
342     cerr << "png_create_info_struct failed\n";
343     fclose(fp);
344     exit(1);
345   }
346
347   png_init_io(png_ptr, fp);
348
349   png_set_IHDR(png_ptr, info_ptr, _width, _height,
350                8, 2, PNG_INTERLACE_NONE,
351                PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
352
353   png_write_info(png_ptr, info_ptr);
354
355   // allocate memory
356   row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * _height);
357   for (int y = 0; y < _height; y++)
358     row_pointers[y] = (png_byte*) malloc(info_ptr->rowbytes);
359
360   int k;
361   for (int y = 0; y < _height; y++) {
362     k = 0;
363     for (int x = 0; x < _width; x++) {
364       row_pointers[y][k++] = _bit_map[x + _width * (y + _height * RED)];
365       row_pointers[y][k++] = _bit_map[x + _width * (y + _height * GREEN)];
366       row_pointers[y][k++] = _bit_map[x + _width * (y + _height * BLUE)];
367     }
368   }
369
370   png_write_image(png_ptr, row_pointers);
371   png_write_end(png_ptr, 0);
372
373   png_destroy_write_struct(&png_ptr, &info_ptr);
374
375   // cleanup heap allocation
376   for (int y = 0; y < _height; y++) free(row_pointers[y]);
377   free(row_pointers);
378
379   fclose(fp);
380 }
381
382 void RGBImage::write_jpg(const char *filename, int quality) {
383   struct jpeg_compress_struct cinfo;
384   struct my_error_mgr jerr;
385   FILE *outfile;                /* target file */
386   JSAMPARRAY buffer;            /* Output row buffer */
387
388   jpeg_create_compress (&cinfo);
389
390   if ((outfile = fopen (filename, "wb")) == 0) {
391     fprintf (stderr, "Can't open %s\n", filename);
392     exit(1);
393   }
394
395   cinfo.err = jpeg_std_error (&jerr.pub);
396   jerr.pub.error_exit = my_error_exit;
397
398   if (setjmp (jerr.setjmp_buffer)) {
399     jpeg_destroy_compress (&cinfo);
400     fclose (outfile);
401     exit(1);
402   }
403
404   jpeg_stdio_dest (&cinfo, outfile);
405
406   cinfo.image_width = _width;
407   cinfo.image_height = _height;
408   cinfo.input_components = RGB_DEPTH;
409
410   cinfo.in_color_space = JCS_RGB;
411
412   jpeg_set_defaults (&cinfo);
413   jpeg_set_quality (&cinfo, quality, TRUE);
414   jpeg_start_compress (&cinfo, TRUE);
415   int y = 0;
416   buffer =
417     (*cinfo.mem->alloc_sarray) ((j_common_ptr) & cinfo, JPOOL_IMAGE,
418                                 _width * RGB_DEPTH, 1);
419   while (int(cinfo.next_scanline) < _height) {
420     for(int d = 0; d < RGB_DEPTH; d++)
421       for(int x = 0; x < _width; x++)
422         buffer[0][x * RGB_DEPTH + d] =
423           (JSAMPLE) ((_bit_map[x + _width * (y + _height * d)] * (MAXJSAMPLE + 1)) / 255);
424     jpeg_write_scanlines (&cinfo, buffer, 1);
425     y++;
426   }
427
428   jpeg_finish_compress (&cinfo);
429   fclose (outfile);
430
431   jpeg_destroy_compress (&cinfo);
432 }
433
434 void RGBImage::read_jpg(const char *filename) {
435   struct jpeg_decompress_struct cinfo;
436   struct my_error_mgr jerr;
437   FILE *infile;
438   JSAMPARRAY buffer;
439
440   deallocate();
441
442   if ((infile = fopen (filename, "rb")) == 0) {
443     fprintf (stderr, "can't open %s\n", filename);
444     return;
445   }
446
447   cinfo.err = jpeg_std_error (&jerr.pub);
448   jerr.pub.error_exit = my_error_exit;
449
450   if (setjmp (jerr.setjmp_buffer)) {
451     jpeg_destroy_decompress (&cinfo);
452     fclose (infile);
453     delete[] _bit_map;
454     _width = 0;
455     _height = 0;
456     _bit_map = 0;
457     return;
458   }
459
460   jpeg_create_decompress (&cinfo);
461   jpeg_stdio_src (&cinfo, infile);
462   jpeg_read_header (&cinfo, TRUE);
463   jpeg_start_decompress (&cinfo);
464
465   _width = cinfo.output_width;
466   _height = cinfo.output_height;
467   int depth = cinfo.output_components;
468
469   allocate();
470
471   buffer =
472     (*cinfo.mem->alloc_sarray) ((j_common_ptr) & cinfo, JPOOL_IMAGE,
473                                 _width * depth, 1);
474
475   int y = 0;
476   while (cinfo.output_scanline < cinfo.output_height) {
477     jpeg_read_scanlines (&cinfo, buffer, 1);
478     if(depth == 1) {
479       for(int d = 0; d < RGB_DEPTH; d++)
480         for(int x = 0; x < _width; x++)
481           _bit_plans[d][y][x] =
482             (unsigned char) ((buffer[0][x * depth] * 255) / (MAXJSAMPLE + 1));
483     } else {
484       for(int d = 0; d < depth; d++)
485         for(int x = 0; x < _width; x++)
486           _bit_plans[d][y][x] =
487             (unsigned char) ((buffer[0][x * depth + d] * 255) / (MAXJSAMPLE + 1));
488     }
489     y++;
490   }
491
492   jpeg_finish_decompress (&cinfo);
493   jpeg_destroy_decompress (&cinfo);
494
495   fclose (infile);
496 }
497
498 void RGBImage::draw_line(int thickness,
499                          unsigned char r, unsigned char g, unsigned char b,
500                          scalar_t x0, scalar_t y0, scalar_t x1, scalar_t y1) {
501   int l = 0;
502   int dx, dy, h, v;
503   int ix0 = int(x0 + 0.5), iy0 = int(y0 + 0.5), ix1 = int(x1 + 0.5), iy1 = int(y1 + 0.5);
504
505   if(ix0 < ix1) { dx = 1; h = ix1 - ix0; } else { dx = -1; h = ix0 - ix1; }
506   if(iy0 < iy1) { dy = 1; v = iy1 - iy0; } else { dy = -1; v = iy0 - iy1; }
507
508   int x = ix0, y = iy0;
509
510   if(h > v) {
511     for(int i = 0; i < h + 1; i++) {
512       for(int ex = - thickness / 2 - 1; ex < (thickness + 1) / 2 + 1; ex++) {
513         for(int ey = - thickness / 2 - 1; ey < (thickness + 1) / 2 + 1; ey++) {
514           if(ex * ex + ey * ey <= thickness * thickness / 4) {
515             int xx = x + ex, yy = y + ey;
516             if(xx >= 0 && xx < _width && yy >= 0 && yy < _height)
517               set_pixel(xx, yy, r, g, b);
518           }
519         }
520       }
521
522       x += dx; l += v;
523       if(l > 0) { y += dy; l -= h; }
524     }
525
526   } else {
527
528     for(int i = 0; i < v + 1; i++) {
529       for(int ex = - thickness / 2 - 1; ex < (thickness + 1) / 2 + 1; ex++) {
530         for(int ey = - thickness / 2 - 1; ey < (thickness + 1) / 2 + 1; ey++) {
531           if(ex * ex + ey * ey <= thickness * thickness / 4) {
532             int xx = x + ex, yy = y + ey;
533             if(xx >= 0 && xx < _width && yy >= 0 && yy < _height)
534               set_pixel(xx, yy, r, g, b);
535           }
536         }
537       }
538
539       y += dy; l -= h;
540       if(l < 0) { x += dx; l += v; }
541     }
542
543   }
544
545 }
546
547 void RGBImage::draw_ellipse(int thickness,
548                             unsigned char r, unsigned char g, unsigned char b,
549                             scalar_t xc, scalar_t yc, scalar_t radius_1, scalar_t radius_2, scalar_t tilt) {
550   scalar_t ux1 =   cos(tilt) * radius_1, uy1 =   sin(tilt) * radius_1;
551   scalar_t ux2 = - sin(tilt) * radius_2, uy2 =   cos(tilt) * radius_2;
552
553   const int nb_points_to_draw = 80;
554   scalar_t x, y, px = 0, py = 0;
555
556   for(int i = 0; i <= nb_points_to_draw; i++) {
557     scalar_t alpha = (M_PI * 2 * scalar_t(i)) / scalar_t(nb_points_to_draw);
558
559     x = xc + cos(alpha) * ux1 + sin(alpha) * ux2;
560     y = yc + cos(alpha) * uy1 + sin(alpha) * uy2;
561
562     if(i > 0) {
563       draw_line(thickness, r, g, b, px, py, x, y);
564     }
565
566     px = x; py = y;
567   }
568 }