File indexing completed on 2025-01-19 12:59:20
0001 /************************************************************************ 0002 * * 0003 * This file is part of Kooka, a scanning/OCR application using * 0004 * Qt <http://www.qt.io> and KDE Frameworks <http://www.kde.org>. * 0005 * * 0006 * Copyright (C) 1999-2016 Klaas Freitag <freitag@suse.de> * 0007 * Jonathan Marten <jjm@keelhaul.me.uk> * 0008 * * 0009 * Kooka is free software; you can redistribute it and/or modify it * 0010 * under the terms of the GNU Library General Public License as * 0011 * published by the Free Software Foundation and appearing in the * 0012 * file COPYING included in the packaging of this file; either * 0013 * version 2 of the License, or (at your option) any later version. * 0014 * * 0015 * As a special exception, permission is given to link this program * 0016 * with any version of the KADMOS OCR/ICR engine (a product of * 0017 * reRecognition GmbH, Kreuzlingen), and distribute the resulting * 0018 * executable without including the source code for KADMOS in the * 0019 * source distribution. * 0020 * * 0021 * This program is distributed in the hope that it will be useful, * 0022 * but WITHOUT ANY WARRANTY; without even the implied warranty of * 0023 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * 0024 * GNU General Public License for more details. * 0025 * * 0026 * You should have received a copy of the GNU General Public * 0027 * License along with this program; see the file COPYING. If * 0028 * not, see <http://www.gnu.org/licenses/>. * 0029 * * 0030 ************************************************************************/ 0031 0032 #include "scanimage.h" 0033 0034 #include <stdint.h> 0035 0036 #include <qfile.h> 0037 0038 #include <klocalizedstring.h> 0039 0040 #include "imageformat.h" 0041 #include "libkookascan_logging.h" 0042 0043 #ifdef HAVE_TIFF 0044 extern "C" 0045 { 0046 #include <tiffio.h> 0047 #include <tiff.h> 0048 } 0049 #endif 0050 0051 0052 ScanImage::ScanImage(int width, int height, QImage::Format format) 0053 : QImage(width, height, format) 0054 { 0055 init(); 0056 } 0057 0058 0059 ScanImage::ScanImage(const QImage &img) 0060 : QImage(img) 0061 { 0062 init(); 0063 } 0064 0065 0066 ScanImage::ScanImage(const QUrl &url) 0067 : QImage() 0068 { 0069 init(); 0070 0071 bool isTiff = false; // do we have a TIFF file? 0072 bool haveTiff = false; // can it be read via TIFF lib? 0073 0074 if (!url.isLocalFile()) 0075 { 0076 m_errorString = i18n("Loading non-local images is not yet implemented"); 0077 return; 0078 } 0079 0080 if (url.hasFragment()) // is this a subimage? 0081 { 0082 int subno = url.fragment().toInt(); // get subimage number 0083 if (subno>0) // valid number from fragment 0084 { // get local file without fragment 0085 const QString fileName = url.adjusted(QUrl::RemoveFragment).toLocalFile(); 0086 qCDebug(LIBKOOKASCAN_LOG) << "subimage" << subno << "from" << fileName; 0087 m_errorString = loadTiffDir(fileName, subno); 0088 return; // load TIFF subimage 0089 } 0090 } 0091 0092 const QString filename = url.toLocalFile(); // local path of image 0093 ImageFormat format = ImageFormat::formatForUrl(url); 0094 0095 // If the file is TIFF and QImageReader supports TIFF files, 'format' as 0096 // returned by ImageFormat::formatForUrl() will be "TIF". If TIFF files 0097 // are not supported but the MIME type of the file says that it is TIFF, 0098 // then 'format' will be "TIFFLIB". So either of these format names means 0099 // that a TIFF file is being loaded. 0100 isTiff = format.isTiff(); 0101 qCDebug(LIBKOOKASCAN_LOG) << "Loading image format" << format << "from" << filename; 0102 0103 #ifdef HAVE_TIFF 0104 if (isTiff) // if it is TIFF, check 0105 { // for multiple images 0106 qCDebug(LIBKOOKASCAN_LOG) << "Checking for multi-page TIFF"; 0107 TIFF *tif = TIFFOpen(QFile::encodeName(filename).constData(), "r"); 0108 if (tif!=nullptr) 0109 { 0110 do 0111 { 0112 ++m_subImages; 0113 } while (TIFFReadDirectory(tif)); 0114 qCDebug(LIBKOOKASCAN_LOG) << "found" << m_subImages << "TIFF directories"; 0115 if (m_subImages>1) haveTiff = true; 0116 // This format will have been specially detected 0117 // in ImageFormat::formatForMime(), if the file is TIFF 0118 // but QImageReader does not have TIFF support. 0119 // The TIFF file can still be read by loadTiffDir() below. 0120 else if (m_subImages==1 && format==ImageFormat("TIFFLIB")) haveTiff = true; 0121 TIFFClose(tif); 0122 } 0123 } 0124 #endif 0125 if (!haveTiff) // not TIFF, or not multi-page 0126 { 0127 QImage::load(filename); // Qt can only read one image 0128 if (isNull()) m_errorString = i18n("Image loading failed"); 0129 m_subImages = 0; 0130 } 0131 #ifdef HAVE_TIFF 0132 else // a TIFF file, multi-page or not 0133 { 0134 m_errorString = loadTiffDir(filename, 0); // read by TIFFlib directly 0135 } 0136 #endif 0137 0138 m_url = url; // record image source as from file 0139 } 0140 0141 0142 void ScanImage::init() 0143 { 0144 m_subImages = 0; // no subimages present 0145 m_errorString.clear(); // no error detected yet 0146 mImageType = ScanImage::None; // image type not yet set 0147 } 0148 0149 0150 bool ScanImage::isFileBound() const 0151 { 0152 return (m_url.isValid()); 0153 } 0154 0155 0156 QString ScanImage::loadTiffDir(const QString &filename, int subno) 0157 { 0158 #ifdef HAVE_TIFF 0159 // if it is TIFF, check with TIFFlib if it is multiple images 0160 qCDebug(LIBKOOKASCAN_LOG) << "Trying to load TIFF, subimage" << subno; 0161 TIFF *tif = TIFFOpen(QFile::encodeName(filename).constData(), "r"); 0162 if (tif==nullptr) return (i18n("Failed to open TIFF file")); 0163 0164 if (subno>0) // want a subimage 0165 { 0166 if (!TIFFSetDirectory(tif, subno-1)) 0167 { 0168 TIFFClose(tif); 0169 return (i18n("Failed to read TIFF directory")); 0170 } 0171 } 0172 0173 int imgWidth, imgHeight; 0174 TIFFGetField(tif, TIFFTAG_IMAGEWIDTH, &imgWidth); 0175 TIFFGetField(tif, TIFFTAG_IMAGELENGTH, &imgHeight); 0176 0177 // TODO: load bw-image correctly only 2 bit 0178 // 0179 // The Qt3 version of this did: 0180 // 0181 // create( imgWidth, imgHeight, 32 ); 0182 // 0183 // and then read the TIFF image into the image data as returned by bits(). 0184 // 0185 // This is not possible on Qt4, where the size/depth of a QImage is set in 0186 // its constructor and cannot be subsequently changed. Instead we read 0187 // the TIFF file into a temporary image and then use QImage::operator= 0188 // to shallow copy that temporary image into ours. After that temporary 0189 // image has been destroyed, we have the only copy of the TIFF read image. 0190 // 0191 QImage tmpImg(imgWidth, imgHeight, QImage::Format_RGB32); 0192 uint32_t *data = (uint32_t *)(tmpImg.bits()); 0193 if (TIFFReadRGBAImage(tif, imgWidth, imgHeight, data, 0)) { 0194 // Successfully read, now convert 0195 tmpImg = tmpImg.rgbSwapped(); // swap red and blue 0196 tmpImg = tmpImg.mirrored(); // reverse (it's upside down) 0197 } else { 0198 TIFFClose(tif); 0199 return (i18n("Failed to read TIFF image")); 0200 } 0201 0202 // Fetch the x- and y-resolutions to adjust images 0203 float xReso, yReso; 0204 bool resosFound = TIFFGetField(tif, TIFFTAG_XRESOLUTION, &xReso) && 0205 TIFFGetField(tif, TIFFTAG_YRESOLUTION, &yReso); 0206 //qCDebug(LIBKOOKASCAN_LOG) << "TIFF image: X-Res" << xReso << "Y-Res" << yReso; 0207 0208 TIFFClose(tif); // finished with TIFF file 0209 0210 // Check now if resolution in x- and y-directions differ. 0211 // If so, stretch the image accordingly. 0212 if (resosFound && xReso != yReso) { 0213 if (xReso > yReso) { 0214 float yScalefactor = xReso / yReso; 0215 //qCDebug(LIBKOOKASCAN_LOG) << "Different resolution X/Y, rescaling Y with factor" << yScalefactor; 0216 /* rescale the image */ 0217 tmpImg = tmpImg.scaled(imgWidth, int(imgHeight * yScalefactor), 0218 Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0219 } else { 0220 float xScalefactor = yReso / xReso; 0221 //qCDebug(LIBKOOKASCAN_LOG) << "Different resolution X/Y, rescaling X with factor" << xScalefactor; 0222 tmpImg = tmpImg.scaled(int(imgWidth * xScalefactor), imgHeight, 0223 Qt::IgnoreAspectRatio, Qt::SmoothTransformation); 0224 } 0225 } 0226 0227 QImage::operator=(tmpImg); // copy as our image 0228 #else 0229 return (i18n("TIFF not supported")); 0230 #endif // HAVE_TIFF 0231 return (QString()); // TIFF read succeeded 0232 } 0233 0234 0235 bool ScanImage::isSubImage() const 0236 { 0237 return (m_url.isValid() && m_url.fragment().toInt()>0); 0238 } 0239 0240 0241 void ScanImage::setScannerName(const QByteArray &scanner) 0242 { 0243 setText("ScannerName", QString::fromLocal8Bit(scanner)); 0244 } 0245 0246 0247 QByteArray ScanImage::getScannerName() const 0248 { 0249 return (text("ScannerName").toLocal8Bit()); 0250 } 0251 0252 0253 void ScanImage::setXResolution(int res) 0254 { 0255 setDotsPerMeterX(DPI_TO_DPM(res)); 0256 } 0257 0258 0259 void ScanImage::setYResolution(int res) 0260 { 0261 setDotsPerMeterY(DPI_TO_DPM(res)); 0262 } 0263 0264 0265 int ScanImage::getXResolution() const 0266 { 0267 return (DPM_TO_DPI(dotsPerMeterX())); 0268 } 0269 0270 0271 int ScanImage::getYResolution() const 0272 { 0273 return (DPM_TO_DPI(dotsPerMeterY())); 0274 } 0275 0276 0277 // This was originally ImageMetaInfo::findImageType() and then 0278 // ImgSaver::findImageType(). It is used to deduce the image 0279 // type, if it has not been possible to derive it from the 0280 // SANE parameters by KScanDevice. 0281 // 0282 // The logic is very similar to KScanDevice::getImageFormat(), 0283 // except that it looks at the actual result image instead of the 0284 // initial SANE parameters. This means that the image type could 0285 // possibly be resolved as LowColour here. 0286 ScanImage::ImageType ScanImage::imageType() const 0287 { 0288 if (mImageType!=ScanImage::None) return (mImageType); // image type as set 0289 if (isNull()) return (ScanImage::None); // null image 0290 0291 if (depth()==1 || colorCount()==2) return (ScanImage::BlackWhite); 0292 // black/white bitmap 0293 if (depth()>8) return (ScanImage::HighColour); // high colour 0294 if (allGray()) return (ScanImage::Greyscale); // grey scale 0295 return (ScanImage::LowColour); // low (indexed) colour 0296 }