library of assembled shared sources

http://lass.cocamware.com

image.cpp

Go to the documentation of this file.
00001 /** @file
00002  *  @author Bram de Greve (bramz@users.sourceforge.net)
00003  *  @author Tom De Muer (tomdemuer@users.sourceforge.net)
00004  *
00005  *  *** BEGIN LICENSE INFORMATION ***
00006  *  
00007  *  The contents of this file are subject to the Common Public Attribution License 
00008  *  Version 1.0 (the "License"); you may not use this file except in compliance with 
00009  *  the License. You may obtain a copy of the License at 
00010  *  http://lass.sourceforge.net/cpal-license. The License is based on the 
00011  *  Mozilla Public License Version 1.1 but Sections 14 and 15 have been added to cover 
00012  *  use of software over a computer network and provide for limited attribution for 
00013  *  the Original Developer. In addition, Exhibit A has been modified to be consistent 
00014  *  with Exhibit B.
00015  *  
00016  *  Software distributed under the License is distributed on an "AS IS" basis, WITHOUT 
00017  *  WARRANTY OF ANY KIND, either express or implied. See the License for the specific 
00018  *  language governing rights and limitations under the License.
00019  *  
00020  *  The Original Code is LASS - Library of Assembled Shared Sources.
00021  *  
00022  *  The Initial Developer of the Original Code is Bram de Greve and Tom De Muer.
00023  *  The Original Developer is the Initial Developer.
00024  *  
00025  *  All portions of the code written by the Initial Developer are:
00026  *  Copyright (C) 2004-2007 the Initial Developer.
00027  *  All Rights Reserved.
00028  *  
00029  *  Contributor(s):
00030  *
00031  *  Alternatively, the contents of this file may be used under the terms of the 
00032  *  GNU General Public License Version 2 or later (the GPL), in which case the 
00033  *  provisions of GPL are applicable instead of those above.  If you wish to allow use
00034  *  of your version of this file only under the terms of the GPL and not to allow 
00035  *  others to use your version of this file under the CPAL, indicate your decision by 
00036  *  deleting the provisions above and replace them with the notice and other 
00037  *  provisions required by the GPL License. If you do not delete the provisions above,
00038  *  a recipient may use your version of this file under either the CPAL or the GPL.
00039  *  
00040  *  *** END LICENSE INFORMATION ***
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; // 'lass' in little endian
00083 std::string Image::magicRadiance_ = "#?RADIANCE\n";
00084 num::Tint32 Image::magicIgi_ = 66613373;
00085 
00086 // --- public --------------------------------------------------------------------------------------
00087 
00088 /** Default constructor.  empty image.
00089  */
00090 Image::Image():
00091     colorSpace_(defaultColorSpace()),
00092     rows_(0),
00093     cols_(0),
00094     raster_()
00095 {
00096 }
00097 
00098 
00099 
00100 /** Construct image of given width and height.
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 /** Construct image from given file.
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 /** Copy constructor
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 /** Destructor
00141  */
00142 Image::~Image()
00143 {
00144 }
00145 
00146 
00147 
00148 // --- PUBLIC METHODS ----------------------------------------------------------
00149 
00150 /** Reset image to empty image.
00151  */
00152 void Image::reset()
00153 {
00154     Image temp;
00155     swap(temp);
00156 }
00157 
00158 
00159 
00160 /** Reset image to (black) image of given width.
00161  */
00162 void Image::reset(unsigned rows, unsigned cols)
00163 {
00164     Image temp(rows, cols);
00165     swap(temp);
00166 }
00167 
00168 
00169 
00170 /** Reset image to the one in the given file.
00171  */
00172 void Image::reset(const std::string& filename)
00173 {
00174     Image temp(filename);
00175     swap(temp);
00176 }
00177 
00178 
00179 
00180 /** Reset image to copy of given image.
00181  */
00182 void Image::reset(const Image& other)
00183 {
00184     Image temp(other);
00185     swap(temp);
00186 }
00187 
00188 
00189 
00190 /** Open image from file
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 /** Open image from binary stream
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 /** Save image to file
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 /** Save image to file
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 /** Copy other into this image.
00296  */
00297 Image& Image::operator=(const Image& other)
00298 {
00299     Image temp(other);
00300     swap(temp);
00301     return *this;
00302 }
00303 
00304 
00305 
00306 /** swap two images
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 /** Return const pixel at position row, col.
00319  *  @param row row of pixel, y coordinate.
00320  *  @param col column of pixel, x coordinate.
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 /** Return reference to pixel at position row, col.
00331  *  @param row row of pixel, y coordinate.
00332  *  @param col column of pixel, x coordinate.
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 /** Return const pixel at position row, col.  position is wrapped around.
00343  *  @param row row of pixel, y coordinate.
00344  *  @param col column of pixel, x coordinate.
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 /** Return reference to pixel at position row, col. position is wrapped around.
00356  *  @param row row of pixel, y coordinate.
00357  *  @param col column of pixel, x coordinate.
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 /** Return const data block.
00369  */
00370 const Image::TPixel* const Image::data() const
00371 {
00372     return &raster_[0];
00373 }
00374 
00375 
00376 
00377 /** Return data block.
00378  */
00379 Image::TPixel* const Image::data()
00380 {
00381     return &raster_[0];
00382 }
00383 
00384 
00385 
00386 /** Return colorSpace of image data
00387  */
00388 const Image::ColorSpace& Image::colorSpace() const
00389 {
00390     return colorSpace_;
00391 }
00392 
00393 
00394 
00395 /** Return colorSpace of image data
00396  *
00397  *  You can change the parameters of the current color space without transforming
00398  *  the pixels.  This is most useful if you know that the pixels are in a different
00399  *  color space that the current one is set to.  For example, if you know the pixels
00400  *  have a gamma correction 2.2 applied, but the this->colorSpace().gamma == 1.
00401  */
00402 Image::ColorSpace& Image::colorSpace()
00403 {
00404     return colorSpace_;
00405 }
00406 
00407 
00408 
00409 /** Transform the colors from the current color spacer to @a destColorSpace.
00410  *
00411  *  The transformation consists of three stages:
00412  *  @arg linearise pixels if gamma of current color space != 1
00413  *  @arg transform colors to linearised destination colour space
00414  *  @arg apply gamma correction if @a destColorSpace.gamma != 1
00415  *
00416  *  For the color transformation, two transformations are concatenated.
00417  *  Both of them will also perform a chromatic adaption transform if 
00418  *  the white point of either color space is not the equal energy one (E):
00419  *  @arg from source RGB to XYZ
00420  *  @arg from XYZ to dest RGB
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 /** Return height of image.
00483  */
00484 const unsigned Image::rows() const
00485 {
00486     return rows_;
00487 }
00488 
00489 
00490 
00491 /** Return width of image.
00492  */
00493 const unsigned Image::cols() const
00494 {
00495     return cols_;
00496 }
00497 
00498 
00499 
00500 /** Return true if image is empty (no data)
00501  */
00502 const bool Image::isEmpty() const
00503 {
00504     return raster_.empty();
00505 }
00506 
00507 
00508 
00509 /** this = this over other
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 /** this = this in other
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 /** this = this out other
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 /** this = this atop other
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 /** this = this through other
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 /** this = other over this
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 /** this = other in this
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 /** this = other out this
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 /** this = other atop this
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 /** this = other through this
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 /** this = this plus other = other plus this
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 /** clamp all negative pixel components to zero.
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 /** Apply a median filter on image.
00636  *  @param boxSize size of box filter, must be odd.
00637  *
00638  *  - Filters only pixels with alphachannel != 0.
00639  *  - We use the vector median filter for colour images, as described on page
00640  *    in GAUCH J. M. (1998). Noise Removal and contrast enhancement. In:
00641  *    Sangwine S. J. & Horne R. E. N. (Eds.) The Colour Image Processing
00642  *    Handbook. London, Chapman & Hall, 149-162.
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; // no filtering on pixels with alphachannel == 0
00666             }
00667 
00668             // fill filterbox
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             // get median in filterbox
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 /** apply gamma correction to image
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 /** apply exposure to image
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 /** apply exposure to image
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 // --- protected -----------------------------------------------------------------------------------
00769 
00770 
00771 
00772 
00773 // --- private -------------------------------------------------------------------------------------
00774 
00775 /** (re)allocate data chunck
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 /** Open LASS Raw file.
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 /** Open TARGA file.
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 /** Open Type 2 and 11 TARGA file
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             // decode rle
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         // decode scanline
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         // unpack scanline to buffer
01012         //
01013         for (std::ptrdiff_t x = firstX; x != lastX; /* increment in loop */ )
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                 // new rle
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                 // no rle or old rle
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         // decode rgbe information
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     // The current file format does not specify chromaticies, only distinguishes
01165     // between RGB and XYZ.  Use default RGB space in case of the former.
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 /** Save TARGA file (type 2, 32 bit).
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     // STEP 1: Make a header of the right type
01230     HeaderTarga header;
01231     header.idLength = 0;
01232     header.colorMapType = 0;
01233     header.imageType = 10; // rle true color
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; // 8 attribute bits
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         // encode in scanline buffer
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         // run-length encode buffer
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 /** Save RADIANCE HDR
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         // first, transform a line of rgb pixels to rgbe reprentation
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         // rle encode each channel
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 /** return sRGB as colorSpace
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 // --- HeaderLass -----------------------------------------------------------------------------------
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 // --- HeaderTarga ---------------------------------------------------------------------------------
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 // --- HeaderRadianceHdr ---------------------------------------------------------------------------
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     // first, read the real header
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     // second, interpret the resolution line
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 // --- HeaderPfm -----------------------------------------------------------------------------------
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     // we need to read a number of attributes that may be spread across one or more lines
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 // --- HeaderIgi -----------------------------------------------------------------------------------
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 // --- free ----------------------------------------------------------------------------------------
01851 
01852 
01853 
01854 }
01855 
01856 }
01857 
01858 // EOF

Generated on Mon Nov 10 14:20:04 2008 for Library of Assembled Shared Sources by doxygen 1.5.7.1
SourceForge.net Logo