FilePng.h
1 /*
2 
3 MIT License
4 
5 Copyright (c) 2017 FMI Open Development / Markus Peura, first.last@fmi.fi
6 
7 Permission is hereby granted, free of charge, to any person obtaining a copy
8 of this software and associated documentation files (the "Software"), to deal
9 in the Software without restriction, including without limitation the rights
10 to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 copies of the Software, and to permit persons to whom the Software is
12 furnished to do so, subject to the following conditions:
13 
14 The above copyright notice and this permission notice shall be included in all
15 copies or substantial portions of the Software.
16 
17 THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 SOFTWARE.
24 
25 */
26 /*
27 Part of Rack development has been done in the BALTRAD projects part-financed
28 by the European Union (European Regional Development Fund and European
29 Neighbourhood Partnership Instrument, Baltic Sea Region Programme 2007-2013)
30 */
31 #ifndef FILEPNG_H_
32 #define FILEPNG_H_
33 
34 
35 #include <string>
36 #include <iostream>
37 #include <fstream>
38 #include <exception>
39 
40 #include <png.h>
41 
42 #include "drain/util/FileInfo.h"
43 // #include "drain/util/RegExp.h"
44 #include "drain/util/JSON.h" // for reading attribute value
45 
46 #include "Image.h"
47 //
48 
49 
50 namespace drain
51 {
52 namespace image
53 {
54 
55 // using namespace std;
56 
58 
62 class FilePng
63 {
64 public:
65 
67  //static
68  //const drain::RegExp fileNameRegExp;
69 
70 
72 
81  template <class T>
82  static
83  void readOld(T & image, const std::string &path, int png_transforms = 0); //(PNG_TRANSFORM_PACKING || PNG_TRANSFORM_EXPAND)); 16 >> 8?
84 
85  static
86  void read(ImageFrame & image, const std::string &path, int png_transforms = 0); //(PNG_TRANSFORM_PACKING || PNG_TRANSFORM_EXPAND)); 16 >> 8?
87 
88  // consider readFrame() like with PNM
89 
91 
99  static void write(const ImageFrame &image, const std::string &path);
100 
102 
104  static
105  short int compressionLevel;
106 
107 protected:
108  static FileInfo & initFileInfo; //???
109 
110 };
111 
112 
113 template <class T> // , const CommentReader & commentReader = CommentReader()
114 void FilePng::readOld(T & image, const std::string & path, int png_transforms ) {
115 
116  drain::Logger mout(getImgLog(), __FILE__, __FUNCTION__);
117 
118  mout.debug("path='" , path , "'" );
119 
120 
121  // Try to open the file
122  FILE *fp = fopen(path.c_str(), "rb");
123  if (fp == NULL){
124  throw std::runtime_error(std::string("FilePng: could not open file: ") + path);
125  }
126 
127  // For checking magic code (signature)
128  //const unsigned int PNG_BYTES_TO_CHECK=4;
129  const size_t PNG_BYTES_TO_CHECK=4;
130  png_byte buf[PNG_BYTES_TO_CHECK];
131 
132  /* Read in some of the signature bytes */
133  if (fread((void *)buf, size_t(1), PNG_BYTES_TO_CHECK, fp) != PNG_BYTES_TO_CHECK){
134  fclose(fp);
135  throw std::runtime_error(std::string("FilePng: suspicious size of file: ") + path);
136  }
137 
138  /* Compare the first PNG_BYTES_TO_CHECK bytes of the signature.
139  Return nonzero (true) if they match */
140  if (png_sig_cmp(buf, (png_size_t)0, PNG_BYTES_TO_CHECK) != 0){
141  fclose(fp);
142  throw std::runtime_error(std::string("FilePng: not a png file: ") + path);
143  }
144 
145 
146  png_structp png_ptr = png_create_read_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL);
147  if (!png_ptr){
148  fclose(fp);
149  throw std::runtime_error(std::string("FilePng: problem in allocating image memory for: ")+path);
150  }
151 
152  png_infop info_ptr = png_create_info_struct(png_ptr);
153  if (!info_ptr){
154  fclose(fp);
155  png_destroy_read_struct(&png_ptr,(png_infopp)NULL, (png_infopp)NULL);
156  throw std::runtime_error(std::string("FilePng: problem in allocating info memory for: ")+path);
157  }
158 
159  /*
160  png_infop end_info = png_create_info_struct(png_ptr);
161  if (!end_info){
162  png_destroy_read_struct(&png_ptr, &info_ptr,(png_infopp)NULL);
163  throw std::runtime_error(std::string("FilePng: problem in allocating end_info memory for: ")+path);
164  }
165  */
166 
167  // This may be unstable. According to the documentation, if one uses the high-level interface png_read_png()
168  // one can only configure it with png_transforms flags (PNG_TRANSFORM_*)
169  png_set_palette_to_rgb(png_ptr);
170 
171  png_init_io(png_ptr, fp);
172  png_set_sig_bytes(png_ptr, PNG_BYTES_TO_CHECK);
173 
175  //if (drain::Debug > 2)
176  mout.debug3("reading data" );
177 
178  png_read_png(png_ptr, info_ptr, png_transforms, NULL);
179 
180 
181 
182 
183 
185  mout.debug3("reading image comments" );
186  int num_text = 0;
187  png_textp text_ptr = NULL;
188  png_get_text(png_ptr, info_ptr,&text_ptr, &num_text);
189  //mout.debug3() << '\n';
190  for (int i = 0; i < num_text; ++i) {
191  mout << text_ptr[i].key << '=' << text_ptr[i].text << '\n';
192  // ValueReader::scanValue(text_ptr[i].text, image.properties[text_ptr[i].key]);
193  JSON::readValue(text_ptr[i].text, image.properties[text_ptr[i].key]);
194  }
195  mout << mout.endl;
196 
197 
198  const unsigned int inputBitDepth = png_get_bit_depth(png_ptr, info_ptr);
199  drain::Type t;
200  switch (inputBitDepth) {
201  case 16:
202  //image.initialize<unsigned short>();
203  t.setType<unsigned short>();
204  break;
205  case 8:
206  t.setType<unsigned char>();
207  break;
208  default:
209  fclose(fp);
210  png_destroy_read_struct(&png_ptr,&info_ptr, (png_infopp)NULL);
211  //png_free_data(png_ptr,info_ptr,PNG_FREE_ALL,-1); // ???
212  throw std::runtime_error(std::string("FilePng: unsupported bit depth in : ")+path);
213  return;
214  }
215 
216  //image.setType(t);
217  mout.debug("initialize, type " , image.getType().name() );
218 
220  const unsigned int width = png_get_image_width(png_ptr, info_ptr);
221  const unsigned int height = png_get_image_height(png_ptr, info_ptr);
222  const unsigned int channels = png_get_channels(png_ptr, info_ptr);
223 
224 
225  Geometry g(image.getGeometry());
226 
227  // This test enables read into an alpha channel.
228  if ((channels!=g.channels.getChannelCount()) || (width!=g.area.getWidth()) || (height!=g.area.getHeight())){
229  switch (channels) {
230  case 4:
231  g.set(width,height,3,1);
232  break;
233  case 3:
234  g.setGeometry(width,height,3); // check alpha=0!
235  break;
236  case 2:
237  g.set(width,height,1,1);
238  break;
239  case 1:
240  g.setGeometry(width,height,1); // check alpha=0!
241  break;
242  default:
243  throw std::runtime_error(std::string("FilePng: invalid channel count in : ")+path);
244  }
245  }
246 
247  // Form Image, set target type and geometry. For ImageFrame, compare target type and geometry. If differences, throw exception.
248  image.initialize(t, g);
249 
250  mout.debug3() << "png geometry ok, ";
251  mout << "png channels =" << channels << "\n";
252  mout << "png bit_depth=" << inputBitDepth << "\n";
253  mout << mout.endl;
254 
255  // TODO: use png_get_pCal(.........)
256 #ifdef PNG_pCAL_SUPPORTED___DEFUNCT
258  if (info_ptr->pcal_X0 == info_ptr->pcal_X1){
259  mout.toOStr() << "physical scale supported, but no intensity range, pcalX0=" << info_ptr->pcal_X0 << ", pcalX1=" << info_ptr->pcal_X1 << mout.endl;
260  image.setDefaultLimits();
261  }
262  else {
263  image.setLimits(info_ptr->pcal_X0, info_ptr->pcal_X1);
264  mout.note("setting physical scale: " , image );
265  }
266 
267 #endif
268 
269  /*
270  if ((bit_depth!=8) && (bit_depth != 16)){
271  fclose(fp);
272  png_destroy_read_struct(&png_ptr,&info_ptr, (png_infopp)NULL);
273  //png_free_data(png_ptr,info_ptr,PNG_FREE_ALL,-1); // ???
274  throw std::runtime_error(std::string("FilePng: unsupported bit depth in : ")+path);
275  }
276  */
277  const unsigned int targetBitDepth = 8*image.getConf().getElementSize();
278 
279  const bool from8to8 = (inputBitDepth == 8) && (targetBitDepth == 8);
280  const bool from8to16 = (inputBitDepth == 8) && (targetBitDepth == 16);
281 
282  if (from8to8) {
283  mout.debug("8-bit input, 8-bit target, easy..." );
284  }
285  else if (from8to16) {
286  mout.note("-bit input, 16-bit target, rescaling..." );
287  }
288  else {
289  if ((inputBitDepth == 16) && (targetBitDepth == 16)){
290  mout.debug("16-bit input, 16-bit target, ok..." );
291  }
292  else {
293  mout.warn(inputBitDepth , "-bit input, ", targetBitDepth , "-bit target, problems ahead?" );
294  }
295  }
296 
297  png_bytep *row_pointers = png_get_rows(png_ptr, info_ptr);
298  png_bytep p;
299  int i0;
300  for (unsigned int j = 0; j < height; ++j) {
301  p = row_pointers[j];
302  for (unsigned int i = 0; i < width; ++i) {
303  for (unsigned int k = 0; k < channels; ++k) {
304  i0 = channels*i + k;
305  if (from8to8) {
306  image.put(i,j,k, p[i0]);
307  }
308  else if (from8to16) {
309  image.put(i,j,k, p[i0]<<8);
310  }
311  else {
312  image.put(i,j,k, (p[i0*2]<<8) + (p[i0*2+1]<<0));
313  }
314  }
315  }
316  }
317 
318  fclose(fp);
319  png_destroy_read_struct(&png_ptr,&info_ptr, (png_infopp)NULL);
320  //png_free_data(png_ptr,info_ptr,PNG_FREE_ALL,-1); // ???
321 
322  //png_destroy_read_struct(&png_ptr,(png_infopp)NULL, (png_infopp)NULL);
323  //png_destroy_info_struct(png_ptr,&info_ptr);
324 
325 
326 }
327 
328 
329 
330 }
331 
332 }
333 
334 #endif /*FILEPng_H_*/
335 
336 // Drain
Definition: FileInfo.h:48
static void readValue(std::istream &istr, Castable &v, bool keepType=false)
Read a value (JSON syntax). Read stream until a value has been extracted, with type recognition.
Definition: JSON.cpp:52
LogSourc e is the means for a function or any program segment to "connect" to a Log.
Definition: Log.h:308
Logger & note(const TT &... args)
For top-level information.
Definition: Log.h:485
Logger & warn(const TT &... args)
Possible error, but execution can continue.
Definition: Log.h:426
Logger & debug(const TT &... args)
Public, yet typically used "internally", when TIMING=true.
Definition: Log.h:676
Utilities related to std::type_info.
Definition: Type.h:51
size_t getChannelCount() const
Return the number of channels (image and alpha)
Definition: Geometry.h:108
For reading and writing images in PNG format.
Definition: FilePng.h:63
static void write(const ImageFrame &image, const std::string &path)
Writes image to a png file.
Definition: FilePng.cpp:291
static FileInfo fileInfo
Syntax for recognising image files.
Definition: FilePng.h:101
static void read(ImageFrame &image, const std::string &path, int png_transforms=0)
Definition: FilePng.cpp:70
static short int compressionLevel
Default compression level for png_set_compression_level(png_ptr, ...);.
Definition: FilePng.h:105
static void readOld(T &image, const std::string &path, int png_transforms=0)
Syntax for recognising png files.
Definition: FilePng.h:114
Definition: Geometry.h:145
Image with static geometry.
Definition: ImageFrame.h:67
Definition: DataSelector.cpp:1277