Panda3D
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...
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
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.