Panda3D
Loading...
Searching...
No Matches
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...
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.
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.