57#define LASS_IO_IMAGE_ENFORCE_SAME_SIZE(a, b)\
58 *LASS_UTIL_IMPL_MAKE_ENFORCER(\
59 ::lass::util::impl::TruePredicate,\
60 ::lass::util::impl::DefaultRaiser,\
61 ((a).rows() == (b).rows() && (a).cols() == (b).cols()),\
63 "Images '" LASS_STRINGIFY(a) "' and '" LASS_STRINGIFY(b) "' have different size in '" LASS_HERE "'.")
65#if LASS_COMPILER_TYPE == LASS_COMPILER_TYPE_MSVC
66# pragma warning(disable: 4351)
78 Bytes4(): values_() {}
79 num::Tuint8 operator[](
size_t k)
const { LASS_ASSERT(k < 4);
return values_[k]; }
80 num::Tuint8& operator[](
size_t k) { LASS_ASSERT(k < 4);
return values_[k]; }
81 const num::Tuint8* get()
const {
return values_; }
82 num::Tuint8* get() {
return values_; }
83 bool operator==(
const Bytes4& other)
const {
return std::equal(values_, values_ + 4, other.values_); }
85 num::Tuint8 values_[4];
89Image::TFileFormats Image::fileFormats_ = Image::fillFileFormats();
90num::Tuint32 Image::magicLass_ = 0x7373616c;
91std::string Image::magicRadiance_ =
"#?RADIANCE\n";
92num::Tint32 Image::magicIgi_ = 66613373;
99 colorSpace_(defaultColorSpace()),
111 colorSpace_(defaultColorSpace()),
118 std::fill(raster_.begin(), raster_.end(), black);
126 colorSpace_(defaultColorSpace()),
136#if LASS_HAVE_WCHAR_SUPPORT
141 colorSpace_(defaultColorSpace()),
153#if LASS_HAVE_STD_FILESYSTEM
158 colorSpace_(defaultColorSpace()),
173 colorSpace_(other.colorSpace_),
176 raster_(other.raster_)
222#if LASS_HAVE_WCHAR_SUPPORT
236#if LASS_HAVE_STD_FILESYSTEM
267 LASS_THROW(
"could not open file to read.");
279 TFileOpener opener = findFormat(formatTag).open;
282 LASS_THROW_EX(BadFormat,
"cannot open images in file format '" << formatTag <<
"'.");
285 (temp.*opener)(stream);
291 else if (stream.eof())
293 LASS_THROW_EX(BadFormat,
"tried to read past end of file.");
297 LASS_THROW_EX(BadFormat,
"unknown failure in file.");
310 LASS_THROW(
"could not open file to write.");
321 TFileSaver saver = findFormat(formatTag).save;
324 LASS_THROW_EX(BadFormat,
"cannot save images in file format '" << formatTag <<
"'.");
329 (this->*saver)(stream);
331 catch (
const num::BadNumCast&)
333 LASS_THROW_EX(BadFormat,
"image size is too large for this format");
338 LASS_THROW_EX(BadFormat,
"tried to write past end of file.");
342 LASS_THROW_EX(BadFormat,
"unknown failure in file.");
348#if LASS_HAVE_WCHAR_SUPPORT
357 LASS_THROW(
"could not open file to read.");
368 BinaryOFile file(path);
371 LASS_THROW(
"could not open file to write.");
380#if LASS_HAVE_STD_FILESYSTEM
386 BinaryIFile file(path);
389 LASS_THROW(
"could not open file to read.");
400 BinaryOFile file(path);
403 LASS_THROW(
"could not open file to write.");
426 std::swap(colorSpace_, other.colorSpace_);
427 std::swap(rows_, other.rows_);
428 std::swap(cols_, other.cols_);
429 raster_.swap(other.raster_);
440 LASS_ASSERT(row < rows_ && col < cols_);
441 return raster_[flatIndex(row, col)];
452 LASS_ASSERT(row < rows_ && col < cols_);
453 return raster_[flatIndex(row, col)];
462const Image::TPixel&
Image::at(TSignedSize row, TSignedSize col)
const
464 const size_t i = num::mod(row, rows_);
465 const size_t j = num::mod(col, cols_);
466 return raster_[flatIndex(i, j)];
475Image::TPixel&
Image::at(TSignedSize row, TSignedSize col)
477 const size_t i = num::mod(row, rows_);
478 const size_t j = num::mod(col, cols_);
479 return raster_[flatIndex(i, j)];
544 static const TTransformation rgb2xyz(
const ColorSpace&
colorSpace)
546 typedef TTransformation::TVector TVector;
548 TValue primaries[16] =
550 C.red.x, C.green.x, C.blue.x, 0,
551 C.red.y, C.green.y, C.blue.y, 0,
552 1 - C.red.x - C.red.y, 1 - C.green.x - C.green.y, 1 - C.blue.x - C.blue.y, 0,
555 TTransformation M(primaries, primaries + 16);
556 const TVector w(C.white.x / C.white.y, 1, (1 - C.white.x - C.white.y) / C.white.y);
557 const TVector S = transform(w, M.inverse());
558 M = concatenate(TTransformation::scaler(S), M);
560 TValue bradford[16] =
562 0.8951f, 0.2664f, -0.1614f, 0.f,
563 -0.7502f, 1.7135f, 0.0367f, 0.f,
564 0.0389f, -0.0685f, 1.0296f, 0.f,
567 const TTransformation B(bradford, bradford + 16);
568 const TVector sw = transform(w, B);
569 TTransformation M_cat = concatenate(B, TTransformation::scaler(sw.reciprocal()));
570 M_cat = concatenate(M_cat, B.inverse());
572 return concatenate(M, M_cat);
576 const TTransformation A = Impl::rgb2xyz(colorSpace_);
577 const TTransformation B = Impl::rgb2xyz(newColorSpace);
578 const TTransformation C = concatenate(A, B.inverse());
580 if (colorSpace_.gamma != 1)
584 for (TRaster::iterator i = raster_.begin(); i != raster_.end(); ++i)
586 *i = transform(*i, C);
588 if (newColorSpace.gamma != 1)
592 colorSpace_ = newColorSpace;
593 colorSpace_.isFromFile =
false;
620 return raster_.empty();
629 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*
this, other);
630 std::transform(raster_.begin(), raster_.end(), other.raster_.begin(), raster_.begin(),
prim::over);
639 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*
this, other);
640 std::transform(raster_.begin(), raster_.end(), other.raster_.begin(), raster_.begin(),
prim::in);
649 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*
this, other);
650 std::transform(raster_.begin(), raster_.end(), other.raster_.begin(), raster_.begin(),
prim::out);
659 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*
this, other);
660 std::transform(raster_.begin(), raster_.end(), other.raster_.begin(), raster_.begin(),
prim::atop);
669 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*
this, other);
670 std::transform(raster_.begin(), raster_.end(), other.raster_.begin(), raster_.begin(),
prim::through);
679 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*
this, other);
680 std::transform(other.raster_.begin(), other.raster_.end(), raster_.begin(), raster_.begin(),
prim::over);
689 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*
this, other);
690 std::transform(other.raster_.begin(), other.raster_.end(), raster_.begin(), raster_.begin(),
prim::in);
699 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*
this, other);
700 std::transform(other.raster_.begin(), other.raster_.end(), raster_.begin(), raster_.begin(),
prim::out);
709 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*
this, other);
710 std::transform(other.raster_.begin(), other.raster_.end(), raster_.begin(), raster_.begin(),
prim::atop);
719 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*
this, other);
720 std::transform(other.raster_.begin(), other.raster_.end(), raster_.begin(), raster_.begin(),
prim::through);
729 LASS_IO_IMAGE_ENFORCE_SAME_SIZE(*
this, other);
730 std::transform(raster_.begin(), raster_.end(), other.raster_.begin(), raster_.begin(),
prim::plus);
739 const TRaster::iterator last = raster_.end();
740 for (TRaster::iterator i = raster_.begin(); i != last; ++i)
742 i->r = std::max(i->r, 0.f);
743 i->g = std::max(i->g, 0.f);
744 i->b = std::max(i->b, 0.f);
745 i->a = std::max(i->a, 0.f);
766 if ((boxSize & 0x1) == 0)
768 LASS_THROW(
"boxSize '" << boxSize <<
"' isn't odd as requested.");
771 const size_t boxArea = boxSize * boxSize;
772 const size_t boxRadius = (boxSize - 1) / 2;
773 const size_t invalidCandidate = boxArea;
775 TRaster box(boxArea);
776 std::vector<float> distances(boxArea);
778 for (
size_t y0 = 0; y0 < rows_; ++y0)
780 const size_t yFirst = std::max(y0, boxRadius) - boxRadius;
781 const size_t yLast = std::min(y0 + boxRadius + 1, rows_);
782 LASS_ASSERT(yLast - yFirst <= boxSize);
783 for (
size_t x0 = 0; x0 < cols_; ++x0)
785 TPixel& center = (*this)(y0, x0);
786 if (center.a == TNumTraits::zero)
791 const size_t xFirst = std::max(x0, boxRadius) - boxRadius;
792 const size_t xLast = std::min(x0 + boxRadius + 1, cols_);
793 LASS_ASSERT(xLast - xFirst <= boxSize);
797 size_t pixelsInBox = 0;
798 size_t candidate = invalidCandidate;
799 for (
size_t y = yFirst; y < yLast; ++y)
801 for (
size_t x = xFirst; x < xLast; ++x)
803 const TPixel& pixel = (*this)(y0, x0);
804 if (pixel.a != TNumTraits::zero)
806 box[pixelsInBox] = pixel;
807 if (x == x0 && y == y0)
809 candidate = pixelsInBox;
818 LASS_ASSERT(pixelsInBox > 0 && pixelsInBox <= boxArea);
819 LASS_ASSERT(candidate != invalidCandidate && candidate < pixelsInBox);
820 for (
size_t i = 0; i < pixelsInBox; ++i)
823 for (
size_t j = 0; j < pixelsInBox; ++j)
825 distances[i] += prim::distance(box[i], box[j]);
828 for (
size_t i = 0; i < pixelsInBox; ++i)
830 if (distances[i] < distances[candidate])
836 LASS_ASSERT(candidate < pixelsInBox);
837 center = box[candidate];
848 const size_t size = raster_.size();
849 for (
size_t i = 0; i < size; ++i)
851 raster_[i] = raster_[i].gammaCorrected(gammaExponent);
853 colorSpace_.gamma *= gammaExponent;
862 const size_t size = raster_.size();
863 for (
size_t i = 0; i < size; ++i)
865 raster_[i] = raster_[i].exposed(exposureTime);
875 const size_t size = raster_.size();
876 for (
size_t i = 0; i < size; ++i)
878 raster_[i] = raster_[i].invExposed(exposureTime);
893size_t Image::resize(
size_t rows,
size_t cols)
896 raster_.resize(size);
906BinaryIStream& Image::openLass(BinaryIStream& stream)
909 header.readFrom(stream);
910 if (!stream || header.lass != magicLass_ || header.version > 3)
912 LASS_THROW_EX(BadFormat,
"not a LASS RAW version 1 - 3 file.");
915 EndiannessSetter(stream, num::littleEndian);
917 if (header.version >= 2)
919 for (
size_t i = 0; i < numChromaticities; ++i)
923 colorSpace_[i] = TChromaticity(x, y);
925 colorSpace_.isFromFile =
true;
929 colorSpace_ = defaultColorSpace();
932 if (header.version >= 3)
936 colorSpace_.gamma = g;
939 resize(header.rows, header.cols);
940 num::Tfloat32 r, g, b, a;
941 for (TRaster::iterator i = raster_.begin(); i != raster_.end(); ++i)
943 stream >> r >> g >> b >> a;
960 header.readFrom(stream);
961 colorSpace_ = defaultColorSpace();
962 colorSpace_.gamma = 2.2f;
963 resize(header.imageHeight, header.imageWidth);
965 switch (header.imageType)
969 openTargaTrueColor(stream, header);
972 LASS_THROW_EX(BadFormat,
"unsupported image type '" <<
static_cast<unsigned>(header.imageType) <<
"'");
985 const TValue scale =
num::inv(255.f);
987 std::size_t numBytes = 0;
988 switch (header.imagePixelSize)
997 LASS_THROW_EX(BadFormat,
"image pixel size '" << header.imagePixelSize <<
"' not supported.");
1000 LASS_ASSERT(cols_ == header.imageWidth);
1001 const int xBegin = header.flipHorizontalFlag() ?
static_cast<int>(cols_) - 1 : 0;
1002 const int xEnd = header.flipHorizontalFlag() ? -1 :
static_cast<int>(cols_);
1003 const int xDelta = header.flipHorizontalFlag() ? -1 : 1;
1005 LASS_ASSERT(rows_ == header.imageHeight);
1006 const int yBegin = header.flipVerticalFlag() ? 0 :
static_cast<int>(rows_) - 1;
1007 const int yEnd = header.flipVerticalFlag() ?
static_cast<int>(rows_) : -1;
1008 const int yDelta = header.flipVerticalFlag() ? 1 : -1;
1010 stream.seekg(header.idLength + header.colorMapLength * header.colorMapEntrySize, std::ios_base::cur);
1011 std::vector<impl::Bytes4> buffer(cols_);
1012 for (
int y = yBegin; y != yEnd; y += yDelta)
1014 if (header.imageType == 10)
1023 const num::Tuint8 packetSize =
static_cast<num::Tuint8
>((code & 0x7f) + 1);
1027 stream.read(bytes.get(), numBytes);
1028 for (num::Tuint8 i = 0; i < packetSize; ++i)
1030 LASS_ASSERT(x < cols_);
1031 buffer[x++] = bytes;
1036 for (num::Tuint8 i = 0; i < packetSize; ++i)
1038 stream.read(bytes.get(), numBytes);
1039 LASS_ASSERT(x < cols_);
1040 buffer[x++] = bytes;
1047 LASS_ASSERT(header.imageType == 2);
1048 for (
unsigned x = 0; x < cols_; ++x)
1050 stream.read(buffer[x].get(), numBytes);
1056 LASS_ASSERT(y >= 0 &&
static_cast<size_t>(y) < rows_);
1057 TPixel* pixel = &raster_[
static_cast<size_t>(y) * cols_];
1058 for (
int x = xBegin; x != xEnd; x += xDelta)
1060 LASS_ASSERT(x >= 0 &&
static_cast<size_t>(x) < cols_);
1061 const impl::Bytes4& bytes = buffer[
static_cast<size_t>(x)];
1062 pixel->r =
static_cast<TValue
>(bytes[2]) * scale;
1063 pixel->g =
static_cast<TValue
>(bytes[1]) * scale;
1064 pixel->b =
static_cast<TValue
>(bytes[0]) * scale;
1065 pixel->a = numBytes == 3 ? TNumTraits::one :
static_cast<TValue
>(bytes[3]) * scale;
1077 HeaderRadianceHdr header;
1078 header.readFrom(stream);
1081 LASS_THROW_EX(BadFormat,
"syntax error in RADIANCE HDR header.");
1086 for (
size_t i = 0; i < numChromaticities; ++i)
1088 colorSpace_[i] = TChromaticity(header.primaries[2 * i], header.primaries[2 * i + 1]);
1093 colorSpace_.red = TChromaticity(1, 0);
1094 colorSpace_.green = TChromaticity(0, 1);
1095 colorSpace_.blue = TChromaticity(0, 0);
1096 colorSpace_.white = TChromaticity(1.f / 3, 1.f / 3);
1098 colorSpace_.gamma = 1;
1099 colorSpace_.isFromFile =
true;
1101 float exponents[256];
1103 for (
int i = 1; i < 256; ++i)
1105 exponents[i] = ::ldexpf(1.f, i - 128 - 8);
1107 float inverseCorrections[3];
1108 for (
size_t i = 0; i < 3; ++i)
1110 inverseCorrections[i] =
num::inv(header.exposure * header.colorCorr[i]);
1113 resize(header.height, header.width);
1115 const std::ptrdiff_t firstY = !header.yIncreasing ? 0 : (
static_cast<std::ptrdiff_t
>(header.height) - 1);
1116 const std::ptrdiff_t lastY = !header.yIncreasing ?
static_cast<std::ptrdiff_t
>(header.height) : -1;
1117 const std::ptrdiff_t deltaY = !header.yIncreasing ? 1 : -1;
1118 const std::ptrdiff_t firstX = header.xIncreasing ? 0 : (
static_cast<std::ptrdiff_t
>(header.width) - 1);
1119 const std::ptrdiff_t lastX = header.xIncreasing ?
static_cast<std::ptrdiff_t
>(header.width) : -1;
1120 const std::ptrdiff_t deltaX = header.xIncreasing ? 1 : -1;
1122 std::vector<impl::Bytes4> buffer(header.width);
1123 size_t rleCount = 0;
1124 size_t rleCountByte = 0;
1126 for (std::ptrdiff_t y = firstY; y != lastY; y += deltaY)
1130 for (std::ptrdiff_t x = firstX; x != lastX; )
1132 impl::Bytes4 rgbe, previous;
1133 stream.read(rgbe.get(), 4);
1134 if (rgbe[0] == 2 && rgbe[1] == 2 && (rgbe[2] & 0x80) == 0)
1138 const std::ptrdiff_t lineLength = rgbe[2] * 256 + rgbe[3];
1139 LASS_ASSERT(lineLength >= 0 && lineLength < 32768);
1140 const std::ptrdiff_t lastX2 = x + lineLength * deltaX;
1141 LASS_ASSERT((lastX - lastX2) * deltaX >= 0);
1142 for (
size_t k = 0; k < 4; ++k)
1144 std::ptrdiff_t x2 = x;
1145 while (x2 != lastX2)
1147 num::Tuint8 spanField = 0, value = 0;
1148 stream >> spanField;
1149 const bool isHomogenousSpan = spanField > 128;
1150 const size_t spanSize = isHomogenousSpan ? spanField & 0x7f : spanField;
1151 if (isHomogenousSpan)
1155 for (
size_t i = 0; i < spanSize; ++i)
1157 if (!isHomogenousSpan)
1161 LASS_ASSERT(x2 >= 0 && x2 != lastX2);
1162 buffer[
static_cast<size_t>(x2)][k] = value;
1173 if (rgbe[0] == 1 && rgbe[1] == 1 && rgbe[2] == 1)
1175 LASS_ASSERT(rleCountByte <
sizeof(rleCount));
1176 rleCount |=
static_cast<size_t>(rgbe[3]) << (8 * rleCountByte);
1183 for (
size_t k = rleCount; k > 0; --k)
1185 buffer[
static_cast<size_t>(x)] = previous;
1191 buffer[
static_cast<size_t>(x)] = rgbe;
1200 TPixel* scanline = &raster_[
static_cast<size_t>(y) * header.width];
1201 for (
size_t x = 0; x != header.width; ++x)
1203 const impl::Bytes4 rgbe = buffer[x];
1204 TPixel& pixel = scanline[x];
1205 const float exponent = exponents[rgbe[3]];
1206 for (
size_t k = 0; k < 3; ++k)
1208 pixel[k] =
static_cast<float>(rgbe[k]) * inverseCorrections[k] * exponent;
1220 header.readFrom(stream);
1223 LASS_THROW_EX(BadFormat,
"not a PFM file.");
1226 colorSpace_ = defaultColorSpace();
1227 colorSpace_.gamma = 1;
1229 resize(header.height, header.width);
1230 EndiannessSetter(stream, header.endianness);
1232 for (
size_t y = rows_; y > 0; --y)
1234 TPixel* scanline = &raster_[(y - 1) * cols_];
1237 for (
size_t x = 0; x < cols_; ++x)
1241 scanline[x] = TPixel(g, g, g, 1);
1246 for (
size_t x = 0; x < cols_; ++x)
1248 num::Tfloat32 r, g, b;
1249 stream >> r >> g >> b;
1250 scanline[x] = TPixel(r, g, b, 1);
1263 header.readFrom(stream);
1265 if (!stream || header.magic != magicIgi_ || header.version < 1 || header.version > 2)
1267 LASS_THROW_EX(BadFormat,
"not an IGI version <= 2 file.");
1271 LASS_THROW_EX(BadFormat,
"cannot read zipped IGI files.");
1273 if (header.dataSize < 0 ||
static_cast<num::Tuint32
>(header.dataSize) != 12 * header.width * header.height)
1275 LASS_THROW_EX(BadFormat,
"error in dataSize field.");
1280 colorSpace_ = header.rgb ? defaultColorSpace() : xyzColorSpace();
1281 colorSpace_.gamma = 1;
1282 colorSpace_.isFromFile =
true;
1284 resize(header.height, header.width);
1285 num::Tfloat32 r, g, b;
1286 EndiannessSetter(stream, num::littleEndian);
1287 for (TRaster::iterator i = raster_.begin(); i != raster_.end(); ++i)
1289 stream >> r >> g >> b;
1303 header.lass = magicLass_;
1305 header.rows = num::numCast<num::Tuint32>(rows_);
1306 header.cols = num::numCast<num::Tuint32>(cols_);
1307 header.writeTo(stream);
1309 EndiannessSetter(stream, num::littleEndian);
1311 for (
size_t i = 0; i < numChromaticities; ++i)
1313 const num::Tfloat32 x = colorSpace_[i].x;
1314 const num::Tfloat32 y = colorSpace_[i].y;
1318 const num::Tfloat32 c = colorSpace_.gamma;
1321 for (TRaster::const_iterator i = raster_.begin(); i != raster_.end(); ++i)
1323 const num::Tfloat32 r = i->r;
1324 const num::Tfloat32 g = i->g;
1325 const num::Tfloat32 b = i->b;
1326 const num::Tfloat32 a = i->a;
1327 stream << r << g << b << a;
1338 const TValue scale(255);
1339 const TValue zero(0);
1340 const TValue one(1);
1344 header.idLength = 0;
1345 header.colorMapType = 0;
1346 header.imageType = 10;
1347 header.colorMapOrigin = 0;
1348 header.colorMapLength = 0;
1349 header.colorMapEntrySize = 0;
1350 header.imageXorigin = 0;
1351 header.imageYorigin = 0;
1352 header.imageWidth = num::numCast<num::Tuint16>(cols_);;
1353 header.imageHeight = num::numCast<num::Tuint16>(rows_);
1354 header.imagePixelSize = 32;
1355 header.imageDescriptor = 0x08;
1357 header.writeTo(stream);
1359 std::vector<impl::Bytes4> buffer(header.imageWidth);
1360 std::vector<impl::Bytes4> rleBuffer(128);
1361 for (
size_t y = rows_; y > 0; --y)
1367 const TPixel* scanline = &raster_[(y - 1) * cols_];
1368 for (x = 0; x < cols_; ++x)
1370 const TPixel& pixel = scanline[x];
1371 impl::Bytes4& bytes = buffer[x];
1372 bytes[0] =
static_cast<num::Tuint8
>(
num::clamp(pixel.b, zero, one) * scale);
1373 bytes[1] =
static_cast<num::Tuint8
>(
num::clamp(pixel.g, zero, one) * scale);
1374 bytes[2] =
static_cast<num::Tuint8
>(
num::clamp(pixel.r, zero, one) * scale);
1375 bytes[3] =
static_cast<num::Tuint8
>(
num::clamp(pixel.a, zero, one) * scale);
1380 size_t LASS_UNUSED(totalLength) = 0;
1381 num::Tuint8 numDiff = 0;
1385 const impl::Bytes4& bytes = buffer[x];
1387 num::Tuint8 numSame = 0;
1388 while (x2 < cols_ && numSame < 128 && buffer[x2] == bytes)
1395 rleBuffer[numDiff] = bytes;
1399 if (numDiff == 128 || ((numSame > 1 || x == cols_) && numDiff > 0))
1401 stream << static_cast<num::Tuint8>(numDiff - 1);
1402 for (num::Tuint8 i = 0; i < numDiff; ++i)
1404 stream.write(&rleBuffer[i], 4);
1406 totalLength += numDiff;
1407 LASS_ASSERT(totalLength == x);
1412 stream << static_cast<num::Tuint8>((numSame - 1) | 0x80);
1413 stream.write(bytes.get(), 4);
1415 totalLength += numSame;
1416 LASS_ASSERT(totalLength == x);
1420 LASS_ASSERT(totalLength == cols_);
1433 LASS_THROW_EX(BadFormat,
"cannot save this as RADIANCE HDR, because image is too wide");
1435 HeaderRadianceHdr header;
1436 header.height = rows_;
1437 header.width = cols_;
1438 header.yIncreasing =
false;
1439 header.xIncreasing =
true;
1440 header.isRgb = colorSpace_ != xyzColorSpace();
1441 for (
size_t i = 0; i < numChromaticities; ++i)
1443 header.primaries[2 * i] = colorSpace_[i].x;
1444 header.primaries[2 * i + 1] = colorSpace_[i].y;
1446 header.writeTo(stream);
1448 std::vector<impl::Bytes4> buffer(cols_);
1449 std::vector<num::Tuint8> rleBuffer(128);
1451 for (
size_t y = 0; y < rows_; ++y)
1455 const TPixel* line = &raster_[y * cols_];
1456 for (
size_t x = 0; x < cols_; ++x)
1458 const TPixel& pixel = line[x];
1459 impl::Bytes4& rgbe = buffer[x];
1460 const float maximum = std::max(std::max(pixel.r, pixel.g), pixel.b);
1461 if (maximum < 1e-32)
1463 rgbe[0] = rgbe[1] = rgbe[2] = rgbe[3] = 0;
1468 const float mantissa = ::frexpf(maximum, &exponent);
1469 for (
size_t k = 0; k < 3; ++k)
1471 const float normalized = pixel[k] * (256.f * mantissa / maximum);
1472 rgbe[k] =
static_cast<num::Tuint8
>(
num::clamp(normalized, 0.f, 255.f));
1474 rgbe[3] =
static_cast<num::Tuint8
>(
num::clamp(exponent + 128, 0, 255));
1480 num::Tuint8 bytes[4];
1483 bytes[2] =
static_cast<num::Tuint8
>((cols_ & 0x7f00) >> 8);
1484 bytes[3] =
static_cast<num::Tuint8
>(cols_ & 0xff);
1485 stream.write(bytes, 4);
1487 for (
size_t k = 0; k < 4; ++k)
1489 num::Tuint8 numDiff = 0;
1493 num::Tuint8 value = buffer[x][k];
1495 num::Tuint8 numSame = 0;
1496 while (x2 < cols_ && numSame < 127 && buffer[x2][k] == value)
1506 stream.write(&rleBuffer[0], numDiff);
1510 const num::Tuint8 spanField =
static_cast<num::Tuint8
>(numSame | 0x80);
1511 stream << spanField << value;
1519 stream.write(&rleBuffer[0], numDiff);
1522 rleBuffer[numDiff] = value;
1530 stream.write(&rleBuffer[0], numDiff);
1542 header.height = rows_;
1543 header.width = cols_;
1544 header.writeTo(stream);
1546 EndiannessSetter(stream, header.endianness);
1548 for (
size_t y = rows_; y > 0; --y)
1550 const TPixel* scanline = &raster_[(y - 1) * cols_];
1551 for (
size_t x = 0; x < cols_; ++x)
1553 const TPixel& p = scanline[x];
1554 const num::Tfloat32 r = p.r;
1555 const num::Tfloat32 g = p.g;
1556 const num::Tfloat32 b = p.b;
1557 stream << r << g << b;
1569 header.magic = magicIgi_;
1571 header.numSamples =
static_cast<num::Tfloat64
>(rows_ * cols_);
1572 header.width = num::numCast<num::Tuint32>(cols_);
1573 header.height = num::numCast<num::Tuint32>(rows_);
1574 header.superSampling = 1;
1576 header.dataSize = num::numCast<num::Tint32>(12 * rows_ * cols_);
1577 header.rgb = colorSpace_ == xyzColorSpace() ? 0 : 1;
1578 header.writeTo(stream);
1580 EndiannessSetter(stream, num::littleEndian);
1582 for (TRaster::const_iterator i = raster_.begin(); i != raster_.end(); ++i)
1584 const num::Tfloat32 r = i->r;
1585 const num::Tfloat32 g = i->g;
1586 const num::Tfloat32 b = i->b;
1587 stream << r << g << b;
1595Image::FileFormat Image::findFormat(
const std::string& formatTag)
1598 TFileFormats::const_iterator i = fileFormats_.find(key);
1599 if (i == fileFormats_.end())
1601 LASS_THROW_EX(BadFormat,
"unknown fileformat '" << key <<
"'.");
1614 stream >> character;
1619 if (character ==
'\n')
1624 result += character;
1632 stream.
write(iString.data(), iString.size());
1639Image::TFileFormats Image::fillFileFormats()
1641 TFileFormats formats;
1642 formats[
"lass"] = FileFormat(&Image::openLass, &Image::saveLass);
1643 formats[
"targa"] = FileFormat(&Image::openTarga, &Image::saveTarga);
1644 formats[
"tga"] = formats[
"targa"];
1645 formats[
"hdr"] = FileFormat(&Image::openRadianceHdr, &Image::saveRadianceHdr);
1646 formats[
"pic"] = formats[
"hdr"];
1647 formats[
"rgbe"] = formats[
"hdr"];
1648 formats[
"pfm"] = FileFormat(&Image::openPfm, &Image::savePfm);
1649 formats[
"igi"] = FileFormat(&Image::openIgi, &Image::saveIgi);
1657const Image::ColorSpace Image::defaultColorSpace()
1660 result.red = TChromaticity(0.6400f, 0.3300f);
1661 result.green = TChromaticity(0.3000f, 0.6000f);
1662 result.blue = TChromaticity(0.1500f, 0.0600f);
1663 result.white = TChromaticity(0.3127f, 0.3290f);
1665 result.isFromFile =
false;
1671const Image::ColorSpace Image::xyzColorSpace()
1674 result.red = TChromaticity(1.f, 0.f);
1675 result.green = TChromaticity(0.f, 1.f);
1676 result.blue = TChromaticity(0.f, 0.f);
1677 result.white = TChromaticity(1.f / 3, 1.f / 3);
1679 result.isFromFile =
false;
1689 EndiannessSetter(stream, num::littleEndian);
1690 stream >> lass >> version >> rows >> cols;
1695void Image::HeaderLass::writeTo(BinaryOStream& stream)
1697 EndiannessSetter(stream, num::littleEndian);
1698 stream << lass << version <<
rows <<
cols;
1705void Image::HeaderTarga::readFrom(BinaryIStream& stream)
1707 EndiannessSetter(stream, num::littleEndian);
1708 stream >> idLength >> colorMapType >> imageType >> colorMapOrigin >> colorMapLength >>
1709 colorMapEntrySize >> imageXorigin >> imageYorigin >> imageWidth >> imageHeight >>
1710 imagePixelSize >> imageDescriptor;
1715void Image::HeaderTarga::writeTo(BinaryOStream& stream)
1717 EndiannessSetter(stream, num::littleEndian);
1718 stream << idLength << colorMapType << imageType << colorMapOrigin << colorMapLength <<
1719 colorMapEntrySize << imageXorigin << imageYorigin << imageWidth << imageHeight <<
1720 imagePixelSize << imageDescriptor;
1727Image::HeaderRadianceHdr::HeaderRadianceHdr():
1734 isDefaultPrimaries(true)
1736 std::fill_n(colorCorr,
static_cast<int>(sizeColorCorr), 1.f);
1737 const float defaultPrimaries[sizePrimaries] =
1738 { 0.640f, 0.330f, 0.290f, 0.600f, 0.150f, 0.060f, 0.333f, 0.333f };
1739 stde::copy_n(defaultPrimaries,
static_cast<int>(sizePrimaries), primaries);
1742void Image::HeaderRadianceHdr::readFrom(
BinaryIStream& stream)
1744 std::size_t magicSize = magicRadiance_.size();
1745 std::vector<char> magic(magicSize);
1746 stream.read(&magic[0], magicSize);
1747 if (!stream.good() || !
stde::equal_r(magicRadiance_, magic))
1749 LASS_THROW_EX(BadFormat,
"file is not of RADIANCE file format");
1757 readLine(stream, line);
1760 LASS_THROW_EX(BadFormat,
"syntax error in header of RADIANCE HDR file");
1770 std::vector<std::string> splitted =
stde::split(line, std::string(
"="), 1);
1771 if (splitted.size() != 2)
1773 LASS_THROW_EX(BadFormat,
"syntax error in header of RADIANCE HDR file");
1777 if (command ==
"FORMAT")
1779 if (value ==
"32-bit_rle_rgbe")
1783 else if (value ==
"32-bit_rle_xyze")
1789 LASS_THROW_EX(BadFormat,
"unknown value for FORMAT field of header of RADIANCE HDR file");
1792 else if (command ==
"EXPOSURE")
1794 exposure *= util::stringCast<float>(value);
1796 else if (command ==
"COLORCORR")
1798 std::vector<std::string> values =
stde::split(value);
1799 if (values.size() != sizeColorCorr)
1801 LASS_THROW_EX(BadFormat,
"syntax error in COLORCORR field of header of RADIANCE HDR file");
1805 else if (command ==
"PRIMARIES")
1807 std::vector<std::string> values =
stde::split(value);
1808 if (values.size() != sizePrimaries)
1810 LASS_THROW_EX(BadFormat,
"syntax error in PRIMARIES field of header of RADIANCE HDR file");
1813 isDefaultPrimaries =
false;
1819 readLine(stream, line);
1822 LASS_THROW_EX(BadFormat,
"syntax error in resolution line of RADIANCE HDR file");
1824 std::vector<std::string> splitted =
stde::split(line);
1825 if (splitted.size() != 4)
1827 LASS_THROW_EX(BadFormat,
"resolution line of RADIANCE HDR file not of 4 elements");
1831 if (!(y ==
"+Y" || y ==
"-Y") || !(x ==
"+X" || x ==
"-X"))
1833 LASS_THROW_EX(BadFormat,
"syntax error in resolution line of RADIANCE HDR file");
1835 yIncreasing = y ==
"+Y";
1836 xIncreasing = x ==
"+X";
1837 height = util::stringCast<size_t>(splitted[1]);
1838 width = util::stringCast<size_t>(splitted[3]);
1843void Image::HeaderRadianceHdr::writeTo(BinaryOStream& stream)
1845 stream.write(magicRadiance_.data(), magicRadiance_.length());
1846 writeLine(stream,
"SOFTWARE=LASS, http://lass.sourceforge.net");
1847 writeLine(stream, std::string(
"FORMAT=32-bit_rle_") + (isRgb ?
"rgbe" :
"xyze"));
1848 writeLine(stream,
"EXPOSURE=" + util::stringCast<std::string>(exposure));
1849 writeLine(stream, std::string(
"COLORCORR=") +
1850 stde::join(std::string(
" "), colorCorr, colorCorr + sizeColorCorr));
1851 writeLine(stream, std::string(
"PRIMARIES=") +
1852 stde::join(std::string(
" "), primaries, primaries + sizePrimaries));
1853 writeLine(stream,
"");
1854 std::ostringstream resolution;
1855 resolution << (yIncreasing ?
"+Y" :
"-Y") <<
" " << height;
1856 resolution <<
" " << (xIncreasing ?
"+X" :
"-X") <<
" " << width;
1857 writeLine(stream, resolution.str());
1864Image::HeaderPfm::HeaderPfm():
1868 endianness(num::littleEndian),
1877 const std::string magicPF =
"PF";
1878 const std::string magicPf =
"Pf";
1879 LASS_ASSERT(magicPF.length() == magicPf.length());
1880 const size_t magicSize = magicPF.length();
1881 std::vector<char> magic(magicSize);
1882 stream.read(&magic[0], magicSize);
1885 LASS_THROW_EX(BadFormat,
"file is not a PFM file");
1892 const size_t numAttributes = 3;
1893 std::string attributes[numAttributes];
1897 while (k < numAttributes)
1899 Image::readLine(stream, line);
1902 LASS_THROW_EX(BadFormat,
"syntax error in header of PFM file");
1904 const size_t startComments = line.find(
"#");
1905 std::istringstream buffer(line.substr(0, startComments));
1906 while (k < numAttributes && buffer.good())
1910 attributes[k++] = attr;
1917 width = util::stringCast<size_t>(attributes[0]);
1918 height = util::stringCast<size_t>(attributes[1]);
1919 aspect = util::stringCast<float>(attributes[2]);
1921 catch (
const util::BadStringCast&)
1923 LASS_THROW_EX(BadFormat,
"syntax error in header of PFM file");
1926 endianness = aspect < 0 ? num::littleEndian : num::bigEndian;
1932void Image::HeaderPfm::writeTo(BinaryOStream& stream)
1934 std::ostringstream buffer;
1935 buffer <<
"PF\n" << width <<
" " << height <<
"\n" << (endianness == num::littleEndian ? -aspect : aspect);
1936 writeLine(stream, buffer.str());
1943void Image::HeaderIgi::readFrom(BinaryIStream& stream)
1945 EndiannessSetter(stream, num::littleEndian);
1946 stream >> magic >> version >> numSamples >> width >> height >> superSampling >> zipped
1948 stream.seekg(padding, std::ios_base::cur);
1953void Image::HeaderIgi::writeTo(BinaryOStream& stream)
1955 EndiannessSetter(stream, num::littleEndian);
1956 stream << magic << version << numSamples << width << height << superSampling << zipped
1958 stream.seekp(padding, std::ios_base::cur);
Input Stream for binary files.
base class of binary input streams.
base class of binary output streams.
size_t write(const void *buffer, size_t byteLength)
write a buffer of bytes to the stream
void rout(const Image &other)
this = other out this
void filterInverseExposure(TParam exposureTime)
apply exposure to image
void filterMedian(size_t boxSize)
Apply a median filter on image.
const TPixel * data() const
Return const data block.
void transformColors(const ColorSpace &newColorSpace)
Transform the colors from the current color spacer to destColorSpace.
void atop(const Image &other)
this = this atop other
void swap(Image &other)
swap two images
size_t cols() const
Return width of image.
void clampNegatives()
clamp all negative pixel components to zero.
void ratop(const Image &other)
this = other atop this
void out(const Image &other)
this = this out other
void filterGamma(TParam gammaExponent)
apply gamma correction to image
void through(const Image &other)
this = this through other
size_t rows() const
Return height of image.
const TPixel & operator()(size_t row, size_t col) const
Return const pixel at position row, col.
bool isEmpty() const
Return true if image is empty (no data)
Image()
Default constructor.
void open(const std::string &path)
Open image from file.
void rthrough(const Image &other)
this = other through this
void plus(const Image &other)
this = this plus other = other plus this
void rover(const Image &other)
this = other over this
void save(const std::string &path)
Save image to file.
void filterExposure(TParam exposureTime)
apply exposure to image
void in(const Image &other)
this = this in other
void reset()
Reset image to empty image.
void over(const Image &other)
this = this over other
Image & operator=(const Image &other)
Copy other into this image.
void rin(const Image &other)
this = other in this
const TPixel & at(TSignedSize row, TSignedSize col) const
Return const pixel at position row, col.
const ColorSpace & colorSpace() const
Return colorSpace of image data.
T inv(const T &x)
return x ^ -1
const T & clamp(const T &x, const T &min, const T &max)
if x < min return min, else if x > max return max, else return x.
T abs(const T &x)
if x < 0 return -x, else return x.
std::string fileExtension(const std::string &fileName)
return the part of the file name behind the last dot.
OutputIterator copy_n(InputIterator first, Size count, OutputIterator result)
copy count elements from sequence starting at first to sequence starting at result
bool begins_with(const std::basic_string< Char, Traits, Alloc > &input, const std::basic_string< Char, Traits, Alloc > &prefix)
returns true if input begins with the input prefix
std::basic_string< Char, Traits, Alloc > tolower(const std::basic_string< Char, Traits, Alloc > &input, const std::locale &locale=std::locale())
convert std::basic_string to lower case by using user locale
std::vector< std::basic_string< Char, Traits, Alloc > > split(const std::basic_string< Char, Traits, Alloc > &to_be_split)
Reflects the Python function split without seperator argument.
std::basic_string< Char, Traits, Alloc > toupper(const std::basic_string< Char, Traits, Alloc > &input, const std::locale &locale=std::locale())
convert std::basic_string to upper case by using user locale
std::basic_string< Char, Traits, Alloc > strip(const std::basic_string< Char, Traits, Alloc > &to_be_stripped, const std::basic_string< Char, Traits, Alloc > &to_be_removed)
Return a copy of the string to_be_stripped with both leading and trailing characters removed.
bool equal_r(const InputRange1 &range1, const InputRange2 &range2)
std::mismatch wrapper for ranges
OutputIterator transform_r(const InputRange &range, OutputIterator result, UnaryOperation op)
std::transform wrapper for ranges
streams, binary streams, vrmlstreams, ...
ColorRGBA plus(const ColorRGBA &a, const ColorRGBA &b)
ColorRGBA over(const ColorRGBA &a, const ColorRGBA &b)
placement of foreground a in front of background b.
ColorRGBA out(const ColorRGBA &a, const ColorRGBA &b)
a held out by b, part of a outside b.
ColorRGBA through(const ColorRGBA &a, const ColorRGBA &b)
a seen through color filter b.
ColorRGBA in(const ColorRGBA &a, const ColorRGBA &b)
part of a inside b.
ColorRGBA atop(const ColorRGBA &a, const ColorRGBA &b)
union of a in b and b out a.
Library for Assembled Shared Sources.