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"