#pragma region CPL License
/*
Nuclex Native Framework
Copyright (C) 2002-2021 Nuclex Development Labs
This library is free software; you can redistribute it and/or
modify it under the terms of the IBM Common Public License as
published by the IBM Corporation; either version 1.0 of the
License, or (at your option) any later version.
This library is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
IBM Common Public License for more details.
You should have received a copy of the IBM Common Public
License along with this library
*/
#pragma endregion // CPL License
// If the library is compiled as a DLL, this ensures symbols are exported
#define NUCLEX_PIXELS_SOURCE 1
#include "TiffBitmapCodec.h"
#if defined(NUCLEX_PIXELS_HAVE_LIBTIFF)
#include "Nuclex/Pixels/Storage/VirtualFile.h"
#include "Nuclex/Pixels/Errors/FileFormatError.h"
#include "LibTiffHelpers.h"
namespace {
// ------------------------------------------------------------------------------------------- //
/// RAII helper class that frees a TIFF file again
class TiffFileScope {
/// Initializes a new TIFF file deleter
///
/// TIFF file IO structure that should be deleted on scope exit
///
public: TiffFileScope(::TIFF *tiffFile) :
tiffFile(tiffFile) {}
/// Frees the TIFF file io structure
public: ~TiffFileScope() {
Nuclex::Pixels::Storage::Tiff::Helpers::Close(this->tiffFile);
}
/// TIFF file IO structure that will be deleted
private: ::TIFF *tiffFile;
};
// ------------------------------------------------------------------------------------------- //
} // anonymous namespace
namespace Nuclex { namespace Pixels { namespace Storage { namespace Tiff {
// ------------------------------------------------------------------------------------------- //
TiffBitmapCodec::TiffBitmapCodec() :
name(u8"Tag Image File Format (.tif) via libtiff") {
this->knownFileExtensions.push_back(u8"tif");
this->knownFileExtensions.push_back(u8"tiff");
}
// ------------------------------------------------------------------------------------------- //
std::optional TiffBitmapCodec::TryReadInfo(
const VirtualFile &source, const std::string &extensionHint /* = std::string() */
) const {
(void)extensionHint;
// If the file is too small to be a PNG file, bail out
if(source.GetSize() < SmallestPossibleTiffSize) {
return std::optional();
}
// If the file header is not indicative of a PNG file, bail out
{
std::uint8_t fileHeader[16];
source.ReadAt(0, 16, fileHeader);
if(!Helpers::IsValidTiffHeader(fileHeader)) {
return std::optional();
}
}
std::uint32_t width, height;
{
TIFF *tiffFile = Helpers::OpenForReading(source);
TiffFileScope tiffFileScope(tiffFile);
int result = TIFFGetField(tiffFile, TIFFTAG_IMAGEWIDTH, &width);
if(result != 1) {
throw Errors::FileFormatError(u8"TIFF file has no image width tag. Corrupt file?");
}
result = TIFFGetField(tiffFile, TIFFTAG_IMAGELENGTH, &height);
if(result != 1) {
throw Errors::FileFormatError(u8"TIFF file hs no image height tag. Corrupt file?");
}
}
BitmapInfo info;
info.Width = static_cast(width);
info.Height = static_cast(height);
info.PixelFormat = PixelFormat::R8_G8_B8_A8_Unsigned;
info.MemoryUsage = info.Width * info.Height * 4;
return info;
}
// ------------------------------------------------------------------------------------------- //
bool TiffBitmapCodec::CanLoad(
const VirtualFile &source, const std::string &extensionHint /* = std::string() */
) const {
// If a file extension is offered, do an early exit if it doesn't match.
// Should the codec be used through the BitmapSerializer (which is very likely
// always the case), the extension will either match or be missing.
bool mightBeTiff;
{
std::size_t hintLength = extensionHint.length();
if(hintLength == 3) { // extension without dot possible
mightBeTiff = (
((extensionHint[0] == 't') || (extensionHint[0] == 'T')) &&
((extensionHint[1] == 'i') || (extensionHint[1] == 'I')) &&
((extensionHint[2] == 'f') || (extensionHint[2] == 'F'))
);
} else if(hintLength == 4) { // extension with dot or long possible
mightBeTiff = (
(extensionHint[0] == '.') &&
((extensionHint[1] == 't') || (extensionHint[1] == 'T')) &&
((extensionHint[2] == 'i') || (extensionHint[2] == 'I')) &&
((extensionHint[3] == 'f') || (extensionHint[3] == 'F'))
);
mightBeTiff |= (
((extensionHint[0] == 't') || (extensionHint[0] == 'T')) &&
((extensionHint[1] == 'i') || (extensionHint[1] == 'I')) &&
((extensionHint[2] == 'f') || (extensionHint[2] == 'F')) &&
((extensionHint[3] == 'f') || (extensionHint[3] == 'F'))
);
} else if(hintLength == 5) { // extension with dot and long possible
mightBeTiff = (
(extensionHint[0] == '.') &&
((extensionHint[1] == 't') || (extensionHint[1] == 'T')) &&
((extensionHint[2] == 'i') || (extensionHint[2] == 'I')) &&
((extensionHint[3] == 'f') || (extensionHint[3] == 'F')) &&
((extensionHint[4] == 'f') || (extensionHint[4] == 'F'))
);
} else if(extensionHint.empty()) { // extension missing
mightBeTiff = true;
} else { // extension wrong
mightBeTiff = false;
}
}
// If the extension indicates a TIFF file (or no extension was provided),
// check the file header to see if this is really a TIFF file
if(mightBeTiff) {
std::size_t fileLength = source.GetSize();
if(fileLength >= SmallestPossibleTiffSize) {
std::uint8_t fileHeader[16];
source.ReadAt(0, 16, fileHeader); // We only need 8
return Helpers::IsValidTiffHeader(fileHeader);
} else { // file is too short to be a TIFF
return false;
}
} else { // wrong file extension
return false;
}
}
// ------------------------------------------------------------------------------------------- //
bool TiffBitmapCodec::CanSave() const {
return false; // Still working on this...
}
// ------------------------------------------------------------------------------------------- //
}}}} // namespace Nuclex::Pixels::Storage::Tiff
#endif //defined(NUCLEX_PIXELS_HAVE_LIBTIFF)