Panda3D

Loading...
Searching...
No Matches
pnmFileTypeEXR.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 pnmFileTypeEXR.cxx
10 * @author drose
11 * @date 2000-06-19
12 */
13
14#include "pnmFileTypeEXR.h"
15
16#ifdef HAVE_OPENEXR
17
18#include "config_pnmimagetypes.h"
19
20#include "pnmFileTypeRegistry.h"
21#include "bamReader.h"
22#include "pfmFile.h"
23
24#include <ImfOutputFile.h>
25#include <ImfChannelList.h>
26#include <ImfVersion.h>
27#include <ImfIO.h>
28#include <ImfFrameBuffer.h>
29#include <ImfHeader.h>
30
31#include <ImathBox.h>
32
33#ifndef IMATH_NAMESPACE
34#define IMATH_NAMESPACE Imath
35#endif
36
37 using std::istream;
38 using std::ostream;
39 using std::string;
40
41 TypeHandle PNMFileTypeEXR::_type_handle;
42
43 static const char * const extensions_exr[] = {
44 "exr"
45};
46 static const int num_extensions_exr = sizeof(extensions_exr) / sizeof(const char *);
47
48// A wrapper class to map OpenEXR's OStream class onto std::ostream.
49 class ImfStdOstream : public IMF::OStream {
50 public:
51 ImfStdOstream(std::ostream &strm) : IMF::OStream("ostream"), _strm(strm) {}
52
53 virtual void write(const char c[/*n*/], int n) {
54 _strm.write(c, n);
55 }
56
57#if OPENEXR_VERSION_MAJOR >= 3
58 virtual uint64_t tellp() {
59 return _strm.tellp();
60 }
61
62 virtual void seekp(uint64_t pos) {
63 _strm.seekp(pos);
64 }
65#else
66 virtual IMF::Int64 tellp() {
67 return _strm.tellp();
68 }
69
70 virtual void seekp(IMF::Int64 pos) {
71 _strm.seekp(pos);
72 }
73#endif
74
75 private:
76 std::ostream &_strm;
77};
78
79// A wrapper class to map OpenEXR's IStream class onto std::istream.
80 class ImfStdIstream : public IMF::IStream {
81 public:
82 ImfStdIstream(std::istream &strm, const std::string &magic_number) : IMF::IStream("istream"), _strm(strm) {
83 // Start by putting back the magic number.
84 for (std::string::const_reverse_iterator mi = magic_number.rbegin();
85 mi != magic_number.rend();
86 mi++) {
87 _strm.putback(*mi);
88 }
89 }
90
91 virtual bool isMemoryMapped () const {
92 return false;
93 }
94
95 virtual bool read (char c[/*n*/], int n) {
96 _strm.read(c, n);
97 if (_strm.gcount() != n) {
98 throw std::exception();
99 }
100
101 bool not_eof = !_strm.eof();
102 return not_eof;
103 }
104
105#if OPENEXR_VERSION_MAJOR >= 3
106 virtual uint64_t tellg() {
107 return _strm.tellg();
108 }
109
110 virtual void seekg(uint64_t pos) {
111 _strm.seekg(pos);
112 }
113#else
114 virtual IMF::Int64 tellg() {
115 return _strm.tellg();
116 }
117
118 virtual void seekg(IMF::Int64 pos) {
119 _strm.seekg(pos);
120 }
121#endif
122
123 virtual void clear() {
124 _strm.clear();
125 }
126
127 private:
128 std::istream &_strm;
129};
130
131PNMFileTypeEXR::
132PNMFileTypeEXR() {
133}
134
135/**
136 * Returns a few words describing the file type.
137 */
138 string PNMFileTypeEXR::
139get_name() const {
140 return "OpenEXR";
141}
142
143/**
144 * Returns the number of different possible filename extensions associated
145 * with this particular file type.
146 */
147 int PNMFileTypeEXR::
148get_num_extensions() const {
149 return num_extensions_exr;
150}
151
152/**
153 * Returns the nth possible filename extension associated with this particular
154 * file type, without a leading dot.
155 */
156 string PNMFileTypeEXR::
157get_extension(int n) const {
158 nassertr(n >= 0 && n < num_extensions_exr, string());
159 return extensions_exr[n];
160}
161
162/**
163 * Returns a suitable filename extension (without a leading dot) to suggest
164 * for files of this type, or empty string if no suggestions are available.
165 */
166 string PNMFileTypeEXR::
167get_suggested_extension() const {
168 return "exr";
169}
170
171/**
172 * Returns true if this particular file type uses a magic number to identify
173 * it, false otherwise.
174 */
175 bool PNMFileTypeEXR::
176has_magic_number() const {
177 return true;
178}
179
180/**
181 * Returns true if the indicated "magic number" byte stream (the initial few
182 * bytes read from the file) matches this particular file type, false
183 * otherwise.
184 */
185 bool PNMFileTypeEXR::
186matches_magic_number(const string &magic_number) const {
187 nassertr(magic_number.size() >= 2, false);
188
189 if (magic_number.size() >= 4) {
190 // If we have already read all four bytes, use the built-in
191 // function to check them.
192 return IMF::isImfMagic(magic_number.data());
193 } else {
194 // Otherwise, check only the first two bytes and call it good enough.
195 return magic_number[0] == ((IMF::MAGIC >> 0) & 0x00ff) &&
196 magic_number[1] == ((IMF::MAGIC >> 8) & 0x00ff);
197 }
198}
199
200/**
201 * Allocates and returns a new PNMReader suitable for reading from this file
202 * type, if possible. If reading from this file type is not supported,
203 * returns NULL.
204 */
205 PNMReader *PNMFileTypeEXR::
206make_reader(istream *file, bool owns_file, const string &magic_number) {
207 init_pnm();
208 return new Reader(this, file, owns_file, magic_number);
209}
210
211/**
212 * Allocates and returns a new PNMWriter suitable for reading from this file
213 * type, if possible. If writing files of this type is not supported, returns
214 * NULL.
215 */
216 PNMWriter *PNMFileTypeEXR::
217make_writer(ostream *file, bool owns_file) {
218 init_pnm();
219 return new Writer(this, file, owns_file);
220}
221
222/**
223 *
224 */
225PNMFileTypeEXR::Reader::
226Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
227 PNMReader(type, file, owns_file),
228 _strm(new ImfStdIstream(*_file, magic_number)),
229 _imf_file(*_strm)
230{
231 const IMF::Header &header = _imf_file.header();
232
233 IMATH_NAMESPACE::Box2i dw = header.dataWindow();
234 _x_size = dw.max.x - dw.min.x + 1;
235 _y_size = dw.max.y - dw.min.y + 1;
236
237 // Find the channels we care about, and ensure they're placed in the
238 // correct order.
239 _channel_names.clear();
240
241 const IMF::ChannelList &channels = header.channels();
242
243 // Note: including Y in this list allows us to handle grayscale or
244 // grayscale/alpha images correctly, but also incorrectly detects
245 // luminance/chroma images as grayscale only. However, these kind
246 // of images are a pain to handle anyway, so maybe that's OK.
247 const char *possible_channel_names[] = { "R", "G", "B", "Y", "A", nullptr };
248 for (const char **pni = possible_channel_names; *pni != nullptr; ++pni) {
249 std::string name = *pni;
250 IMF::ChannelList::ConstIterator ci = channels.find(name.c_str());
251 if (ci != channels.end()) {
252 // Found a match.
253 if (name == "Y" && !_channel_names.empty()) {
254 // Y is luminance or grayscale. Ignore Y if there are
255 // already any RGB channels.
256 } else {
257 _channel_names.push_back(name);
258 }
259 }
260 }
261
262 if (_channel_names.empty()) {
263 // Didn't find any channel names that match R, G, B, A, so just
264 // ask for RGB anyway and trust the OpenEXR library to do the
265 // right thing. Actually, it just fills them with black, but
266 // whatever.
267 _channel_names.push_back("R");
268 _channel_names.push_back("G");
269 _channel_names.push_back("B");
270 }
271
272 _num_channels = (int)_channel_names.size();
273 if (_num_channels == 0 || _num_channels > 4) {
274 _is_valid = false;
275 return;
276 }
277 // We read all OpenEXR files to floating-point, even UINT type, so
278 // _maxval doesn't matter. But we set it anyway.
279 _maxval = 65535;
280
281 _is_valid = true;
282}
283
284/**
285 *
286 */
287PNMFileTypeEXR::Reader::
288~Reader() {
289 delete _strm;
290}
291
292/**
293 * Returns true if this PNMFileType represents a floating-point image type,
294 * false if it is a normal, integer type. If this returns true, read_pfm() is
295 * implemented instead of read_data().
296 */
297 bool PNMFileTypeEXR::Reader::
298is_floating_point() {
299 // We read everything to floating-point, since even the UINT type is
300 // 32 bits, more fidelity than we can represent in our 16-bit
301 // PNMImage.
302 return true;
303}
304
305/**
306 * Reads floating-point data directly into the indicated PfmFile. Returns
307 * true on success, false on failure.
308 */
309 bool PNMFileTypeEXR::Reader::
310read_pfm(PfmFile &pfm) {
311 pfm.clear(_x_size, _y_size, _num_channels);
312 vector_float table;
313 pfm.swap_table(table);
314
315 PN_float32 *table_data = table.data();
316 size_t x_stride = sizeof(PN_float32) * pfm.get_num_channels();
317 size_t y_stride = x_stride * pfm.get_x_size();
318 nassertr(y_stride * pfm.get_y_size() <= table.size() * sizeof(PN_float32), false);
319
320 const IMF::Header &header = _imf_file.header();
321 IMATH_NAMESPACE::Box2i dw = header.dataWindow();
322
323 IMF::FrameBuffer frameBuffer;
324 for (int ci = 0; ci < pfm.get_num_channels(); ++ci) {
325 char *base = (char *)(table_data - (dw.min.x + dw.min.y * pfm.get_x_size()) * pfm.get_num_channels() + ci);
326 frameBuffer.insert(_channel_names[ci].c_str(),
327 IMF::Slice(IMF::FLOAT, base, x_stride, y_stride,
328 1, 1, 0.0));
329 }
330
331 _imf_file.setFrameBuffer(frameBuffer);
332
333 try {
334 _imf_file.readPixels(dw.min.y, dw.max.y);
335 } catch (const std::exception &exc) {
336 pnmimage_exr_cat.error()
337 << exc.what() << "\n";
338 return false;
339 }
340
341 pfm.swap_table(table);
342 return true;
343}
344
345/**
346 * Reads in an entire image all at once, storing it in the pre-allocated
347 * _x_size * _y_size array and alpha pointers. (If the image type has no
348 * alpha channel, alpha is ignored.) Returns the number of rows correctly
349 * read.
350 *
351 * Derived classes need not override this if they instead provide
352 * supports_read_row() and read_row(), below.
353 */
354 int PNMFileTypeEXR::Reader::
355read_data(xel *array, xelval *alpha) {
356 // This should never come here, since we always read to
357 // floating-point data.
358 nassertr(false, 0);
359 return 0;
360}
361
362/**
363 *
364 */
365PNMFileTypeEXR::Writer::
366Writer(PNMFileType *type, ostream *file, bool owns_file) :
367 PNMWriter(type, file, owns_file)
368{
369}
370
371/**
372 * Returns true if this PNMFileType can accept a floating-point image type,
373 * false if it can only accept a normal, integer type. If this returns true,
374 * write_pfm() is implemented.
375 */
376 bool PNMFileTypeEXR::Writer::
377supports_floating_point() {
378 return true;
379}
380
381/**
382 * Returns true if this PNMFileType can accept an integer image type, false if
383 * it can only accept a floating-point type. If this returns true,
384 * write_data() or write_row() is implemented.
385 */
386 bool PNMFileTypeEXR::Writer::
387supports_integer() {
388 return false;
389}
390
391/**
392 * Writes floating-point data from the indicated PfmFile. Returns true on
393 * success, false on failure.
394 */
395 bool PNMFileTypeEXR::Writer::
396write_pfm(const PfmFile &pfm) {
397 const vector_float &table = pfm.get_table();
398 const PN_float32 *table_data = table.data();
399 size_t x_stride = sizeof(PN_float32) * pfm.get_num_channels();
400 size_t y_stride = x_stride * pfm.get_x_size();
401 nassertr(y_stride * pfm.get_y_size() <= table.size() * sizeof(PN_float32), false);
402
403 const char *channel_names_1[] = { "G" };
404 const char *channel_names_2[] = { "G", "A" };
405 const char *channel_names_3[] = { "R", "G", "B" };
406 const char *channel_names_4[] = { "R", "G", "B", "A" };
407 const char **channel_names = nullptr;
408
409 switch (pfm.get_num_channels()) {
410 case 1:
411 channel_names = channel_names_1;
412 break;
413
414 case 2:
415 channel_names = channel_names_2;
416 break;
417
418 case 3:
419 channel_names = channel_names_3;
420 break;
421
422 case 4:
423 channel_names = channel_names_4;
424 break;
425
426 default:
427 return false;
428 };
429
430 IMF::Header header(pfm.get_x_size(), pfm.get_y_size());
431 for (int ci = 0; ci < pfm.get_num_channels(); ++ci) {
432 header.channels().insert(channel_names[ci], IMF::Channel(IMF::FLOAT));
433 }
434
435 IMF::FrameBuffer frameBuffer;
436 for (int ci = 0; ci < pfm.get_num_channels(); ++ci) {
437 const char *base = (const char *)(table_data + ci);
438 frameBuffer.insert(channel_names[ci],
439 IMF::Slice(IMF::FLOAT, (char *)base, x_stride, y_stride));
440 }
441
442 ImfStdOstream strm(*_file);
443 IMF::OutputFile file(strm, header);
444 file.setFrameBuffer(frameBuffer);
445
446 try {
447 file.writePixels(pfm.get_y_size());
448 } catch (const std::exception &exc) {
449 pnmimage_exr_cat.error()
450 << exc.what() << "\n";
451 return false;
452 }
453
454 return true;
455}
456
457/**
458 * Writes out an entire image all at once, including the header, based on the
459 * image data stored in the given _x_size * _y_size array and alpha pointers.
460 * (If the image type has no alpha channel, alpha is ignored.) Returns the
461 * number of rows correctly written.
462 *
463 * It is the user's responsibility to fill in the header data via calls to
464 * set_x_size(), set_num_channels(), etc., or copy_header_from(), before
465 * calling write_data().
466 *
467 * It is important to delete the PNMWriter class after successfully writing
468 * the data. Failing to do this may result in some data not getting flushed!
469 *
470 * Derived classes need not override this if they instead provide
471 * supports_streaming() and write_row(), below.
472 */
473 int PNMFileTypeEXR::Writer::
474write_data(xel *array, xelval *alpha) {
475 // This should never come here, since we always write to
476 // floating-point data.
477 nassertr(false, 0);
478 return 0;
479}
480
481/**
482 * Registers the current object as something that can be read from a Bam file.
483 */
484 void PNMFileTypeEXR::
485register_with_read_factory() {
486 BamReader::get_factory()->
487 register_factory(get_class_type(), make_PNMFileTypeEXR);
488}
489
490/**
491 * This method is called by the BamReader when an object of this type is
492 * encountered in a Bam file; it should allocate and return a new object with
493 * all the data read.
494 *
495 * In the case of the PNMFileType objects, since these objects are all shared,
496 * we just pull the object from the registry.
497 */
498 TypedWritable *PNMFileTypeEXR::
499make_PNMFileTypeEXR(const FactoryParams &params) {
500 return PNMFileTypeRegistry::get_global_ptr()->get_type_by_handle(get_class_type());
501}
502
503#endif // HAVE_OPENEXR
bamReader.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
BamReader::get_factory
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition bamReader.I:177
FactoryParams
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition factoryParams.h:36
PNMFileTypeRegistry::get_type_by_handle
PNMFileType * get_type_by_handle(TypeHandle handle) const
Returns the PNMFileType instance stored in the registry for the given TypeHandle, e....
Definition pnmFileTypeRegistry.cxx:233
PNMFileTypeRegistry::get_global_ptr
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
Definition pnmFileTypeRegistry.cxx:277
PNMFileType
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition pnmFileType.h:32
PNMImageHeader::get_x_size
int get_x_size() const
Returns the number of pixels in the X direction.
Definition pnmImageHeader.I:144
PNMImageHeader::get_num_channels
get_num_channels
Returns the number of channels in the image.
Definition pnmImageHeader.h:60
PNMImageHeader::get_y_size
int get_y_size() const
Returns the number of pixels in the Y direction.
Definition pnmImageHeader.I:153
PNMReader
This is an abstract base class that defines the interface for reading image files of various types.
Definition pnmReader.h:27
PNMWriter
This is an abstract base class that defines the interface for writing image files of various types.
Definition pnmWriter.h:27
PfmFile
Defines a pfm file, a 2-d table of floating-point numbers, either 3-component or 1-component,...
Definition pfmFile.h:31
PfmFile::swap_table
void swap_table(vector_float &table)
This is a very low-level function that completely exchanges the PfmFile's internal table of floating-...
Definition pfmFile.I:549
PfmFile::get_table
const vector_float & get_table() const
This is a very low-level function that returns a read-only reference to the internal table of floatin...
Definition pfmFile.I:538
PfmFile::clear
void clear()
Eliminates all data in the file.
Definition pfmFile.cxx:77
TypeHandle
TypeHandle is the identifier used to differentiate C++ class types.
Definition typeHandle.h:81
TypedWritable
Base class for objects that can be written to and read from Bam files.
Definition typedWritable.h:35
array
STL class.
istream
STL class.
ostream
STL class.
config_pnmimagetypes.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pfmFile.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pnmFileTypeEXR.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
pnmFileTypeRegistry.h
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.