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                                           //
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::read_png(const char *name) {
95   // This is the number of bytes the read_png routine will read to
96   // decide if the file is a PNG or not. According to the png
97   // documentation, it can be 1 to 8 bytes, 8 being the max and the
98   // best.
99
100   const int header_size = 8;
101
102   png_byte header[header_size];
103   png_bytep *row_pointers;
104
105   deallocate();
106
107   // open file
108   FILE *fp = fopen(name, "rb");
109   if (!fp) {
110     cerr << "Unable to open file " << name << " for reading.\n";
111     exit(1);
112   }
113
114   // read header
115   fread(header, 1, header_size, fp);
116   if (png_sig_cmp(header, 0, header_size)) {
117     cerr << "File " << name << " does not look like PNG.\n";
118     fclose(fp);
119     exit(1);
120   }
121
122   // create png pointer
123   png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
124   if (!png_ptr) {
125     cerr << "png_create_read_struct failed\n";
126     fclose(fp);
127     exit(1);
128   }
129
130   // create png info struct
131   png_infop info_ptr = png_create_info_struct(png_ptr);
132   if (!info_ptr) {
133     png_destroy_read_struct(&png_ptr, (png_infopp) 0, (png_infopp) 0);
134     cerr << "png_create_info_struct failed\n";
135     fclose(fp);
136     exit(1);
137   }
138
139   // get image info
140   png_init_io(png_ptr, fp);
141   png_set_sig_bytes(png_ptr, header_size);
142   png_read_info(png_ptr, info_ptr);
143
144   _width = info_ptr->width;
145   _height = info_ptr->height;
146
147   png_byte bit_depth, color_type, channels;
148   color_type = info_ptr->color_type;
149   bit_depth = info_ptr->bit_depth;
150   channels = info_ptr->channels;
151
152   if(bit_depth != 8) {
153     cerr << "Can only read 8-bits PNG images." << endl;
154     exit(1);
155   }
156
157   // allocate image pointer
158   row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * _height);
159   for (int y = 0; y < _height; y++)
160     row_pointers[y] = (png_byte*) malloc(info_ptr->rowbytes);
161
162   allocate();
163
164   // read image
165   png_read_image(png_ptr, row_pointers);
166
167   // send image to red, green and blue buffers
168   switch (color_type) {
169   case PNG_COLOR_TYPE_GRAY:
170     {
171       unsigned char pixel = 0;
172       for (int y = 0; y < _height; y++) for (int x = 0; x < _width; x++) {
173         pixel = row_pointers[y][x];
174         _bit_plans[RED][y][x] = pixel;
175         _bit_plans[GREEN][y][x] = pixel;
176         _bit_plans[BLUE][y][x] = pixel;
177       }
178     }
179     break;
180
181   case PNG_COLOR_TYPE_GRAY_ALPHA:
182     cerr << "PNG type GRAY_ALPHA not supported.\n";
183     exit(1);
184     break;
185
186   case PNG_COLOR_TYPE_PALETTE:
187     cerr << "PNG type PALETTE not supported.\n";
188     exit(1);
189     break;
190
191   case PNG_COLOR_TYPE_RGB:
192     {
193       if(channels != RGB_DEPTH) {
194         cerr << "Unsupported number of channels for RGB type\n";
195         break;
196       }
197       int k;
198       for (int y = 0; y < _height; y++) {
199         k = 0;
200         for (int x = 0; x < _width; x++) {
201           _bit_plans[RED][y][x] = row_pointers[y][k++];
202           _bit_plans[GREEN][y][x] = row_pointers[y][k++];
203           _bit_plans[BLUE][y][x] = row_pointers[y][k++];
204         }
205       }
206     }
207     break;
208
209   case PNG_COLOR_TYPE_RGB_ALPHA:
210     cerr << "PNG type RGB_ALPHA not supported.\n";
211     exit(1);
212     break;
213
214   default:
215     cerr << "Unknown PNG type\n";
216     exit(1);
217   }
218
219   // release memory
220   png_destroy_read_struct(&png_ptr, &info_ptr, 0);
221
222   for (int y = 0; y < _height; y++) free(row_pointers[y]);
223   free(row_pointers);
224
225   fclose(fp);
226 }
227
228 void RGBImage::write_png(const char *name) {
229   png_bytep *row_pointers;
230
231   // create file
232   FILE *fp = fopen(name, "wb");
233
234   if (!fp) {
235     cerr << "Unable to create image '" << name << "'\n";
236     exit(1);
237   }
238
239   png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
240
241   if (!png_ptr) {
242     cerr << "png_create_write_struct failed\n";
243     fclose(fp);
244     exit(1);
245   }
246
247   png_infop info_ptr = png_create_info_struct(png_ptr);
248   if (!info_ptr) {
249     cerr << "png_create_info_struct failed\n";
250     fclose(fp);
251     exit(1);
252   }
253
254   png_init_io(png_ptr, fp);
255
256   png_set_IHDR(png_ptr, info_ptr, _width, _height,
257                8, 2, PNG_INTERLACE_NONE,
258                PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
259
260   png_write_info(png_ptr, info_ptr);
261
262   // allocate memory
263   row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * _height);
264   for (int y = 0; y < _height; y++)
265     row_pointers[y] = (png_byte*) malloc(info_ptr->rowbytes);
266
267   int k;
268   for (int y = 0; y < _height; y++) {
269     k = 0;
270     for (int x = 0; x < _width; x++) {
271       row_pointers[y][k++] = _bit_map[x + _width * (y + _height * RED)];
272       row_pointers[y][k++] = _bit_map[x + _width * (y + _height * GREEN)];
273       row_pointers[y][k++] = _bit_map[x + _width * (y + _height * BLUE)];
274     }
275   }
276
277   png_write_image(png_ptr, row_pointers);
278   png_write_end(png_ptr, 0);
279
280   png_destroy_write_struct(&png_ptr, &info_ptr);
281
282   // cleanup heap allocation
283   for (int y = 0; y < _height; y++) free(row_pointers[y]);
284   free(row_pointers);
285
286   fclose(fp);
287 }
288
289 void RGBImage::write_jpg(const char *filename, int quality) {
290   struct jpeg_compress_struct cinfo;
291   struct my_error_mgr jerr;
292   FILE *outfile;                /* target file */
293   JSAMPARRAY buffer;            /* Output row buffer */
294
295   jpeg_create_compress (&cinfo);
296
297   if ((outfile = fopen (filename, "wb")) == 0) {
298     fprintf (stderr, "Can't open %s\n", filename);
299     exit(1);
300   }
301
302   cinfo.err = jpeg_std_error (&jerr.pub);
303   jerr.pub.error_exit = my_error_exit;
304
305   if (setjmp (jerr.setjmp_buffer)) {
306     jpeg_destroy_compress (&cinfo);
307     fclose (outfile);
308     exit(1);
309   }
310
311   jpeg_stdio_dest (&cinfo, outfile);
312
313   cinfo.image_width = _width;
314   cinfo.image_height = _height;
315   cinfo.input_components = RGB_DEPTH;
316
317   cinfo.in_color_space = JCS_RGB;
318
319   jpeg_set_defaults (&cinfo);
320   jpeg_set_quality (&cinfo, quality, TRUE);
321   jpeg_start_compress (&cinfo, TRUE);
322   int y = 0;
323   buffer =
324     (*cinfo.mem->alloc_sarray) ((j_common_ptr) & cinfo, JPOOL_IMAGE,
325                                 _width * RGB_DEPTH, 1);
326   while (int(cinfo.next_scanline) < _height) {
327     for(int d = 0; d < RGB_DEPTH; d++)
328       for(int x = 0; x < _width; x++)
329         buffer[0][x * RGB_DEPTH + d] =
330           (JSAMPLE) ((_bit_map[x + _width * (y + _height * d)] * (MAXJSAMPLE + 1)) / 255);
331     jpeg_write_scanlines (&cinfo, buffer, 1);
332     y++;
333   }
334
335   jpeg_finish_compress (&cinfo);
336   fclose (outfile);
337
338   jpeg_destroy_compress (&cinfo);
339 }
340
341 void RGBImage::read_jpg(const char *filename) {
342   struct jpeg_decompress_struct cinfo;
343   struct my_error_mgr jerr;
344   FILE *infile;
345   JSAMPARRAY buffer;
346
347   deallocate();
348
349   if ((infile = fopen (filename, "rb")) == 0) {
350     fprintf (stderr, "can't open %s\n", filename);
351     return;
352   }
353
354   cinfo.err = jpeg_std_error (&jerr.pub);
355   jerr.pub.error_exit = my_error_exit;
356
357   if (setjmp (jerr.setjmp_buffer)) {
358     jpeg_destroy_decompress (&cinfo);
359     fclose (infile);
360     delete[] _bit_map;
361     _width = 0;
362     _height = 0;
363     _bit_map = 0;
364     return;
365   }
366
367   jpeg_create_decompress (&cinfo);
368   jpeg_stdio_src (&cinfo, infile);
369   jpeg_read_header (&cinfo, TRUE);
370   jpeg_start_decompress (&cinfo);
371
372   _width = cinfo.output_width;
373   _height = cinfo.output_height;
374   int depth = cinfo.output_components;
375
376   allocate();
377
378   buffer =
379     (*cinfo.mem->alloc_sarray) ((j_common_ptr) & cinfo, JPOOL_IMAGE,
380                                 _width * depth, 1);
381
382   int y = 0;
383   while (cinfo.output_scanline < cinfo.output_height) {
384     jpeg_read_scanlines (&cinfo, buffer, 1);
385     if(depth == 1) {
386       for(int d = 0; d < RGB_DEPTH; d++)
387         for(int x = 0; x < _width; x++)
388           _bit_plans[d][y][x] =
389             (unsigned char) ((buffer[0][x * depth] * 255) / (MAXJSAMPLE + 1));
390     } else {
391       for(int d = 0; d < depth; d++)
392         for(int x = 0; x < _width; x++)
393           _bit_plans[d][y][x] =
394             (unsigned char) ((buffer[0][x * depth + d] * 255) / (MAXJSAMPLE + 1));
395     }
396     y++;
397   }
398
399   jpeg_finish_decompress (&cinfo);
400   jpeg_destroy_decompress (&cinfo);
401
402   fclose (infile);
403 }
404
405 void RGBImage::draw_line(int thickness,
406                          unsigned char r, unsigned char g, unsigned char b,
407                          scalar_t x0, scalar_t y0, scalar_t x1, scalar_t y1) {
408   int l = 0;
409   int dx, dy, h, v;
410   int ix0 = int(x0 + 0.5), iy0 = int(y0 + 0.5), ix1 = int(x1 + 0.5), iy1 = int(y1 + 0.5);
411
412   if(ix0 < ix1) { dx = 1; h = ix1 - ix0; } else { dx = -1; h = ix0 - ix1; }
413   if(iy0 < iy1) { dy = 1; v = iy1 - iy0; } else { dy = -1; v = iy0 - iy1; }
414
415   int x = ix0, y = iy0;
416
417   if(h > v) {
418     for(int i = 0; i < h + 1; i++) {
419       for(int ex = - thickness / 2 - 1; ex < (thickness + 1) / 2 + 1; ex++) {
420         for(int ey = - thickness / 2 - 1; ey < (thickness + 1) / 2 + 1; ey++) {
421           if(ex * ex + ey * ey <= thickness * thickness / 4) {
422             int xx = x + ex, yy = y + ey;
423             if(xx >= 0 && xx < _width && yy >= 0 && yy < _height)
424               set_pixel(xx, yy, r, g, b);
425           }
426         }
427       }
428
429       x += dx; l += v;
430       if(l > 0) { y += dy; l -= h; }
431     }
432
433   } else {
434
435     for(int i = 0; i < v + 1; i++) {
436       for(int ex = - thickness / 2 - 1; ex < (thickness + 1) / 2 + 1; ex++) {
437         for(int ey = - thickness / 2 - 1; ey < (thickness + 1) / 2 + 1; ey++) {
438           if(ex * ex + ey * ey <= thickness * thickness / 4) {
439             int xx = x + ex, yy = y + ey;
440             if(xx >= 0 && xx < _width && yy >= 0 && yy < _height)
441               set_pixel(xx, yy, r, g, b);
442           }
443         }
444       }
445
446       y += dy; l -= h;
447       if(l < 0) { x += dx; l += v; }
448     }
449
450   }
451
452 }
453
454 void RGBImage::draw_ellipse(int thickness,
455                             unsigned char r, unsigned char g, unsigned char b,
456                             scalar_t xc, scalar_t yc, scalar_t radius_1, scalar_t radius_2, scalar_t tilt) {
457   scalar_t ux1 =   cos(tilt) * radius_1, uy1 =   sin(tilt) * radius_1;
458   scalar_t ux2 = - sin(tilt) * radius_2, uy2 =   cos(tilt) * radius_2;
459
460   const int nb_points_to_draw = 80;
461   scalar_t x, y, px = 0, py = 0;
462
463   for(int i = 0; i <= nb_points_to_draw; i++) {
464     scalar_t alpha = (M_PI * 2 * scalar_t(i)) / scalar_t(nb_points_to_draw);
465
466     x = xc + cos(alpha) * ux1 + sin(alpha) * ux2;
467     y = yc + cos(alpha) * uy1 + sin(alpha) * uy2;
468
469     if(i > 0) {
470       draw_line(thickness, r, g, b, px, py, x, y);
471     }
472
473     px = x; py = y;
474   }
475 }