Commit 5e6e06e9 authored by Alexander Stippich's avatar Alexander Stippich
Browse files

merge the preview and scan thread

parent 91d8df12
......@@ -25,7 +25,6 @@ target_sources(KF5Sane PRIVATE
ksanefinddevicesthread.cpp
ksanewidget.cpp
ksanescanthread.cpp
ksanepreviewthread.cpp
ksanepreviewimagebuilder.cpp
ksanewidget_p.cpp
splittercollapser.cpp
......
/* ============================================================
*
* SPDX-FileCopyrightText: 2009 Kare Sars <kare dot sars at iki dot fi>
* SPDX-FileCopyrightText: 2014 Gregor Mitsch : port to KDE5 frameworks
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*
* ============================================================ */
#include "ksanepreviewthread.h"
#include <QMutexLocker>
#include <QImage>
#include <ksane_debug.h>
namespace KSaneIface
{
KSanePreviewThread::KSanePreviewThread(SANE_Handle handle, QImage *img):
QThread(),
m_saneHandle(handle),
m_frameSize(0),
m_frameRead(0),
m_frame_t_count(0),
m_dataSize(0),
m_saneStatus(SANE_STATUS_GOOD),
m_readStatus(READ_READY),
// m_scanProgress(0),
m_saneStartDone(false),
m_invertColors(false),
m_imageBuilder(img)
{
}
void KSanePreviewThread::setPreviewInverted(bool inverted)
{
m_invertColors = inverted;
}
SANE_Status KSanePreviewThread::saneStatus()
{
return m_saneStatus;
}
void KSanePreviewThread::cancelScan()
{
m_readStatus = READ_CANCEL;
}
void KSanePreviewThread::run()
{
m_dataSize = 0;
m_readStatus = READ_ON_GOING;
m_saneStartDone = false;
// Start the scanning with sane_start
m_saneStatus = sane_start(m_saneHandle);
if (m_saneStatus != SANE_STATUS_GOOD) {
qCDebug(KSANE_LOG) << "sane_start=" << sane_strstatus(m_saneStatus);
sane_cancel(m_saneHandle);
m_readStatus = READ_ERROR;
return;
}
// Read image parameters
m_saneStatus = sane_get_parameters(m_saneHandle, &m_params);
if (m_saneStatus != SANE_STATUS_GOOD) {
qCDebug(KSANE_LOG) << "sane_get_parameters=" << sane_strstatus(m_saneStatus);
sane_cancel(m_saneHandle);
m_readStatus = READ_ERROR;
return;
}
// calculate data size
m_frameSize = m_params.lines * m_params.bytes_per_line;
if ((m_params.format == SANE_FRAME_RED) ||
(m_params.format == SANE_FRAME_GREEN) ||
(m_params.format == SANE_FRAME_BLUE)) {
// this is unfortunately calculated again for every frame....
m_dataSize = m_frameSize * 3;
} else {
m_dataSize = m_frameSize;
}
m_imageBuilder.start(m_params);
m_frameRead = 0;
m_frame_t_count = 0;
// set the m_saneStartDone here so the new QImage gets allocated before updating the preview.
m_saneStartDone = true;
while (m_readStatus == READ_ON_GOING) {
readData();
}
}
int KSanePreviewThread::scanProgress()
{
// handscanners have negative data size
if (m_dataSize <= 0) {
return 0;
}
int bytesRead;
if (m_frameSize < m_dataSize) {
bytesRead = m_frameRead + (m_frameSize * m_frame_t_count);
} else {
bytesRead = m_frameRead;
}
return (int)(((float)bytesRead * 100.0) / m_dataSize);
}
void KSanePreviewThread::readData()
{
SANE_Int readBytes;
m_saneStatus = sane_read(m_saneHandle, m_readData, PREVIEW_READ_CHUNK_SIZE, &readBytes);
switch (m_saneStatus) {
case SANE_STATUS_GOOD:
// continue to parsing the data
break;
case SANE_STATUS_EOF:
// (handscanners have negative frame size)
if (m_frameRead < m_frameSize) {
qCDebug(KSANE_LOG) << "frameRead =" << m_frameRead << ", frameSize =" << m_frameSize;
m_readStatus = READ_ERROR;
return;
}
if (m_params.last_frame == SANE_TRUE) {
// this is where it all ends well :)
m_readStatus = READ_READY;
return;
} else {
// start reading next frame
SANE_Status status = sane_start(m_saneHandle);
if (status != SANE_STATUS_GOOD) {
qCDebug(KSANE_LOG) << "sane_start =" << sane_strstatus(status);
m_readStatus = READ_ERROR;
return;
}
status = sane_get_parameters(m_saneHandle, &m_params);
if (status != SANE_STATUS_GOOD) {
qCDebug(KSANE_LOG) << "sane_get_parameters =" << sane_strstatus(status);
m_readStatus = READ_ERROR;
sane_cancel(m_saneHandle);
return;
}
//qCDebug(KSANE_LOG) << "New Frame";
m_imageBuilder.beginFrame(m_params);
m_frameRead = 0;
m_frame_t_count++;
break;
}
default:
qCDebug(KSANE_LOG) << "sane_read=" << m_saneStatus << "=" << sane_strstatus(m_saneStatus);
m_readStatus = READ_ERROR;
sane_cancel(m_saneHandle);
return;
}
copyToPreviewImg(readBytes);
}
void KSanePreviewThread::copyToPreviewImg(int readBytes)
{
QMutexLocker locker(&imgMutex);
if (m_invertColors) {
if (m_params.depth == 16) {
//if (readBytes%2) qCDebug(KSANE_LOG) << "readBytes=" << readBytes;
quint16 *u16ptr = reinterpret_cast<quint16 *>(m_readData);
for (int i = 0; i < readBytes / 2; i++) {
u16ptr[i] = 0xFFFF - u16ptr[i];
}
} else if (m_params.depth == 8) {
for (int i = 0; i < readBytes; i++) {
m_readData[i] = 0xFF - m_readData[i];
}
} else if (m_params.depth == 1) {
for (int i = 0; i < readBytes; i++) {
m_readData[i] = ~m_readData[i];
}
}
}
if (m_imageBuilder.copyToImage(m_readData, readBytes)) {
m_frameRead += readBytes;
} else {
m_readStatus = READ_ERROR;
}
}
bool KSanePreviewThread::saneStartDone()
{
return m_saneStartDone;
}
bool KSanePreviewThread::imageResized()
{
return m_imageBuilder.imageResized();
}
} // NameSpace KSaneIface
/* ============================================================
*
* SPDX-FileCopyrightText: 2009 Kare Sars <kare dot sars at iki dot fi>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*
* ============================================================ */
#ifndef KSANE_PREVIEW_THREAD_H
#define KSANE_PREVIEW_THREAD_H
#include "ksanepreviewimagebuilder.h"
// Sane includes
extern "C"
{
#include <sane/saneopts.h>
#include <sane/sane.h>
}
#include <QThread>
#include <QMutex>
#include <QImage>
#define PREVIEW_READ_CHUNK_SIZE 100000
namespace KSaneIface
{
class KSanePreviewThread: public QThread
{
Q_OBJECT
public:
typedef enum {
READ_ON_GOING,
READ_ERROR,
READ_CANCEL,
READ_READY
} ReadStatus;
KSanePreviewThread(SANE_Handle handle, QImage *img);
void run() override;
void setPreviewInverted(bool);
void cancelScan();
int scanProgress();
bool saneStartDone();
bool imageResized();
SANE_Status saneStatus();
QMutex imgMutex;
private:
void readData();
void copyToPreviewImg(int readBytes);
SANE_Byte m_readData[PREVIEW_READ_CHUNK_SIZE];
SANE_Handle m_saneHandle;
int m_frameSize;
int m_frameRead;
int m_frame_t_count;
int m_dataSize;
SANE_Parameters m_params;
SANE_Status m_saneStatus;
ReadStatus m_readStatus;
// int m_scanProgress;
bool m_saneStartDone;
bool m_invertColors;
KSanePreviewImageBuilder m_imageBuilder;
};
}
#endif
......@@ -9,33 +9,37 @@
#include "ksanescanthread.h"
#include <QMutexLocker>
#include <QImage>
#include <ksane_debug.h>
namespace KSaneIface
{
KSaneScanThread::KSaneScanThread(SANE_Handle handle, QImage *img, QByteArray *data):
QThread(), m_saneHandle(handle), m_imageBuilder(img), m_data(data), m_image(img)
{
m_emitProgressUpdateTimer.setSingleShot(false);
m_emitProgressUpdateTimer.setInterval(300);
connect(&m_emitProgressUpdateTimer, &QTimer::timeout, this, &KSaneScanThread::updateScanProgress);
connect(this, &QThread::started, &m_emitProgressUpdateTimer, QOverload<>::of(&QTimer::start));
connect(this, &QThread::finished, this, &KSaneScanThread::finishProgress);
}
KSaneScanThread::KSaneScanThread(SANE_Handle handle, QByteArray *data):
QThread(),
m_data(data),
m_saneHandle(handle),
m_frameSize(0),
m_frameRead(0),
m_frame_t_count(0),
m_dataSize(0),
m_saneStatus(SANE_STATUS_GOOD),
m_readStatus(READ_READY),
m_saneStartDone(false),
m_invertColors(false)
{}
void KSaneScanThread::setImageInverted(bool inverted)
void KSaneScanThread::setImageInverted(QVariant newValue)
{
m_invertColors = inverted;
const bool newInvert = newValue.toBool();
if (m_invertColors != newInvert) {
m_invertColors = newInvert;
if (m_image != nullptr) {
m_image->invertPixels();
}
}
}
SANE_Status KSaneScanThread::saneStatus()
void KSaneScanThread::setPreview(bool isPreview)
{
return m_saneStatus;
m_isPreview = isPreview;
}
KSaneScanThread::ReadStatus KSaneScanThread::frameStatus()
......@@ -43,9 +47,9 @@ KSaneScanThread::ReadStatus KSaneScanThread::frameStatus()
return m_readStatus;
}
void KSaneScanThread::cancelScan()
SANE_Status KSaneScanThread::saneStatus()
{
m_readStatus = READ_CANCEL;
return m_saneStatus;
}
SANE_Parameters KSaneScanThread::saneParameters()
......@@ -53,10 +57,25 @@ SANE_Parameters KSaneScanThread::saneParameters()
return m_params;
}
void KSaneScanThread::lockImage()
{
m_imageMutex.lock();
}
void KSaneScanThread::unlockImage()
{
m_imageMutex.unlock();
}
void KSaneScanThread::cancelScan()
{
m_readStatus = ReadCancel;
}
void KSaneScanThread::run()
{
m_dataSize = 0;
m_readStatus = READ_ON_GOING;
m_readStatus = ReadOngoing;
m_saneStartDone = false;
// Start the scanning with sane_start
......@@ -64,14 +83,14 @@ void KSaneScanThread::run()
m_saneStartDone = true;
if (m_readStatus == READ_CANCEL) {
if (m_readStatus == ReadCancel) {
return;
}
if (m_saneStatus != SANE_STATUS_GOOD) {
qCDebug(KSANE_LOG) << "sane_start=" << sane_strstatus(m_saneStatus);
m_readStatus = READ_ERROR;
// oneFinalScanDone() does the sane_cancel()
sane_cancel(m_saneHandle);
m_readStatus = ReadError;
return;
}
......@@ -79,8 +98,8 @@ void KSaneScanThread::run()
m_saneStatus = sane_get_parameters(m_saneHandle, &m_params);
if (m_saneStatus != SANE_STATUS_GOOD) {
qCDebug(KSANE_LOG) << "sane_get_parameters=" << sane_strstatus(m_saneStatus);
m_readStatus = READ_ERROR;
// oneFinalScanDone() does the sane_cancel()
sane_cancel(m_saneHandle);
m_readStatus = ReadError;
return;
}
......@@ -89,28 +108,33 @@ void KSaneScanThread::run()
if ((m_params.format == SANE_FRAME_RED) ||
(m_params.format == SANE_FRAME_GREEN) ||
(m_params.format == SANE_FRAME_BLUE)) {
// this is unfortunately calculated again for every frame....
m_dataSize = m_frameSize * 3;
} else {
m_dataSize = m_frameSize;
}
if (m_isPreview) {
m_imageBuilder.start(m_params);
}
m_frameRead = 0;
m_frame_t_count = 0;
m_data->clear();
if (m_dataSize > 0) {
m_data->reserve(m_dataSize);
}
m_frameRead = 0;
m_frame_t_count = 0;
m_readStatus = READ_ON_GOING;
while (m_readStatus == READ_ON_GOING) {
while (m_readStatus == ReadOngoing) {
readData();
}
}
int KSaneScanThread::scanProgress()
void KSaneScanThread::updateScanProgress()
{
if (m_dataSize == 0) {
return 0;
// handscanners have negative data size
if (m_dataSize <= 0) {
return;
}
int bytesRead;
......@@ -120,7 +144,14 @@ int KSaneScanThread::scanProgress()
} else {
bytesRead = m_frameRead;
}
return (int)(((float)bytesRead * 100.0) / m_dataSize);
Q_EMIT scanProgressUpdated(static_cast<int>((static_cast<float>(bytesRead) * 100.0) / m_dataSize));
}
void KSaneScanThread::finishProgress()
{
m_emitProgressUpdateTimer.stop();
Q_EMIT scanProgressUpdated(100);
}
void KSaneScanThread::readData()
......@@ -134,6 +165,7 @@ void KSaneScanThread::readData()
break;
case SANE_STATUS_EOF:
// (handscanners have negative frame size)
if (m_frameRead < m_frameSize) {
qCDebug(KSANE_LOG) << "frameRead =" << m_frameRead << ", frameSize =" << m_frameSize << "readBytes =" << readBytes;
if ((readBytes > 0) && ((m_frameRead + readBytes) <= m_frameSize)) {
......@@ -146,36 +178,37 @@ void KSaneScanThread::readData()
qCDebug(KSANE_LOG) << "Warning!! Trying to correct the value!";
m_params.bytes_per_line = m_frameRead / m_params.lines;
}
m_readStatus = READ_READY; // It is better to return a broken image than nothing
m_readStatus = ReadReady; // It is better to return a broken image than nothing
return;
}
if (m_params.last_frame == SANE_TRUE) {
// this is where it all ends well :)
m_readStatus = READ_READY;
m_readStatus = ReadReady;
return;
} else {
// start reading next frame
m_saneStatus = sane_start(m_saneHandle);
if (m_saneStatus != SANE_STATUS_GOOD) {
qCDebug(KSANE_LOG) << "sane_start =" << sane_strstatus(m_saneStatus);
m_readStatus = READ_ERROR;
m_readStatus = ReadError;
return;
}
m_saneStatus = sane_get_parameters(m_saneHandle, &m_params);
if (m_saneStatus != SANE_STATUS_GOOD) {
qCDebug(KSANE_LOG) << "sane_get_parameters =" << sane_strstatus(m_saneStatus);
m_readStatus = READ_ERROR;
m_readStatus = ReadError;
sane_cancel(m_saneHandle);
return;
}
//qCDebug(KSANE_LOG) << "New Frame";
m_imageBuilder.beginFrame(m_params);
m_frameRead = 0;
m_frame_t_count++;
break;
}
default:
qCDebug(KSANE_LOG) << "sane_read=" << m_saneStatus << "=" << sane_strstatus(m_saneStatus);
m_readStatus = READ_ERROR;
m_readStatus = ReadError;
sane_cancel(m_saneHandle);
return;
}
......@@ -183,15 +216,6 @@ void KSaneScanThread::readData()
copyToScanData(readBytes);
}
#define index_red8_to_rgb8(i) (i*3)
#define index_red16_to_rgb16(i) ((i/2)*6 + i%2)
#define index_green8_to_rgb8(i) (i*3 + 1)
#define index_green16_to_rgb16(i) ((i/2)*6 + i%2 + 2)
#define index_blue8_to_rgb8(i) (i*3 + 2)
#define index_blue16_to_rgb16(i) ((i/2)*6 + i%2 + 4)
void KSaneScanThread::copyToScanData(int readBytes)
{
if (m_invertColors) {
......@@ -211,16 +235,45 @@ void KSaneScanThread::copyToScanData(int readBytes)
}
}
}
if (m_isPreview) {
copyToPreviewImg(readBytes);
} else {
copyToByteArray(readBytes);
}
}
void KSaneScanThread::copyToPreviewImg(int readBytes)
{
QMutexLocker locker(&m_imageMutex);
if (m_imageBuilder.copyToImage(m_readData, readBytes)) {
m_frameRead += readBytes;
} else {
m_readStatus = ReadError;
}
}
#define index_red8_to_rgb8(i) (i*3)
#define index_red16_to_rgb16(i) ((i/2)*6 + i%2)
#define index_green8_to_rgb8(i) (i*3 + 1)
#define index_green16_to_rgb16(i) ((i/2)*6 + i%2 + 2)
#define index_blue8_to_rgb8(i) (i*3 + 2)
#define index_blue16_to_rgb16(i) ((i/2)*6 + i%2 + 4)
void KSaneScanThread::copyToByteArray(int readBytes)
{
switch (m_params.format) {
case SANE_FRAME_GRAY:
m_data->append((const char *)m_readData, readBytes);
m_data->append(reinterpret_cast<const char *>(m_readData), readBytes);
m_frameRead += readBytes;
return;
case SANE_FRAME_RGB:
if (m_params.depth == 1) {
break;
}
m_data->append((const char *)m_readData, readBytes);
m_data->append(reinterpret_cast<const char *>(m_readData), readBytes);
m_frameRead += readBytes;
return;
......@@ -276,13 +329,18 @@ void KSaneScanThread::copyToScanData(int readBytes)
qCDebug(KSANE_LOG) << "Format" << m_params.format
<< "and depth" << m_params.depth
<< "is not yet supported by libksane!";
m_readStatus = READ_ERROR;
m_readStatus = ReadError;
return;
}
bool KSaneScanThread::saneStartDone()
{
return m_saneStartDone;
return m_saneStartDone;
}
bool KSaneScanThread::imageResized()
{
return m_imageBuilder.imageResized();
}
} // NameSpace KSaneIface
......@@ -2,7 +2,7 @@
*
* SPDX-FileCopyrightText: 2009 Kare Sars <kare dot sars at iki dot fi>
* SPDX-FileCopyrightText: 2014 Gregor Mitsch : port to KDE5 frameworks
*
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*
* ============================================================ */
......@@ -10,6 +10,8 @@
#ifndef KSANE_SCAN_THREAD_H
#define KSANE_SCAN_THREAD_H
#include "ksanepreviewimagebuilder.h"
// Sane includes
extern "C"
{
......@@ -18,7 +20,10 @@ extern "C"
}
#include <QThread>
#include <QMutex>
#include <QByteArray>
#include <QImage>
#include <QTimer>
#define SCAN_READ_CHUNK_SIZE 100000
......@@ -28,41 +33,60 @@ class KSaneScanThread: public QThread