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"