Panda3D
pnmImageHeader.cxx
Go to the documentation of this file.
1 /**
2  * PANDA 3D SOFTWARE
3  * Copyright (c) Carnegie Mellon University. All rights reserved.
4  *
5  * All use of this software is subject to the terms of the revised BSD
6  * license. You should have received a copy of this license along
7  * with this source code in a file named "LICENSE."
8  *
9  * @file pnmImageHeader.cxx
10  * @author drose
11  * @date 2000-06-14
12  */
13 
14 #include "pnmImageHeader.h"
15 #include "pnmFileTypeRegistry.h"
16 #include "pnmFileType.h"
17 #include "pnmReader.h"
18 #include "pnmWriter.h"
19 #include "config_pnmimage.h"
20 #include "virtualFileSystem.h"
21 #include "zStream.h"
22 
23 using std::istream;
24 using std::ostream;
25 using std::string;
26 
27 /**
28  * Opens up the image file and tries to read its header information to
29  * determine its size, number of channels, etc. If successful, updates the
30  * header information and returns true; otherwise, returns false.
31  */
33 read_header(const Filename &filename, PNMFileType *type,
34  bool report_unknown_type) {
35  PNMReader *reader = make_reader(filename, type, report_unknown_type);
36  if (reader != nullptr) {
37  (*this) = (*reader);
38  delete reader;
39  return true;
40  }
41 
42  return false;
43 }
44 
45 /**
46  * Reads the image header information only from the indicated stream.
47  *
48  * The filename is advisory only, and may be used to suggest a type if it has
49  * a known extension.
50  *
51  * If type is non-NULL, it is a suggestion for the type of file it is (and a
52  * non-NULL type will override any magic number test or filename extension
53  * lookup).
54  *
55  * Returns true if successful, false on error.
56  */
58 read_header(istream &data, const string &filename, PNMFileType *type,
59  bool report_unknown_type) {
61  (&data, false, filename, string(), type, report_unknown_type);
62  if (reader != nullptr) {
63  (*this) = (*reader);
64  delete reader;
65  return true;
66  }
67 
68  return false;
69 }
70 
71 /**
72  * Returns a newly-allocated PNMReader of the suitable type for reading from
73  * the indicated image filename, or NULL if the filename cannot be read for
74  * some reason. The filename "-" always stands for standard input. If type
75  * is specified, it is a suggestion for the file type to use.
76  *
77  * The PNMReader should be deleted when it is no longer needed.
78  */
80 make_reader(const Filename &filename, PNMFileType *type,
81  bool report_unknown_type) const {
82  if (pnmimage_cat.is_debug()) {
83  pnmimage_cat.debug()
84  << "Reading image from " << filename << "\n";
85  }
86  bool owns_file = false;
87  istream *file = nullptr;
88 
89  if (filename == "-") {
90  owns_file = false;
91  file = &std::cin;
92 
93  if (pnmimage_cat.is_debug()) {
94  pnmimage_cat.debug()
95  << "(reading standard input)\n";
96  }
97  } else {
99  owns_file = true;
100  file = vfs->open_read_file(filename, true);
101  }
102 
103  if (file == nullptr) {
104  if (pnmimage_cat.is_debug()) {
105  pnmimage_cat.debug()
106  << "Unable to open file.\n";
107  }
108  return nullptr;
109  }
110 
111  return make_reader(file, owns_file, filename, string(), type,
112  report_unknown_type);
113 }
114 
115 
116 /**
117  * Returns a newly-allocated PNMReader of the suitable type for reading from
118  * the already-opened image file, or NULL if the file cannot be read for some
119  * reason.
120  *
121  * owns_file should be set true if the PNMReader is to be considered the owner
122  * of the stream pointer (in which case the stream will be deleted on
123  * completion, whether successful or not), or false if it should not delete
124  * it.
125  *
126  * The filename parameter is optional here, since the file has already been
127  * opened; it is only used to examine the extension and attempt to guess the
128  * file type.
129  *
130  * If magic_number is nonempty, it is assumed to represent the first few bytes
131  * that have already been read from the file. Some file types may have
132  * difficulty if this is more than two bytes.
133  *
134  * If type is non-NULL, it is a suggestion for the file type to use.
135  *
136  * The PNMReader should be deleted when it is no longer needed.
137  */
139 make_reader(istream *file, bool owns_file, const Filename &filename,
140  string magic_number, PNMFileType *type,
141  bool report_unknown_type) const {
142  if (type == nullptr) {
143  if (!read_magic_number(file, magic_number, 2)) {
144  // No magic number. No image.
145  if (pnmimage_cat.is_debug()) {
146  pnmimage_cat.debug()
147  << "Image file appears to be empty.\n";
148  }
149  if (owns_file) {
151 
152  // We're assuming here that the file was opened via VFS. That may not
153  // necessarily be the case, but we don't make that distinction.
154  // However, at the moment at least, that distinction doesn't matter,
155  // since vfs->close_read_file() just deletes the file pointer anyway.
156  vfs->close_read_file(file);
157  }
158  return nullptr;
159  }
160 
162  get_type_from_magic_number(magic_number);
163 
164  if (pnmimage_cat.is_debug()) {
165  if (type != nullptr) {
166  pnmimage_cat.debug()
167  << "By magic number, image file appears to be type "
168  << type->get_name() << ".\n";
169  } else {
170  pnmimage_cat.debug()
171  << "Unable to determine image file type from magic number.\n";
172  }
173  }
174  }
175 
176  if (type == nullptr && !filename.empty()) {
177  // We still don't know the type; attempt to guess it from the filename
178  // extension.
180 
181  if (pnmimage_cat.is_debug()) {
182  if (type != nullptr) {
183  pnmimage_cat.debug()
184  << "From its extension, image file is probably type "
185  << type->get_name() << ".\n";
186  } else {
187  pnmimage_cat.debug()
188  << "Unable to guess image file type from its extension ("
189  << filename.get_extension() << ").\n";
190  }
191  }
192  }
193 
194  if (type == nullptr) {
195  // No? How about the default type associated with this image header.
196  type = _type;
197 
198  if (pnmimage_cat.is_debug() && type != nullptr) {
199  pnmimage_cat.debug()
200  << "Assuming image file type is " << type->get_name() << ".\n";
201  }
202  }
203 
204  if (type == nullptr) {
205  // We can't figure out what type the file is; give up.
206  if (report_unknown_type && pnmimage_cat.is_error()) {
207  pnmimage_cat.error()
208  << "Cannot determine type of image file " << filename << ".\n"
209  << "Currently supported image types:\n";
211  write(pnmimage_cat.error(false), 2);
212  }
213  if (owns_file) {
215 
216  // We're assuming here that the file was opened via VFS. That may not
217  // necessarily be the case, but we don't make that distinction.
218  // However, at the moment at least, that distinction doesn't matter,
219  // since vfs->close_read_file() just deletes the file pointer anyway.
220  vfs->close_read_file(file);
221  }
222  return nullptr;
223  }
224 
225  PNMReader *reader = type->make_reader(file, owns_file, magic_number);
226  if (reader == nullptr && owns_file) {
228  vfs->close_read_file(file);
229  }
230 
231  if (!reader->is_valid()) {
232  delete reader;
233  reader = nullptr;
234  }
235 
236  return reader;
237 }
238 
239 /**
240  * Returns a newly-allocated PNMWriter of the suitable type for writing an
241  * image to the indicated filename, or NULL if the filename cannot be written
242  * for some reason. The filename "-" always stands for standard output. If
243  * type is specified, it is a suggestion for the file type to use.
244  *
245  * The PNMWriter should be deleted when it is no longer needed.
246  */
248 make_writer(const Filename &filename, PNMFileType *type) const {
249  if (pnmimage_cat.is_debug()) {
250  pnmimage_cat.debug()
251  << "Writing image to " << filename << "\n";
252  }
253  bool owns_file = false;
254  ostream *file = nullptr;
255 
256  if (filename == "-") {
257  owns_file = false;
258  file = &std::cout;
259 
260  if (pnmimage_cat.is_debug()) {
261  pnmimage_cat.debug()
262  << "(writing to standard output)\n";
263  }
264 
265  } else {
267  Filename actual_name = Filename::binary_filename(filename);
268  file = vfs->open_write_file(actual_name, true, true);
269  if (file != nullptr) {
270  owns_file = true;
271  }
272  }
273 
274  if (file == nullptr) {
275  if (pnmimage_cat.is_debug()) {
276  pnmimage_cat.debug()
277  << "Unable to write to file.\n";
278  }
279  return nullptr;
280  }
281 
282  return make_writer(file, owns_file, filename, type);
283 }
284 
285 /**
286  * Returns a newly-allocated PNMWriter of the suitable type for writing to the
287  * already-opened image file, or NULL if the file cannot be written for some
288  * reason.
289  *
290  * owns_file should be set true if the PNMWriter is to be considered the owner
291  * of the stream pointer (in which case the stream will be deleted on
292  * completion, whether successful or not), or false if it should not delete
293  * it.
294  *
295  * The filename parameter is optional here, since the file has already been
296  * opened; it is only used to examine the extension and attempt to guess the
297  * intended file type.
298  *
299  * If type is non-NULL, it is a suggestion for the file type to use.
300  *
301  * The PNMWriter should be deleted when it is no longer needed.
302  */
304 make_writer(ostream *file, bool owns_file, const Filename &filename,
305  PNMFileType *type) const {
306  if (type == nullptr && !filename.empty()) {
307  // We don't know the type; attempt to guess it from the filename
308  // extension.
310 
311  if (pnmimage_cat.is_debug()) {
312  if (type != nullptr) {
313  pnmimage_cat.debug()
314  << "From its extension, image file is intended to be type "
315  << type->get_name() << ".\n";
316  } else {
317  pnmimage_cat.debug()
318  << "Unable to guess image file type from its extension.\n";
319  }
320  }
321  }
322 
323  if (type == nullptr) {
324  // No? How about the default type associated with this image header.
325  type = _type;
326 
327  if (pnmimage_cat.is_debug() && type != nullptr) {
328  pnmimage_cat.debug()
329  << "Assuming image file type is " << type->get_name() << ".\n";
330  }
331  }
332 
333  if (type == nullptr) {
334  // We can't figure out what type the file is; give up.
335  if (pnmimage_cat.is_debug()) {
336  pnmimage_cat.debug()
337  << "Cannot determine type of image file " << filename << ".\n";
338  }
339  if (owns_file) {
340  delete file;
341  }
342  return nullptr;
343  }
344 
345  PNMWriter *writer = type->make_writer(file, owns_file);
346  if (writer == nullptr && owns_file) {
347  delete file;
348  }
349 
350  if (writer != nullptr && !writer->is_valid()) {
351  delete writer;
352  writer = nullptr;
353  }
354 
355  return writer;
356 }
357 
358 /**
359  * Ensures that the first n bytes of the file are read into magic_number. If
360  * magic_number is initially nonempty, assumes these represent the first few
361  * bytes already extracted. Returns true if successful, false if an end of
362  * file or error occurred before num_bytes could be read.
363  */
364 bool PNMImageHeader::
365 read_magic_number(istream *file, string &magic_number, int num_bytes) {
366  while ((int)magic_number.size() < num_bytes) {
367  int ch = file->get();
368  if (file->eof() || file->fail()) {
369  return false;
370  }
371  magic_number += (char)ch;
372  }
373 
374  return true;
375 }
376 
377 /**
378  *
379  */
380 void PNMImageHeader::
381 output(ostream &out) const {
382  out << "image: " << _x_size << " by " << _y_size << " pixels, "
383  << _num_channels << " channels, " << _maxval << " maxval.";
384 }
385 
386 /**
387  * Computes a histogram of the colors used in the indicated rgb/grayscale
388  * array and/or alpha array. This is most likely to be useful in a PNMWriter
389  * class, but it is defined at this level in case it has general utilty for
390  * PNMImages.
391  *
392  * Also see PNMImage::make_histogram(), which is a higher-level function.
393  *
394  * The max_colors parameter, if greater than zero, limits the maximum number
395  * of colors we are interested in. If we encounter more than this number of
396  * colors, the function aborts before completion and returns false; otherwise,
397  * it returns true.
398  */
399 bool PNMImageHeader::
400 compute_histogram(PNMImageHeader::HistMap &hist,
401  xel *array, xelval *alpha, int max_colors) {
402  int num_pixels = _x_size * _y_size;
403  int pi;
404 
405  switch (get_color_type()) {
406  case CT_invalid:
407  return false;
408 
409  case CT_grayscale:
410  for (pi = 0; pi < num_pixels; pi++) {
411  record_color(hist, PixelSpec(PPM_GETB(array[pi])));
412  if (max_colors > 0 && (int)hist.size() > max_colors) {
413  return false;
414  }
415  }
416  return true;
417 
418  case CT_two_channel:
419  for (pi = 0; pi < num_pixels; pi++) {
420  record_color(hist, PixelSpec(PPM_GETB(array[pi]), alpha[pi]));
421  if (max_colors > 0 && (int)hist.size() > max_colors) {
422  return false;
423  }
424  }
425  return true;
426 
427  case CT_color:
428  for (pi = 0; pi < num_pixels; pi++) {
429  record_color(hist, PixelSpec(PPM_GETR(array[pi]), PPM_GETG(array[pi]), PPM_GETB(array[pi])));
430  if (max_colors > 0 && (int)hist.size() > max_colors) {
431  return false;
432  }
433  }
434  return true;
435 
436  case CT_four_channel:
437  for (pi = 0; pi < num_pixels; pi++) {
438  record_color(hist, PixelSpec(PPM_GETR(array[pi]), PPM_GETG(array[pi]), PPM_GETB(array[pi]), alpha[pi]));
439  if (max_colors > 0 && (int)hist.size() > max_colors) {
440  return false;
441  }
442  }
443  return true;
444  }
445 
446  return false;
447 }
448 
449 /**
450  * Returns a linear list of all of the colors in the image, similar to
451  * compute_histogram().
452  */
453 bool PNMImageHeader::
454 compute_palette(PNMImageHeader::Palette &palette,
455  xel *array, xelval *alpha, int max_colors) {
456  HistMap hist;
457 
458  int num_pixels = _x_size * _y_size;
459 
460  // In case there are already entries in the palette, preserve them.
461  Palette::const_iterator pi;
462  for (pi = palette.begin(); pi != palette.end(); ++pi) {
463  hist.insert(HistMap::value_type(*pi, num_pixels + 1));
464  }
465 
466  if (!compute_histogram(hist, array, alpha, max_colors)) {
467  return false;
468  }
469 
470  // Now append the new entries discovered in the histogram onto the end of
471  // the palette.
472  palette.reserve(hist.size());
473  HistMap::const_iterator hi;
474  for (hi = hist.begin(); hi != hist.end(); ++hi) {
475  if ((*hi).second <= num_pixels) {
476  palette.push_back((*hi).first);
477  }
478  }
479 
480  return true;
481 }
482 
483 /**
484  *
485  */
486 void PNMImageHeader::PixelSpec::
487 output(ostream &out) const {
488  out << "(" << _red << ", " << _green << ", " << _blue << ", " << _alpha << ")";
489 }
490 
491 /**
492  *
493  */
494 void PNMImageHeader::Histogram::
495 write(ostream &out) const {
496  out << "Histogram: {\n";
497  PixelCount::const_iterator pi;
498  for (pi = _pixels.begin(); pi != _pixels.end(); ++pi) {
499  out << " " << (*pi)._pixel << ": " << (*pi)._count << ",\n";
500  }
501  out << "}\n";
502 }
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
bool read_header(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true)
Opens up the image file and tries to read its header information to determine its size,...
bool is_valid() const
Returns true if the PNMWriter can be used to write data, false if something is wrong.
Definition: pnmWriter.I:92
PNMReader * make_reader(const Filename &filename, PNMFileType *type=nullptr, bool report_unknown_type=true) const
Returns a newly-allocated PNMReader of the suitable type for reading from the indicated image filenam...
PNMWriter * make_writer(const Filename &filename, PNMFileType *type=nullptr) const
Returns a newly-allocated PNMWriter of the suitable type for writing an image to the indicated filena...
A hierarchy of directories and files that appears to be one continuous file system,...
std::istream * open_read_file(const Filename &filename, bool auto_unwrap) const
Convenience function; returns a newly allocated istream if the file exists and can be read,...
virtual PNMReader * make_reader(std::istream *file, bool owns_file=true, const std::string &magic_number=std::string())
Allocates and returns a new PNMReader suitable for reading from this file type, if possible.
Definition: pnmFileType.cxx:96
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:32
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
bool is_valid() const
Returns true if the PNMReader can be used to read data, false if something is wrong.
Definition: pnmReader.I:53
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
This is an abstract base class that defines the interface for reading image files of various types.
Definition: pnmReader.h:27
PNMFileType * get_type_from_extension(const std::string &filename) const
Tries to determine what the PNMFileType is likely to be for a particular image file based on its exte...
This is an abstract base class that defines the interface for writing image files of various types.
Definition: pnmWriter.h:27
std::string get_extension() const
Returns the file extension.
Definition: filename.I:400
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static bool read_magic_number(std::istream *file, std::string &magic_number, int num_bytes)
Ensures that the first n bytes of the file are read into magic_number.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
std::ostream * open_write_file(const Filename &filename, bool auto_wrap, bool truncate)
Convenience function; returns a newly allocated ostream if the file exists and can be written,...
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
ColorType get_color_type() const
Returns the image type of the image, as an enumerated value.
virtual PNMWriter * make_writer(std::ostream *file, bool owns_file=true)
Allocates and returns a new PNMWriter suitable for reading from this file type, if possible.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.