File indexing completed on 2025-01-19 03:51:17
0001 /* ============================================================ 0002 * 0003 * This file is a part of digiKam project 0004 * https://www.digikam.org 0005 * 0006 * Date : 2009-10-11 0007 * Description : save image thread for scanned data 0008 * 0009 * SPDX-FileCopyrightText: 2009-2024 by Gilles Caulier <caulier dot gilles at gmail dot com> 0010 * 0011 * SPDX-License-Identifier: GPL-2.0-or-later 0012 * 0013 * ============================================================ */ 0014 0015 #include "saveimgthread.h" 0016 0017 // Qt includes 0018 0019 #include <QDateTime> 0020 #include <QScopedPointer> 0021 0022 // KDE includes 0023 0024 #include <ksanewidget.h> 0025 0026 // Local includes 0027 0028 #include "digikam_debug.h" 0029 #include "dimg.h" 0030 #include "dmetadata.h" 0031 0032 using namespace Digikam; 0033 using namespace KSaneIface; 0034 0035 namespace DigikamGenericDScannerPlugin 0036 { 0037 0038 class Q_DECL_HIDDEN SaveImgThread::Private 0039 { 0040 public: 0041 0042 explicit Private() 0043 { 0044 } 0045 0046 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0047 0048 QImage imageData; 0049 0050 #elif KSANE_VERSION < QT_VERSION_CHECK(21,8,0) 0051 0052 int width = 0; 0053 int height = 0; 0054 int bytesPerLine = 0; 0055 int frmt = 0; 0056 0057 QByteArray ksaneData; 0058 0059 #else 0060 0061 QImage imageData; 0062 0063 #endif 0064 0065 QString make; 0066 QString model; 0067 QString format; 0068 0069 QUrl newUrl; 0070 }; 0071 0072 SaveImgThread::SaveImgThread(QObject* const parent) 0073 : QThread(parent), 0074 d (new Private) 0075 { 0076 } 0077 0078 SaveImgThread::~SaveImgThread() 0079 { 0080 // wait for the thread to finish 0081 0082 wait(); 0083 0084 delete d; 0085 } 0086 0087 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0088 0089 void SaveImgThread::setImageData(const QImage& imageData) 0090 { 0091 d->imageData = imageData; 0092 } 0093 0094 #elif KSANE_VERSION < QT_VERSION_CHECK(21,8,0) 0095 0096 void SaveImgThread::setImageData(const QByteArray& ksaneData, int width, int height, 0097 int bytesPerLine, int ksaneFormat) 0098 { 0099 d->width = width; 0100 d->height = height; 0101 d->bytesPerLine = bytesPerLine; 0102 d->frmt = ksaneFormat; 0103 d->ksaneData = ksaneData; 0104 } 0105 0106 #else 0107 0108 void SaveImgThread::setImageData(const QImage& imageData) 0109 { 0110 d->imageData = imageData; 0111 } 0112 0113 #endif 0114 0115 void SaveImgThread::setTargetFile(const QUrl& url, const QString& format) 0116 { 0117 d->newUrl = url; 0118 d->format = format; 0119 } 0120 0121 void SaveImgThread::setScannerModel(const QString& make, const QString& model) 0122 { 0123 d->make = make; 0124 d->model = model; 0125 } 0126 0127 void SaveImgThread::run() 0128 { 0129 Q_EMIT signalProgress(d->newUrl, 10); 0130 0131 #if (QT_VERSION >= QT_VERSION_CHECK(6, 0, 0)) 0132 0133 bool sixteenBit = ((d->imageData.format() == QImage::Format_RGBX64) || 0134 (d->imageData.format() == QImage::Format_Grayscale16)); 0135 DImg img((uint)d->imageData.width(), (uint)d->imageData.height(), sixteenBit, false); 0136 int progress; 0137 0138 if (!sixteenBit) 0139 { 0140 uchar* dst = img.bits(); 0141 0142 for (int h = 0 ; h < d->imageData.height() ; ++h) 0143 { 0144 for (int w = 0 ; w < d->imageData.width() ; ++w) 0145 { 0146 if (d->imageData.format() == QImage::Format_RGB32) // Color 8 bits 0147 { 0148 const QRgb* rgbData = reinterpret_cast<QRgb*>(d->imageData.scanLine(h)); 0149 dst[0] = qBlue(rgbData[w]); // Blue 0150 dst[1] = qGreen(rgbData[w]); // Green 0151 dst[2] = qRed(rgbData[w]); // Red 0152 dst[3] = 0x00; // Alpha 0153 0154 dst += 4; 0155 } 0156 else if (d->imageData.format() == QImage::Format_Grayscale8) // Gray 0157 { 0158 const uchar* grayScale = d->imageData.scanLine(h); 0159 dst[0] = grayScale[w]; // Blue 0160 dst[1] = grayScale[w]; // Green 0161 dst[2] = grayScale[w]; // Red 0162 dst[3] = 0x00; // Alpha 0163 0164 dst += 4; 0165 } 0166 else if (d->imageData.format() == QImage::Format_Mono) // Lineart 0167 { 0168 const uchar* mono = d->imageData.scanLine(h); 0169 const int index = w / 8; 0170 const int mod = w % 8; 0171 0172 if (mono[index] & (1 << mod)) 0173 { 0174 dst[0] = 0x00; // Blue 0175 dst[1] = 0x00; // Green 0176 dst[2] = 0x00; // Red 0177 dst[3] = 0x00; // Alpha 0178 } 0179 else 0180 { 0181 dst[0] = 0xFF; // Blue 0182 dst[1] = 0xFF; // Green 0183 dst[2] = 0xFF; // Red 0184 dst[3] = 0x00; // Alpha 0185 } 0186 0187 dst += 4; 0188 } 0189 } 0190 0191 progress = 10 + (int)(((double)h * 50.0) / d->imageData.height()); 0192 0193 if (progress % 5 == 0) 0194 { 0195 Q_EMIT signalProgress(d->newUrl, progress); 0196 } 0197 } 0198 } 0199 else 0200 { 0201 unsigned short* dst = reinterpret_cast<unsigned short*>(img.bits()); 0202 0203 for (int h = 0 ; h < d->imageData.height() ; ++h) 0204 { 0205 for (int w = 0 ; w < d->imageData.width() ; ++w) 0206 { 0207 if (d->imageData.format() == QImage::Format_RGBX64) // Color 16 bits 0208 { 0209 const QRgba64* rgbData = reinterpret_cast<QRgba64*>(d->imageData.scanLine(h)); 0210 dst[0] = rgbData[w].blue(); // Blue 0211 dst[1] = rgbData[w].green(); // Green 0212 dst[2] = rgbData[w].red(); // Red 0213 dst[3] = 0x0000; // Alpha 0214 0215 dst += 4; 0216 } 0217 else if (d->imageData.format() == QImage::Format_Grayscale16) // Gray16 0218 { 0219 const unsigned short* grayScale = reinterpret_cast<unsigned short*>(d->imageData.scanLine(h)); 0220 dst[0] = grayScale[w]; // Blue 0221 dst[1] = grayScale[w]; // Green 0222 dst[2] = grayScale[w]; // Red 0223 dst[3] = 0x0000; // Alpha 0224 0225 dst += 4; 0226 } 0227 } 0228 0229 progress = 10 + (int)(((double)h * 50.0) / d->imageData.height()); 0230 0231 if ((progress % 5) == 0) 0232 { 0233 Q_EMIT signalProgress(d->newUrl, progress); 0234 } 0235 } 0236 } 0237 0238 #elif KSANE_VERSION < QT_VERSION_CHECK(21,8,0) 0239 0240 bool sixteenBit = ((d->frmt == KSaneWidget::FormatRGB_16_C) || 0241 (d->frmt == KSaneWidget::FormatGrayScale16)); 0242 DImg img((uint)d->width, (uint)d->height, sixteenBit, false); 0243 int progress; 0244 0245 if (!sixteenBit) 0246 { 0247 uchar* src = (uchar*)d->ksaneData.data(); 0248 uchar* dst = img.bits(); 0249 0250 for (int h = 0 ; h < d->height ; ++h) 0251 { 0252 for (int w = 0 ; w < d->width ; ++w) 0253 { 0254 if (d->frmt == KSaneWidget::FormatRGB_8_C) // Color 8 bits 0255 { 0256 dst[0] = src[2]; // Blue 0257 dst[1] = src[1]; // Green 0258 dst[2] = src[0]; // Red 0259 dst[3] = 0x00; // Alpha 0260 0261 dst += 4; 0262 src += 3; 0263 } 0264 else if (d->frmt == KSaneWidget::FormatGrayScale8) // Gray 0265 { 0266 dst[0] = src[0]; // Blue 0267 dst[1] = src[0]; // Green 0268 dst[2] = src[0]; // Red 0269 dst[3] = 0x00; // Alpha 0270 0271 dst += 4; 0272 src += 1; 0273 } 0274 else if (d->frmt == KSaneWidget::FormatBlackWhite) // Lineart 0275 { 0276 for (int i = 0 ; i < 8 ; ++i) 0277 { 0278 if (*src & (1 << (7 - i))) 0279 { 0280 dst[0] = 0x00; // Blue 0281 dst[1] = 0x00; // Green 0282 dst[2] = 0x00; // Red 0283 dst[3] = 0x00; // Alpha 0284 } 0285 else 0286 { 0287 dst[0] = 0xFF; // Blue 0288 dst[1] = 0xFF; // Green 0289 dst[2] = 0xFF; // Red 0290 dst[3] = 0x00; // Alpha 0291 } 0292 0293 dst += 4; 0294 } 0295 0296 src += 1; 0297 w += 7; 0298 } 0299 } 0300 0301 progress = 10 + (int)(((double)h * 50.0) / d->height); 0302 0303 if (progress % 5 == 0) 0304 { 0305 Q_EMIT signalProgress(d->newUrl, progress); 0306 } 0307 } 0308 } 0309 else 0310 { 0311 unsigned short* src = reinterpret_cast<unsigned short*>(d->ksaneData.data()); 0312 unsigned short* dst = reinterpret_cast<unsigned short*>(img.bits()); 0313 0314 for (int h = 0 ; h < d->height ; ++h) 0315 { 0316 for (int w = 0 ; w < d->width ; ++w) 0317 { 0318 if (d->frmt == KSaneWidget::FormatRGB_16_C) // Color 16 bits 0319 { 0320 dst[0] = src[2]; // Blue 0321 dst[1] = src[1]; // Green 0322 dst[2] = src[0]; // Red 0323 dst[3] = 0x0000; // Alpha 0324 0325 dst += 4; 0326 src += 3; 0327 } 0328 else if (d->frmt == KSaneWidget::FormatGrayScale16) // Gray16 0329 { 0330 dst[0] = src[0]; // Blue 0331 dst[1] = src[0]; // Green 0332 dst[2] = src[0]; // Red 0333 dst[3] = 0x0000; // Alpha 0334 0335 dst += 4; 0336 src += 1; 0337 } 0338 } 0339 0340 progress = 10 + (int)(((double)h * 50.0) / d->height); 0341 0342 if ((progress % 5) == 0) 0343 { 0344 Q_EMIT signalProgress(d->newUrl, progress); 0345 } 0346 } 0347 } 0348 0349 #else 0350 0351 bool sixteenBit = ((d->imageData.format() == QImage::Format_RGBX64) || 0352 (d->imageData.format() == QImage::Format_Grayscale16)); 0353 DImg img((uint)d->imageData.width(), (uint)d->imageData.height(), sixteenBit, false); 0354 int progress; 0355 0356 if (!sixteenBit) 0357 { 0358 uchar* dst = img.bits(); 0359 0360 for (int h = 0 ; h < d->imageData.height() ; ++h) 0361 { 0362 for (int w = 0 ; w < d->imageData.width() ; ++w) 0363 { 0364 if (d->imageData.format() == QImage::Format_RGB32) // Color 8 bits 0365 { 0366 const QRgb* rgbData = reinterpret_cast<QRgb*>(d->imageData.scanLine(h)); 0367 dst[0] = qBlue(rgbData[w]); // Blue 0368 dst[1] = qGreen(rgbData[w]); // Green 0369 dst[2] = qRed(rgbData[w]); // Red 0370 dst[3] = 0x00; // Alpha 0371 0372 dst += 4; 0373 } 0374 else if (d->imageData.format() == QImage::Format_Grayscale8) // Gray 0375 { 0376 const uchar* grayScale = d->imageData.scanLine(h); 0377 dst[0] = grayScale[w]; // Blue 0378 dst[1] = grayScale[w]; // Green 0379 dst[2] = grayScale[w]; // Red 0380 dst[3] = 0x00; // Alpha 0381 0382 dst += 4; 0383 } 0384 else if (d->imageData.format() == QImage::Format_Mono) // Lineart 0385 { 0386 const uchar* mono = d->imageData.scanLine(h); 0387 const int index = w / 8; 0388 const int mod = w % 8; 0389 0390 if (mono[index] & (1 << mod)) 0391 { 0392 dst[0] = 0x00; // Blue 0393 dst[1] = 0x00; // Green 0394 dst[2] = 0x00; // Red 0395 dst[3] = 0x00; // Alpha 0396 } 0397 else 0398 { 0399 dst[0] = 0xFF; // Blue 0400 dst[1] = 0xFF; // Green 0401 dst[2] = 0xFF; // Red 0402 dst[3] = 0x00; // Alpha 0403 } 0404 0405 dst += 4; 0406 } 0407 } 0408 0409 progress = 10 + (int)(((double)h * 50.0) / d->imageData.height()); 0410 0411 if (progress % 5 == 0) 0412 { 0413 Q_EMIT signalProgress(d->newUrl, progress); 0414 } 0415 } 0416 } 0417 else 0418 { 0419 unsigned short* dst = reinterpret_cast<unsigned short*>(img.bits()); 0420 0421 for (int h = 0 ; h < d->imageData.height() ; ++h) 0422 { 0423 for (int w = 0 ; w < d->imageData.width() ; ++w) 0424 { 0425 if (d->imageData.format() == QImage::Format_RGBX64) // Color 16 bits 0426 { 0427 const QRgba64* rgbData = reinterpret_cast<QRgba64*>(d->imageData.scanLine(h)); 0428 dst[0] = rgbData[w].blue(); // Blue 0429 dst[1] = rgbData[w].green(); // Green 0430 dst[2] = rgbData[w].red(); // Red 0431 dst[3] = 0x0000; // Alpha 0432 0433 dst += 4; 0434 } 0435 else if (d->imageData.format() == QImage::Format_Grayscale16) // Gray16 0436 { 0437 const unsigned short* grayScale = reinterpret_cast<unsigned short*>(d->imageData.scanLine(h)); 0438 dst[0] = grayScale[w]; // Blue 0439 dst[1] = grayScale[w]; // Green 0440 dst[2] = grayScale[w]; // Red 0441 dst[3] = 0x0000; // Alpha 0442 0443 dst += 4; 0444 } 0445 } 0446 0447 progress = 10 + (int)(((double)h * 50.0) / d->imageData.height()); 0448 0449 if ((progress % 5) == 0) 0450 { 0451 Q_EMIT signalProgress(d->newUrl, progress); 0452 } 0453 } 0454 } 0455 0456 #endif 0457 0458 Q_EMIT signalProgress(d->newUrl, 60); 0459 0460 bool success = img.save(d->newUrl.toLocalFile(), d->format); 0461 0462 Q_EMIT signalProgress(d->newUrl, 80); 0463 0464 if (!success) 0465 { 0466 Q_EMIT signalComplete(d->newUrl, success); 0467 0468 return; 0469 } 0470 0471 QScopedPointer<DMetadata> meta(new DMetadata(d->newUrl.toLocalFile())); 0472 meta->setExifTagString("Exif.Image.DocumentName", QLatin1String("Scanned Image")); // not i18n 0473 meta->setExifTagString("Exif.Image.Make", d->make); 0474 meta->setXmpTagString("Xmp.tiff.Make", d->make); 0475 meta->setExifTagString("Exif.Image.Model", d->model); 0476 meta->setXmpTagString("Xmp.tiff.Model", d->model); 0477 meta->setItemOrientation(DMetadata::ORIENTATION_NORMAL); 0478 meta->setItemColorWorkSpace(DMetadata::WORKSPACE_SRGB); 0479 0480 Q_EMIT signalProgress(d->newUrl, 90); 0481 0482 meta->applyChanges(true); 0483 0484 Q_EMIT signalProgress(d->newUrl, 100); 0485 0486 Q_EMIT signalComplete(d->newUrl, success); 0487 } 0488 0489 } // namespace DigikamGenericDScannerPlugin 0490 0491 #include "moc_saveimgthread.cpp"