diff --git a/src/ksanepreviewimagebuilder.cpp b/src/ksanepreviewimagebuilder.cpp index a5e8a45..000883c 100644 --- a/src/ksanepreviewimagebuilder.cpp +++ b/src/ksanepreviewimagebuilder.cpp @@ -1,311 +1,311 @@ /* ============================================================ * * This file is part of the KDE project * * Description : Sane interface for KDE * * Copyright (C) 2009 by Kare Sars * Copyright (C) 2018 by Alexander Volkov * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * ============================================================ */ #include "ksanepreviewimagebuilder.h" #include #include 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 ¶ms) { 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 ¶ms) { 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()) { // resize the image *m_img = m_img->copy(0, 0, m_img->width(), m_img->height() + m_img->width()); m_imageResized = true; } 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->byteCount()) { // resize the image *m_img = m_img->copy(0, 0, m_img->width(), m_img->height() + m_img->width()); imgBits = m_img->bits(); m_imageResized = true; } 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->byteCount()) { // resize the image *m_img = m_img->copy(0, 0, m_img->width(), m_img->height() + m_img->width()); imgBits = m_img->bits(); m_imageResized = true; } 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()) { // resize the image *m_img = m_img->copy(0, 0, m_img->width(), m_img->height() + m_img->width()); m_imageResized = true; } 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()) { // resize the image *m_img = m_img->copy(0, 0, m_img->width(), m_img->height() + m_img->width()); m_imageResized = true; } 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->byteCount()) { // resize the image *m_img = m_img->copy(0, 0, m_img->width(), m_img->height() + m_img->width()); imgBits = m_img->bits(); m_imageResized = true; } 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->byteCount()) { // resize the image *m_img = m_img->copy(0, 0, m_img->width(), m_img->height() + m_img->width()); imgBits = m_img->bits(); m_imageResized = true; } imgBits[index_red16_to_argb8(m_frameRead)] = readData[i + 1]; } m_frameRead++; } return true; } break; case SANE_FRAME_GREEN: if (m_params.depth == 8) { for (int i = 0; i < read_bytes; i++) { if (index_green8_to_argb8(m_frameRead) > m_img->byteCount()) { // resize the image *m_img = m_img->copy(0, 0, m_img->width(), m_img->height() + m_img->width()); imgBits = m_img->bits(); m_imageResized = true; } imgBits[index_green8_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_green16_to_argb8(m_frameRead) > m_img->byteCount()) { // resize the image *m_img = m_img->copy(0, 0, m_img->width(), m_img->height() + m_img->width()); imgBits = m_img->bits(); m_imageResized = true; } imgBits[index_green16_to_argb8(m_frameRead)] = readData[i + 1]; } m_frameRead++; } return true; } break; case SANE_FRAME_BLUE: if (m_params.depth == 8) { for (int i = 0; i < read_bytes; i++) { if (index_blue8_to_argb8(m_frameRead) > m_img->byteCount()) { // resize the image *m_img = m_img->copy(0, 0, m_img->width(), m_img->height() + m_img->width()); imgBits = m_img->bits(); m_imageResized = true; } imgBits[index_blue8_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_blue16_to_argb8(m_frameRead) > m_img->byteCount()) { // resize the image *m_img = m_img->copy(0, 0, m_img->width(), m_img->height() + m_img->width()); imgBits = m_img->bits(); m_imageResized = true; } imgBits[index_blue16_to_argb8(m_frameRead)] = readData[i + 1]; } m_frameRead++; } return true; } break; } qWarning() << "Format" << m_params.format - << "and depth" << m_params.format + << "and depth" << m_params.depth << "is not yet supported by libksane!"; return false; } bool KSanePreviewImageBuilder::imageResized() { if (m_imageResized) { m_imageResized = false; return true; } return false; } } // NameSpace KSaneIface diff --git a/src/ksanescanthread.cpp b/src/ksanescanthread.cpp index 9347e44..defd496 100644 --- a/src/ksanescanthread.cpp +++ b/src/ksanescanthread.cpp @@ -1,307 +1,307 @@ /* ============================================================ * * This file is part of the KDE project * * Date : 2009-11-13 * Description : Sane interface for KDE * * Copyright (C) 2009 by Kare Sars * Copyright (C) 2014 by Gregor Mitsch: port to KDE5 frameworks * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) version 3, or any * later version accepted by the membership of KDE e.V. (or its * successor approved by the membership of KDE e.V.), which shall * act as a proxy defined in Section 6 of version 3 of the license. * * This library is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this program. If not, see . * * ============================================================ */ #include "ksanescanthread.h" #include namespace KSaneIface { 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_invertColors(false), m_saneStartDone(false) {} void KSaneScanThread::setImageInverted(bool inverted) { m_invertColors = inverted; } SANE_Status KSaneScanThread::saneStatus() { return m_saneStatus; } KSaneScanThread::ReadStatus KSaneScanThread::frameStatus() { return m_readStatus; } void KSaneScanThread::cancelScan() { m_readStatus = READ_CANCEL; } int KSaneScanThread::scanProgress() { 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); } SANE_Parameters KSaneScanThread::saneParameters() { return m_params; } void KSaneScanThread::run() { m_dataSize = 0; m_readStatus = READ_ON_GOING; m_saneStartDone = false; // Start the scanning with sane_start m_saneStatus = sane_start(m_saneHandle); m_saneStartDone = true; if (m_readStatus == READ_CANCEL) { return; } if (m_saneStatus != SANE_STATUS_GOOD) { qDebug() << "sane_start=" << sane_strstatus(m_saneStatus); m_readStatus = READ_ERROR; // oneFinalScanDone() does the sane_cancel() return; } // Read image parameters m_saneStatus = sane_get_parameters(m_saneHandle, &m_params); if (m_saneStatus != SANE_STATUS_GOOD) { qDebug() << "sane_get_parameters=" << sane_strstatus(m_saneStatus); m_readStatus = READ_ERROR; // oneFinalScanDone() does the sane_cancel() 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)) { m_dataSize = m_frameSize * 3; } else { m_dataSize = m_frameSize; } 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) { readData(); } } void KSaneScanThread::readData() { SANE_Int readBytes = 0; m_saneStatus = sane_read(m_saneHandle, m_readData, SCAN_READ_CHUNK_SIZE, &readBytes); switch (m_saneStatus) { case SANE_STATUS_GOOD: // continue to parsing the data break; case SANE_STATUS_EOF: if (m_frameRead < m_frameSize) { qDebug() << "frameRead =" << m_frameRead << ", frameSize =" << m_frameSize << "readBytes =" << readBytes; if ((readBytes > 0) && ((m_frameRead + readBytes) <= m_frameSize)) { qDebug() << "This is not a standard compliant backend"; copyToScanData(readBytes); } // There are broken backends that return wrong number for bytes_per_line if (m_params.depth == 1 && m_params.lines > 0 && m_params.lines * m_params.pixels_per_line <= m_frameRead * 8) { qDebug() << "Warning!! This backend seems to return wrong bytes_per_line for line-art images!"; qDebug() << "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 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 m_saneStatus = sane_start(m_saneHandle); if (m_saneStatus != SANE_STATUS_GOOD) { qDebug() << "sane_start =" << sane_strstatus(m_saneStatus); m_readStatus = READ_ERROR; return; } m_saneStatus = sane_get_parameters(m_saneHandle, &m_params); if (m_saneStatus != SANE_STATUS_GOOD) { qDebug() << "sane_get_parameters =" << sane_strstatus(m_saneStatus); m_readStatus = READ_ERROR; sane_cancel(m_saneHandle); return; } //qDebug() << "New Frame"; m_frameRead = 0; m_frame_t_count++; break; } default: qDebug() << "sane_read=" << m_saneStatus << "=" << sane_strstatus(m_saneStatus); m_readStatus = READ_ERROR; sane_cancel(m_saneHandle); return; } 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) { if (m_params.depth == 16) { //if (readBytes%2) qDebug() << "readBytes=" << readBytes; quint16 *u16ptr = reinterpret_cast(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]; } } } switch (m_params.format) { case SANE_FRAME_GRAY: m_data->append((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_frameRead += readBytes; return; case SANE_FRAME_RED: if (m_params.depth == 8) { for (int i = 0; i < readBytes; i++) { (*m_data)[index_red8_to_rgb8(m_frameRead)] = m_readData[i]; m_frameRead++; } return; } else if (m_params.depth == 16) { for (int i = 0; i < readBytes; i++) { (*m_data)[index_red16_to_rgb16(m_frameRead)] = m_readData[i]; m_frameRead++; } return; } break; case SANE_FRAME_GREEN: if (m_params.depth == 8) { for (int i = 0; i < readBytes; i++) { (*m_data)[index_green8_to_rgb8(m_frameRead)] = m_readData[i]; m_frameRead++; } return; } else if (m_params.depth == 16) { for (int i = 0; i < readBytes; i++) { (*m_data)[index_green16_to_rgb16(m_frameRead)] = m_readData[i]; m_frameRead++; } return; } break; case SANE_FRAME_BLUE: if (m_params.depth == 8) { for (int i = 0; i < readBytes; i++) { (*m_data)[index_blue8_to_rgb8(m_frameRead)] = m_readData[i]; m_frameRead++; } return; } else if (m_params.depth == 16) { for (int i = 0; i < readBytes; i++) { (*m_data)[index_blue16_to_rgb16(m_frameRead)] = m_readData[i]; m_frameRead++; } return; } break; } qDebug() << "Format" << m_params.format - << "and depth" << m_params.format + << "and depth" << m_params.depth << "is not yet suppoeted by libksane!"; m_readStatus = READ_ERROR; return; } bool KSaneScanThread::saneStartDone() { return m_saneStartDone; } } // NameSpace KSaneIface