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