(no commit message)
[pom.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) Ecole Polytechnique Federale de Lausanne                                 //
17 // Contact <pom@epfl.ch> for comments & bug reports                             //
18 //////////////////////////////////////////////////////////////////////////////////
19
20 #include <iostream>
21 #include <stdio.h>
22
23 using namespace std;
24
25 #include <libpng/png.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() {
51   deallocate();
52 }
53
54 void RGBImage::write_ppm(const char *filename) {
55   FILE *outfile;
56
57   if ((outfile = fopen (filename, "wb")) == 0) {
58     fprintf (stderr, "Can't open %s for reading\n", filename);
59     exit(1);
60   }
61
62   fprintf(outfile, "P6\n%d %d\n255\n", _width, _height);
63
64   char *raw = new char[_width * _height * 3];
65
66   int k = 0;
67   for(int y = 0; y < _height; y++) for(int x = 0; x < _width; x++) {
68     raw[k++] = _bit_map[x + _width * (y + _height * RED)];
69     raw[k++] = _bit_map[x + _width * (y + _height * GREEN)];
70     raw[k++] = _bit_map[x + _width * (y + _height * BLUE)];
71   }
72
73   fwrite((void *) raw, sizeof(unsigned char), _width * _height * 3, outfile);
74   fclose(outfile);
75
76   delete[] raw;
77 }
78
79 void RGBImage::read_ppm(const char *filename) {
80   const int buffer_size = 1024;
81   FILE *infile;
82   char buffer[buffer_size];
83   int max;
84
85   deallocate();
86
87   if((infile = fopen (filename, "r")) == 0) {
88     fprintf (stderr, "Can't open %s for reading\n", filename);
89     exit(1);
90   }
91
92   fgets(buffer, buffer_size, infile);
93
94   if(strncmp(buffer, "P6", 2) == 0) {
95
96     do {
97       fgets(buffer, buffer_size, infile);
98     } while((buffer[0] < '0') || (buffer[0] > '9'));
99     sscanf(buffer, "%d %d", &_width, &_height);
100     fgets(buffer, buffer_size, infile);
101     sscanf(buffer, "%d", &max);
102
103     allocate();
104
105     unsigned char *raw = new unsigned char[_width * _height * RGB_DEPTH];
106     fread(raw, sizeof(unsigned char), _width * _height * RGB_DEPTH, infile);
107
108     int k = 0;
109     for(int y = 0; y < _height; y++) for(int x = 0; x < _width; x++) {
110       _bit_plans[RED][y][x] = raw[k++];
111       _bit_plans[GREEN][y][x] = raw[k++];
112       _bit_plans[BLUE][y][x] = raw[k++];
113     }
114
115     delete[] raw;
116
117   } else if(strncmp(buffer, "P5", 2) == 0) {
118
119     do {
120       fgets(buffer, buffer_size, infile);
121     } while((buffer[0] < '0') || (buffer[0] > '9'));
122     sscanf(buffer, "%d %d", &_width, &_height);
123     fgets(buffer, buffer_size, infile);
124     sscanf(buffer, "%d", &max);
125
126     allocate();
127
128     unsigned char *pixbuf = new unsigned char[_width * _height];
129     fread(buffer, sizeof(unsigned char), _width * _height, infile);
130
131     int k = 0, l = 0;
132     for(int y = 0; y < _height; y++) for(int x = 0; x < _width; x++) {
133       unsigned char c = pixbuf[k++];
134       _bit_map[l++] = c;
135       _bit_map[l++] = c;
136       _bit_map[l++] = c;
137     }
138
139     delete[] pixbuf;
140
141   } else {
142     cerr << "Can not read ppm of type [" << buffer << "] from " << filename << ".\n";
143     exit(1);
144   }
145 }
146
147 void RGBImage::read_png(const char* filename) {
148   // This is the number of bytes the read_png routine will read to
149   // decide if the file is a PNG or not. According to the png
150   // documentation, it can be 1 to 8 bytes, 8 being the max and the
151   // best.
152
153   const int header_size = 8;
154
155   png_byte header[header_size];
156   png_bytep *row_pointers;
157
158   deallocate();
159
160   // open file
161   FILE *fp = fopen(filename, "rb");
162   if (!fp) {
163     cerr << "Unable to open file " << filename << " for reading.\n";
164     exit(1);
165   }
166
167   // read header
168   fread(header, 1, header_size, fp);
169   if (png_sig_cmp(header, 0, header_size)) {
170     cerr << "File " << filename << " does not look like PNG.\n";
171     fclose(fp);
172     exit(1);
173   }
174
175   // create png pointer
176   png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
177   if (!png_ptr) {
178     cerr << "png_create_read_struct failed\n";
179     fclose(fp);
180     exit(1);
181   }
182
183   // create png info struct
184   png_infop info_ptr = png_create_info_struct(png_ptr);
185   if (!info_ptr) {
186     png_destroy_read_struct(&png_ptr, (png_infopp) 0, (png_infopp) 0);
187     cerr << "png_create_info_struct failed\n";
188     fclose(fp);
189     exit(1);
190   }
191
192   // get image info
193   png_init_io(png_ptr, fp);
194   png_set_sig_bytes(png_ptr, header_size);
195   png_read_info(png_ptr, info_ptr);
196
197   _width = info_ptr->width;
198   _height = info_ptr->height;
199
200   png_byte bit_depth, color_type, channels;
201   color_type = info_ptr->color_type;
202   bit_depth = info_ptr->bit_depth;
203   channels = info_ptr->channels;
204
205   if(bit_depth != 8) {
206     cerr << "Can only read 8-bits PNG images." << endl;
207     exit(1);
208   }
209
210   // allocate image pointer
211   row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * _height);
212   for (int y = 0; y < _height; y++)
213     row_pointers[y] = (png_byte*) malloc(info_ptr->rowbytes);
214
215   allocate();
216
217   // read image
218   png_read_image(png_ptr, row_pointers);
219
220   // send image to red, green and blue buffers
221   switch (color_type) {
222   case PNG_COLOR_TYPE_GRAY:
223     {
224       unsigned char pixel = 0;
225       for (int y = 0; y < _height; y++) for (int x = 0; x < _width; x++) {
226         pixel = row_pointers[y][x];
227         _bit_plans[RED][y][x] = pixel;
228         _bit_plans[GREEN][y][x] = pixel;
229         _bit_plans[BLUE][y][x] = pixel;
230       }
231     }
232     break;
233
234   case PNG_COLOR_TYPE_GRAY_ALPHA:
235     cerr << "PNG type GRAY_ALPHA not supported.\n";
236     exit(1);
237     break;
238
239   case PNG_COLOR_TYPE_PALETTE:
240     cerr << "PNG type PALETTE not supported.\n";
241     exit(1);
242     break;
243
244   case PNG_COLOR_TYPE_RGB:
245     {
246       if(channels != RGB_DEPTH) {
247         cerr << "Unsupported number of channels for RGB type\n";
248         break;
249       }
250       int k;
251       for (int y = 0; y < _height; y++) {
252         k = 0;
253         for (int x = 0; x < _width; x++) {
254           _bit_plans[RED][y][x] = row_pointers[y][k++];
255           _bit_plans[GREEN][y][x] = row_pointers[y][k++];
256           _bit_plans[BLUE][y][x] = row_pointers[y][k++];
257         }
258       }
259     }
260     break;
261
262   case PNG_COLOR_TYPE_RGB_ALPHA:
263     cerr << "PNG type RGB_ALPHA not supported.\n";
264     exit(1);
265     break;
266
267   default:
268     cerr << "Unknown PNG type\n";
269     exit(1);
270   }
271
272   // release memory
273   png_destroy_read_struct(&png_ptr, &info_ptr, 0);
274
275   for (int y = 0; y < _height; y++) free(row_pointers[y]);
276   free(row_pointers);
277
278   fclose(fp);
279 }
280
281 void RGBImage::write_png(const char *filename) {
282   png_bytep *row_pointers;
283
284   // create file
285   FILE *fp = fopen(filename, "wb");
286
287   if (!fp) {
288     cerr << "Unable to create image '" << filename << "'\n";
289     exit(1);
290   }
291
292   png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, 0, 0, 0);
293
294   if (!png_ptr) {
295     cerr << "png_create_write_struct failed\n";
296     fclose(fp);
297     exit(1);
298   }
299
300   png_infop info_ptr = png_create_info_struct(png_ptr);
301   if (!info_ptr) {
302     cerr << "png_create_info_struct failed\n";
303     fclose(fp);
304     exit(1);
305   }
306
307   png_init_io(png_ptr, fp);
308
309   png_set_IHDR(png_ptr, info_ptr, _width, _height,
310                8, 2, PNG_INTERLACE_NONE,
311                PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE);
312
313   png_write_info(png_ptr, info_ptr);
314
315   // allocate memory
316   row_pointers = (png_bytep*) malloc(sizeof(png_bytep) * _height);
317   for (int y = 0; y < _height; y++)
318     row_pointers[y] = (png_byte*) malloc(info_ptr->rowbytes);
319
320   int k;
321   for (int y = 0; y < _height; y++) {
322     k = 0;
323     for (int x = 0; x < _width; x++) {
324       row_pointers[y][k++] = _bit_map[x + _width * (y + _height * RED)];
325       row_pointers[y][k++] = _bit_map[x + _width * (y + _height * GREEN)];
326       row_pointers[y][k++] = _bit_map[x + _width * (y + _height * BLUE)];
327     }
328   }
329
330   png_write_image(png_ptr, row_pointers);
331   png_write_end(png_ptr, 0);
332
333   png_destroy_write_struct(&png_ptr, &info_ptr);
334
335   // cleanup heap allocation
336   for (int y = 0; y < _height; y++) free(row_pointers[y]);
337   free(row_pointers);
338
339   fclose(fp);
340 }