Panda3D
Loading...
Searching...
No Matches
pnmFileTypeSoftImage.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 pnmFileTypeSoftImage.cxx
10 * @author drose
11 * @date 2000-06-17
12 */
13
15
16#ifdef HAVE_SOFTIMAGE_PIC
17
19
20#include "pnmFileTypeRegistry.h"
21#include "bamReader.h"
22
23using std::istream;
24using std::ostream;
25using std::string;
26
27static const float imageVersionNumber = 3.0;
28static const int imageCommentLength = 80;
29static const char imageComment[imageCommentLength+1] =
30 "Written by pnmimage.";
31
32// Values to indicate compresseduncompressed types
33#define UNCOMPRESSED 0x00
34#define MIXED_RUN_LENGTH 0x02
35
36// Bits to indicate channel type
37#define RGB_CHANNEL 0xe0
38#define ALPHA_CHANNEL 0x10
39
40// SoftImage magic number: high word, low word
41#define SOFTIMAGE_MAGIC1 0x5380
42#define SOFTIMAGE_MAGIC2 0xf634
43
44static const char * const extensions_softimage[] = {
45 "pic", "soft"
46};
47static const int num_extensions_softimage = sizeof(extensions_softimage) / sizeof(const char *);
48
49TypeHandle PNMFileTypeSoftImage::_type_handle;
50
51inline float
52read_float(istream *file) {
53 long l;
54
55 if (pm_readbiglong(file, &l)==0) {
56 return *(float *)&l;
57 } else {
58 return 0.0;
59 }
60}
61
62inline unsigned short
63read_ushort_SI(istream *file) {
64 unsigned short x;
65 return pm_readbigshort(file, (short *)&x)==0 ? x : 0;
66}
67
68inline unsigned char
69read_uchar_SI(istream *file) {
70 int x;
71 x = file->get();
72 return (x!=EOF) ? (unsigned char)x : 0;
73}
74
75inline void
76write_ushort_SI(ostream *file, unsigned short x) {
77 pm_writebigshort(file, (short)x);
78}
79
80inline void
81write_uchar_SI(ostream *file, unsigned char x) {
82 file->put(x);
83}
84
85inline void
86write_float(ostream *file, float x) {
87 pm_writebiglong(file, *(long *)&x);
88}
89
90static int
91read_channel_pkt(istream *file,
92 int &chained, int &size, int &type, int &channel) {
93 chained = read_uchar_SI(file);
94 size = read_uchar_SI(file);
95 type = read_uchar_SI(file);
96 channel = read_uchar_SI(file);
97
98 if (file->eof() || file->fail()) {
99 return false;
100 }
101
102 if (size!=8) {
103 pnmimage_soft_cat.error()
104 << "Don't know how to interpret " << size << " bits per pixel!\n";
105 return false;
106 }
107
108 return true;
109}
110
111static void
112read_rgb(xel *row_data, xelval *, istream *file, int x, int repeat) {
113 xelval red, grn, blu;
114 red = read_uchar_SI(file);
115 grn = read_uchar_SI(file);
116 blu = read_uchar_SI(file);
117
118 while (repeat>0) {
119 PPM_ASSIGN(row_data[x], red, grn, blu);
120 x++;
121 repeat--;
122 }
123}
124
125static void
126read_alpha(xel *, xelval *alpha_data, istream *file, int x, int repeat) {
127 xelval alpha = read_uchar_SI(file);
128
129 while (repeat>0) {
130 alpha_data[x] = alpha;
131 x++;
132 repeat--;
133 }
134}
135
136static void
137read_rgba(xel *row_data, xelval *alpha_data, istream *file, int x, int repeat) {
138 xelval red, grn, blu, alpha;
139 red = read_uchar_SI(file);
140 grn = read_uchar_SI(file);
141 blu = read_uchar_SI(file);
142 alpha = read_uchar_SI(file);
143
144 while (repeat>0) {
145 PPM_ASSIGN(row_data[x], red, grn, blu);
146 alpha_data[x] = alpha;
147 x++;
148 repeat--;
149 }
150}
151
152
153static int
154read_scanline(xel *row_data, xelval *alpha_data, int cols, istream *file,
155 void (*read_data)(xel *row_data, xelval *alpha_data, istream *file,
156 int x, int repeat),
157 int ctype) {
158 if (ctype==UNCOMPRESSED) {
159 for (int x = 0; x<cols; x++) {
160 read_data(row_data, alpha_data, file, x, 1);
161 }
162 return true;
163 } else {
164 int x;
165 int num;
166
167 x = 0;
168 while (x < cols) {
169 num = read_uchar_SI(file);
170
171 if (num<128) {
172 // Sequence of non-repeated values.
173 num++;
174 if (x+num > cols) {
175 return false;
176 }
177 while (num>0) {
178 read_data(row_data, alpha_data, file, x, 1);
179 if (file->eof() || file->fail()) {
180 return false;
181 }
182 x++;
183 num--;
184 }
185 } else {
186 // Sequence of repeated values.
187 if (num==128) {
188 num = read_ushort_SI(file);
189 } else {
190 num -= 127;
191 }
192 if (x+num > cols) {
193 return false;
194 }
195 read_data(row_data, alpha_data, file, x, num);
196 if (file->eof() || file->fail()) {
197 return false;
198 }
199 x += num;
200 }
201 }
202
203 return (x==cols);
204 }
205}
206
207/**
208 *
209 */
210PNMFileTypeSoftImage::
211PNMFileTypeSoftImage() {
212}
213
214/**
215 * Returns a few words describing the file type.
216 */
217string PNMFileTypeSoftImage::
218get_name() const {
219 return "SoftImage";
220}
221
222/**
223 * Returns the number of different possible filename extensions_softimage
224 * associated with this particular file type.
225 */
226int PNMFileTypeSoftImage::
227get_num_extensions() const {
228 return num_extensions_softimage;
229}
230
231/**
232 * Returns the nth possible filename extension associated with this particular
233 * file type, without a leading dot.
234 */
235string PNMFileTypeSoftImage::
236get_extension(int n) const {
237 nassertr(n >= 0 && n < num_extensions_softimage, string());
238 return extensions_softimage[n];
239}
240
241/**
242 * Returns a suitable filename extension (without a leading dot) to suggest
243 * for files of this type, or empty string if no suggestions are available.
244 */
245string PNMFileTypeSoftImage::
246get_suggested_extension() const {
247 return "pic";
248}
249
250/**
251 * Returns true if this particular file type uses a magic number to identify
252 * it, false otherwise.
253 */
254bool PNMFileTypeSoftImage::
255has_magic_number() const {
256 return true;
257}
258
259/**
260 * Returns true if the indicated "magic number" byte stream (the initial few
261 * bytes read from the file) matches this particular file type, false
262 * otherwise.
263 */
264bool PNMFileTypeSoftImage::
265matches_magic_number(const string &magic_number) const {
266 nassertr(magic_number.size() >= 2, false);
267 int mn =
268 ((unsigned char)magic_number[0] << 8) |
269 ((unsigned char)magic_number[1]);
270 return (mn == SOFTIMAGE_MAGIC1);
271}
272
273/**
274 * Allocates and returns a new PNMReader suitable for reading from this file
275 * type, if possible. If reading from this file type is not supported,
276 * returns NULL.
277 */
278PNMReader *PNMFileTypeSoftImage::
279make_reader(istream *file, bool owns_file, const string &magic_number) {
280 init_pnm();
281 return new Reader(this, file, owns_file, magic_number);
282}
283
284/**
285 * Allocates and returns a new PNMWriter suitable for reading from this file
286 * type, if possible. If writing files of this type is not supported, returns
287 * NULL.
288 */
289PNMWriter *PNMFileTypeSoftImage::
290make_writer(ostream *file, bool owns_file) {
291 init_pnm();
292 return new Writer(this, file, owns_file);
293}
294
295
296/**
297 *
298 */
299PNMFileTypeSoftImage::Reader::
300Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
301 PNMReader(type, file, owns_file)
302{
303 if (!read_magic_number(_file, magic_number, 4)) {
304 // No magic number, no image.
305 if (pnmimage_soft_cat.is_debug()) {
306 pnmimage_soft_cat.debug()
307 << "SoftImage image file appears to be empty.\n";
308 }
309 _is_valid = false;
310 return;
311 }
312
313 int magic1 =
314 ((unsigned char)magic_number[0] << 8) |
315 ((unsigned char)magic_number[1]);
316 int magic2 =
317 ((unsigned char)magic_number[2] << 8) |
318 ((unsigned char)magic_number[3]);
319
320 if (magic1 != SOFTIMAGE_MAGIC1 || magic2 != SOFTIMAGE_MAGIC2) {
321 _is_valid = false;
322 return;
323 }
324
325 // skip version number
326 read_float(_file);
327
328 // Skip comment
329 _file->seekg(imageCommentLength, std::ios::cur);
330
331 char pict_id[4];
332 _file->read(pict_id, 4);
333 if (_file->gcount() < 4) {
334 _is_valid = false;
335 return;
336 }
337
338 if (memcmp(pict_id, "PICT", 4)!=0) {
339 _is_valid = false;
340 return;
341 }
342
343 _x_size = read_ushort_SI(_file);
344 _y_size = read_ushort_SI(_file);
345
346 /* float ratio = */ read_float(_file);
347 /* int fields = */ read_ushort_SI(_file);
348 read_ushort_SI(_file);
349
350 int chained, size, channel;
351 if (!read_channel_pkt(_file, chained, size, rgb_ctype, channel)) {
352 _is_valid = false;
353 return;
354 }
355
356 soft_color = unknown;
357
358 if (channel == (RGB_CHANNEL | ALPHA_CHANNEL)) {
359 // Four components in the first part: RGBA.
360 soft_color = rgba;
361
362 } else if (channel == RGB_CHANNEL) {
363 // Three components in the first part: RGB.
364 soft_color = rgb;
365
366 if (chained) {
367 if (!read_channel_pkt(_file, chained, size, alpha_ctype, channel)) {
368 _is_valid = false;
369 return;
370 }
371
372 if (channel == ALPHA_CHANNEL) {
373 // Alpha component in the second part: RGBA.
374 soft_color = rgb_a;
375 }
376 }
377 }
378
379 switch (soft_color) {
380 case rgb:
381 _num_channels = 3;
382 break;
383
384 case rgba:
385 case rgb_a:
386 _num_channels = 4;
387 break;
388
389 default:
390 pnmimage_soft_cat.error()
391 << "Image is not RGB or RGBA!\n";
392 _is_valid = false;
393 return;
394 }
395
396 if (chained) {
397 pnmimage_soft_cat.error()
398 << "Unexpected additional channels in image file.\n";
399 _is_valid = false;
400 return;
401 }
402
403 _maxval = 255;
404
405 if (pnmimage_soft_cat.is_debug()) {
406 pnmimage_soft_cat.debug()
407 << "Reading SoftImage " << *this << "\n";
408 }
409}
410
411
412/**
413 * Returns true if this particular PNMReader supports a streaming interface to
414 * reading the data: that is, it is capable of returning the data one row at a
415 * time, via repeated calls to read_row(). Returns false if the only way to
416 * read from this file is all at once, via read_data().
417 */
418bool PNMFileTypeSoftImage::Reader::
419supports_read_row() const {
420 return true;
421}
422
423/**
424 * If supports_read_row(), above, returns true, this function may be called
425 * repeatedly to read the image, one horizontal row at a time, beginning from
426 * the top. Returns true if the row is successfully read, false if there is
427 * an error or end of file.
428 */
429bool PNMFileTypeSoftImage::Reader::
430read_row(xel *row_data, xelval *alpha_data, int x_size, int) {
431 if (!is_valid()) {
432 return false;
433 }
434 switch (soft_color) {
435 case rgb:
436 if (!read_scanline(row_data, alpha_data, x_size, _file,
437 read_rgb, rgb_ctype)) {
438 return false;
439 }
440 break;
441
442 case rgba:
443 if (!read_scanline(row_data, alpha_data, x_size, _file,
444 read_rgba, rgb_ctype)) {
445 return false;
446 }
447 break;
448
449 case rgb_a:
450 if (!read_scanline(row_data, alpha_data, x_size, _file,
451 read_rgb, rgb_ctype)) {
452 return false;
453 }
454 if (!read_scanline(row_data, alpha_data, x_size, _file,
455 read_alpha, alpha_ctype)) {
456 return false;
457 }
458 break;
459
460 default:
461 break;
462 }
463
464 return true;
465}
466
467
468static void
469write_channel_pkt(ostream *file,
470 int chained, int size, int type, int channel) {
471 write_uchar_SI(file, chained);
472 write_uchar_SI(file, size);
473 write_uchar_SI(file, type);
474 write_uchar_SI(file, channel);
475}
476
477static void
478write_rgb(xel *row_data, xelval *, ostream *file, int x) {
479 write_uchar_SI(file, PPM_GETR(row_data[x]));
480 write_uchar_SI(file, PPM_GETG(row_data[x]));
481 write_uchar_SI(file, PPM_GETB(row_data[x]));
482}
483
484static int
485compare_rgb(xel *row_data, xelval *, int x1, int x2) {
486 return PPM_EQUAL(row_data[x1], row_data[x2]);
487}
488
489static void
490write_gray(xel *row_data, xelval *, ostream *file, int x) {
491 write_uchar_SI(file, PPM_GETB(row_data[x]));
492 write_uchar_SI(file, PPM_GETB(row_data[x]));
493 write_uchar_SI(file, PPM_GETB(row_data[x]));
494}
495
496static int
497compare_gray(xel *row_data, xelval *, int x1, int x2) {
498 return (PPM_GETB(row_data[x1])==PPM_GETB(row_data[x2]));
499}
500
501static void
502write_alpha(xel *, xelval *alpha_data, ostream *file, int x) {
503 write_uchar_SI(file, alpha_data[x]);
504}
505
506static int
507compare_alpha(xel *, xelval *alpha_data, int x1, int x2) {
508 return (alpha_data[x1]==alpha_data[x2]);
509}
510
511static void
512write_diff(xel *row_data, xelval *alpha_data, ostream *file,
513 void (*write_data)(xel *row_data, xelval *alpha_data, ostream *file,
514 int x),
515 int tox, int length) {
516 if (length>0) {
517 nassertv(length<=128);
518
519 write_uchar_SI(file, length-1);
520 while (length>0) {
521 length--;
522 write_data(row_data, alpha_data, file, tox-length);
523 }
524 }
525}
526
527static void
528write_same(xel *row_data, xelval *alpha_data, ostream *file,
529 void (*write_data)(xel *row_data, xelval *alpha_data, ostream *file,
530 int x),
531 int tox, int length) {
532 if (length==1) {
533 write_diff(row_data, alpha_data, file, write_data, tox, length);
534
535 } else if (length>0) {
536 if (length<128) {
537 write_uchar_SI(file, length+127);
538 } else {
539 write_uchar_SI(file, 128);
540 write_ushort_SI(file, length);
541 }
542 write_data(row_data, alpha_data, file, tox);
543 }
544}
545
546
547static void
548write_scanline(xel *row_data, xelval *alpha_data, int cols, ostream *file,
549 int (*compare_data)(xel *row_data, xelval *alpha_data,
550 int x1, int x2),
551 void (*write_data)(xel *row_data, xelval *alpha_data,
552 ostream *file, int x)) {
553 int run_length = 0;
554
555 int x = 0;
556 int same = true;
557
558 // Go through each value in the scanline, from beginning to end, looking for
559 // runs of identical values.
560 while (x < cols) {
561
562 if (same) {
563
564 // We have been scanning past a run of identical values. In this case,
565 // the run is the sequence of values from x-run_length to x-1.
566
567 if (!compare_data(row_data, alpha_data, x, x-run_length)) {
568 // Oops, the end of a run.
569
570 if (run_length <= 1) {
571 // If run_length is only 1, no big deal--this is actually the
572 // beginning of a different-valued run.
573
574 same = false;
575
576 } else {
577 // Write out the old run and begin a new one. We'll be optimistic
578 // and hope the new run will also represent a sequence of identical
579 // values (until we find otherwise).
580
581 write_same(row_data, alpha_data, file, write_data, x-1, run_length);
582 same = true;
583 run_length = 0;
584 }
585 }
586
587 } else { // !same
588
589 // We have been scanning past a run of different values. In this case,
590 // the run is the sequence of values from x-run_length to x-1.
591
592 if (run_length>128) {
593 // We can't have different runs of more than 128 characters. Close
594 // off the old run.
595
596 int excess = run_length - 128;
597 write_diff(row_data, alpha_data, file, write_data, x-excess-1, 128);
598 run_length = excess;
599
600 } else if (run_length > 2 &&
601 compare_data(row_data, alpha_data, x, x-1) &&
602 compare_data(row_data, alpha_data, x, x-2)) {
603
604 // If the last three values have been the same, then it's time to
605 // begin a new run of similar values. Close off the old run.
606
607 write_diff(row_data, alpha_data, file, write_data, x-3, run_length-2);
608 same = true;
609 run_length = 2;
610 }
611 }
612
613 x++;
614 run_length++;
615 }
616
617 // We made it all the way to the end. Flush out the last run.
618
619 if (run_length>0) {
620 if (same) {
621 write_same(row_data, alpha_data, file, write_data, cols-1, run_length);
622 } else {
623
624 // Mighty unlikely, but we might have just run over the 128-pixel limit.
625 if (run_length>128) {
626 int excess = run_length - 128;
627 write_diff(row_data, alpha_data, file, write_data, cols-excess-1, 128);
628 run_length = excess;
629 }
630
631 write_diff(row_data, alpha_data, file, write_data, cols-1, run_length);
632 }
633 }
634}
635
636/**
637 *
638 */
639PNMFileTypeSoftImage::Writer::
640Writer(PNMFileType *type, ostream *file, bool owns_file) :
641 PNMWriter(type, file, owns_file)
642{
643}
644
645/**
646 * Returns true if this particular PNMWriter supports a streaming interface to
647 * writing the data: that is, it is capable of writing the image one row at a
648 * time, via repeated calls to write_row(). Returns false if the only way to
649 * write from this file is all at once, via write_data().
650 */
651bool PNMFileTypeSoftImage::Writer::
652supports_write_row() const {
653 return true;
654}
655
656/**
657 * If supports_write_row(), above, returns true, this function may be called
658 * to write out the image header in preparation to writing out the image data
659 * one row at a time. Returns true if the header is successfully written,
660 * false if there is an error.
661 *
662 * It is the user's responsibility to fill in the header data via calls to
663 * set_x_size(), set_num_channels(), etc., or copy_header_from(), before
664 * calling write_header().
665 */
666bool PNMFileTypeSoftImage::Writer::
667write_header() {
668 write_ushort_SI(_file, SOFTIMAGE_MAGIC1);
669 write_ushort_SI(_file, SOFTIMAGE_MAGIC2);
670 write_float(_file, imageVersionNumber);
671
672 _file->write(imageComment, imageCommentLength);
673 _file->write("PICT", 4);
674
675 write_ushort_SI(_file, _x_size);
676 write_ushort_SI(_file, _y_size);
677
678 write_float(_file, 1.0); // pixel aspect ratio; we don't know.
679 write_ushort_SI(_file, 3); // fields value; we don't really know either.
680 write_ushort_SI(_file, 0); // padding
681
682 // There doesn't seem to be a variation on SoftImage image formats for
683 // grayscale images. We'll write out grayscale as a 3-channel image.
684
685 if (has_alpha()) {
686 write_channel_pkt(_file, 1, 8, MIXED_RUN_LENGTH, RGB_CHANNEL);
687 write_channel_pkt(_file, 0, 8, MIXED_RUN_LENGTH, ALPHA_CHANNEL);
688 } else {
689 write_channel_pkt(_file, 0, 8, MIXED_RUN_LENGTH, RGB_CHANNEL);
690 }
691
692 return true;
693}
694
695/**
696 * If supports_write_row(), above, returns true, this function may be called
697 * repeatedly to write the image, one horizontal row at a time, beginning from
698 * the top. Returns true if the row is successfully written, false if there
699 * is an error.
700 *
701 * You must first call write_header() before writing the individual rows. It
702 * is also important to delete the PNMWriter class after successfully writing
703 * the last row. Failing to do this may result in some data not getting
704 * flushed!
705 */
706bool PNMFileTypeSoftImage::Writer::
707write_row(xel *row_data, xelval *alpha_data) {
708 if (is_grayscale()) {
709 write_scanline(row_data, alpha_data, _x_size, _file, compare_gray, write_gray);
710
711 } else {
712 write_scanline(row_data, alpha_data, _x_size, _file, compare_rgb, write_rgb);
713 }
714
715 if (has_alpha()) {
716 write_scanline(row_data, alpha_data, _x_size, _file, compare_alpha, write_alpha);
717 }
718
719 return !_file->fail();
720}
721
722
723
724/**
725 * Registers the current object as something that can be read from a Bam file.
726 */
727void PNMFileTypeSoftImage::
728register_with_read_factory() {
730 register_factory(get_class_type(), make_PNMFileTypeSoftImage);
731}
732
733/**
734 * This method is called by the BamReader when an object of this type is
735 * encountered in a Bam file; it should allocate and return a new object with
736 * all the data read.
737 *
738 * In the case of the PNMFileType objects, since these objects are all shared,
739 * we just pull the object from the registry.
740 */
741TypedWritable *PNMFileTypeSoftImage::
742make_PNMFileTypeSoftImage(const FactoryParams &params) {
744}
745
746#endif // HAVE_SOFTIMAGE_PIC
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
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.