Panda3D
Loading...
Searching...
No Matches
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
23using std::istream;
24using std::ostream;
25using 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 */
33read_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 */
58read_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 */
80make_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 */
139make_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 */
248make_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 */
304make_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 */
365read_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 */
380void PNMImageHeader::
381output(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 */
399bool PNMImageHeader::
400compute_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 */
453bool PNMImageHeader::
454compute_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 */
486void PNMImageHeader::PixelSpec::
487output(ostream &out) const {
488 out << "(" << _red << ", " << _green << ", " << _blue << ", " << _alpha << ")";
489}
490
491/**
492 *
493 */
494void PNMImageHeader::Histogram::
495write(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:44
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.
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.