Commit 8eeec9a6 authored by Alexander Stippich's avatar Alexander Stippich
Browse files

directly save image data to a QImage

parent 5e6e06e9
......@@ -39,12 +39,12 @@ KSaneWidget::KSaneWidget(QWidget *parent)
lay->addWidget(openDialog);
setLayout(lay);
connect(openDialog, SIGNAL(clicked()), d, SLOT(ReOpenDialog()));
connect(openDialog, &QPushButton::clicked, d, &KSaneWidgetPrivate::ReOpenDialog);
// Forward signals from the private class
//connect(d, SIGNAL(scanProgress(int)), this, SIGNAL(scanProgress(int)));
connect(d, SIGNAL(ImageReady(QByteArray&,int,int,int,int)),
this, SIGNAL(imageReady(QByteArray&,int,int,int,int)));
connect(d, &KSaneWidgetPrivate::ImageReady, this, &KSaneWidget::imageReady);
connect(d, &KSaneWidgetPrivate::qImageReady, this, &KSaneWidget::scannedImageReady);
//connect(d, SIGNAL(scanDone(int,QString)), this, SIGNAL(scanDone(int,QString)));
}
......@@ -66,7 +66,7 @@ bool KSaneWidget::openDevice(const QString &device_name)
if (!d->OpenSource(device_name)) {
return false;
}
QTimer::singleShot(0, d, SLOT(OpenDialog()));
QTimer::singleShot(0, d, &KSaneWidgetPrivate::OpenDialog);
return true;
}
......
......@@ -598,6 +598,7 @@ void KSaneWidgetPrivate::ImageData(TW_MEMREF pdata, TW_IMAGEINFO &info)
ds.writeRawData(bits, size);
Q_EMIT ImageReady(baBmp, 0, 0, 0, (int)KSaneWidget::FormatBMP);
Q_EMIT qImageReady(QImage::fromData(data, "BMP"));
GlobalUnlock(hDIB);
......
......@@ -52,6 +52,7 @@ public Q_SLOTS:
Q_SIGNALS:
void ImageReady(QByteArray &data, int width, int height, int bytes_per_line, int format);
void qImageReady(const QImage &image);
private:
......
......@@ -25,7 +25,7 @@ target_sources(KF5Sane PRIVATE
ksanefinddevicesthread.cpp
ksanewidget.cpp
ksanescanthread.cpp
ksanepreviewimagebuilder.cpp
ksaneimagebuilder.cpp
ksanewidget_p.cpp
splittercollapser.cpp
ksaneauth.cpp
......
/* ============================================================
*
* SPDX-FileCopyrightText: 2009 Kare Sars <kare dot sars at iki dot fi>
* SPDX-FileCopyrightText: 2018 Alexander Volkov <a.volkov@rusbitech.ru>
* SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*
* ============================================================ */
#include "ksaneimagebuilder.h"
#include <QImage>
#include <ksane_debug.h>
namespace KSaneIface
{
KSaneImageBuilder::KSaneImageBuilder(QImage *image, int *dpi)
: m_image(image), m_dpi(dpi)
{
m_pixelData[0] = 0;
m_pixelData[1] = 0;
m_pixelData[2] = 0;
m_pixelData[3] = 0;
m_pixelData[4] = 0;
m_pixelData[5] = 0;
}
void KSaneImageBuilder::start(const SANE_Parameters &params)
{
beginFrame(params);
QImage::Format imageFormat = QImage::Format_RGB32;
if (m_params.format == SANE_FRAME_GRAY) {
switch (m_params.depth) {
case 1:
imageFormat = QImage::Format_Mono;
break;
case 16:
imageFormat = QImage::Format_Grayscale16;
break;
default:
imageFormat = QImage::Format_Grayscale8;
break;
}
} else if (m_params.depth > 8) {
imageFormat = QImage::Format_RGBX64;
}
// create a new image if necessary
if ((m_image->height() != m_params.lines) ||
(m_image->width() != m_params.pixels_per_line) || m_image->format() != imageFormat) {
// just hope that the frame size is not changed between different frames of the same image.
int pixelLines = m_params.lines;
// handscanners have the number of lines -1 -> make room for something
if (m_params.lines <= 0) {
pixelLines = m_params.pixels_per_line;
}
*m_image = QImage(m_params.pixels_per_line, pixelLines, imageFormat);
m_image->fill(0xFFFFFFFF);
if (m_image->format() == QImage::Format_Mono) {
m_image->setColorTable(QVector<QRgb>({0xFFFFFFFF,0xFF000000}));
}
int dpm = *m_dpi * (1000.0 / 25.4);
m_image->setDotsPerMeterX(dpm);
m_image->setDotsPerMeterY(dpm);
}
m_imageResized = false;
}
void KSaneImageBuilder::beginFrame(const SANE_Parameters &params)
{
m_params = params;
m_frameRead = 0;
m_pixelX = 0;
m_pixelY = 0;
m_pixelDataIndex = 0;
}
bool KSaneImageBuilder::copyToImage(const SANE_Byte readData[], int read_bytes)
{
switch (m_params.format) {
case SANE_FRAME_GRAY:
if (m_params.depth == 1) {
for (int i = 0; i < read_bytes; i++) {
if (m_pixelY >= m_image->height()) {
renewImage();
}
uchar *imageBits = m_image->scanLine(m_pixelY);
imageBits[m_pixelX / 8] = readData[i];
m_pixelX += 8;
if (m_pixelX >= m_params.pixels_per_line) {
m_pixelX = 0;
m_pixelY++;
}
m_frameRead++;
}
return true;
} else if (m_params.depth == 8) {
for (int i = 0; i < read_bytes; i++) {
uchar *grayScale = m_image->scanLine(m_pixelY);
grayScale[m_pixelX] = readData[i];
incrementPixelData();
m_frameRead++;
}
return true;
} else if (m_params.depth == 16) {
for (int i = 0; i < read_bytes; i++) {
m_pixelData[m_pixelDataIndex] = readData[i];
m_pixelDataIndex++;
if (m_pixelDataIndex == 2) {
m_pixelDataIndex = 0;
}
if (m_pixelDataIndex == 0) {
if (m_pixelY >= m_image->height()) {
renewImage();
}
quint16 *grayScale = reinterpret_cast<quint16*>(m_image->scanLine(m_pixelY));
grayScale[m_pixelX] = m_pixelData[0] + (m_pixelData[1] << 8);
incrementPixelData();
}
m_frameRead++;
}
return true;
}
break;
case SANE_FRAME_RGB:
if (m_params.depth == 8) {
for (int i = 0; i < read_bytes; i++) {
m_pixelData[m_pixelDataIndex] = readData[i];
m_pixelDataIndex++;
if (m_pixelDataIndex == 3) {
m_pixelDataIndex = 0;
}
if (m_pixelDataIndex == 0) {
if (m_pixelY >= m_image->height()) {
renewImage();
}
QRgb *rgbData = reinterpret_cast<QRgb*>(m_image->scanLine(m_pixelY));
rgbData[m_pixelX] = qRgb(m_pixelData[0], m_pixelData[1], m_pixelData[2]);
incrementPixelData();
}
m_frameRead++;
}
return true;
} else if (m_params.depth == 16) {
for (int i = 0; i < read_bytes; i++) {
m_pixelData[m_pixelDataIndex] = readData[i];
m_pixelDataIndex++;
if (m_pixelDataIndex == 6) {
m_pixelDataIndex = 0;
}
if (m_pixelDataIndex == 0) {
if (m_pixelY >= m_image->height()) {
renewImage();
}
QRgba64 *rgbData = reinterpret_cast<QRgba64*>(m_image->scanLine(m_pixelY));
rgbData[m_pixelX] = QRgba64::fromRgba64((m_pixelData[0] + (m_pixelData[1] << 8)),
(m_pixelData[2] + (m_pixelData[3] << 8)), (m_pixelData[4] + (m_pixelData[5] << 8)), 0xFFFF);
incrementPixelData();
}
}
m_frameRead++;
return true;
}
break;
case SANE_FRAME_RED: {
uchar *imgBits = m_image->bits();
int index = 0;
if (m_params.depth == 8) {
for (int i = 0; i < read_bytes; i++) {
index = m_frameRead * 4 + 2;
if (index > m_image->sizeInBytes()) {
renewImage();
}
imgBits[index] = readData[i];
m_frameRead++;
}
return true;
} else if (m_params.depth == 16) {
for (int i = 0; i < read_bytes; i++) {
index = (m_frameRead - m_frameRead % 2) * 4 + m_frameRead % 2;
if (index > m_image->sizeInBytes()) {
renewImage();
}
imgBits[index] = readData[i];
m_frameRead++;
}
return true;
}
break;
}
case SANE_FRAME_GREEN: {
uchar *imgBits = m_image->bits();
int index = 0;
if (m_params.depth == 8) {
for (int i = 0; i < read_bytes; i++) {
int index = m_frameRead * 4 + 1;
if (index > m_image->sizeInBytes()) {
renewImage();
}
imgBits[index] = readData[i];
m_frameRead++;
}
return true;
} else if (m_params.depth == 16) {
for (int i = 0; i < read_bytes; i++) {
index = (m_frameRead - m_frameRead % 2) * 4 + 2 + m_frameRead % 2;
if (index > m_image->sizeInBytes()) {
renewImage();
}
imgBits[index] = readData[i];
m_frameRead++;
}
return true;
}
break;
}
case SANE_FRAME_BLUE: {
uchar *imgBits = m_image->bits();
int index = 0;
if (m_params.depth == 8) {
for (int i = 0; i < read_bytes; i++) {
int index = m_frameRead * 4;
if (index > m_image->sizeInBytes()) {
renewImage();
}
imgBits[index] = readData[i];
m_frameRead++;
}
return true;
} else if (m_params.depth == 16) {
for (int i = 0; i < read_bytes; i++) {
index = (m_frameRead - m_frameRead % 2) * 4 + 4 + m_frameRead % 2;
if (index > m_image->sizeInBytes()) {
renewImage();
}
imgBits[index] = readData[i];
m_frameRead++;
}
return true;
}
break;
}
}
qCWarning(KSANE_LOG) << "Format" << m_params.format
<< "and depth" << m_params.depth
<< "is not yet supported by libksane!";
return false;
}
bool KSaneImageBuilder::imageResized()
{
if (m_imageResized) {
m_imageResized = false;
return true;
}
return false;
}
void KSaneImageBuilder::renewImage()
{
// resize the image
*m_image = m_image->copy(0, 0, m_image->width(), m_image->height() + m_image->width());
m_imageResized = true;
}
void KSaneImageBuilder::incrementPixelData()
{
m_pixelX++;
if (m_pixelX >= m_params.pixels_per_line) {
m_pixelY++;
m_pixelX=0;
}
}
} // NameSpace KSaneIface
......@@ -2,13 +2,14 @@
*
* SPDX-FileCopyrightText: 2009 Kare Sars <kare dot sars at iki dot fi>
* SPDX-FileCopyrightText: 2018 Alexander Volkov <a.volkov@rusbitech.ru>
* SPDX-FileCopyrightText: 2021 Alexander Stippich <a.stippich@gmx.net>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*
* ============================================================ */
#ifndef KSANE_PREVIEW_IMAGE_BUILDER_H
#define KSANE_PREVIEW_IMAGE_BUILDER_H
#ifndef KSANE_IMAGE_BUILDER_H
#define KSANE_IMAGE_BUILDER_H
extern "C"
{
......@@ -19,30 +20,35 @@ class QImage;
namespace KSaneIface
{
class KSanePreviewImageBuilder
/* Constructs a QImage out of the raw scanned data retrieved via libsane */
class KSaneImageBuilder
{
public:
KSanePreviewImageBuilder(QImage *img);
KSaneImageBuilder(QImage *image, int *dpi);
void start(const SANE_Parameters &params);
void beginFrame(const SANE_Parameters &params);
bool copyToImage(const SANE_Byte readData[], int read_bytes);
bool imageResized();
void setDPI(int dpi);
private:
void renewImage();
void incrementPixelData();
SANE_Parameters m_params;
int m_frameRead;
int m_pixel_x;
int m_pixel_y;
int m_px_colors[3];
int m_px_c_index;
int m_frameRead = 0;
int m_pixelX = 0;
int m_pixelY = 0;
int m_pixelData[6];
int m_pixelDataIndex = 0;
QImage *m_img;
QImage *m_image;
int *m_dpi;
bool m_imageResized;
bool m_imageResized = false;
};
}
#endif // KSANE_PREVIEW_IMAGE_BUILDER_H
#endif // KSANE_IMAGE_BUILDER_H
/* ============================================================
*
* SPDX-FileCopyrightText: 2009 Kare Sars <kare dot sars at iki dot fi>
* SPDX-FileCopyrightText: 2018 Alexander Volkov <a.volkov@rusbitech.ru>
*
* SPDX-License-Identifier: LGPL-2.1-only OR LGPL-3.0-only OR LicenseRef-KDE-Accepted-LGPL
*
* ============================================================ */
#include "ksanepreviewimagebuilder.h"
#include <QImage>
#include <ksane_debug.h>
namespace KSaneIface
{
KSanePreviewImageBuilder::KSanePreviewImageBuilder(QImage *img)
: m_frameRead(0),
m_pixel_x(0),
m_pixel_y(0),
m_px_c_index(0),
m_img(img),
m_imageResized(false)
{
m_px_colors[0] = 0;
m_px_colors[1] = 0;
m_px_colors[2] = 0;
}
void KSanePreviewImageBuilder::start(const SANE_Parameters &params)
{
beginFrame(params);
// create a new image if necessary
if ((m_img->height() != m_params.lines) ||
(m_img->width() != m_params.pixels_per_line)) {
// just hope that the frame size is not changed between different frames of the same image.
if (m_params.lines > 0) {
*m_img = QImage(m_params.pixels_per_line, m_params.lines, QImage::Format_RGB32);
} else {
// handscanners have the number of lines -1 -> make room for something
*m_img = QImage(m_params.pixels_per_line, m_params.pixels_per_line, QImage::Format_RGB32);
}
m_img->fill(0xFFFFFFFF);
}
m_imageResized = false;
}
void KSanePreviewImageBuilder::beginFrame(const SANE_Parameters &params)
{
m_params = params;
m_frameRead = 0;
m_pixel_x = 0;
m_pixel_y = 0;
m_px_c_index = 0;
}
#define inc_pixel(x,y,ppl) { x++; if (x>=ppl) { y++; x=0;} }
#define inc_color_index(index) { index++; if (index==3) index=0;}
#define index_red8_to_argb8(i) (i*4 + 2)
#define index_red16_to_argb8(i) (i*2 + 2)
#define index_green8_to_argb8(i) (i*4 + 1)
#define index_green16_to_argb8(i) (i*2 + 1)
#define index_blue8_to_argb8(i) (i*4)
#define index_blue16_to_argb8(i) (i*2)
bool KSanePreviewImageBuilder::copyToImage(const SANE_Byte readData[], int read_bytes)
{
int index;
uchar *imgBits = m_img->bits();
switch (m_params.format) {
case SANE_FRAME_GRAY:
if (m_params.depth == 1) {
int i, j;
for (i = 0; i < read_bytes; i++) {
if (m_pixel_y >= m_img->height()) {
renewImage();
}
for (j = 7; j >= 0; --j) {
if ((readData[i] & (1 << j)) == 0) {
m_img->setPixel(m_pixel_x,
m_pixel_y,
qRgb(255, 255, 255));
} else {
m_img->setPixel(m_pixel_x,
m_pixel_y,
qRgb(0, 0, 0));
}
m_pixel_x++;
if (m_pixel_x >= m_params.pixels_per_line) {
m_pixel_x = 0;
m_pixel_y++;
break;
}
if (m_pixel_y >= m_params.lines) {
break;
}
}
m_frameRead++;
}
return true;
} else if (m_params.depth == 8) {
for (int i = 0; i < read_bytes; i++) {
index = m_frameRead * 4;
if ((index + 2) > m_img->sizeInBytes()) {
renewImage();
imgBits = m_img->bits();
}
imgBits[index ] = readData[i];
imgBits[index + 1] = readData[i];
imgBits[index + 2] = readData[i];
m_frameRead++;
}
return true;
} else if (m_params.depth == 16) {
for (int i = 0; i < read_bytes; i++) {
if (m_frameRead % 2 == 0) {
index = m_frameRead * 2;
if ((index + 2) > m_img->sizeInBytes()) {
renewImage();
imgBits = m_img->bits();
}
imgBits[index ] = readData[i + 1];
imgBits[index + 1] = readData[i + 1];
imgBits[index + 2] = readData[i + 1];
}
m_frameRead++;
}
return true;
}
break;
case SANE_FRAME_RGB:
if (m_params.depth == 8) {
for (int i = 0; i < read_bytes; i++) {
m_px_colors[m_px_c_index] = readData[i];
inc_color_index(m_px_c_index);
m_frameRead++;
if (m_px_c_index == 0) {
if (m_pixel_y >= m_img->height()) {
renewImage();
}
m_img->setPixel(m_pixel_x,
m_pixel_y,
qRgb(m_px_colors[0],
m_px_colors[1],
m_px_colors[2]));
inc_pixel(m_pixel_x, m_pixel_y, m_params.pixels_per_line);
}
}
return true;
} else if (m_params.depth == 16) {
for (int i = 0; i < read_bytes; i++) {
m_frameRead++;
if (m_frameRead % 2 == 0) {
m_px_colors[m_px_c_index] = readData[i];
inc_color_index(m_px_c_index);
if (m_px_c_index == 0) {
if (m_pixel_y >= m_img->height()) {
renewImage();
}
m_img->setPixel(m_pixel_x,
m_pixel_y,
qRgb(m_px_colors[0],
m_px_colors[1],
m_px_colors[2]));
inc_pixel(m_pixel_x, m_pixel_y, m_params.pixels_per_line);
}
}
}
return true;
}
break;
case SANE_FRAME_RED:
if (m_params.depth == 8) {
for (int i = 0; i < read_bytes; i++) {
if (index_red8_to_argb8(m_frameRead) > m_img->sizeInBytes()) {
renewImage();
imgBits = m_img->bits();
}
imgBits[index_red8_to_argb8(m_frameRead)] = readData[i];
m_frameRead++;
}
return true;
} else if (m_params.depth == 16) {
for (int i = 0; i < read_bytes; i++) {
if (m_frameRead % 2 == 0) {
if (index_red16_to_argb8(m_frameRead) > m_img->sizeInBytes()) {
renewImage();
imgBits = m_img->bits();
}
imgBits[index_red16_to_argb8(m_frameRead)] = readData[i + 1];
}
m_frameRead++;
}
return true;
}
break;
case SANE_FRAME_GREEN:
if (m_params.depth == 8) {