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"