File indexing completed on 2024-05-12 15:49:09
0001 /* 0002 SPDX-FileCopyrightText: 2022 Volker Krause <vkrause@kde.org> 0003 SPDX-License-Identifier: MIT 0004 */ 0005 0006 #include "config-prison-scanner.h" 0007 #include "format_p.h" 0008 #include "scanresult_p.h" 0009 #include "videoscannerframe_p.h" 0010 #include "videoscannerworker_p.h" 0011 0012 #include <QDebug> 0013 #include <QImage> 0014 #include <QTransform> 0015 0016 #define ZX_USE_UTF8 1 0017 #include <ZXing/ReadBarcode.h> 0018 #include <ZXing/TextUtfEncoding.h> 0019 0020 using namespace Prison; 0021 0022 VideoScannerWorker::VideoScannerWorker(QObject *parent) 0023 : QObject(parent) 0024 { 0025 connect(this, &VideoScannerWorker::scanFrameRequest, this, &VideoScannerWorker::slotScanFrame, Qt::QueuedConnection); 0026 } 0027 0028 void VideoScannerWorker::slotScanFrame(VideoScannerFrame frame) 0029 { 0030 #if ZXING_VERSION < QT_VERSION_CHECK(1, 4, 0) 0031 ZXing::Result zxRes(ZXing::DecodeStatus::FormatError); 0032 #else 0033 ZXing::Result zxRes; 0034 #endif 0035 ZXing::DecodeHints hints; 0036 hints.setFormats(frame.formats() == Format::NoFormat ? ZXing::BarcodeFormats::all() : Format::toZXing(frame.formats())); 0037 0038 frame.map(); 0039 0040 switch (frame.pixelFormat()) { 0041 case QVideoFrame::Format_Invalid: // already checked before we get here 0042 case QVideoFrame::NPixelFormats: // just to silence the unhandled case warning 0043 break; 0044 0045 // formats ZXing can consume directly 0046 case QVideoFrame::Format_ARGB32: 0047 case QVideoFrame::Format_ARGB32_Premultiplied: 0048 case QVideoFrame::Format_RGB32: 0049 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::XRGB, frame.bytesPerLine()}, hints); 0050 break; 0051 case QVideoFrame::Format_RGB24: 0052 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::RGB, frame.bytesPerLine()}, hints); 0053 break; 0054 case QVideoFrame::Format_BGRA32: 0055 case QVideoFrame::Format_BGRA32_Premultiplied: 0056 case QVideoFrame::Format_BGR32: 0057 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::BGRX, frame.bytesPerLine()}, hints); 0058 break; 0059 case QVideoFrame::Format_ABGR32: 0060 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::XBGR, frame.bytesPerLine()}, hints); 0061 break; 0062 case QVideoFrame::Format_BGR24: 0063 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::BGR, frame.bytesPerLine()}, hints); 0064 break; 0065 case QVideoFrame::Format_AYUV444: 0066 case QVideoFrame::Format_AYUV444_Premultiplied: 0067 zxRes = ZXing::ReadBarcode({frame.bits() + 1, frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine(), 4}, hints); 0068 break; 0069 case QVideoFrame::Format_YUV444: 0070 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine(), 3}, hints); 0071 break; 0072 case QVideoFrame::Format_YUV420P: 0073 case QVideoFrame::Format_YUV422P: 0074 case QVideoFrame::Format_YV12: 0075 case QVideoFrame::Format_NV12: 0076 case QVideoFrame::Format_NV21: 0077 case QVideoFrame::Format_IMC1: 0078 case QVideoFrame::Format_IMC2: 0079 case QVideoFrame::Format_IMC3: 0080 case QVideoFrame::Format_IMC4: 0081 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine()}, hints); 0082 break; 0083 case QVideoFrame::Format_UYVY: 0084 zxRes = ZXing::ReadBarcode({frame.bits() + 1, frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine(), 2}, hints); 0085 break; 0086 case QVideoFrame::Format_YUYV: 0087 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine(), 2}, hints); 0088 break; 0089 case QVideoFrame::Format_Y8: 0090 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine()}, hints); 0091 break; 0092 case QVideoFrame::Format_Y16: 0093 zxRes = ZXing::ReadBarcode({frame.bits() + 1, frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine(), 1}, hints); 0094 break; 0095 0096 // formats needing conversion before ZXing can consume them 0097 case QVideoFrame::Format_RGB565: 0098 case QVideoFrame::Format_RGB555: 0099 case QVideoFrame::Format_ARGB8565_Premultiplied: 0100 case QVideoFrame::Format_BGR565: 0101 case QVideoFrame::Format_BGR555: 0102 case QVideoFrame::Format_BGRA5658_Premultiplied: 0103 case QVideoFrame::Format_Jpeg: 0104 case QVideoFrame::Format_CameraRaw: 0105 case QVideoFrame::Format_AdobeDng: 0106 case QVideoFrame::Format_User: 0107 frame.convertToImage(); 0108 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum}, hints); 0109 break; 0110 } 0111 frame.unmap(); 0112 0113 // process scan result 0114 ScanResult scanResult; 0115 if (zxRes.isValid()) { 0116 auto res = ScanResultPrivate::get(scanResult); 0117 0118 #if ZXING_VERSION < QT_VERSION_CHECK(1, 4, 0) 0119 // distinguish between binary and text content 0120 const auto hasWideChars = std::any_of(zxRes.text().begin(), zxRes.text().end(), [](auto c) { 0121 return c > 255; 0122 }); 0123 const auto hasControlChars = std::any_of(zxRes.text().begin(), zxRes.text().end(), [](auto c) { 0124 return c < 0x20 && c != 0x0a && c != 0x0d; 0125 }); 0126 if (hasWideChars || !hasControlChars) { 0127 res->content = QString::fromStdString(ZXing::TextUtfEncoding::ToUtf8(zxRes.text())); 0128 } else { 0129 QByteArray b; 0130 b.resize(zxRes.text().size()); 0131 std::copy(zxRes.text().begin(), zxRes.text().end(), b.begin()); 0132 res->content = b; 0133 } 0134 #else 0135 if (zxRes.contentType() == ZXing::ContentType::Text) { 0136 res->content = QString::fromStdString(zxRes.text()); 0137 } else { 0138 QByteArray b; 0139 b.resize(zxRes.bytes().size()); 0140 std::copy(zxRes.bytes().begin(), zxRes.bytes().end(), b.begin()); 0141 res->content = b; 0142 } 0143 #endif 0144 0145 // determine the bounding rect 0146 // the cooridinates we get from ZXing are a polygon, we need to determine the 0147 // bounding rect manually from its coordinates 0148 const auto p = zxRes.position(); 0149 int x1 = std::numeric_limits<int>::max(); 0150 int y1 = std::numeric_limits<int>::max(); 0151 int x2 = std::numeric_limits<int>::min(); 0152 int y2 = std::numeric_limits<int>::min(); 0153 for (int i = 0; i < 4; ++i) { 0154 x1 = std::min(x1, p[i].x); 0155 y1 = std::min(y1, p[i].y); 0156 x2 = std::max(x2, p[i].x); 0157 y2 = std::max(y2, p[i].y); 0158 } 0159 res->boundingRect = QRect(QPoint(x1, y1), QPoint(x2, y2)); 0160 0161 // apply frame transformations to the bounding rect 0162 if (frame.isVerticallyFlipped()) { 0163 QTransform t; 0164 t.scale(1, -1); 0165 t.translate(0, -frame.height()); 0166 res->boundingRect = t.mapRect(res->boundingRect); 0167 } 0168 0169 res->format = Format::toFormat(zxRes.format()); 0170 } 0171 0172 Q_EMIT result(scanResult); 0173 } 0174 0175 void VideoScannerThread::run() 0176 { 0177 exec(); 0178 } 0179 0180 #include "moc_videoscannerworker_p.cpp"