File indexing completed on 2024-05-19 08:34:16

0001 /*
0002  * SPDX-FileCopyrightText: 2009 Kare Sars <kare dot sars at iki dot fi>
0003  * SPDX-FileCopyrightText: 2014 Gregor Mitsch : port to KDE5 frameworks
0004  * SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net>
0005  *
0006  * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
0007  */
0008 
0009 #include "scanthread.h"
0010 
0011 #include <QMutexLocker>
0012 #include <QVariant>
0013 
0014 #include <ksanecore_debug.h>
0015 
0016 namespace KSaneCore
0017 {
0018 
0019 ScanThread::ScanThread(SANE_Handle handle):
0020     QThread(), m_saneHandle(handle), m_imageBuilder(&m_image, &m_dpi)
0021 {
0022     m_emitProgressUpdateTimer.setSingleShot(false);
0023     m_emitProgressUpdateTimer.setInterval(500);
0024     connect(&m_emitProgressUpdateTimer, &QTimer::timeout, this, &ScanThread::updateScanProgress);
0025     connect(this, &QThread::started, &m_emitProgressUpdateTimer, QOverload<>::of(&QTimer::start));
0026     connect(this, &QThread::finished,&m_emitProgressUpdateTimer, &QTimer::stop);
0027 }
0028 
0029 void ScanThread::setImageInverted(const QVariant &newValue)
0030 {
0031     const bool newInvert = newValue.toBool();
0032     if (m_invertColors != newInvert) {
0033         m_invertColors = newInvert;
0034         m_image.invertPixels();
0035     }
0036 }
0037 
0038 void ScanThread::setImageResolution(const QVariant &newValue)
0039 {
0040     bool ok;
0041     const int newDPI = newValue.toInt(&ok);
0042     if (ok && m_dpi != newDPI) {
0043         m_dpi = newDPI;
0044     }
0045 }
0046 
0047 ScanThread::ReadStatus ScanThread::frameStatus()
0048 {
0049     return m_readStatus;
0050 }
0051 
0052 SANE_Status ScanThread::saneStatus()
0053 {
0054     return m_saneStatus;
0055 }
0056 
0057 QImage *ScanThread::scanImage()
0058 {
0059     return &m_image;
0060 }
0061 
0062 void ScanThread::lockScanImage()
0063 {
0064     m_imageMutex.lock();
0065 }
0066 
0067 void ScanThread::unlockScanImage()
0068 {
0069     m_imageMutex.unlock();
0070 }
0071 
0072 void ScanThread::cancelScan()
0073 {
0074     m_readStatus = ReadCancel;
0075 }
0076 
0077 void ScanThread::run()
0078 {
0079     m_dataSize = 0;
0080     m_readStatus = ReadOngoing;
0081     m_announceFirstRead = true;
0082 
0083     // Start the scanning with sane_start
0084     m_saneStatus = sane_start(m_saneHandle);
0085 
0086     if (m_readStatus == ReadCancel) {
0087         return;
0088     }
0089 
0090     if (m_saneStatus != SANE_STATUS_GOOD) {
0091         qCDebug(KSANECORE_LOG) << "sane_start=" << sane_strstatus(m_saneStatus);
0092         sane_cancel(m_saneHandle);
0093         m_readStatus = ReadError;
0094         return;
0095     }
0096 
0097     // Read image parameters
0098     m_saneStatus = sane_get_parameters(m_saneHandle, &m_params);
0099     if (m_saneStatus != SANE_STATUS_GOOD) {
0100         qCDebug(KSANECORE_LOG) << "sane_get_parameters=" << sane_strstatus(m_saneStatus);
0101         sane_cancel(m_saneHandle);
0102         m_readStatus = ReadError;
0103         return;
0104     }
0105 
0106     // calculate data size
0107     m_frameSize  = m_params.lines * m_params.bytes_per_line;
0108     if ((m_params.format == SANE_FRAME_RED) ||
0109             (m_params.format == SANE_FRAME_GREEN) ||
0110             (m_params.format == SANE_FRAME_BLUE)) {
0111         // this is unfortunately calculated again for every frame....
0112         m_dataSize = m_frameSize * 3;
0113     } else {
0114         m_dataSize = m_frameSize;
0115     }
0116 
0117     m_imageBuilder.start(m_params);
0118     m_frameRead = 0;
0119     m_frame_t_count = 0;
0120 
0121     while (m_readStatus == ReadOngoing) {
0122         readData();
0123     }
0124 }
0125 
0126 void ScanThread::updateScanProgress()
0127 {
0128     // handscanners have negative data size
0129     if (m_dataSize <= 0) {
0130         return;
0131     }
0132 
0133     int bytesRead;
0134 
0135     if (m_frameSize < m_dataSize) {
0136         bytesRead = m_frameRead + (m_frameSize * m_frame_t_count);
0137     } else {
0138         bytesRead = m_frameRead;
0139     }
0140 
0141     if (bytesRead > 0) {
0142         Q_EMIT scanProgressUpdated(static_cast<int>((static_cast<float>(bytesRead) * 100.0) / m_dataSize));
0143     }
0144 }
0145 
0146 void ScanThread::readData()
0147 {
0148     SANE_Int readBytes = 0;
0149     m_saneStatus = sane_read(m_saneHandle, m_readData, SCAN_READ_CHUNK_SIZE, &readBytes);
0150 
0151     if (readBytes > 0 && m_announceFirstRead) {
0152         Q_EMIT scanProgressUpdated(0);
0153         m_announceFirstRead = false;
0154     }
0155 
0156     switch (m_saneStatus) {
0157     case SANE_STATUS_GOOD:
0158         // continue to parsing the data
0159         break;
0160 
0161     case SANE_STATUS_EOF:
0162         // (handscanners have negative frame size)
0163         if (m_frameRead < m_frameSize) {
0164             qCDebug(KSANECORE_LOG) << "frameRead =" << m_frameRead  << ", frameSize =" << m_frameSize << "readBytes =" << readBytes;
0165             if ((readBytes > 0) && ((m_frameRead + readBytes) <= m_frameSize)) {
0166                 qCDebug(KSANECORE_LOG) << "This is not a standard compliant backend";
0167                 copyToScanData(readBytes);
0168             }
0169             // There are broken backends that return wrong number for bytes_per_line
0170             if (m_params.depth == 1 && m_params.lines > 0 && m_params.lines * m_params.pixels_per_line <= m_frameRead * 8) {
0171                 qCDebug(KSANECORE_LOG) << "Warning!! This backend seems to return wrong bytes_per_line for line-art images!";
0172                 qCDebug(KSANECORE_LOG) << "Warning!! Trying to correct the value!";
0173                 m_params.bytes_per_line = m_frameRead / m_params.lines;
0174             }
0175             m_readStatus = ReadReady; // It is better to return a broken image than nothing
0176             return;
0177         }
0178         if (m_params.last_frame == SANE_TRUE) {
0179             // this is where it all ends well :)
0180             m_imageBuilder.cropImagetoSize();
0181             m_readStatus = ReadReady;
0182             return;
0183         } else {
0184             // start reading next frame
0185             m_saneStatus = sane_start(m_saneHandle);
0186             if (m_saneStatus != SANE_STATUS_GOOD) {
0187                 qCDebug(KSANECORE_LOG) << "sane_start =" << sane_strstatus(m_saneStatus);
0188                 m_readStatus = ReadError;
0189                 return;
0190             }
0191             m_saneStatus = sane_get_parameters(m_saneHandle, &m_params);
0192             if (m_saneStatus != SANE_STATUS_GOOD) {
0193                 qCDebug(KSANECORE_LOG) << "sane_get_parameters =" << sane_strstatus(m_saneStatus);
0194                 m_readStatus = ReadError;
0195                 sane_cancel(m_saneHandle);
0196                 return;
0197             }
0198             //qCDebug(KSANECORE_LOG) << "New Frame";
0199             m_imageBuilder.beginFrame(m_params);
0200             m_frameRead = 0;
0201             m_frame_t_count++;
0202             break;
0203         }
0204     default:
0205         qCDebug(KSANECORE_LOG) << "sane_read=" << m_saneStatus << "=" << sane_strstatus(m_saneStatus);
0206         m_readStatus = ReadError;
0207         sane_cancel(m_saneHandle);
0208         return;
0209     }
0210 
0211     copyToScanData(readBytes);
0212 }
0213 
0214 void ScanThread::copyToScanData(int readBytes)
0215 {
0216     if (m_invertColors) {
0217         if (m_params.depth == 16) {
0218             //if (readBytes%2) qCDebug(KSANECORE_LOG) << "readBytes=" << readBytes;
0219             quint16 *u16ptr = reinterpret_cast<quint16 *>(m_readData);
0220             for (int i = 0; i < readBytes / 2; i++) {
0221                 u16ptr[i] = 0xFFFF - u16ptr[i];
0222             }
0223         } else if (m_params.depth == 8) {
0224             for (int i = 0; i < readBytes; i++) {
0225                 m_readData[i] = 0xFF - m_readData[i];
0226             }
0227         } else if (m_params.depth == 1) {
0228             for (int i = 0; i < readBytes; i++) {
0229                 m_readData[i] = ~m_readData[i];
0230             }
0231         }
0232     }
0233 
0234     QMutexLocker locker(&m_imageMutex);
0235     if (m_imageBuilder.copyToImage(m_readData, readBytes)) {
0236         m_frameRead += readBytes;
0237     } else {
0238         m_readStatus = ReadError;
0239     }
0240 }
0241 
0242 } // namespace KSaneCore
0243 
0244 #include "moc_scanthread.cpp"