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