Mercurial > hg > orthanc-wsi
diff Framework/Inputs/PlainTiff.cpp @ 298:fa734a851551
New option: "tiff-alignment" to control deep zoom of plain TIFF over IIIF
author | Sebastien Jodogne <s.jodogne@gmail.com> |
---|---|
date | Tue, 18 Jul 2023 08:19:16 +0200 |
parents | |
children | 7020852a8fa9 |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/Framework/Inputs/PlainTiff.cpp Tue Jul 18 08:19:16 2023 +0200 @@ -0,0 +1,166 @@ +/** + * Orthanc - A Lightweight, RESTful DICOM Store + * Copyright (C) 2012-2016 Sebastien Jodogne, Medical Physics + * Department, University Hospital of Liege, Belgium + * Copyright (C) 2017-2023 Osimis S.A., Belgium + * Copyright (C) 2021-2023 Sebastien Jodogne, ICTEAM UCLouvain, Belgium + * + * This program is free software: you can redistribute it and/or + * modify it under the terms of the GNU Affero General Public License + * as published by the Free Software Foundation, either version 3 of + * the License, or (at your option) any later version. + * + * This program 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 GNU + * Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + **/ + + +#include "PlainTiff.h" + +#include "../TiffReader.h" + +#include <Images/Image.h> +#include <Images/ImageProcessing.h> +#include <Logging.h> +#include <OrthancException.h> + +#include <cassert> +#include <string.h> + + +namespace OrthancWSI +{ + PlainTiff::PlainTiff(const std::string& path, + unsigned int tileWidth, + unsigned int tileHeight, + unsigned int paddingAlignement, + uint8_t paddingRed, + uint8_t paddingGreen, + uint8_t paddingBlue) : + SingleLevelDecodedPyramid(tileWidth, tileHeight) + { + TiffReader reader(path); + + // Look for the largest sub-image + bool first = true; + unsigned int width = 0; + unsigned int height = 0; + tdir_t largest = 0; + + tdir_t pos = 0; + + do + { + uint32_t w, h, tw, th; + + if (TIFFSetDirectory(reader.GetTiff(), pos) && + !TIFFGetField(reader.GetTiff(), TIFFTAG_TILEWIDTH, &tw) && // Must not be a tiled image + !TIFFGetField(reader.GetTiff(), TIFFTAG_TILELENGTH, &th) && // Must not be a tiled image + TIFFGetField(reader.GetTiff(), TIFFTAG_IMAGEWIDTH, &w) && + TIFFGetField(reader.GetTiff(), TIFFTAG_IMAGELENGTH, &h) && + w > 0 && + h > 0) + { + if (first) + { + first = false; + width = w; + height = h; + largest = pos; + } + else if (w > width && + h > height) + { + width = w; + height = h; + largest = pos; + } + } + + pos++; + } + while (TIFFReadDirectory(reader.GetTiff())); + + if (first) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_BadFileFormat, "This is an empty TIFF image"); + } + + // Back to the largest directory + if (!TIFFSetDirectory(reader.GetTiff(), largest)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_CorruptedFile); + } + + ImageCompression compression; + Orthanc::PixelFormat pixelFormat; + Orthanc::PhotometricInterpretation photometric; + + if (!reader.GetCurrentDirectoryInformation(compression, pixelFormat, photometric)) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + if (pixelFormat != Orthanc::PixelFormat_RGB24) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_NotImplemented); + } + + LOG(WARNING) << "Size of the source TIFF image: " << width << "x" << height; + + const unsigned int paddedWidth = paddingAlignement * CeilingDivision(width, paddingAlignement); + const unsigned int paddedHeight = paddingAlignement * CeilingDivision(height, paddingAlignement); + assert(paddedWidth >= width && + paddedHeight >= height); + + LOG(WARNING) << "Size of the padded TIFF image: " << paddedWidth << "x" << paddedHeight; + + decoded_.reset(new Orthanc::Image(pixelFormat, paddedWidth, paddedHeight, false)); + Orthanc::ImageProcessing::Set(*decoded_, paddingRed, paddingGreen, paddingBlue, 255); + + std::string strip; + strip.resize(TIFFStripSize(reader.GetTiff())); + + const size_t stripPitch = width * Orthanc::GetBytesPerPixel(pixelFormat); + + if (strip.empty() || + stripPitch == 0 || + strip.size() < stripPitch || + strip.size() % stripPitch != 0) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + const size_t stripHeight = (strip.size() / stripPitch); + const size_t stripCount = CeilingDivision(height, stripHeight); + + if (TIFFNumberOfStrips(reader.GetTiff()) != stripCount) + { + throw Orthanc::OrthancException(Orthanc::ErrorCode_InternalError); + } + + for (unsigned int i = 0; i < stripCount; i++) + { + TIFFReadEncodedStrip(reader.GetTiff(), i, &strip[0], static_cast<tsize_t>(-1)); + + const unsigned int y = i * stripHeight; + + const uint8_t* p = reinterpret_cast<const uint8_t*>(&strip[0]); + uint8_t* q = reinterpret_cast<uint8_t*>(decoded_->GetRow(y)); + + for (unsigned j = 0; j < stripHeight && y + j < height; j++) + { + memcpy(q, p, stripPitch); + p += stripPitch; + q += decoded_->GetPitch(); + } + } + + SetImage(*decoded_); + } +}