File indexing completed on 2024-05-12 04:01:32
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 switch (frame.pixelFormat()) { 0040 case QVideoFrameFormat::Format_Invalid: // already checked before we get here 0041 break; 0042 // formats ZXing can consume directly 0043 case QVideoFrameFormat::Format_ARGB8888: 0044 case QVideoFrameFormat::Format_ARGB8888_Premultiplied: 0045 case QVideoFrameFormat::Format_XRGB8888: 0046 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::XRGB, frame.bytesPerLine()}, hints); 0047 break; 0048 case QVideoFrameFormat::Format_BGRA8888: 0049 case QVideoFrameFormat::Format_BGRA8888_Premultiplied: 0050 case QVideoFrameFormat::Format_BGRX8888: 0051 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::BGRX, frame.bytesPerLine()}, hints); 0052 break; 0053 case QVideoFrameFormat::Format_ABGR8888: 0054 case QVideoFrameFormat::Format_XBGR8888: 0055 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::XBGR, frame.bytesPerLine()}, hints); 0056 break; 0057 case QVideoFrameFormat::Format_RGBA8888: 0058 case QVideoFrameFormat::Format_RGBX8888: 0059 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::RGBX, frame.bytesPerLine()}, hints); 0060 break; 0061 case QVideoFrameFormat::Format_AYUV: 0062 case QVideoFrameFormat::Format_AYUV_Premultiplied: 0063 zxRes = ZXing::ReadBarcode({frame.bits() + 1, frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine(), 4}, hints); 0064 break; 0065 case QVideoFrameFormat::Format_YUV420P: 0066 case QVideoFrameFormat::Format_YUV422P: 0067 case QVideoFrameFormat::Format_YV12: 0068 case QVideoFrameFormat::Format_NV12: 0069 case QVideoFrameFormat::Format_NV21: 0070 case QVideoFrameFormat::Format_IMC1: 0071 case QVideoFrameFormat::Format_IMC2: 0072 case QVideoFrameFormat::Format_IMC3: 0073 case QVideoFrameFormat::Format_IMC4: 0074 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine()}, hints); 0075 break; 0076 case QVideoFrameFormat::Format_UYVY: 0077 zxRes = ZXing::ReadBarcode({frame.bits() + 1, frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine(), 2}, hints); 0078 break; 0079 case QVideoFrameFormat::Format_YUYV: 0080 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine(), 2}, hints); 0081 break; 0082 case QVideoFrameFormat::Format_Y8: 0083 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine()}, hints); 0084 break; 0085 case QVideoFrameFormat::Format_Y16: 0086 zxRes = ZXing::ReadBarcode({frame.bits() + 1, frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine(), 1}, hints); 0087 break; 0088 case QVideoFrameFormat::Format_P010: 0089 case QVideoFrameFormat::Format_P016: 0090 case QVideoFrameFormat::Format_YUV420P10: 0091 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine(), 1}, hints); 0092 break; 0093 0094 // formats needing conversion before ZXing can consume them 0095 case QVideoFrameFormat::Format_Jpeg: 0096 case QVideoFrameFormat::Format_SamplerExternalOES: 0097 case QVideoFrameFormat::Format_SamplerRect: 0098 frame.convertToImage(); 0099 zxRes = ZXing::ReadBarcode({frame.bits(), frame.width(), frame.height(), ZXing::ImageFormat::Lum, frame.bytesPerLine()}, hints); 0100 break; 0101 } 0102 frame.unmap(); 0103 0104 // process scan result 0105 ScanResult scanResult; 0106 if (zxRes.isValid()) { 0107 auto res = ScanResultPrivate::get(scanResult); 0108 0109 #if ZXING_VERSION < QT_VERSION_CHECK(1, 4, 0) 0110 // distinguish between binary and text content 0111 const auto hasWideChars = std::any_of(zxRes.text().begin(), zxRes.text().end(), [](auto c) { 0112 return c > 255; 0113 }); 0114 const auto hasControlChars = std::any_of(zxRes.text().begin(), zxRes.text().end(), [](auto c) { 0115 return c < 0x20 && c != 0x0a && c != 0x0d; 0116 }); 0117 if (hasWideChars || !hasControlChars) { 0118 res->content = QString::fromStdString(ZXing::TextUtfEncoding::ToUtf8(zxRes.text())); 0119 } else { 0120 QByteArray b; 0121 b.resize(zxRes.text().size()); 0122 std::copy(zxRes.text().begin(), zxRes.text().end(), b.begin()); 0123 res->content = b; 0124 } 0125 #else 0126 if (zxRes.contentType() == ZXing::ContentType::Text) { 0127 res->content = QString::fromStdString(zxRes.text()); 0128 } else { 0129 QByteArray b; 0130 b.resize(zxRes.bytes().size()); 0131 std::copy(zxRes.bytes().begin(), zxRes.bytes().end(), b.begin()); 0132 res->content = b; 0133 } 0134 #endif 0135 0136 // determine the bounding rect 0137 // the cooridinates we get from ZXing are a polygon, we need to determine the 0138 // bounding rect manually from its coordinates 0139 const auto p = zxRes.position(); 0140 int x1 = std::numeric_limits<int>::max(); 0141 int y1 = std::numeric_limits<int>::max(); 0142 int x2 = std::numeric_limits<int>::min(); 0143 int y2 = std::numeric_limits<int>::min(); 0144 for (int i = 0; i < 4; ++i) { 0145 x1 = std::min(x1, p[i].x); 0146 y1 = std::min(y1, p[i].y); 0147 x2 = std::max(x2, p[i].x); 0148 y2 = std::max(y2, p[i].y); 0149 } 0150 res->boundingRect = QRect(QPoint(x1, y1), QPoint(x2, y2)); 0151 0152 // apply frame transformations to the bounding rect 0153 if (frame.isVerticallyFlipped()) { 0154 QTransform t; 0155 t.scale(1, -1); 0156 t.translate(0, -frame.height()); 0157 res->boundingRect = t.mapRect(res->boundingRect); 0158 } 0159 0160 res->format = Format::toFormat(zxRes.format()); 0161 } 0162 0163 Q_EMIT result(scanResult); 0164 } 0165 0166 void VideoScannerThread::run() 0167 { 0168 exec(); 0169 } 0170 0171 #include "moc_videoscannerworker_p.cpp"