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