00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030
00031
00032
00033
00034
00035
00036
00037
00038
00039
00040
00041
00042
00043 #include "io_common.h"
00044 #include "image.h"
00045 #include "binary_i_file.h"
00046 #include "binary_o_file.h"
00047 #include "file_attribute.h"
00048 #include "../stde/extended_string.h"
00049 #include "../stde/extended_algorithm.h"
00050 #include "../stde/range_algorithm.h"
00051 #include "../stde/static_vector.h"
00052 #include "../prim/transformation_3d.h"
00053
00054 #define LASS_IO_IMAGE_ENFORCE_SAME_SIZE(a, b)\
00055 LASS_UTIL_IMPL_MAKE_ENFORCER(\
00056 ::lass::util::impl::TruePredicate,\
00057 ::lass::util::impl::DefaultRaiser,\
00058 ((a).rows() == (b).rows() && (a).cols() == (b).cols()),\
00059 int(0),\
00060 "Images '" LASS_STRINGIFY(a) "' and '" LASS_STRINGIFY(b) "' have different size in '" LASS_HERE "'.")
00061
00062 namespace lass
00063 {
00064 namespace io
00065 {
00066 namespace impl
00067 {
00068 class Bytes4
00069 {
00070 public:
00071 const num::Tuint8 operator[](std::size_t k) const { LASS_ASSERT(k < 4); return values_[k]; }
00072 num::Tuint8& operator[](std::size_t k) { LASS_ASSERT(k < 4); return values_[k]; }
00073 const num::Tuint8* get() const { return values_; }
00074 num::Tuint8* get() { return values_; }
00075 bool operator==(const Bytes4& other) const { return std::equal(values_, values_ + 4, other.values_); }
00076 private:
00077 num::Tuint8 values_[4];
00078 };
00079 }
00080
00081 Image::TFileFormats Image::fileFormats_ = Image::fillFileFormats();
00082 num::Tuint32 Image::magicLass_ = 0x7373616c;
00083 std::string Image::magicRadiance_ = "#?RADIANCE\n";
00084 num::Tint32 Image::magicIgi_ = 66613373;
00085
00086
00087
00088
00089
00090 Image::Image():
00091 colorSpace_(defaultColorSpace()),
00092 rows_(0),
00093 cols_(0),
00094 raster_()
00095 {
00096 }
00097
00098
00099
00100
00101
00102 Image::Image(unsigned rows, unsigned cols):
00103 colorSpace_(defaultColorSpace()),
00104 rows_(0),
00105 cols_(0),
00106 raster_()
00107 {
00108 const TPixel black;
00109 resize(rows, cols);
00110 std::fill(raster_.begin(), raster_.end(), black);
00111 }
00112
00113
00114
00115
00116
00117 Image::Image(const std::string& filename):
00118 colorSpace_(defaultColorSpace()),
00119 rows_(0),
00120 cols_(0),
00121 raster_()
00122 {
00123 open(filename);
00124 }
00125
00126
00127
00128
00129
00130 Image::Image(const Image& other):
00131 colorSpace_(other.colorSpace_),
00132 rows_(other.rows_),
00133 cols_(other.cols_),
00134 raster_(other.raster_)
00135 {
00136 }
00137
00138
00139
00140
00141
00142 Image::~Image()
00143 {
00144 }
00145
00146
00147
00148
00149
00150
00151
00152 void Image::reset()
00153 {
00154 Image temp;
00155 swap(temp);
00156 }
00157
00158
00159
00160
00161
00162 void Image::reset(unsigned rows, unsigned cols)
00163 {
00164 Image temp(rows, cols);
00165 swap(temp);
00166 }
00167
00168
00169
00170
00171
00172 void Image::reset(const std::string& filename)
00173 {
00174 Image temp(filename);
00175 swap(temp);
00176 }
00177
00178
00179
00180
00181
00182 void Image::reset(const Image& other)
00183 {
00184 Image temp(other);
00185 swap(temp);
00186 }
00187
00188
00189
00190
00191
00192 void Image::open(const std::string& filename)
00193 {
00194 try
00195 {
00196 BinaryIFile file(filename);
00197 if (!file)
00198 {
00199 LASS_THROW("could not open file to read.");
00200 }
00201
00202 open(file, fileExtension(filename));
00203 }
00204 catch (const BadFormat& error)
00205 {
00206 LASS_THROW_EX(BadFormat, "'" << filename << "': " << error.message());
00207 }
00208 catch (const util::Exception& error)
00209 {
00210 LASS_THROW("'" << filename << "': " << error.message());
00211 }
00212 }
00213
00214
00215
00216
00217
00218 void Image::open(BinaryIStream& stream, const std::string& formatTag)
00219 {
00220 Image temp;
00221 TFileOpener opener = findFormat(formatTag).open;
00222 if (!opener)
00223 {
00224 LASS_THROW_EX(BadFormat, "cannot open images in file format '" << formatTag << "'.");
00225 }
00226
00227 (temp.*opener)(stream);
00228
00229 if (stream.good())
00230 {
00231 swap(temp);
00232 }
00233 else if (stream.eof())
00234 {
00235 LASS_THROW_EX(BadFormat, "tried to read past end of file.");
00236 }
00237 else
00238 {
00239 LASS_THROW_EX(BadFormat, "unknown failure in file.");
00240 }
00241 }
00242
00243
00244
00245
00246
00247 void Image::save(const std::string& filename)
00248 {
00249 try
00250 {
00251 BinaryOFile file(filename);
00252 if (!file)
00253 {
00254 LASS_THROW("could not open file to write.");
00255 }
00256
00257 save(file, fileExtension(filename));
00258 }
00259 catch (const BadFormat& error)
00260 {
00261 LASS_THROW_EX(BadFormat, "'" << filename << "': " << error.message());
00262 }
00263 catch (const util::Exception& error)
00264 {
00265 LASS_THROW_EX(BadFormat, "'" << filename << "': " << error.message());
00266 }
00267 }
00268
00269
00270
00271
00272
00273 void Image::save(BinaryOStream& stream, const std::string& formatTag)
00274 {
00275 TFileSaver saver = findFormat(formatTag).save;
00276 if (!saver)
00277 {
00278 LASS_THROW_EX(BadFormat, "cannot save images in file format '" << formatTag << "'.");
00279 }
00280
00281 (this->*saver)(stream);
00282
00283 if (stream.eof())
00284 {
00285 LASS_THROW_EX(BadFormat, "tried to write past end of file.");
00286 }
00287 if (!stream.good())
00288 {
00289 LASS_THROW_EX(BadFormat, "unknown failure in file.");
00290 }
00291 }
00292
00293
00294
00295
00296
00297 Image& Image::operator=(const Image& other)
00298 {
00299 Image temp(other);
00300 swap(temp);
00301 return *this;
00302 }
00303
00304
00305
00306
00307
00308 void Image::swap(Image& other)
00309 {
00310 std::swap(colorSpace_, other.colorSpace_);
00311 std::swap(rows_, other.rows_);
00312 std::swap(cols_, other.cols_);
00313 raster_.swap(other.raster_);
00314 }
00315
00316
00317
00318
00319
00320
00321
00322 const Image::TPixel& Image::operator()(unsigned row, unsigned col) const
00323 {
00324 LASS_ASSERT(row < rows_ && col < cols_);
00325 return raster_[flatIndex(row, col)];
00326 }
00327
00328
00329
00330
00331
00332
00333
00334 Image::TPixel& Image::operator()(unsigned row, unsigned col)
00335 {
00336 LASS_ASSERT(row < rows_ && col < cols_);
00337 return raster_[flatIndex(row, col)];
00338 }
00339
00340
00341
00342
00343
00344
00345
00346 const Image::TPixel& Image::at(signed row, signed col) const
00347 {
00348 const unsigned i = num::mod(row, rows_);
00349 const unsigned j = num::mod(col, cols_);
00350 return raster_[flatIndex(i, j)];
00351 }
00352
00353
00354
00355
00356
00357
00358
00359 Image::TPixel& Image::at(signed row, signed col)
00360 {
00361 const unsigned i = num::mod(row, rows_);
00362 const unsigned j = num::mod(col, cols_);
00363 return raster_[flatIndex(i, j)];
00364 }
00365
00366
00367
00368
00369
00370 const Image::TPixel* const Image::data() const
00371 {
00372 return &raster_[0];
00373 }
00374
00375
00376
00377
00378
00379 Image::TPixel* const Image::data()
00380 {
00381 return &raster_[0];
00382 }
00383
00384
00385
00386
00387
00388 const Image::ColorSpace& Image::colorSpace() const
00389 {
00390 return colorSpace_;
00391 }
00392
00393
00394
00395
00396
00397
00398
00399
00400
00401
00402 Image::ColorSpace& Image::colorSpace()
00403 {
00404 return colorSpace_;
00405 }
00406
00407
00408
00409
00410
00411
00412
00413
00414
00415
00416
00417
00418
00419
00420
00421
00422 void Image::transformColors(const ColorSpace& newColorSpace)
00423 {
00424 typedef prim::Transformation3D<TValue> TTransformation;
00425
00426 struct Impl
00427 {
00428 static const TTransformation rgb2xyz(const ColorSpace& colorSpace)
00429 {
00430 typedef TTransformation::TVector TVector;
00431 const ColorSpace& C = colorSpace;
00432 TValue primaries[16] =
00433 {
00434 C.red.x, C.green.x, C.blue.x, 0,
00435 C.red.y, C.green.y, C.blue.y, 0,
00436 1 - C.red.x - C.red.y, 1 - C.green.x - C.green.y, 1 - C.blue.x - C.blue.y, 0,
00437 0, 0, 0, 1
00438 };
00439 TTransformation M(primaries, primaries + 16);
00440 const TVector w(C.white.x / C.white.y, 1, (1 - C.white.x - C.white.y) / C.white.y);
00441 const TVector S = transform(w, M.inverse());
00442 M = concatenate(TTransformation::scaler(S), M);
00443
00444 TValue bradford[16] =
00445 {
00446 0.8951f, 0.2664f, -0.1614f, 0.f,
00447 -0.7502f, 1.7135f, 0.0367f, 0.f,
00448 0.0389f, -0.0685f, 1.0296f, 0.f,
00449 0.f, 0.f, 0.f, 1.f
00450 };
00451 const TTransformation B(bradford, bradford + 16);
00452 const TVector sw = transform(w, B);
00453 TTransformation M_cat = concatenate(B, TTransformation::scaler(sw.reciprocal()));
00454 M_cat = concatenate(M_cat, B.inverse());
00455
00456 return concatenate(M, M_cat);
00457 }
00458 };
00459
00460 const TTransformation A = Impl::rgb2xyz(colorSpace_);
00461 const TTransformation B = Impl::rgb2xyz(newColorSpace);
00462 const TTransformation C = concatenate(A, B.inverse());
00463
00464 if (colorSpace_.gamma != 1)
00465 {
00466 filterGamma(1 / colorSpace_.gamma);
00467 }
00468 for (TRaster::iterator i = raster_.begin(); i != raster_.end(); ++i)
00469 {
00470 *i = transform(*i, C);
00471 }
00472 if (newColorSpace.gamma != 1)
00473 {
00474 filterGamma(newColorSpace.gamma);
00475 }
00476 colorSpace_ = newColorSpace;
00477 colorSpace_.isFromFile = false;
00478 }
00479
00480
00481
00482
00483
00484 const unsigned Image::rows() const
00485 {
00486 return rows_;
00487 }
00488
00489
00490
00491
00492
00493 const unsigned Image::cols() const
00494 {
00495 return cols_;
00496 }
00497
00498
00499
00500
00501
00502 const bool Image::isEmpty() const
00503 {
00504 return raster_.empty();
00505 }
00506
00507
00508
00509
00510
00511 void Image::over(const Image& other)
00512 {
00513 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*this, other);
00514 std::transform(raster_.begin(), raster_.end(), other.raster_.begin(), raster_.begin(), prim::over);
00515 }
00516
00517
00518
00519
00520
00521 void Image::in(const Image& other)
00522 {
00523 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*this, other);
00524 std::transform(raster_.begin(), raster_.end(), other.raster_.begin(), raster_.begin(), prim::in);
00525 }
00526
00527
00528
00529
00530
00531 void Image::out(const Image& other)
00532 {
00533 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*this, other);
00534 std::transform(raster_.begin(), raster_.end(), other.raster_.begin(), raster_.begin(), prim::out);
00535 }
00536
00537
00538
00539
00540
00541 void Image::atop(const Image& other)
00542 {
00543 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*this, other);
00544 std::transform(raster_.begin(), raster_.end(), other.raster_.begin(), raster_.begin(), prim::atop);
00545 }
00546
00547
00548
00549
00550
00551 void Image::through(const Image& other)
00552 {
00553 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*this, other);
00554 std::transform(raster_.begin(), raster_.end(), other.raster_.begin(), raster_.begin(), prim::through);
00555 }
00556
00557
00558
00559
00560
00561 void Image::rover(const Image& other)
00562 {
00563 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*this, other);
00564 std::transform(other.raster_.begin(), other.raster_.end(), raster_.begin(), raster_.begin(), prim::over);
00565 }
00566
00567
00568
00569
00570
00571 void Image::rin(const Image& other)
00572 {
00573 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*this, other);
00574 std::transform(other.raster_.begin(), other.raster_.end(), raster_.begin(), raster_.begin(), prim::in);
00575 }
00576
00577
00578
00579
00580
00581 void Image::rout(const Image& other)
00582 {
00583 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*this, other);
00584 std::transform(other.raster_.begin(), other.raster_.end(), raster_.begin(), raster_.begin(), prim::out);
00585 }
00586
00587
00588
00589
00590
00591 void Image::ratop(const Image& other)
00592 {
00593 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*this, other);
00594 std::transform(other.raster_.begin(), other.raster_.end(), raster_.begin(), raster_.begin(), prim::atop);
00595 }
00596
00597
00598
00599
00600
00601 void Image::rthrough(const Image& other)
00602 {
00603 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*this, other);
00604 std::transform(other.raster_.begin(), other.raster_.end(), raster_.begin(), raster_.begin(), prim::through);
00605 }
00606
00607
00608
00609
00610
00611 void Image::plus(const Image& other)
00612 {
00613 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*this, other);
00614 std::transform(raster_.begin(), raster_.end(), other.raster_.begin(), raster_.begin(), prim::plus);
00615 }
00616
00617
00618
00619
00620
00621 void Image::clampNegatives()
00622 {
00623 const TRaster::iterator last = raster_.end();
00624 for (TRaster::iterator i = raster_.begin(); i != last; ++i)
00625 {
00626 i->r = std::max(i->r, 0.f);
00627 i->g = std::max(i->g, 0.f);
00628 i->b = std::max(i->b, 0.f);
00629 i->a = std::max(i->a, 0.f);
00630 }
00631 }
00632
00633
00634
00635
00636
00637
00638
00639
00640
00641
00642
00643
00644 void Image::filterMedian(unsigned boxSize)
00645 {
00646 if ((boxSize & 0x1) == 0)
00647 {
00648 LASS_THROW("boxSize '" << boxSize << "' isn't odd as requested.");
00649 return;
00650 }
00651
00652 const unsigned boxArea = boxSize * boxSize;
00653 const int boxRadius = (boxSize - 1) / 2;
00654
00655 TRaster box(boxArea);
00656 std::vector<float> distances(boxArea);
00657
00658 for (unsigned y0 = 0; y0 < rows_; ++y0)
00659 {
00660 for (unsigned x0 = 0; x0 < cols_; ++x0)
00661 {
00662 TPixel& center = raster_[y0 * cols_ + x0];
00663 if (center.a == TNumTraits::zero)
00664 {
00665 continue;
00666 }
00667
00668
00669
00670 unsigned pixelsInBox = 0;
00671 int candidate = -1;
00672 for (int dy = -boxRadius; dy <= boxRadius; ++dy)
00673 {
00674 const int y = y0 + dy;
00675 const int offset = y * cols_;
00676 if (y >= 0 && y < signed(rows_))
00677 {
00678 for (int dx = -boxRadius; dx <= boxRadius; ++dx)
00679 {
00680 const int x = x0 + dx;
00681 if (x >= 0 && x < signed(cols_))
00682 {
00683 const TPixel& pixel = raster_[offset + x];
00684 if (pixel.a != TNumTraits::zero)
00685 {
00686 box[pixelsInBox] = pixel;
00687 if (dx == 0 && dy == 0)
00688 {
00689 candidate = pixelsInBox;
00690 }
00691 ++pixelsInBox;
00692 }
00693 }
00694 }
00695 }
00696 }
00697
00698
00699
00700 LASS_ASSERT(pixelsInBox > 0);
00701 LASS_ASSERT(candidate >= 0 && candidate < signed(pixelsInBox));
00702 unsigned i;
00703 for (i = 0; i < pixelsInBox; ++i)
00704 {
00705 distances[i] = 0.0;
00706 for (unsigned j = 0; j < pixelsInBox; ++j)
00707 {
00708 distances[i] += prim::distance(box[i], box[j]);
00709 }
00710 }
00711
00712 for (i = 0; i < pixelsInBox; ++i)
00713 {
00714 if (distances[i] < distances[candidate])
00715 {
00716 candidate = i;
00717 }
00718 }
00719
00720 LASS_ASSERT(candidate >= 0 && candidate < signed(pixelsInBox));
00721 center = box[candidate];
00722 }
00723 }
00724 }
00725
00726
00727
00728
00729
00730 void Image::filterGamma(TParam gammaExponent)
00731 {
00732 const TRaster::size_type size = raster_.size();
00733 for (TRaster::size_type i = 0; i < size; ++i)
00734 {
00735 raster_[i] = raster_[i].gammaCorrected(gammaExponent);
00736 }
00737 colorSpace_.gamma *= gammaExponent;
00738 }
00739
00740
00741
00742
00743
00744 void Image::filterExposure(TParam exposureTime)
00745 {
00746 const TRaster::size_type size = raster_.size();
00747 for (TRaster::size_type i = 0; i < size; ++i)
00748 {
00749 raster_[i] = raster_[i].exposed(exposureTime);
00750 }
00751 }
00752
00753
00754
00755
00756
00757 void Image::filterInverseExposure(TParam exposureTime)
00758 {
00759 const TRaster::size_type size = raster_.size();
00760 for (TRaster::size_type i = 0; i < size; ++i)
00761 {
00762 raster_[i] = raster_[i].invExposed(exposureTime);
00763 }
00764 }
00765
00766
00767
00768
00769
00770
00771
00772
00773
00774
00775
00776
00777 unsigned Image::resize(unsigned rows, unsigned cols)
00778 {
00779 const unsigned size = rows * cols;
00780 raster_.resize(size);
00781 rows_ = rows;
00782 cols_ = cols;
00783 return size;
00784 }
00785
00786
00787
00788
00789
00790 BinaryIStream& Image::openLass(BinaryIStream& stream)
00791 {
00792 HeaderLass header;
00793 header.readFrom(stream);
00794 if (!stream || header.lass != magicLass_ || header.version > 3)
00795 {
00796 LASS_THROW_EX(BadFormat, "not a LASS RAW version 1 - 3 file.");
00797 }
00798
00799 EndiannessSetter(stream, num::littleEndian);
00800
00801 if (header.version >= 2)
00802 {
00803 for (size_t i = 0; i < numChromaticities; ++i)
00804 {
00805 num::Tfloat32 x, y;
00806 stream >> x >> y;
00807 colorSpace_[i] = TChromaticity(x, y);
00808 }
00809 colorSpace_.isFromFile = true;
00810 }
00811 else
00812 {
00813 colorSpace_ = defaultColorSpace();
00814 }
00815
00816 if (header.version >= 3)
00817 {
00818 num::Tfloat32 g;
00819 stream >> g;
00820 colorSpace_.gamma = g;
00821 }
00822
00823 resize(header.rows, header.cols);
00824 num::Tfloat32 r, g, b, a;
00825 for (TRaster::iterator i = raster_.begin(); i != raster_.end(); ++i)
00826 {
00827 stream >> r >> g >> b >> a;
00828 i->r = r;
00829 i->g = g;
00830 i->b = b;
00831 i->a = a;
00832 }
00833
00834 return stream;
00835 }
00836
00837
00838
00839
00840
00841 BinaryIStream& Image::openTarga(BinaryIStream& stream)
00842 {
00843 HeaderTarga header;
00844 header.readFrom(stream);
00845 colorSpace_ = defaultColorSpace();
00846 colorSpace_.gamma = 2.2f;
00847 resize(header.imageHeight, header.imageWidth);
00848
00849 switch (header.imageType)
00850 {
00851 case 2:
00852 case 10:
00853 openTargaTrueColor(stream, header);
00854 break;
00855 default:
00856 LASS_THROW_EX(BadFormat, "unsupported image type '" << static_cast<unsigned>(header.imageType) << "'");
00857 break;
00858 };
00859
00860 return stream;
00861 }
00862
00863
00864
00865
00866
00867 BinaryIStream& Image::openTargaTrueColor(BinaryIStream& stream, const HeaderTarga& iHeader)
00868 {
00869 const TValue scale = num::inv(255.f);
00870
00871 std::size_t numBytes = 0;
00872 switch (iHeader.imagePixelSize)
00873 {
00874 case 24:
00875 numBytes = 3;
00876 break;
00877 case 32:
00878 numBytes = 4;
00879 break;
00880 default:
00881 LASS_THROW_EX(BadFormat, "image pixel size '" << iHeader.imagePixelSize << "' not supported.");
00882 };
00883
00884 LASS_ASSERT(cols_ == iHeader.imageWidth);
00885 const int xBegin = iHeader.flipHorizontalFlag() ? static_cast<int>(cols_) - 1 : 0;
00886 const int xEnd = iHeader.flipHorizontalFlag() ? -1 : static_cast<int>(cols_);
00887 const int xDelta = iHeader.flipHorizontalFlag() ? -1 : 1;
00888
00889 LASS_ASSERT(rows_ == iHeader.imageHeight);
00890 const int yBegin = iHeader.flipVerticalFlag() ? 0 : static_cast<int>(rows_) - 1;
00891 const int yEnd = iHeader.flipVerticalFlag() ? static_cast<int>(rows_) : -1;
00892 const int yDelta = iHeader.flipVerticalFlag() ? 1 : -1;
00893
00894 stream.seekg(iHeader.idLength + iHeader.colorMapLength * iHeader.colorMapEntrySize,
00895 std::ios_base::cur);
00896 std::vector<impl::Bytes4> buffer(cols_);
00897 for (int y = yBegin; y != yEnd; y += yDelta)
00898 {
00899 if (iHeader.imageType == 10)
00900 {
00901
00902
00903 unsigned x = 0;
00904 while (x < cols_)
00905 {
00906 num::Tuint8 code;
00907 stream >> code;
00908 const num::Tuint8 packetSize = (code & 0x7f) + 1;
00909 impl::Bytes4 bytes;
00910 if (code & 0x80)
00911 {
00912 stream.read(bytes.get(), numBytes);
00913 for (num::Tuint8 i = 0; i < packetSize; ++i)
00914 {
00915 LASS_ASSERT(x < cols_);
00916 buffer[x++] = bytes;
00917 }
00918 }
00919 else
00920 {
00921 for (num::Tuint8 i = 0; i < packetSize; ++i)
00922 {
00923 stream.read(bytes.get(), numBytes);
00924 LASS_ASSERT(x < cols_);
00925 buffer[x++] = bytes;
00926 }
00927 }
00928 }
00929 }
00930 else
00931 {
00932 LASS_ASSERT(iHeader.imageType == 2);
00933 for (unsigned x = 0; x < cols_; ++x)
00934 {
00935 stream.read(buffer[x].get(), numBytes);
00936 }
00937 }
00938
00939
00940
00941 TPixel* pixel = &raster_[y * cols_];
00942 for (int x = xBegin; x != xEnd; x += xDelta)
00943 {
00944 const impl::Bytes4& bytes = buffer[x];
00945 pixel->r = static_cast<TValue>(bytes[2]) * scale;
00946 pixel->g = static_cast<TValue>(bytes[1]) * scale;
00947 pixel->b = static_cast<TValue>(bytes[0]) * scale;
00948 pixel->a = numBytes == 3 ? TNumTraits::one : static_cast<TValue>(bytes[3]) * scale;
00949 ++pixel;
00950 }
00951 }
00952
00953 return stream;
00954 }
00955
00956
00957
00958 BinaryIStream& Image::openRadianceHdr(BinaryIStream& stream)
00959 {
00960 HeaderRadianceHdr header;
00961 header.readFrom(stream);
00962 if (!stream)
00963 {
00964 LASS_THROW_EX(BadFormat, "syntax error in RADIANCE HDR header.");
00965 }
00966
00967 if (header.isRgb)
00968 {
00969 for (size_t i = 0; i < numChromaticities; ++i)
00970 {
00971 colorSpace_[i] = TChromaticity(header.primaries[2 * i], header.primaries[2 * i + 1]);
00972 }
00973 }
00974 else
00975 {
00976 colorSpace_.red = TChromaticity(1, 0);
00977 colorSpace_.green = TChromaticity(0, 1);
00978 colorSpace_.blue = TChromaticity(0, 0);
00979 colorSpace_.white = TChromaticity(1.f / 3, 1.f / 3);
00980 }
00981 colorSpace_.gamma = 1;
00982 colorSpace_.isFromFile = true;
00983
00984 float exponents[256];
00985 exponents[0] = 0.f;
00986 for (int i = 1; i < 256; ++i)
00987 {
00988 exponents[i] = ::ldexpf(1.f, i - 128 - 8);
00989 }
00990 float inverseCorrections[3];
00991 for (int i = 0; i < 3; ++i)
00992 {
00993 inverseCorrections[i] = num::inv(header.exposure * header.colorCorr[i]);
00994 }
00995
00996 resize(header.height, header.width);
00997
00998 const std::ptrdiff_t firstY = !header.yIncreasing ? 0 : (header.height - 1);
00999 const std::ptrdiff_t lastY = !header.yIncreasing ? static_cast<std::ptrdiff_t>(header.height) : -1;
01000 const std::ptrdiff_t deltaY = !header.yIncreasing ? 1 : -1;
01001 const std::ptrdiff_t firstX = header.xIncreasing ? 0 : (header.width - 1);
01002 const std::ptrdiff_t lastX = header.xIncreasing ? static_cast<std::ptrdiff_t>(header.width) : -1;
01003 const std::ptrdiff_t deltaX = header.xIncreasing ? 1 : -1;
01004
01005 std::vector<impl::Bytes4> buffer(header.width);
01006 unsigned rleCount = 0;
01007 unsigned rleCountByte = 0;
01008
01009 for (std::ptrdiff_t y = firstY; y != lastY; y += deltaY)
01010 {
01011
01012
01013 for (std::ptrdiff_t x = firstX; x != lastX; )
01014 {
01015 impl::Bytes4 rgbe;
01016 stream.read(rgbe.get(), 4);
01017 if (rgbe[0] == 2 && rgbe[1] == 2 && (rgbe[2] & 0x80) == 0)
01018 {
01019
01020
01021 const unsigned lineLength = rgbe[2] * 256 + rgbe[3];
01022 const ptrdiff_t lastX2 = x + lineLength * deltaX;
01023 for (unsigned k = 0; k < 4; ++k)
01024 {
01025 ptrdiff_t x2 = x;
01026 while (x2 != lastX2)
01027 {
01028 num::Tuint8 spanSize = 0, value = 0;
01029 stream >> spanSize;
01030 const bool isHomogenousSpan = spanSize > 128;
01031 if (isHomogenousSpan)
01032 {
01033 spanSize -= 128;
01034 stream >> value;
01035 }
01036 for (num::Tuint8 i = spanSize; i > 0; --i)
01037 {
01038 if (!isHomogenousSpan)
01039 {
01040 stream >> value;
01041 }
01042 LASS_ASSERT(x2 != lastX2);
01043 buffer[x2][k] = value;
01044 x2 += deltaX;
01045 }
01046 }
01047 }
01048 x = lastX2;
01049 }
01050 else
01051 {
01052
01053
01054 if (rgbe[0] == 1 && rgbe[1] == 1 && rgbe[2] == 1)
01055 {
01056 rleCount |= rgbe[4] << rleCountByte;
01057 ++rleCountByte;
01058 }
01059 else
01060 {
01061 if (rleCount > 0)
01062 {
01063 for (unsigned k = rleCount - 1; k > 0; --k)
01064 {
01065 buffer[x] = rgbe;
01066 x += deltaX;
01067 }
01068 rleCount = 0;
01069 rleCountByte = 0;
01070 }
01071 buffer[x] = rgbe;
01072 x += deltaX;
01073 }
01074 }
01075 }
01076
01077
01078
01079 TPixel* scanline = &raster_[y * header.width];
01080 for (std::ptrdiff_t x = firstX; x != lastX; x += deltaX)
01081 {
01082 const impl::Bytes4 rgbe = buffer[x];
01083 TPixel& pixel = scanline[x];
01084 const float exponent = exponents[rgbe[3]];
01085 for (unsigned k = 0; k < 3; ++k)
01086 {
01087 pixel[k] = rgbe[k] * inverseCorrections[k] * exponent;
01088 }
01089 }
01090 }
01091 return stream;
01092 }
01093
01094
01095
01096 BinaryIStream& Image::openPfm(BinaryIStream& stream)
01097 {
01098 HeaderPfm header;
01099 header.readFrom(stream);
01100 if (!stream)
01101 {
01102 LASS_THROW_EX(BadFormat, "not a PFM file.");
01103 }
01104
01105 colorSpace_ = defaultColorSpace();
01106 colorSpace_.gamma = 1;
01107
01108 resize(header.height, header.width);
01109 EndiannessSetter(stream, header.endianness);
01110
01111 for (unsigned k = rows_; k > 0; --k)
01112 {
01113 const TRaster::iterator first = raster_.begin() + (k - 1) * cols_;
01114 const TRaster::iterator last = first + cols_;
01115 if (header.isGrey)
01116 {
01117 num::Tfloat32 y;
01118 for (TRaster::iterator i = first; i != last; ++i)
01119 {
01120 stream >> y;
01121 i->r = y;
01122 i->g = y;
01123 i->b = y;
01124 i->a = 1;
01125 }
01126 }
01127 else
01128 {
01129 num::Tfloat32 r, g, b;
01130 for (TRaster::iterator i = first; i != last; ++i)
01131 {
01132 stream >> r >> g >> b;
01133 i->r = r;
01134 i->g = g;
01135 i->b = b;
01136 i->a = 1;
01137 }
01138 }
01139 }
01140
01141 return stream;
01142 }
01143
01144
01145
01146 BinaryIStream& Image::openIgi(BinaryIStream& stream)
01147 {
01148 HeaderIgi header;
01149 header.readFrom(stream);
01150
01151 if (!stream || header.magic != magicIgi_ || header.version < 1 || header.version > 2)
01152 {
01153 LASS_THROW_EX(BadFormat, "not an IGI version <= 2 file.");
01154 }
01155 if (header.zipped)
01156 {
01157 LASS_THROW_EX(BadFormat, "cannot read zipped IGI files.");
01158 }
01159 if (header.dataSize < 0 || static_cast<num::Tuint32>(header.dataSize) != 12 * header.width * header.height)
01160 {
01161 LASS_THROW_EX(BadFormat, "error in dataSize field.");
01162 }
01163
01164
01165
01166 colorSpace_ = header.rgb ? defaultColorSpace() : xyzColorSpace();
01167 colorSpace_.gamma = 1;
01168 colorSpace_.isFromFile = true;
01169
01170 resize(header.height, header.width);
01171 num::Tfloat32 r, g, b;
01172 EndiannessSetter(stream, num::littleEndian);
01173 for (TRaster::iterator i = raster_.begin(); i != raster_.end(); ++i)
01174 {
01175 stream >> r >> g >> b;
01176 i->r = r;
01177 i->g = g;
01178 i->b = b;
01179 i->a = 1;
01180 }
01181
01182 return stream;
01183 }
01184
01185
01186 BinaryOStream& Image::saveLass(BinaryOStream& stream) const
01187 {
01188 HeaderLass header;
01189 header.lass = magicLass_;
01190 header.version = 2;
01191 header.rows = rows_;
01192 header.cols = cols_;
01193 header.writeTo(stream);
01194
01195 EndiannessSetter(stream, num::littleEndian);
01196
01197 for (size_t i = 0; i < numChromaticities; ++i)
01198 {
01199 const num::Tfloat32 x = colorSpace_[i].x;
01200 const num::Tfloat32 y = colorSpace_[i].y;
01201 stream << x << y;
01202 }
01203
01204 const num::Tfloat32 c = colorSpace_.gamma;
01205 stream << c;
01206
01207 num::Tfloat32 r, g, b, a;
01208 for (TRaster::const_iterator i = raster_.begin(); i != raster_.end(); ++i)
01209 {
01210 r = i->r;
01211 g = i->g;
01212 b = i->b;
01213 a = i->a;
01214 stream << r << g << b << a;
01215 }
01216
01217 return stream;
01218 }
01219
01220
01221
01222
01223 BinaryOStream& Image::saveTarga(BinaryOStream& stream) const
01224 {
01225 const TValue scale(255);
01226 const TValue zero(0);
01227 const TValue one(1);
01228
01229
01230 HeaderTarga header;
01231 header.idLength = 0;
01232 header.colorMapType = 0;
01233 header.imageType = 10;
01234 header.colorMapOrigin = 0;
01235 header.colorMapLength = 0;
01236 header.colorMapEntrySize = 0;
01237 header.imageXorigin = 0;
01238 header.imageYorigin = 0;
01239 header.imageWidth = cols_;
01240 header.imageHeight = rows_;
01241 header.imagePixelSize = 32;
01242 header.imageDescriptor = 0x08;
01243
01244 header.writeTo(stream);
01245
01246 std::vector<impl::Bytes4> buffer(header.imageWidth);
01247 std::vector<impl::Bytes4> rleBuffer(128);
01248 for (unsigned y = rows_; y > 0; --y)
01249 {
01250 unsigned x;
01251
01252
01253
01254 const TPixel* scanline = &raster_[(y - 1) * cols_];
01255 for (x = 0; x < cols_; ++x)
01256 {
01257 const TPixel& pixel = scanline[x];
01258 impl::Bytes4& bytes = buffer[x];
01259 bytes[0] = static_cast<num::Tuint8>(num::clamp(pixel.b, zero, one) * scale);
01260 bytes[1] = static_cast<num::Tuint8>(num::clamp(pixel.g, zero, one) * scale);
01261 bytes[2] = static_cast<num::Tuint8>(num::clamp(pixel.r, zero, one) * scale);
01262 bytes[3] = static_cast<num::Tuint8>(num::clamp(pixel.a, zero, one) * scale);
01263 }
01264
01265
01266
01267 unsigned totalLength = 0;
01268 num::Tuint8 numDiff = 0;
01269 x = 0;
01270 while (x < cols_)
01271 {
01272 const impl::Bytes4& bytes = buffer[x];
01273 unsigned x2 = x;
01274 num::Tuint8 numSame = 0;
01275 while (x2 < cols_ && numSame < 128 && buffer[x2] == bytes)
01276 {
01277 ++x2;
01278 ++numSame;
01279 }
01280 if (numSame == 1)
01281 {
01282 rleBuffer[numDiff] = bytes;
01283 ++numDiff;
01284 ++x;
01285 }
01286 if (numDiff == 128 || ((numSame > 1 || x == cols_) && numDiff > 0))
01287 {
01288 stream << static_cast<num::Tuint8>(numDiff - 1);
01289 for (num::Tuint8 i = 0; i < numDiff; ++i)
01290 {
01291 stream.write(&rleBuffer[i], 4);
01292 }
01293 totalLength += numDiff;
01294 LASS_ASSERT(totalLength == x);
01295 numDiff = 0;
01296 }
01297 if (numSame > 1)
01298 {
01299 stream << static_cast<num::Tuint8>((numSame - 1) | 0x80);
01300 stream.write(bytes.get(), 4);
01301 x = x2;
01302 totalLength += numSame;
01303 LASS_ASSERT(totalLength == x);
01304 numSame = 0;
01305 }
01306 }
01307 LASS_ASSERT(totalLength == cols_);
01308 }
01309
01310 return stream;
01311 }
01312
01313
01314
01315
01316 BinaryOStream& Image::saveRadianceHdr(BinaryOStream& stream) const
01317 {
01318 if (cols_ > 0x7fff)
01319 {
01320 LASS_THROW_EX(BadFormat, "cannot save this as RADIANCE HDR, because image is too wide");
01321 }
01322 HeaderRadianceHdr header;
01323 header.height = rows_;
01324 header.width = cols_;
01325 header.yIncreasing = false;
01326 header.xIncreasing = true;
01327 header.isRgb = colorSpace_ != xyzColorSpace();
01328 for (size_t i = 0; i < numChromaticities; ++i)
01329 {
01330 header.primaries[2 * i] = colorSpace_[i].x;
01331 header.primaries[2 * i + 1] = colorSpace_[i].y;
01332 }
01333 header.writeTo(stream);
01334
01335 std::vector<impl::Bytes4> buffer(cols_);
01336 std::vector<num::Tuint8> rleBuffer(128);
01337
01338 for (unsigned y = 0; y < rows_; ++y)
01339 {
01340
01341
01342 const TPixel* line = &raster_[y * cols_];
01343 for (unsigned x = 0; x < cols_; ++x)
01344 {
01345 const TPixel& pixel = line[x];
01346 impl::Bytes4& rgbe = buffer[x];
01347 const float maximum = std::max(std::max(pixel.r, pixel.g), pixel.b);
01348 if (maximum < 1e-32)
01349 {
01350 rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
01351 }
01352 else
01353 {
01354 int exponent;
01355 const float mantissa = ::frexpf(maximum, &exponent);
01356 for (unsigned k = 0; k < 3; ++k)
01357 {
01358 const float normalized = pixel[k] * (256.f * mantissa / maximum);
01359 rgbe[k] = static_cast<num::Tuint8>(num::clamp(normalized, 0.f, 255.f));
01360 }
01361 rgbe[3] = static_cast<num::Tuint8>(num::clamp(exponent + 128, 0, 255));
01362 }
01363 }
01364
01365
01366
01367 num::Tuint8 bytes[4];
01368 bytes[0] = 2;
01369 bytes[1] = 2;
01370 bytes[2] = (cols_ & 0x7f00) >> 8;
01371 bytes[3] = cols_ & 0xff;
01372 stream.write(bytes, 4);
01373
01374 for (unsigned k = 0; k < 4; ++k)
01375 {
01376 num::Tuint8 numDiff = 0;
01377 unsigned x = 0;
01378 while (x < cols_)
01379 {
01380 num::Tuint8 value = buffer[x][k];
01381 unsigned x2 = x;
01382 num::Tuint8 numSame = 0;
01383 while (x2 < cols_ && numSame < 127 && buffer[x2][k] == value)
01384 {
01385 ++x2;
01386 ++numSame;
01387 }
01388 if (numSame > 2)
01389 {
01390 if (numDiff > 0)
01391 {
01392 stream << numDiff;
01393 stream.write(&rleBuffer[0], numDiff);
01394 numDiff = 0;
01395 }
01396 numSame += 128;
01397 stream << numSame << value;
01398 x = x2;
01399 }
01400 else
01401 {
01402 if (numDiff == 128)
01403 {
01404 stream << numDiff;
01405 stream.write(&rleBuffer[0], numDiff);
01406 numDiff = 0;
01407 }
01408 rleBuffer[numDiff] = value;
01409 ++numDiff;
01410 ++x;
01411 }
01412 }
01413 if (numDiff > 0)
01414 {
01415 stream << numDiff;
01416 stream.write(&rleBuffer[0], numDiff);
01417 }
01418 }
01419 }
01420 return stream;
01421 }
01422
01423
01424
01425 BinaryOStream& Image::savePfm(BinaryOStream& stream) const
01426 {
01427 HeaderPfm header;
01428 header.height = rows_;
01429 header.width = cols_;
01430 header.writeTo(stream);
01431
01432 EndiannessSetter(stream, header.endianness);
01433
01434 for (unsigned k = rows_; k > 0; --k)
01435 {
01436 const TRaster::const_iterator first = raster_.begin() + (k - 1) * cols_;
01437 const TRaster::const_iterator last = first + cols_;
01438 for (TRaster::const_iterator i = first; i != last; ++i)
01439 {
01440 const num::Tfloat32 r = i->r;
01441 const num::Tfloat32 g = i->g;
01442 const num::Tfloat32 b = i->b;
01443 stream << r << g << b;
01444 }
01445 }
01446
01447 return stream;
01448 }
01449
01450
01451
01452 BinaryOStream& Image::saveIgi(BinaryOStream& stream) const
01453 {
01454 HeaderIgi header;
01455 header.magic = magicIgi_;
01456 header.version = 1;
01457 header.numSamples = rows_ * cols_;
01458 header.width = cols_;
01459 header.height = rows_;
01460 header.superSampling = 1;
01461 header.zipped = 0;
01462 header.dataSize = 12 * rows_ * cols_;
01463 header.rgb = colorSpace_ == xyzColorSpace() ? 0 : 1;
01464 header.writeTo(stream);
01465
01466 EndiannessSetter(stream, num::littleEndian);
01467
01468 for (TRaster::const_iterator i = raster_.begin(); i != raster_.end(); ++i)
01469 {
01470 const num::Tfloat32 r = i->r;
01471 const num::Tfloat32 g = i->g;
01472 const num::Tfloat32 b = i->b;
01473 stream << r << g << b;
01474 }
01475
01476 return stream;
01477 }
01478
01479
01480
01481 Image::FileFormat Image::findFormat(const std::string& formatTag)
01482 {
01483 const std::string key = stde::tolower(formatTag);
01484 TFileFormats::const_iterator i = fileFormats_.find(key);
01485 if (i == fileFormats_.end())
01486 {
01487 LASS_THROW_EX(BadFormat, "unknown fileformat '" << key << "'.");
01488 }
01489 return i->second;
01490 }
01491
01492
01493
01494 BinaryIStream& Image::readLine(BinaryIStream& stream, std::string& line)
01495 {
01496 std::string result;
01497 while (stream.good())
01498 {
01499 char character;
01500 stream >> character;
01501 if (stream.good())
01502 {
01503 if (character == '\n')
01504 {
01505 line.swap(result);
01506 }
01507 else
01508 {
01509 result += character;
01510 }
01511 }
01512 }
01513 return stream;
01514 }
01515
01516
01517
01518 BinaryOStream& Image::writeLine(BinaryOStream& stream, const std::string& iString)
01519 {
01520 stream.write(iString.data(), iString.size());
01521 stream << '\n';
01522 return stream;
01523 }
01524
01525
01526
01527 Image::TFileFormats Image::fillFileFormats()
01528 {
01529 TFileFormats formats;
01530 formats["lass"] = FileFormat(&Image::openLass, &Image::saveLass);
01531 formats["targa"] = FileFormat(&Image::openTarga, &Image::saveTarga);
01532 formats["tga"] = formats["targa"];
01533 formats["hdr"] = FileFormat(&Image::openRadianceHdr, &Image::saveRadianceHdr);
01534 formats["pic"] = formats["hdr"];
01535 formats["rgbe"] = formats["hdr"];
01536 formats["pfm"] = FileFormat(&Image::openPfm, &Image::savePfm);
01537 formats["igi"] = FileFormat(&Image::openIgi, &Image::saveIgi);
01538 return formats;
01539 }
01540
01541
01542
01543
01544
01545 const Image::ColorSpace Image::defaultColorSpace()
01546 {
01547 ColorSpace result;
01548 result.red = TChromaticity(0.6400f, 0.3300f);
01549 result.green = TChromaticity(0.3000f, 0.6000f);
01550 result.blue = TChromaticity(0.1500f, 0.0600f);
01551 result.white = TChromaticity(0.3127f, 0.3290f);
01552 result.gamma = 1;
01553 result.isFromFile = false;
01554 return result;
01555 }
01556
01557
01558
01559 const Image::ColorSpace Image::xyzColorSpace()
01560 {
01561 ColorSpace result;
01562 result.red = TChromaticity(1.f, 0.f);
01563 result.green = TChromaticity(0.f, 1.f);
01564 result.blue = TChromaticity(0.f, 0.f);
01565 result.white = TChromaticity(1.f / 3, 1.f / 3);
01566 result.gamma = 1;
01567 result.isFromFile = false;
01568 return result;
01569 }
01570
01571
01572
01573
01574
01575 void Image::HeaderLass::readFrom(BinaryIStream& stream)
01576 {
01577 EndiannessSetter(stream, num::littleEndian);
01578 stream >> lass >> version >> rows >> cols;
01579 }
01580
01581
01582
01583 void Image::HeaderLass::writeTo(BinaryOStream& stream)
01584 {
01585 EndiannessSetter(stream, num::littleEndian);
01586 stream << lass << version << rows << cols;
01587 }
01588
01589
01590
01591
01592
01593 void Image::HeaderTarga::readFrom(BinaryIStream& stream)
01594 {
01595 EndiannessSetter(stream, num::littleEndian);
01596 stream >> idLength >> colorMapType >> imageType >> colorMapOrigin >> colorMapLength >>
01597 colorMapEntrySize >> imageXorigin >> imageYorigin >> imageWidth >> imageHeight >>
01598 imagePixelSize >> imageDescriptor;
01599 }
01600
01601
01602
01603 void Image::HeaderTarga::writeTo(BinaryOStream& stream)
01604 {
01605 EndiannessSetter(stream, num::littleEndian);
01606 stream << idLength << colorMapType << imageType << colorMapOrigin << colorMapLength <<
01607 colorMapEntrySize << imageXorigin << imageYorigin << imageWidth << imageHeight <<
01608 imagePixelSize << imageDescriptor;
01609 }
01610
01611
01612
01613
01614
01615 Image::HeaderRadianceHdr::HeaderRadianceHdr():
01616 exposure(1.f),
01617 height(0),
01618 width(0),
01619 yIncreasing(false),
01620 xIncreasing(true),
01621 isRgb(true),
01622 isDefaultPrimaries(true)
01623 {
01624 std::fill_n(colorCorr, static_cast<int>(sizeColorCorr), 1.f);
01625 const float defaultPrimaries[sizePrimaries] =
01626 { 0.640f, 0.330f, 0.290f, 0.600f, 0.150f, 0.060f, 0.333f, 0.333f };
01627 stde::copy_n(defaultPrimaries, static_cast<int>(sizePrimaries), primaries);
01628 }
01629
01630 void Image::HeaderRadianceHdr::readFrom(BinaryIStream& stream)
01631 {
01632 std::size_t magicSize = magicRadiance_.size();
01633 std::vector<char> magic(magicSize);
01634 stream.read(&magic[0], magicSize);
01635 if (!stream.good() || !stde::equal_r(magicRadiance_, magic))
01636 {
01637 LASS_THROW_EX(BadFormat, "file is not of RADIANCE file format");
01638 }
01639
01640
01641
01642 while (true)
01643 {
01644 std::string line;
01645 readLine(stream, line);
01646 if (!stream.good())
01647 {
01648 LASS_THROW_EX(BadFormat, "syntax error in header of RADIANCE HDR file");
01649 }
01650 if (line.empty())
01651 {
01652 break;
01653 }
01654 if (stde::begins_with(line, std::string("#")))
01655 {
01656 continue;
01657 }
01658 std::vector<std::string> splitted = stde::split(line, std::string("="), 1);
01659 if (splitted.size() != 2)
01660 {
01661 LASS_THROW_EX(BadFormat, "syntax error in header of RADIANCE HDR file");
01662 }
01663 std::string command = stde::toupper(splitted[0]);
01664 std::string value = stde::strip(splitted[1]);
01665 if (command == "FORMAT")
01666 {
01667 if (value == "32-bit_rle_rgbe")
01668 {
01669 isRgb = true;
01670 }
01671 else if (value == "32-bit_rle_xyze")
01672 {
01673 isRgb = false;
01674 }
01675 else
01676 {
01677 LASS_THROW_EX(BadFormat, "unknown value for FORMAT field of header of RADIANCE HDR file");
01678 }
01679 }
01680 else if (command == "EXPOSURE")
01681 {
01682 exposure *= util::stringCast<float>(value);
01683 }
01684 else if (command == "COLORCORR")
01685 {
01686 std::vector<std::string> values = stde::split(value);
01687 if (values.size() != sizeColorCorr)
01688 {
01689 LASS_THROW_EX(BadFormat, "syntax error in COLORCORR field of header of RADIANCE HDR file");
01690 }
01691 stde::transform_r(values, colorCorr, util::stringCast<float, std::string>);
01692 }
01693 else if (command == "PRIMARIES")
01694 {
01695 std::vector<std::string> values = stde::split(value);
01696 if (values.size() != sizePrimaries)
01697 {
01698 LASS_THROW_EX(BadFormat, "syntax error in PRIMARIES field of header of RADIANCE HDR file");
01699 }
01700 stde::transform_r(values, primaries, util::stringCast<float, std::string>);
01701 isDefaultPrimaries = false;
01702 }
01703 }
01704
01705
01706 std::string line;
01707 readLine(stream, line);
01708 if (!stream.good())
01709 {
01710 LASS_THROW_EX(BadFormat, "syntax error in resolution line of RADIANCE HDR file");
01711 }
01712 std::vector<std::string> splitted = stde::split(line);
01713 if (splitted.size() != 4)
01714 {
01715 LASS_THROW_EX(BadFormat, "resolution line of RADIANCE HDR file not of 4 elements");
01716 }
01717 std::string y = stde::toupper(splitted[0]);
01718 std::string x = stde::toupper(splitted[2]);
01719 if (!(y == "+Y" || y == "-Y") || !(x == "+X" || x == "-X"))
01720 {
01721 LASS_THROW_EX(BadFormat, "syntax error in resolution line of RADIANCE HDR file");
01722 }
01723 yIncreasing = y == "+Y";
01724 xIncreasing = x == "+X";
01725 height = util::stringCast<unsigned>(splitted[1]);
01726 width = util::stringCast<unsigned>(splitted[3]);
01727 }
01728
01729
01730
01731 void Image::HeaderRadianceHdr::writeTo(BinaryOStream& stream)
01732 {
01733 stream.write(magicRadiance_.data(), magicRadiance_.length());
01734 writeLine(stream, "SOFTWARE=LASS, http://lass.sourceforge.net");
01735 writeLine(stream, std::string("FORMAT=32-bit_rle_") + (isRgb ? "rgbe" : "xyze"));
01736 writeLine(stream, "EXPOSURE=" + util::stringCast<std::string>(exposure));
01737 writeLine(stream, std::string("COLORCORR=") +
01738 stde::join(std::string(" "), colorCorr, colorCorr + sizeColorCorr));
01739 writeLine(stream, std::string("PRIMARIES=") +
01740 stde::join(std::string(" "), primaries, primaries + sizePrimaries));
01741 writeLine(stream, "");
01742 std::ostringstream resolution;
01743 resolution << (yIncreasing ? "+Y" : "-Y") << " " << height;
01744 resolution << " " << (xIncreasing ? "+X" : "-X") << " " << width;
01745 writeLine(stream, resolution.str());
01746 }
01747
01748
01749
01750
01751
01752 Image::HeaderPfm::HeaderPfm():
01753 width(0),
01754 height(0),
01755 aspect(1),
01756 endianness(num::littleEndian),
01757 isGrey(false)
01758 {
01759 }
01760
01761
01762
01763 void Image::HeaderPfm::readFrom(BinaryIStream& stream)
01764 {
01765 const std::string magicPF = "PF";
01766 const std::string magicPf = "Pf";
01767 LASS_ASSERT(magicPF.length() == magicPf.length());
01768 const size_t magicSize = magicPF.length();
01769 std::vector<char> magic(magicSize);
01770 stream.read(&magic[0], magicSize);
01771 if (!stream.good() || !(stde::equal_r(magicPF, magic) || stde::equal_r(magicPf, magic)))
01772 {
01773 LASS_THROW_EX(BadFormat, "file is not a PFM file");
01774 }
01775
01776 isGrey = stde::equal_r(magicPf, magic);
01777
01778
01779
01780 const size_t numAttributes = 3;
01781 std::string attributes[numAttributes];
01782 size_t k = 0;
01783 std::string line;
01784 std::string attr;
01785 while (k < numAttributes)
01786 {
01787 Image::readLine(stream, line);
01788 if (!stream.good())
01789 {
01790 LASS_THROW_EX(BadFormat, "syntax error in header of PFM file");
01791 }
01792 const size_t startComments = line.find("#");
01793 std::istringstream buffer(line.substr(0, startComments));
01794 while (k < numAttributes && buffer.good())
01795 {
01796 buffer >> attr;
01797 if (buffer.good())
01798 {
01799 attributes[k++] = attr;
01800 }
01801 }
01802 }
01803
01804 try
01805 {
01806 width = util::stringCast<unsigned>(attributes[0]);
01807 height = util::stringCast<unsigned>(attributes[1]);
01808 aspect = util::stringCast<float>(attributes[2]);
01809 }
01810 catch (const util::BadStringCast&)
01811 {
01812 LASS_THROW_EX(BadFormat, "syntax error in header of PFM file");
01813 }
01814
01815 endianness = aspect < 0 ? num::littleEndian : num::bigEndian;
01816 aspect = num::abs(aspect);
01817 }
01818
01819
01820
01821 void Image::HeaderPfm::writeTo(BinaryOStream& stream)
01822 {
01823 std::ostringstream buffer;
01824 buffer << "PF\n" << width << " " << height << "\n" << (endianness == num::littleEndian ? -aspect : aspect);
01825 writeLine(stream, buffer.str());
01826 }
01827
01828
01829
01830
01831
01832 void Image::HeaderIgi::readFrom(BinaryIStream& stream)
01833 {
01834 EndiannessSetter(stream, num::littleEndian);
01835 stream >> magic >> version >> numSamples >> width >> height >> superSampling >> zipped
01836 >> dataSize >> rgb;
01837 stream.seekg(padding, std::ios_base::cur);
01838 }
01839
01840
01841
01842 void Image::HeaderIgi::writeTo(BinaryOStream& stream)
01843 {
01844 EndiannessSetter(stream, num::littleEndian);
01845 stream << magic << version << numSamples << width << height << superSampling << zipped
01846 << dataSize << rgb;
01847 stream.seekp(padding, std::ios_base::cur);
01848 }
01849
01850
01851
01852
01853
01854 }
01855
01856 }
01857
01858