KSaneCore

scanthread.cpp
1/*
2 * SPDX-FileCopyrightText: 2009 Kare Sars <kare dot sars at iki dot fi>
3 * SPDX-FileCopyrightText: 2014 Gregor Mitsch : port to KDE5 frameworks
4 * SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net>
5 *
6 * SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
7 */
8
9#include "scanthread.h"
10
11#include <QMutexLocker>
12#include <QVariant>
13
14#include <ksanecore_debug.h>
15
16namespace KSaneCore
17{
18
19ScanThread::ScanThread(SANE_Handle handle):
20 QThread(), m_saneHandle(handle), m_imageBuilder(&m_image, &m_dpi)
21{
22 m_emitProgressUpdateTimer.setSingleShot(false);
23 m_emitProgressUpdateTimer.setInterval(500);
24 connect(&m_emitProgressUpdateTimer, &QTimer::timeout, this, &ScanThread::updateScanProgress);
25 connect(this, &QThread::started, &m_emitProgressUpdateTimer, QOverload<>::of(&QTimer::start));
26 connect(this, &QThread::finished,&m_emitProgressUpdateTimer, &QTimer::stop);
27}
28
29void ScanThread::setImageInverted(const QVariant &newValue)
30{
31 const bool newInvert = newValue.toBool();
32 if (m_invertColors != newInvert) {
33 m_invertColors = newInvert;
34 m_image.invertPixels();
35 }
36}
37
38void ScanThread::setImageResolution(const QVariant &newValue)
39{
40 bool ok;
41 const int newDPI = newValue.toInt(&ok);
42 if (ok && m_dpi != newDPI) {
43 m_dpi = newDPI;
44 }
45}
46
47ScanThread::ReadStatus ScanThread::frameStatus()
48{
49 return m_readStatus;
50}
51
52SANE_Status ScanThread::saneStatus()
53{
54 return m_saneStatus;
55}
56
57QImage *ScanThread::scanImage()
58{
59 return &m_image;
60}
61
62void ScanThread::lockScanImage()
63{
64 m_imageMutex.lock();
65}
66
67void ScanThread::unlockScanImage()
68{
69 m_imageMutex.unlock();
70}
71
72void ScanThread::cancelScan()
73{
74 m_readStatus = ReadCancel;
75}
76
77void ScanThread::run()
78{
79 m_dataSize = 0;
80 m_readStatus = ReadOngoing;
81 m_announceFirstRead = true;
82
83 // Start the scanning with sane_start
84 m_saneStatus = sane_start(m_saneHandle);
85
86 if (m_readStatus == ReadCancel) {
87 return;
88 }
89
90 if (m_saneStatus != SANE_STATUS_GOOD) {
91 qCDebug(KSANECORE_LOG) << "sane_start=" << sane_strstatus(m_saneStatus);
92 sane_cancel(m_saneHandle);
93 m_readStatus = ReadError;
94 return;
95 }
96
97 // Read image parameters
98 m_saneStatus = sane_get_parameters(m_saneHandle, &m_params);
99 if (m_saneStatus != SANE_STATUS_GOOD) {
100 qCDebug(KSANECORE_LOG) << "sane_get_parameters=" << sane_strstatus(m_saneStatus);
101 sane_cancel(m_saneHandle);
102 m_readStatus = ReadError;
103 return;
104 }
105
106 // calculate data size
107 m_frameSize = m_params.lines * m_params.bytes_per_line;
108 if ((m_params.format == SANE_FRAME_RED) ||
109 (m_params.format == SANE_FRAME_GREEN) ||
110 (m_params.format == SANE_FRAME_BLUE)) {
111 // this is unfortunately calculated again for every frame....
112 m_dataSize = m_frameSize * 3;
113 } else {
114 m_dataSize = m_frameSize;
115 }
116
117 m_imageBuilder.start(m_params);
118 m_frameRead = 0;
119 m_frame_t_count = 0;
120
121 while (m_readStatus == ReadOngoing) {
122 readData();
123 }
124}
125
126void ScanThread::updateScanProgress()
127{
128 // handscanners have negative data size
129 if (m_dataSize <= 0) {
130 return;
131 }
132
133 int bytesRead;
134
135 if (m_frameSize < m_dataSize) {
136 bytesRead = m_frameRead + (m_frameSize * m_frame_t_count);
137 } else {
138 bytesRead = m_frameRead;
139 }
140
141 if (bytesRead > 0) {
142 Q_EMIT scanProgressUpdated(static_cast<int>((static_cast<float>(bytesRead) * 100.0) / m_dataSize));
143 }
144}
145
146void ScanThread::readData()
147{
148 SANE_Int readBytes = 0;
149 m_saneStatus = sane_read(m_saneHandle, m_readData, SCAN_READ_CHUNK_SIZE, &readBytes);
150
151 if (readBytes > 0 && m_announceFirstRead) {
152 Q_EMIT scanProgressUpdated(0);
153 m_announceFirstRead = false;
154 }
155
156 switch (m_saneStatus) {
157 case SANE_STATUS_GOOD:
158 // continue to parsing the data
159 break;
160
161 case SANE_STATUS_EOF:
162 // (handscanners have negative frame size)
163 if (m_frameRead < m_frameSize) {
164 qCDebug(KSANECORE_LOG) << "frameRead =" << m_frameRead << ", frameSize =" << m_frameSize << "readBytes =" << readBytes;
165 if ((readBytes > 0) && ((m_frameRead + readBytes) <= m_frameSize)) {
166 qCDebug(KSANECORE_LOG) << "This is not a standard compliant backend";
167 copyToScanData(readBytes);
168 }
169 // There are broken backends that return wrong number for bytes_per_line
170 if (m_params.depth == 1 && m_params.lines > 0 && m_params.lines * m_params.pixels_per_line <= m_frameRead * 8) {
171 qCDebug(KSANECORE_LOG) << "Warning!! This backend seems to return wrong bytes_per_line for line-art images!";
172 qCDebug(KSANECORE_LOG) << "Warning!! Trying to correct the value!";
173 m_params.bytes_per_line = m_frameRead / m_params.lines;
174 }
175 m_readStatus = ReadReady; // It is better to return a broken image than nothing
176 return;
177 }
178 if (m_params.last_frame == SANE_TRUE) {
179 // this is where it all ends well :)
180 m_imageBuilder.cropImagetoSize();
181 m_readStatus = ReadReady;
182 return;
183 } else {
184 // start reading next frame
185 m_saneStatus = sane_start(m_saneHandle);
186 if (m_saneStatus != SANE_STATUS_GOOD) {
187 qCDebug(KSANECORE_LOG) << "sane_start =" << sane_strstatus(m_saneStatus);
188 m_readStatus = ReadError;
189 return;
190 }
191 m_saneStatus = sane_get_parameters(m_saneHandle, &m_params);
192 if (m_saneStatus != SANE_STATUS_GOOD) {
193 qCDebug(KSANECORE_LOG) << "sane_get_parameters =" << sane_strstatus(m_saneStatus);
194 m_readStatus = ReadError;
195 sane_cancel(m_saneHandle);
196 return;
197 }
198 //qCDebug(KSANECORE_LOG) << "New Frame";
199 m_imageBuilder.beginFrame(m_params);
200 m_frameRead = 0;
201 m_frame_t_count++;
202 break;
203 }
204 default:
205 qCDebug(KSANECORE_LOG) << "sane_read=" << m_saneStatus << "=" << sane_strstatus(m_saneStatus);
206 m_readStatus = ReadError;
207 sane_cancel(m_saneHandle);
208 return;
209 }
210
211 copyToScanData(readBytes);
212}
213
214void ScanThread::copyToScanData(int readBytes)
215{
216 if (m_invertColors) {
217 if (m_params.depth == 16) {
218 //if (readBytes%2) qCDebug(KSANECORE_LOG) << "readBytes=" << readBytes;
219 quint16 *u16ptr = reinterpret_cast<quint16 *>(m_readData);
220 for (int i = 0; i < readBytes / 2; i++) {
221 u16ptr[i] = 0xFFFF - u16ptr[i];
222 }
223 } else if (m_params.depth == 8) {
224 for (int i = 0; i < readBytes; i++) {
225 m_readData[i] = 0xFF - m_readData[i];
226 }
227 } else if (m_params.depth == 1) {
228 for (int i = 0; i < readBytes; i++) {
229 m_readData[i] = ~m_readData[i];
230 }
231 }
232 }
233
234 QMutexLocker locker(&m_imageMutex);
235 if (m_imageBuilder.copyToImage(m_readData, readBytes)) {
236 m_frameRead += readBytes;
237 } else {
238 m_readStatus = ReadError;
239 }
240}
241
242} // namespace KSaneCore
243
244#include "moc_scanthread.cpp"
QFuture< ArgsType< Signal > > connect(Sender *sender, Signal signal)
void finished()
void started()
void start()
void stop()
void timeout()
bool toBool() const const
int toInt(bool *ok) const const
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Sat Apr 27 2024 22:14:00 by doxygen 1.10.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.