File indexing completed on 2024-10-06 10:15: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"