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  */
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 }
The name of a file, such as a texture file or an Egg file.
Definition: filename.h:39
std::string get_extension() const
Returns the file extension.
Definition: filename.I:400
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
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 the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:32
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
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.
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.
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,...
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...
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...
ColorType get_color_type() const
Returns the image type of the image, as an enumerated value.
This is an abstract base class that defines the interface for reading image files of various types.
Definition: pnmReader.h:27
bool is_valid() const
Returns true if the PNMReader can be used to read data, false if something is wrong.
Definition: pnmReader.I:53
This is an abstract base class that defines the interface for writing image files of various types.
Definition: pnmWriter.h:27
bool is_valid() const
Returns true if the PNMWriter can be used to write data, false if something is wrong.
Definition: pnmWriter.I:92
A hierarchy of directories and files that appears to be one continuous file system,...
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,...
static void close_read_file(std::istream *stream)
Closes a file opened by a previous call to open_read_file().
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,...
static VirtualFileSystem * get_global_ptr()
Returns the default global VirtualFileSystem.
This is our own Panda specialization on the default STL vector.
Definition: pvector.h:42
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.