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 
14 #include "pnmFileTypeSoftImage.h"
15 
16 #ifdef HAVE_SOFTIMAGE_PIC
17 
18 #include "config_pnmimagetypes.h"
19 
20 #include "pnmFileTypeRegistry.h"
21 #include "bamReader.h"
22 
23 using std::istream;
24 using std::ostream;
25 using std::string;
26 
27 static const float imageVersionNumber = 3.0;
28 static const int imageCommentLength = 80;
29 static 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 
44 static const char * const extensions_softimage[] = {
45  "pic", "soft"
46 };
47 static const int num_extensions_softimage = sizeof(extensions_softimage) / sizeof(const char *);
48 
49 TypeHandle PNMFileTypeSoftImage::_type_handle;
50 
51 inline float
52 read_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 
62 inline unsigned short
63 read_ushort_SI(istream *file) {
64  unsigned short x;
65  return pm_readbigshort(file, (short *)&x)==0 ? x : 0;
66 }
67 
68 inline unsigned char
69 read_uchar_SI(istream *file) {
70  int x;
71  x = file->get();
72  return (x!=EOF) ? (unsigned char)x : 0;
73 }
74 
75 inline void
76 write_ushort_SI(ostream *file, unsigned short x) {
77  pm_writebigshort(file, (short)x);
78 }
79 
80 inline void
81 write_uchar_SI(ostream *file, unsigned char x) {
82  file->put(x);
83 }
84 
85 inline void
86 write_float(ostream *file, float x) {
87  pm_writebiglong(file, *(long *)&x);
88 }
89 
90 static int
91 read_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 
111 static void
112 read_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 
125 static void
126 read_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 
136 static void
137 read_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 
153 static int
154 read_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  */
210 PNMFileTypeSoftImage::
211 PNMFileTypeSoftImage() {
212 }
213 
214 /**
215  * Returns a few words describing the file type.
216  */
217 string PNMFileTypeSoftImage::
218 get_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  */
226 int PNMFileTypeSoftImage::
227 get_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  */
235 string PNMFileTypeSoftImage::
236 get_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  */
245 string PNMFileTypeSoftImage::
246 get_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  */
254 bool PNMFileTypeSoftImage::
255 has_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  */
264 bool PNMFileTypeSoftImage::
265 matches_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  */
278 PNMReader *PNMFileTypeSoftImage::
279 make_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  */
289 PNMWriter *PNMFileTypeSoftImage::
290 make_writer(ostream *file, bool owns_file) {
291  init_pnm();
292  return new Writer(this, file, owns_file);
293 }
294 
295 
296 /**
297  *
298  */
299 PNMFileTypeSoftImage::Reader::
300 Reader(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  */
418 bool PNMFileTypeSoftImage::Reader::
419 supports_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  */
429 bool PNMFileTypeSoftImage::Reader::
430 read_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 
468 static void
469 write_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 
477 static void
478 write_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 
484 static int
485 compare_rgb(xel *row_data, xelval *, int x1, int x2) {
486  return PPM_EQUAL(row_data[x1], row_data[x2]);
487 }
488 
489 static void
490 write_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 
496 static int
497 compare_gray(xel *row_data, xelval *, int x1, int x2) {
498  return (PPM_GETB(row_data[x1])==PPM_GETB(row_data[x2]));
499 }
500 
501 static void
502 write_alpha(xel *, xelval *alpha_data, ostream *file, int x) {
503  write_uchar_SI(file, alpha_data[x]);
504 }
505 
506 static int
507 compare_alpha(xel *, xelval *alpha_data, int x1, int x2) {
508  return (alpha_data[x1]==alpha_data[x2]);
509 }
510 
511 static void
512 write_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 
527 static void
528 write_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 
547 static void
548 write_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  */
639 PNMFileTypeSoftImage::Writer::
640 Writer(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  */
651 bool PNMFileTypeSoftImage::Writer::
652 supports_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  */
666 bool PNMFileTypeSoftImage::Writer::
667 write_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  */
706 bool PNMFileTypeSoftImage::Writer::
707 write_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  */
727 void PNMFileTypeSoftImage::
728 register_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  */
741 TypedWritable *PNMFileTypeSoftImage::
742 make_PNMFileTypeSoftImage(const FactoryParams &params) {
743  return PNMFileTypeRegistry::get_global_ptr()->get_type_by_handle(get_class_type());
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.