00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015 #include "pnmFileTypeSoftImage.h"
00016
00017 #ifdef HAVE_SOFTIMAGE_PIC
00018
00019 #include "config_pnmimagetypes.h"
00020
00021 #include "pnmFileTypeRegistry.h"
00022 #include "bamReader.h"
00023
00024 static const float imageVersionNumber = 3.0;
00025 static const int imageCommentLength = 80;
00026 static const char imageComment[imageCommentLength+1] =
00027 "Written by pnmimage.";
00028
00029
00030 #define UNCOMPRESSED 0x00
00031 #define MIXED_RUN_LENGTH 0x02
00032
00033
00034 #define RGB_CHANNEL 0xe0
00035 #define ALPHA_CHANNEL 0x10
00036
00037
00038 #define SOFTIMAGE_MAGIC1 0x5380
00039 #define SOFTIMAGE_MAGIC2 0xf634
00040
00041 static const char * const extensions_softimage[] = {
00042 "pic", "soft"
00043 };
00044 static const int num_extensions_softimage = sizeof(extensions_softimage) / sizeof(const char *);
00045
00046 TypeHandle PNMFileTypeSoftImage::_type_handle;
00047
00048 inline float
00049 read_float(istream *file) {
00050 long l;
00051
00052 if (pm_readbiglong(file, &l)==0) {
00053 return *(float *)&l;
00054 } else {
00055 return 0.0;
00056 }
00057 }
00058
00059 inline unsigned short
00060 read_ushort_SI(istream *file) {
00061 unsigned short x;
00062 return pm_readbigshort(file, (short *)&x)==0 ? x : 0;
00063 }
00064
00065 inline unsigned char
00066 read_uchar_SI(istream *file) {
00067 int x;
00068 x = file->get();
00069 return (x!=EOF) ? (unsigned char)x : 0;
00070 }
00071
00072 inline void
00073 write_ushort_SI(ostream *file, unsigned short x) {
00074 pm_writebigshort(file, (short)x);
00075 }
00076
00077 inline void
00078 write_uchar_SI(ostream *file, unsigned char x) {
00079 file->put(x);
00080 }
00081
00082 inline void
00083 write_float(ostream *file, float x) {
00084 pm_writebiglong(file, *(long *)&x);
00085 }
00086
00087 static int
00088 read_channel_pkt(istream *file,
00089 int &chained, int &size, int &type, int &channel) {
00090 chained = read_uchar_SI(file);
00091 size = read_uchar_SI(file);
00092 type = read_uchar_SI(file);
00093 channel = read_uchar_SI(file);
00094
00095 if (file->eof() || file->fail()) {
00096 return false;
00097 }
00098
00099 if (size!=8) {
00100 pnmimage_soft_cat.error()
00101 << "Don't know how to interpret " << size << " bits per pixel!\n";
00102 return false;
00103 }
00104
00105 return true;
00106 }
00107
00108 static void
00109 read_rgb(xel *row_data, xelval *, istream *file, int x, int repeat) {
00110 xelval red, grn, blu;
00111 red = read_uchar_SI(file);
00112 grn = read_uchar_SI(file);
00113 blu = read_uchar_SI(file);
00114
00115 while (repeat>0) {
00116 PPM_ASSIGN(row_data[x], red, grn, blu);
00117 x++;
00118 repeat--;
00119 }
00120 }
00121
00122 static void
00123 read_alpha(xel *, xelval *alpha_data, istream *file, int x, int repeat) {
00124 xelval alpha = read_uchar_SI(file);
00125
00126 while (repeat>0) {
00127 alpha_data[x] = alpha;
00128 x++;
00129 repeat--;
00130 }
00131 }
00132
00133 static void
00134 read_rgba(xel *row_data, xelval *alpha_data, istream *file, int x, int repeat) {
00135 xelval red, grn, blu, alpha;
00136 red = read_uchar_SI(file);
00137 grn = read_uchar_SI(file);
00138 blu = read_uchar_SI(file);
00139 alpha = read_uchar_SI(file);
00140
00141 while (repeat>0) {
00142 PPM_ASSIGN(row_data[x], red, grn, blu);
00143 alpha_data[x] = alpha;
00144 x++;
00145 repeat--;
00146 }
00147 }
00148
00149
00150 static int
00151 read_scanline(xel *row_data, xelval *alpha_data, int cols, istream *file,
00152 void (*read_data)(xel *row_data, xelval *alpha_data, istream *file,
00153 int x, int repeat),
00154 int ctype) {
00155 if (ctype==UNCOMPRESSED) {
00156 for (int x = 0; x<cols; x++) {
00157 read_data(row_data, alpha_data, file, x, 1);
00158 }
00159 return true;
00160 } else {
00161 int x;
00162 int num;
00163
00164 x = 0;
00165 while (x < cols) {
00166 num = read_uchar_SI(file);
00167
00168 if (num<128) {
00169
00170 num++;
00171 if (x+num > cols) {
00172 return false;
00173 }
00174 while (num>0) {
00175 read_data(row_data, alpha_data, file, x, 1);
00176 if (file->eof() || file->fail()) {
00177 return false;
00178 }
00179 x++;
00180 num--;
00181 }
00182 } else {
00183
00184 if (num==128) {
00185 num = read_ushort_SI(file);
00186 } else {
00187 num -= 127;
00188 }
00189 if (x+num > cols) {
00190 return false;
00191 }
00192 read_data(row_data, alpha_data, file, x, num);
00193 if (file->eof() || file->fail()) {
00194 return false;
00195 }
00196 x += num;
00197 }
00198 }
00199
00200 return (x==cols);
00201 }
00202 }
00203
00204
00205
00206
00207
00208
00209 PNMFileTypeSoftImage::
00210 PNMFileTypeSoftImage() {
00211 }
00212
00213
00214
00215
00216
00217
00218 string PNMFileTypeSoftImage::
00219 get_name() const {
00220 return "SoftImage";
00221 }
00222
00223
00224
00225
00226
00227
00228
00229 int PNMFileTypeSoftImage::
00230 get_num_extensions() const {
00231 return num_extensions_softimage;
00232 }
00233
00234
00235
00236
00237
00238
00239
00240
00241 string PNMFileTypeSoftImage::
00242 get_extension(int n) const {
00243 nassertr(n >= 0 && n < num_extensions_softimage, string());
00244 return extensions_softimage[n];
00245 }
00246
00247
00248
00249
00250
00251
00252
00253
00254 string PNMFileTypeSoftImage::
00255 get_suggested_extension() const {
00256 return "pic";
00257 }
00258
00259
00260
00261
00262
00263
00264
00265 bool PNMFileTypeSoftImage::
00266 has_magic_number() const {
00267 return true;
00268 }
00269
00270
00271
00272
00273
00274
00275
00276
00277 bool PNMFileTypeSoftImage::
00278 matches_magic_number(const string &magic_number) const {
00279 nassertr(magic_number.size() >= 2, false);
00280 int mn =
00281 ((unsigned char)magic_number[0] << 8) |
00282 ((unsigned char)magic_number[1]);
00283 return (mn == SOFTIMAGE_MAGIC1);
00284 }
00285
00286
00287
00288
00289
00290
00291
00292
00293 PNMReader *PNMFileTypeSoftImage::
00294 make_reader(istream *file, bool owns_file, const string &magic_number) {
00295 init_pnm();
00296 return new Reader(this, file, owns_file, magic_number);
00297 }
00298
00299
00300
00301
00302
00303
00304
00305
00306 PNMWriter *PNMFileTypeSoftImage::
00307 make_writer(ostream *file, bool owns_file) {
00308 init_pnm();
00309 return new Writer(this, file, owns_file);
00310 }
00311
00312
00313
00314
00315
00316
00317
00318 PNMFileTypeSoftImage::Reader::
00319 Reader(PNMFileType *type, istream *file, bool owns_file, string magic_number) :
00320 PNMReader(type, file, owns_file)
00321 {
00322 if (!read_magic_number(_file, magic_number, 4)) {
00323
00324 if (pnmimage_soft_cat.is_debug()) {
00325 pnmimage_soft_cat.debug()
00326 << "SoftImage image file appears to be empty.\n";
00327 }
00328 _is_valid = false;
00329 return;
00330 }
00331
00332 int magic1 =
00333 ((unsigned char)magic_number[0] << 8) |
00334 ((unsigned char)magic_number[1]);
00335 int magic2 =
00336 ((unsigned char)magic_number[2] << 8) |
00337 ((unsigned char)magic_number[3]);
00338
00339 if (magic1 != SOFTIMAGE_MAGIC1 || magic2 != SOFTIMAGE_MAGIC2) {
00340 _is_valid = false;
00341 return;
00342 }
00343
00344
00345 read_float(_file);
00346
00347
00348 _file->seekg(imageCommentLength, ios::cur);
00349
00350 char pict_id[4];
00351 _file->read(pict_id, 4);
00352 if (_file->gcount() < 4) {
00353 _is_valid = false;
00354 return;
00355 }
00356
00357 if (memcmp(pict_id, "PICT", 4)!=0) {
00358 _is_valid = false;
00359 return;
00360 }
00361
00362 _x_size = read_ushort_SI(_file);
00363 _y_size = read_ushort_SI(_file);
00364
00365 read_float(_file);
00366 read_ushort_SI(_file);
00367 read_ushort_SI(_file);
00368
00369 int chained, size, channel;
00370 if (!read_channel_pkt(_file, chained, size, rgb_ctype, channel)) {
00371 _is_valid = false;
00372 return;
00373 }
00374
00375 soft_color = unknown;
00376
00377 if (channel == (RGB_CHANNEL | ALPHA_CHANNEL)) {
00378
00379 soft_color = rgba;
00380
00381 } else if (channel == RGB_CHANNEL) {
00382
00383 soft_color = rgb;
00384
00385 if (chained) {
00386 if (!read_channel_pkt(_file, chained, size, alpha_ctype, channel)) {
00387 _is_valid = false;
00388 return;
00389 }
00390
00391 if (channel == ALPHA_CHANNEL) {
00392
00393 soft_color = rgb_a;
00394 }
00395 }
00396 }
00397
00398 switch (soft_color) {
00399 case rgb:
00400 _num_channels = 3;
00401 break;
00402
00403 case rgba:
00404 case rgb_a:
00405 _num_channels = 4;
00406 break;
00407
00408 default:
00409 pnmimage_soft_cat.error()
00410 << "Image is not RGB or RGBA!\n";
00411 _is_valid = false;
00412 return;
00413 }
00414
00415 if (chained) {
00416 pnmimage_soft_cat.error()
00417 << "Unexpected additional channels in image file.\n";
00418 _is_valid = false;
00419 return;
00420 }
00421
00422 _maxval = 255;
00423
00424 if (pnmimage_soft_cat.is_debug()) {
00425 pnmimage_soft_cat.debug()
00426 << "Reading SoftImage " << *this << "\n";
00427 }
00428 }
00429
00430
00431
00432
00433
00434
00435
00436
00437
00438
00439
00440
00441 bool PNMFileTypeSoftImage::Reader::
00442 supports_read_row() const {
00443 return true;
00444 }
00445
00446
00447
00448
00449
00450
00451
00452
00453
00454
00455 bool PNMFileTypeSoftImage::Reader::
00456 read_row(xel *row_data, xelval *alpha_data, int x_size, int) {
00457 if (!is_valid()) {
00458 return false;
00459 }
00460 switch (soft_color) {
00461 case rgb:
00462 if (!read_scanline(row_data, alpha_data, x_size, _file,
00463 read_rgb, rgb_ctype)) {
00464 return false;
00465 }
00466 break;
00467
00468 case rgba:
00469 if (!read_scanline(row_data, alpha_data, x_size, _file,
00470 read_rgba, rgb_ctype)) {
00471 return false;
00472 }
00473 break;
00474
00475 case rgb_a:
00476 if (!read_scanline(row_data, alpha_data, x_size, _file,
00477 read_rgb, rgb_ctype)) {
00478 return false;
00479 }
00480 if (!read_scanline(row_data, alpha_data, x_size, _file,
00481 read_alpha, alpha_ctype)) {
00482 return false;
00483 }
00484 break;
00485
00486 default:
00487 break;
00488 }
00489
00490 return true;
00491 }
00492
00493
00494 static void
00495 write_channel_pkt(ostream *file,
00496 int chained, int size, int type, int channel) {
00497 write_uchar_SI(file, chained);
00498 write_uchar_SI(file, size);
00499 write_uchar_SI(file, type);
00500 write_uchar_SI(file, channel);
00501 }
00502
00503 static void
00504 write_rgb(xel *row_data, xelval *, ostream *file, int x) {
00505 write_uchar_SI(file, PPM_GETR(row_data[x]));
00506 write_uchar_SI(file, PPM_GETG(row_data[x]));
00507 write_uchar_SI(file, PPM_GETB(row_data[x]));
00508 }
00509
00510 static int
00511 compare_rgb(xel *row_data, xelval *, int x1, int x2) {
00512 return PPM_EQUAL(row_data[x1], row_data[x2]);
00513 }
00514
00515 static void
00516 write_gray(xel *row_data, xelval *, ostream *file, int x) {
00517 write_uchar_SI(file, PPM_GETB(row_data[x]));
00518 write_uchar_SI(file, PPM_GETB(row_data[x]));
00519 write_uchar_SI(file, PPM_GETB(row_data[x]));
00520 }
00521
00522 static int
00523 compare_gray(xel *row_data, xelval *, int x1, int x2) {
00524 return (PPM_GETB(row_data[x1])==PPM_GETB(row_data[x2]));
00525 }
00526
00527 static void
00528 write_alpha(xel *, xelval *alpha_data, ostream *file, int x) {
00529 write_uchar_SI(file, alpha_data[x]);
00530 }
00531
00532 static int
00533 compare_alpha(xel *, xelval *alpha_data, int x1, int x2) {
00534 return (alpha_data[x1]==alpha_data[x2]);
00535 }
00536
00537 static void
00538 write_diff(xel *row_data, xelval *alpha_data, ostream *file,
00539 void (*write_data)(xel *row_data, xelval *alpha_data, ostream *file,
00540 int x),
00541 int tox, int length) {
00542 if (length>0) {
00543 nassertv(length<=128);
00544
00545 write_uchar_SI(file, length-1);
00546 while (length>0) {
00547 length--;
00548 write_data(row_data, alpha_data, file, tox-length);
00549 }
00550 }
00551 }
00552
00553 static void
00554 write_same(xel *row_data, xelval *alpha_data, ostream *file,
00555 void (*write_data)(xel *row_data, xelval *alpha_data, ostream *file,
00556 int x),
00557 int tox, int length) {
00558 if (length==1) {
00559 write_diff(row_data, alpha_data, file, write_data, tox, length);
00560
00561 } else if (length>0) {
00562 if (length<128) {
00563 write_uchar_SI(file, length+127);
00564 } else {
00565 write_uchar_SI(file, 128);
00566 write_ushort_SI(file, length);
00567 }
00568 write_data(row_data, alpha_data, file, tox);
00569 }
00570 }
00571
00572
00573 static void
00574 write_scanline(xel *row_data, xelval *alpha_data, int cols, ostream *file,
00575 int (*compare_data)(xel *row_data, xelval *alpha_data,
00576 int x1, int x2),
00577 void (*write_data)(xel *row_data, xelval *alpha_data,
00578 ostream *file, int x)) {
00579 int run_length = 0;
00580
00581 int x = 0;
00582 int same = true;
00583
00584
00585
00586 while (x < cols) {
00587
00588 if (same) {
00589
00590
00591
00592
00593 if (!compare_data(row_data, alpha_data, x, x-run_length)) {
00594
00595
00596 if (run_length <= 1) {
00597
00598
00599
00600 same = false;
00601
00602 } else {
00603
00604
00605
00606
00607 write_same(row_data, alpha_data, file, write_data, x-1, run_length);
00608 same = true;
00609 run_length = 0;
00610 }
00611 }
00612
00613 } else {
00614
00615
00616
00617
00618 if (run_length>128) {
00619
00620
00621
00622 int excess = run_length - 128;
00623 write_diff(row_data, alpha_data, file, write_data, x-excess-1, 128);
00624 run_length = excess;
00625
00626 } else if (run_length > 2 &&
00627 compare_data(row_data, alpha_data, x, x-1) &&
00628 compare_data(row_data, alpha_data, x, x-2)) {
00629
00630
00631
00632
00633 write_diff(row_data, alpha_data, file, write_data, x-3, run_length-2);
00634 same = true;
00635 run_length = 2;
00636 }
00637 }
00638
00639 x++;
00640 run_length++;
00641 }
00642
00643
00644
00645 if (run_length>0) {
00646 if (same) {
00647 write_same(row_data, alpha_data, file, write_data, cols-1, run_length);
00648 } else {
00649
00650
00651
00652 if (run_length>128) {
00653 int excess = run_length - 128;
00654 write_diff(row_data, alpha_data, file, write_data, cols-excess-1, 128);
00655 run_length = excess;
00656 }
00657
00658 write_diff(row_data, alpha_data, file, write_data, cols-1, run_length);
00659 }
00660 }
00661 }
00662
00663
00664
00665
00666
00667
00668 PNMFileTypeSoftImage::Writer::
00669 Writer(PNMFileType *type, ostream *file, bool owns_file) :
00670 PNMWriter(type, file, owns_file)
00671 {
00672 }
00673
00674
00675
00676
00677
00678
00679
00680
00681
00682
00683
00684 bool PNMFileTypeSoftImage::Writer::
00685 supports_write_row() const {
00686 return true;
00687 }
00688
00689
00690
00691
00692
00693
00694
00695
00696
00697
00698
00699
00700
00701
00702
00703 bool PNMFileTypeSoftImage::Writer::
00704 write_header() {
00705 write_ushort_SI(_file, SOFTIMAGE_MAGIC1);
00706 write_ushort_SI(_file, SOFTIMAGE_MAGIC2);
00707 write_float(_file, imageVersionNumber);
00708
00709 _file->write(imageComment, imageCommentLength);
00710 _file->write("PICT", 4);
00711
00712 write_ushort_SI(_file, _x_size);
00713 write_ushort_SI(_file, _y_size);
00714
00715 write_float(_file, 1.0);
00716 write_ushort_SI(_file, 3);
00717 write_ushort_SI(_file, 0);
00718
00719
00720
00721
00722 if (has_alpha()) {
00723 write_channel_pkt(_file, 1, 8, MIXED_RUN_LENGTH, RGB_CHANNEL);
00724 write_channel_pkt(_file, 0, 8, MIXED_RUN_LENGTH, ALPHA_CHANNEL);
00725 } else {
00726 write_channel_pkt(_file, 0, 8, MIXED_RUN_LENGTH, RGB_CHANNEL);
00727 }
00728
00729 return true;
00730 }
00731
00732
00733
00734
00735
00736
00737
00738
00739
00740
00741
00742
00743
00744
00745
00746
00747 bool PNMFileTypeSoftImage::Writer::
00748 write_row(xel *row_data, xelval *alpha_data) {
00749 if (is_grayscale()) {
00750 write_scanline(row_data, alpha_data, _x_size, _file, compare_gray, write_gray);
00751
00752 } else {
00753 write_scanline(row_data, alpha_data, _x_size, _file, compare_rgb, write_rgb);
00754 }
00755
00756 if (has_alpha()) {
00757 write_scanline(row_data, alpha_data, _x_size, _file, compare_alpha, write_alpha);
00758 }
00759
00760 return !_file->fail();
00761 }
00762
00763
00764
00765
00766
00767
00768
00769
00770
00771 void PNMFileTypeSoftImage::
00772 register_with_read_factory() {
00773 BamReader::get_factory()->
00774 register_factory(get_class_type(), make_PNMFileTypeSoftImage);
00775 }
00776
00777
00778
00779
00780
00781
00782
00783
00784
00785
00786
00787
00788
00789 TypedWritable *PNMFileTypeSoftImage::
00790 make_PNMFileTypeSoftImage(const FactoryParams ¶ms) {
00791 return PNMFileTypeRegistry::get_global_ptr()->get_type_by_handle(get_class_type());
00792 }
00793
00794 #endif // HAVE_SOFTIMAGE_PIC