Panda3D
pnmFileTypePNG.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 pnmFileTypePNG.cxx
10 * @author drose
11 * @date 2004-03-16
12 */
13
14#include "pnmFileTypePNG.h"
15
16#ifdef HAVE_PNG
17
19
20#include "pnmFileTypeRegistry.h"
21#include "bamReader.h"
22#include "thread.h"
23
24using std::istream;
25using std::ostream;
26using std::string;
27
28static const char * const extensions_png[] = {
29 "png"
30};
31static const int num_extensions_png = sizeof(extensions_png) / sizeof(const char *);
32
33TypeHandle PNMFileTypePNG::_type_handle;
34
35static const int png_max_palette = 256;
36
37// This STL comparison functor is used in write_data(), below. It sorts the
38// non-maxval alpha pixels to the front of the list.
39class LowAlphaCompare {
40public:
41 bool operator() (const PNMImageHeader::PixelSpec &a,
43 if (a._alpha != b._alpha) {
44 return a._alpha < b._alpha;
45 }
46 return a < b;
47 }
48};
49
50/**
51 *
52 */
53PNMFileTypePNG::
54PNMFileTypePNG() {
55 // This constructor may run at static init time, so we use the ->
56 // dereferencing convention on the notify category.
57 if (pnmimage_png_cat->is_debug()) {
58 pnmimage_png_cat->debug()
59 << "PNG version " << PNG_LIBPNG_VER << "\n";
60 }
61}
62
63/**
64 * Returns a few words describing the file type.
65 */
66string PNMFileTypePNG::
67get_name() const {
68 return "PNG";
69}
70
71/**
72 * Returns the number of different possible filename extensions associated
73 * with this particular file type.
74 */
75int PNMFileTypePNG::
76get_num_extensions() const {
77 return num_extensions_png;
78}
79
80/**
81 * Returns the nth possible filename extension associated with this particular
82 * file type, without a leading dot.
83 */
84string PNMFileTypePNG::
85get_extension(int n) const {
86 nassertr(n >= 0 && n < num_extensions_png, string());
87 return extensions_png[n];
88}
89
90/**
91 * Returns a suitable filename extension (without a leading dot) to suggest
92 * for files of this type, or empty string if no suggestions are available.
93 */
94string PNMFileTypePNG::
95get_suggested_extension() const {
96 return "png";
97}
98
99/**
100 * Returns true if this particular file type uses a magic number to identify
101 * it, false otherwise.
102 */
103bool PNMFileTypePNG::
104has_magic_number() const {
105 return true;
106}
107
108/**
109 * Returns true if the indicated "magic number" byte stream (the initial few
110 * bytes read from the file) matches this particular file type, false
111 * otherwise.
112 */
113bool PNMFileTypePNG::
114matches_magic_number(const string &magic_number) const {
115 return png_sig_cmp((png_bytep)magic_number.data(), 0, magic_number.length()) == 0;
116}
117
118/**
119 * Allocates and returns a new PNMReader suitable for reading from this file
120 * type, if possible. If reading from this file type is not supported,
121 * returns NULL.
122 */
123PNMReader *PNMFileTypePNG::
124make_reader(istream *file, bool owns_file, const string &magic_number) {
125 init_pnm();
126 return new Reader(this, file, owns_file, magic_number);
127}
128
129/**
130 * Allocates and returns a new PNMWriter suitable for reading from this file
131 * type, if possible. If writing files of this type is not supported, returns
132 * NULL.
133 */
134PNMWriter *PNMFileTypePNG::
135make_writer(ostream *file, bool owns_file) {
136 init_pnm();
137 return new Writer(this, file, owns_file);
138}
139
140/**
141 * Registers the current object as something that can be read from a Bam file.
142 */
143void PNMFileTypePNG::
144register_with_read_factory() {
146 register_factory(get_class_type(), make_PNMFileTypePNG);
147}
148
149/**
150 * This method is called by the BamReader when an object of this type is
151 * encountered in a Bam file; it should allocate and return a new object with
152 * all the data read.
153 *
154 * In the case of the PNMFileType objects, since these objects are all shared,
155 * we just pull the object from the registry.
156 */
157TypedWritable *PNMFileTypePNG::
158make_PNMFileTypePNG(const FactoryParams &params) {
160}
161
162/**
163 *
164 */
165PNMFileTypePNG::Reader::
166Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
167 PNMReader(type, file, owns_file)
168{
169 _png = nullptr;
170 _info = nullptr;
171 _is_valid = false;
172
173 _png = png_create_read_struct(PNG_LIBPNG_VER_STRING, nullptr,
174 png_error, png_warning);
175 if (_png == nullptr) {
176 return;
177 }
178
179 _info = png_create_info_struct(_png);
180 if (_info == nullptr) {
181 png_destroy_read_struct(&_png, nullptr, nullptr);
182 return;
183 }
184
185 _is_valid = true;
186
187 if (setjmp(_jmpbuf)) {
188 // This is the ANSI C way to handle exceptions. If setjmp(), above,
189 // returns true, it means that libpng detected an exception while
190 // executing the code that reads the header info, below.
191 free_png();
192 return;
193 }
194
195 png_set_read_fn(_png, (void *)this, png_read_data);
196 png_set_sig_bytes(_png, magic_number.length());
197
198 png_read_info(_png, _info);
199
200 png_uint_32 width;
201 png_uint_32 height;
202 int bit_depth;
203 int color_type;
204 int srgb_intent;
205 double gamma;
206
207 png_get_IHDR(_png, _info, &width, &height,
208 &bit_depth, &color_type, nullptr, nullptr, nullptr);
209
210 // Look for an sRGB chunk.
211 if (png_get_sRGB(_png, _info, &srgb_intent) == PNG_INFO_sRGB) {
212 _color_space = CS_sRGB;
213
214 } else if (png_get_gAMA(_png, _info, &gamma) == PNG_INFO_gAMA) {
215 // File specifies a gamma.
216 if (gamma >= 0.99 && gamma <= 1.01) {
217 _color_space = CS_linear;
218
219 } else if (gamma >= 0.44999 && gamma <= 0.455001) {
220 // It's probably close enough to sRGB.
221 _color_space = CS_sRGB;
222
223 } else {
224 pnmimage_png_cat.warning()
225 << "Unsupported image gamma " << gamma << ", "
226 << "please re-export image as sRGB or linear.\n";
227 }
228 }
229
230 if (pnmimage_png_cat.is_debug()) {
231 pnmimage_png_cat.debug()
232 << "width = " << width << " height = " << height << " bit_depth = "
233 << bit_depth << " color_type = " << color_type
234 << " color_space = " << _color_space << "\n";
235 }
236
237 _x_size = width;
238 _y_size = height;
239 _maxval = (1 << bit_depth) - 1;
240
241 if (bit_depth < 8) {
242 png_set_packing(_png);
243 }
244
245 switch (color_type) {
246 case PNG_COLOR_TYPE_GRAY:
247 if (pnmimage_png_cat.is_debug()) {
248 pnmimage_png_cat.debug()
249 << "PNG_COLOR_TYPE_GRAY\n";
250 }
251 _num_channels = 1;
252 break;
253
254 case PNG_COLOR_TYPE_GRAY_ALPHA:
255 if (pnmimage_png_cat.is_debug()) {
256 pnmimage_png_cat.debug()
257 << "PNG_COLOR_TYPE_GRAY_ALPHA\n";
258 }
259 _num_channels = 2;
260 break;
261
262 case PNG_COLOR_TYPE_RGB:
263 if (pnmimage_png_cat.is_debug()) {
264 pnmimage_png_cat.debug()
265 << "PNG_COLOR_TYPE_RGB\n";
266 }
267 _num_channels = 3;
268 break;
269
270 case PNG_COLOR_TYPE_RGB_ALPHA:
271 if (pnmimage_png_cat.is_debug()) {
272 pnmimage_png_cat.debug()
273 << "PNG_COLOR_TYPE_RGB_ALPHA\n";
274 }
275 _num_channels = 4;
276 break;
277
278 case PNG_COLOR_TYPE_PALETTE:
279 if (pnmimage_png_cat.is_debug()) {
280 pnmimage_png_cat.debug()
281 << "PNG_COLOR_TYPE_PALETTE\n";
282 }
283 png_set_palette_to_rgb(_png);
284 _maxval = 255;
285 _num_channels = 3;
286 break;
287
288 default:
289 pnmimage_png_cat.error()
290 << "Unsupported color type: " << color_type << "\n";
291 free_png();
292 return;
293 }
294
295 if (png_get_valid(_png, _info, PNG_INFO_tRNS)) {
296 png_set_tRNS_to_alpha(_png);
297 if (!has_alpha()) {
298 _num_channels++;
299 }
300 }
301
302 png_read_update_info(_png, _info);
303}
304
305/**
306 *
307 */
308PNMFileTypePNG::Reader::
309~Reader() {
310 free_png();
311}
312
313/**
314 * Reads in an entire image all at once, storing it in the pre-allocated
315 * _x_size * _y_size array and alpha pointers. (If the image type has no
316 * alpha channel, alpha is ignored.) Returns the number of rows correctly
317 * read.
318 *
319 * Derived classes need not override this if they instead provide
320 * supports_read_row() and read_row(), below.
321 */
322int PNMFileTypePNG::Reader::
323read_data(xel *array, xelval *alpha_data) {
324 if (!is_valid()) {
325 return 0;
326 }
327
328 if (setjmp(_jmpbuf)) {
329 // This is the ANSI C way to handle exceptions. If setjmp(), above,
330 // returns true, it means that libpng detected an exception while
331 // executing the code that reads the image, below.
332 free_png();
333 return 0;
334 }
335
336 int row_byte_length = _x_size * _num_channels;
337 if (_maxval > 255) {
338 row_byte_length *= 2;
339 }
340
341 int num_rows = _y_size;
342
343 if (pnmimage_png_cat.is_debug()) {
344 pnmimage_png_cat.debug()
345 << "Allocating " << num_rows << " rows of " << row_byte_length
346 << " bytes each.\n";
347 }
348
349 // We need to read a full copy of the image in first, in libpng's 2-d array
350 // format, mainly because we keep array and alpha data separately, and there
351 // doesn't appear to be good support to get this stuff out row-at-a-time for
352 // interlaced files.
353 png_bytep *rows = (png_bytep *)alloca(num_rows * sizeof(png_bytep));
354 int yi;
355
356 png_byte *alloc = (png_byte *)PANDA_MALLOC_ARRAY(row_byte_length * sizeof(png_byte) * num_rows);
357 for (yi = 0; yi < num_rows; yi++) {
358 rows[yi] = alloc + (row_byte_length * sizeof(png_byte)) * yi;
359 }
360
361 png_read_image(_png, rows);
362
363 bool get_color = !is_grayscale();
364 bool get_alpha = has_alpha();
365
366 int pi = 0;
367 for (yi = 0; yi < num_rows; yi++) {
368 png_bytep source = rows[yi];
369 for (int xi = 0; xi < _x_size; xi++) {
370 int red = 0;
371 int green = 0;
372 int blue = 0;
373 int alpha = 0;
374
375 if (_maxval > 255) {
376 if (get_color) {
377 red = (source[0] << 8) | source[1];
378 source += 2;
379
380 green = (source[0] << 8) | source[1];
381 source += 2;
382 }
383
384 blue = (source[0] << 8) | source[1];
385 source += 2;
386
387 if (get_alpha) {
388 alpha = (source[0] << 8) | source[1];
389 source += 2;
390 }
391
392 } else {
393 if (get_color) {
394 red = *source;
395 source++;
396
397 green = *source;
398 source++;
399 }
400
401 blue = *source;
402 source++;
403
404 if (get_alpha) {
405 alpha = *source;
406 source++;
407 }
408 }
409
410 PPM_ASSIGN(array[pi], red, green, blue);
411 if (get_alpha) {
412 alpha_data[pi] = alpha;
413 }
414 pi++;
415 }
416
417 nassertr(source <= rows[yi] + row_byte_length, yi);
418 }
419
420 png_read_end(_png, nullptr);
421 PANDA_FREE_ARRAY(alloc);
422
423 return _y_size;
424}
425
426/**
427 * Releases the internal PNG structures and marks the reader invalid.
428 */
429void PNMFileTypePNG::Reader::
430free_png() {
431 if (_is_valid) {
432 png_destroy_read_struct(&_png, &_info, nullptr);
433 _is_valid = false;
434 }
435}
436
437/**
438 * A callback handler that PNG uses to read data from the iostream.
439 */
440void PNMFileTypePNG::Reader::
441png_read_data(png_structp png_ptr, png_bytep data, png_size_t length) {
442 Reader *self = (Reader *)png_get_io_ptr(png_ptr);
443 self->_file->read((char *)data, length);
444 if (length != (png_size_t)self->_file->gcount()) {
445 pnmimage_png_cat.error()
446 << "Didn't read enough bytes.\n";
447 // Is there no way to indicate a read failure to libpng?
448 }
450}
451
452/**
453 * This is our own warning handler. It is called by the png library to issue
454 * a warning message.
455 */
456void PNMFileTypePNG::Reader::
457png_warning(png_structp, png_const_charp warning_msg) {
458 pnmimage_png_cat.warning()
459 << warning_msg << "\n";
460}
461
462/**
463 * This is our own error handler. It is called by the png library to issue a
464 * fatal error message.
465 */
466void PNMFileTypePNG::Reader::
467png_error(png_structp png_ptr, png_const_charp error_msg) {
468 pnmimage_png_cat.error()
469 << error_msg << "\n";
470
471 // The PNG library insists we should not return, so instead of returning, we
472 // will do a longjmp out of the png code.
473 Reader *self = (Reader *)png_get_io_ptr(png_ptr);
474 if (self == nullptr) {
475 // Oops, we haven't got a self pointer yet. Return anyway and hope we'll
476 // be ok.
477 pnmimage_png_cat.error()
478 << "Returning before opening file.\n";
479 return;
480 }
481
482 longjmp(self->_jmpbuf, true);
483}
484
485/**
486 *
487 */
488PNMFileTypePNG::Writer::
489Writer(PNMFileType *type, ostream *file, bool owns_file) :
490 PNMWriter(type, file, owns_file)
491{
492 _png = nullptr;
493 _info = nullptr;
494 _is_valid = false;
495
496 _png = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr,
497 png_error, png_warning);
498 if (_png == nullptr) {
499 return;
500 }
501
502 _info = png_create_info_struct(_png);
503 if (_info == nullptr) {
504 png_destroy_write_struct(&_png, nullptr);
505 return;
506 }
507
508 _is_valid = true;
509}
510
511/**
512 *
513 */
514PNMFileTypePNG::Writer::
515~Writer() {
516 free_png();
517}
518
519/**
520 * Writes in an entire image all at once, storing it in the pre-allocated
521 * _x_size * _y_size array and alpha pointers. (If the image type has no
522 * alpha channel, alpha is ignored.) Returns the number of rows correctly
523 * write.
524 *
525 * Derived classes need not override this if they instead provide
526 * supports_write_row() and write_row(), below.
527 */
528int PNMFileTypePNG::Writer::
529write_data(xel *array, xelval *alpha_data) {
530 if (!is_valid()) {
531 return 0;
532 }
533
534 if (setjmp(_jmpbuf)) {
535 // This is the ANSI C way to handle exceptions. If setjmp(), above,
536 // returns true, it means that libpng detected an exception while
537 // executing the code that writes the image, below.
538 free_png();
539 return 0;
540 }
541
542 png_set_write_fn(_png, (void *)this, png_write_data, png_flush_data);
543
544 // The compression level corresponds directly to the compression levels for
545 // zlib.
546 png_set_compression_level(_png, png_compression_level);
547
548 // First, write the header.
549
550 int true_bit_depth = pm_maxvaltobits(_maxval);
551 int png_bit_depth = make_png_bit_depth(true_bit_depth);
552
553 png_color_8 sig_bit;
554 sig_bit.red = true_bit_depth;
555 sig_bit.green = true_bit_depth;
556 sig_bit.blue = true_bit_depth;
557 sig_bit.gray = true_bit_depth;
558 sig_bit.alpha = true_bit_depth;
559
560 int color_type = 0;
561
562 if (!is_grayscale()) {
563 color_type |= PNG_COLOR_MASK_COLOR;
564 }
565 if (has_alpha()) {
566 color_type |= PNG_COLOR_MASK_ALPHA;
567 }
568
569 // Determine if we should make a palettized image out of this. In order for
570 // this to be possible and effective, we must have no more than 256 unique
571 // coloralpha combinations for a color image, and the resulting bitdepth
572 // should be smaller than what we would have otherwise.
573 Palette palette;
574 HistMap palette_lookup;
575 png_color png_palette_table[png_max_palette];
576 png_byte png_trans[png_max_palette];
577
578 if (png_palette) {
579 if (png_bit_depth <= 8) {
580 if (compute_palette(palette, array, alpha_data, png_max_palette)) {
581 if (pnmimage_png_cat.is_debug()) {
582 pnmimage_png_cat.debug()
583 << palette.size() << " colors found.\n";
584 }
585
586 int palette_bit_depth = make_png_bit_depth(pm_maxvaltobits(palette.size() - 1));
587
588 int total_bits = png_bit_depth;
589 if (!is_grayscale()) {
590 total_bits *= 3;
591 }
592 if (has_alpha()) {
593 total_bits += png_bit_depth;
594 }
595
596 if (palette_bit_depth < total_bits ||
597 _maxval != (1 << true_bit_depth) - 1) {
598 if (pnmimage_png_cat.is_debug()) {
599 pnmimage_png_cat.debug()
600 << "palette bit depth of " << palette_bit_depth
601 << " improves on bit depth of " << total_bits
602 << "; making a palette image.\n";
603 }
604
605 color_type = PNG_COLOR_TYPE_PALETTE;
606
607 // Re-sort the palette to put the semitransparent pixels at the
608 // beginning.
609 sort(palette.begin(), palette.end(), LowAlphaCompare());
610
611 double palette_scale = 255.0 / _maxval;
612
613 int num_alpha = 0;
614 for (int i = 0; i < (int)palette.size(); i++) {
615 png_palette_table[i].red = (int)(palette[i]._red * palette_scale + 0.5);
616 png_palette_table[i].green = (int)(palette[i]._green * palette_scale + 0.5);
617 png_palette_table[i].blue = (int)(palette[i]._blue * palette_scale + 0.5);
618 png_trans[i] = (int)(palette[i]._alpha * palette_scale + 0.5);
619 if (palette[i]._alpha != _maxval) {
620 num_alpha = i + 1;
621 }
622
623 // Also build a reverse-lookup from color to palette index in the
624 // "histogram" structure.
625 palette_lookup[palette[i]] = i;
626 }
627
628 png_set_PLTE(_png, _info, png_palette_table, palette.size());
629 if (has_alpha()) {
630 if (pnmimage_png_cat.is_debug()) {
631 pnmimage_png_cat.debug()
632 << "palette contains " << num_alpha << " transparent entries.\n";
633 }
634 png_set_tRNS(_png, _info, png_trans, num_alpha, nullptr);
635 }
636 } else if (pnmimage_png_cat.is_debug()) {
637 pnmimage_png_cat.debug()
638 << "palette bit depth of " << palette_bit_depth
639 << " does not improve on bit depth of " << total_bits
640 << "; not making a palette image.\n";
641 }
642
643 } else if (pnmimage_png_cat.is_debug()) {
644 pnmimage_png_cat.debug()
645 << "more than " << png_max_palette
646 << " colors found; not making a palette image.\n";
647 }
648 } else if (pnmimage_png_cat.is_debug()) {
649 pnmimage_png_cat.debug()
650 << "maxval exceeds 255; not making a palette image.\n";
651 }
652 } else if (pnmimage_png_cat.is_debug()) {
653 pnmimage_png_cat.debug()
654 << "palette images are not enabled.\n";
655 }
656
657 if (pnmimage_png_cat.is_debug()) {
658 pnmimage_png_cat.debug()
659 << "width = " << _x_size << " height = " << _y_size
660 << " maxval = " << _maxval << " bit_depth = "
661 << png_bit_depth << " color_type = " << color_type
662 << " color_space = " << _color_space << "\n";
663 }
664
665 png_set_IHDR(_png, _info, _x_size, _y_size, png_bit_depth,
666 color_type, PNG_INTERLACE_NONE,
667 PNG_COMPRESSION_TYPE_DEFAULT, PNG_FILTER_TYPE_DEFAULT);
668
669 // Set the true bit depth of the image data.
670 if (png_bit_depth != true_bit_depth || color_type == PNG_COLOR_TYPE_PALETTE) {
671 png_set_sBIT(_png, _info, &sig_bit);
672 }
673
674 // Set the color space, if we know it.
675 switch (_color_space) {
676 case CS_linear:
677 png_set_gAMA(_png, _info, 1.0);
678 // Not sure if we should set cHRM to anything.
679 break;
680
681 case CS_sRGB:
682 png_set_sRGB_gAMA_and_cHRM(_png, _info, PNG_sRGB_INTENT_RELATIVE);
683 break;
684
685 default:
686 break;
687 }
688
689 png_write_info(_png, _info);
690
691
692 // Now set up the transformations to write the image data.
693 if (png_bit_depth < 8) {
694 png_set_packing(_png);
695 }
696
697 double val_scale = 1.0;
698
699 if (color_type != PNG_COLOR_TYPE_PALETTE) {
700 if (png_bit_depth != true_bit_depth) {
701 png_set_shift(_png, &sig_bit);
702 }
703 // Since this assumes that _maxval is one less than a power of 2, we set
704 // val_scale to the appropriate factor in case it is not.
705 int png_maxval = (1 << png_bit_depth) - 1;
706 val_scale = (double)png_maxval / (double)_maxval;
707 }
708
709 int row_byte_length = _x_size * _num_channels;
710 if (png_bit_depth > 8) {
711 row_byte_length *= 2;
712 }
713
714 int num_rows = _y_size;
715
716 if (pnmimage_png_cat.is_debug()) {
717 pnmimage_png_cat.debug()
718 << "Allocating one row of " << row_byte_length
719 << " bytes.\n";
720 }
721
722 // When writing, we only need to copy the image out one row at a time,
723 // because we don't mess around with writing interlaced files. If we were
724 // writing an interlaced file, we'd have to copy the whole image first.
725
726 png_bytep row = (png_byte *)PANDA_MALLOC_ARRAY(row_byte_length * sizeof(png_byte));
727
728 bool save_color = !is_grayscale();
729 bool save_alpha = has_alpha();
730
731 if (val_scale == 1.0) {
732 // No scale needed; we're already a power of 2.
733 int pi = 0;
734 for (int yi = 0; yi < num_rows; yi++) {
735 png_bytep dest = row;
736
737 if (color_type == PNG_COLOR_TYPE_PALETTE) {
738 for (int xi = 0; xi < _x_size; xi++) {
739 int index;
740
741 if (save_color) {
742 if (save_alpha) {
743 index = palette_lookup[PixelSpec(PPM_GETR(array[pi]), PPM_GETG(array[pi]), PPM_GETB(array[pi]), alpha_data[pi])];
744 } else {
745 index = palette_lookup[PixelSpec(PPM_GETR(array[pi]), PPM_GETG(array[pi]), PPM_GETB(array[pi]))];
746 }
747 } else {
748 if (save_alpha) {
749 index = palette_lookup[PixelSpec(PPM_GETB(array[pi]), alpha_data[pi])];
750 } else {
751 index = palette_lookup[PixelSpec(PPM_GETB(array[pi]))];
752 }
753 }
754
755 *dest++ = index;
756 pi++;
757 }
758
759 } else if (png_bit_depth > 8) {
760 for (int xi = 0; xi < _x_size; xi++) {
761 if (save_color) {
762 xelval red = PPM_GETR(array[pi]);
763 *dest++ = (red >> 8) & 0xff;
764 *dest++ = red & 0xff;
765 xelval green = PPM_GETG(array[pi]);
766 *dest++ = (green >> 8) & 0xff;
767 *dest++ = green & 0xff;
768 }
769 xelval blue = PPM_GETB(array[pi]);
770 *dest++ = (blue >> 8) & 0xff;
771 *dest++ = blue & 0xff;
772
773 if (save_alpha) {
774 xelval alpha = alpha_data[pi];
775 *dest++ = (alpha >> 8) & 0xff;
776 *dest++ = alpha & 0xff;
777 }
778 pi++;
779 }
780
781 } else {
782 for (int xi = 0; xi < _x_size; xi++) {
783 if (save_color) {
784 *dest++ = PPM_GETR(array[pi]);
785 *dest++ = PPM_GETG(array[pi]);
786 }
787
788 *dest++ = PPM_GETB(array[pi]);
789
790 if (save_alpha) {
791 *dest++ = alpha_data[pi];
792 }
793 pi++;
794 }
795 }
796
797 nassertr(dest <= row + row_byte_length, yi);
798 png_write_row(_png, row);
800 }
801 } else {
802 // Here we might need to scale each component to match the png
803 // requirement.
804 nassertr(color_type != PNG_COLOR_TYPE_PALETTE, 0);
805 int pi = 0;
806 for (int yi = 0; yi < num_rows; yi++) {
807 png_bytep dest = row;
808
809 if (png_bit_depth > 8) {
810 for (int xi = 0; xi < _x_size; xi++) {
811 if (save_color) {
812 xelval red = (xelval)(PPM_GETR(array[pi]) * val_scale + 0.5);
813 *dest++ = (red >> 8) & 0xff;
814 *dest++ = red & 0xff;
815 xelval green = (xelval)(PPM_GETG(array[pi]) * val_scale + 0.5);
816 *dest++ = (green >> 8) & 0xff;
817 *dest++ = green & 0xff;
818 }
819 xelval blue = (xelval)(PPM_GETB(array[pi]) * val_scale + 0.5);
820 *dest++ = (blue >> 8) & 0xff;
821 *dest++ = blue & 0xff;
822
823 if (save_alpha) {
824 xelval alpha = (xelval)(alpha_data[pi] * val_scale + 0.5);
825 *dest++ = (alpha >> 8) & 0xff;
826 *dest++ = alpha & 0xff;
827 }
828 pi++;
829 }
830
831 } else {
832 for (int xi = 0; xi < _x_size; xi++) {
833 if (save_color) {
834 *dest++ = (xelval)(PPM_GETR(array[pi]) * val_scale + 0.5);
835 *dest++ = (xelval)(PPM_GETG(array[pi]) * val_scale + 0.5);
836 }
837
838 *dest++ = (xelval)(PPM_GETB(array[pi]) * val_scale + 0.5);
839
840 if (save_alpha) {
841 *dest++ = (xelval)(alpha_data[pi] * val_scale + 0.5);
842 }
843 pi++;
844 }
845 }
846
847 nassertr(dest <= row + row_byte_length, yi);
848 png_write_row(_png, row);
850 }
851 }
852 PANDA_FREE_ARRAY(row);
853
854 png_write_end(_png, nullptr);
855
856 return _y_size;
857}
858
859/**
860 * Releases the internal PNG structures and marks the writer invalid.
861 */
862void PNMFileTypePNG::Writer::
863free_png() {
864 if (_is_valid) {
865 png_destroy_write_struct(&_png, &_info);
866 _is_valid = false;
867 }
868}
869
870/**
871 * Elevates the indicated bit depth to one of the legal PNG bit depths: 1, 2,
872 * 4, 8, or 16.
873 */
874int PNMFileTypePNG::Writer::
875make_png_bit_depth(int bit_depth) {
876 switch (bit_depth) {
877 case 0:
878 case 1:
879 return 1;
880
881 case 2:
882 return 2;
883
884 case 3:
885 case 4:
886 return 4;
887
888 case 5:
889 case 6:
890 case 7:
891 case 8:
892 return 8;
893
894 default:
895 return 16;
896 }
897}
898
899/**
900 * A callback handler that PNG uses to write data to the iostream.
901 */
902void PNMFileTypePNG::Writer::
903png_write_data(png_structp png_ptr, png_bytep data, png_size_t length) {
904 Writer *self = (Writer *)png_get_io_ptr(png_ptr);
905 self->_file->write((char *)data, length);
906 if (self->_file->fail()) {
907 pnmimage_png_cat.error()
908 << "Unable to write to the iostream.\n";
909 // Is there no way to indicate a write failure to libpng?
910 }
911}
912
913/**
914 * A callback handler that PNG uses to write data to the iostream.
915 */
916void PNMFileTypePNG::Writer::
917png_flush_data(png_structp png_ptr) {
918 Writer *self = (Writer *)png_get_io_ptr(png_ptr);
919 self->_file->flush();
920}
921
922/**
923 * This is our own warning handler. It is called by the png library to issue
924 * a warning message.
925 */
926void PNMFileTypePNG::Writer::
927png_warning(png_structp, png_const_charp warning_msg) {
928 pnmimage_png_cat.warning()
929 << warning_msg << "\n";
930}
931
932/**
933 * This is our own error handler. It is called by the png library to issue a
934 * fatal error message.
935 */
936void PNMFileTypePNG::Writer::
937png_error(png_structp png_ptr, png_const_charp error_msg) {
938 pnmimage_png_cat.error()
939 << error_msg << "\n";
940
941 // The PNG library insists we should not return, so instead of returning, we
942 // will do a longjmp out of the png code.
943 Writer *self = (Writer *)png_get_io_ptr(png_ptr);
944 if (self == nullptr) {
945 // Oops, we haven't got a self pointer yet. Return anyway and hope we'll
946 // be ok.
947 pnmimage_png_cat.error()
948 << "Returning before opening file.\n";
949 return;
950 }
951
952 longjmp(self->_jmpbuf, true);
953}
954
955
956#endif // HAVE_PNG
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
static WritableFactory * get_factory()
Returns the global WritableFactory for generating TypedWritable objects.
Definition: bamReader.I:177
An instance of this class is passed to the Factory when requesting it to do its business and construc...
Definition: factoryParams.h:36
PNMFileType * get_type_by_handle(TypeHandle handle) const
Returns the PNMFileType instance stored in the registry for the given TypeHandle, e....
static PNMFileTypeRegistry * get_global_ptr()
Returns a pointer to the global PNMFileTypeRegistry object.
This is the base class of a family of classes that represent particular image file types that PNMImag...
Definition: pnmFileType.h:32
This is an abstract base class that defines the interface for reading image files of various types.
Definition: pnmReader.h:27
This is an abstract base class that defines the interface for writing image files of various types.
Definition: pnmWriter.h:27
static void consider_yield()
Possibly suspends the current thread for the rest of the current epoch, if it has run for enough this...
Definition: thread.I:212
TypeHandle is the identifier used to differentiate C++ class types.
Definition: typeHandle.h:81
Base class for objects that can be written to and read from Bam files.
Definition: typedWritable.h:35
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.
int pm_maxvaltobits(int maxval)
Returns the number of bits sufficient to hold the indicated maxval value.
PANDA 3D SOFTWARE Copyright (c) Carnegie Mellon University.