File indexing completed on 2024-07-21 06:28:26

0001 /*
0002     SPDX-FileCopyrightText: 2017 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include "videowg.h"
0008 #include "collimationoverlaytypes.h"
0009 
0010 #include "kstars_debug.h"
0011 #include "kstarsdata.h"
0012 #include "kstars.h"
0013 
0014 #include <QImageReader>
0015 #include <QMouseEvent>
0016 #include <QResizeEvent>
0017 #include <QRubberBand>
0018 #include <QSqlTableModel>
0019 #include <QSqlRecord>
0020 #include <QtMath>
0021 
0022 VideoWG::VideoWG(QWidget *parent) : QLabel(parent)
0023 {
0024     streamImage.reset(new QImage());
0025 
0026     grayTable.resize(256);
0027 
0028     for (int i = 0; i < 256; i++)
0029         grayTable[i] = qRgb(i, i, i);
0030 }
0031 
0032 bool VideoWG::newBayerFrame(IBLOB *bp, const BayerParams &params)
0033 {
0034     return debayer(bp, params);
0035 }
0036 
0037 bool VideoWG::newFrame(IBLOB *bp)
0038 {
0039     if (bp->size <= 0)
0040         return false;
0041 
0042     bool rc = false;
0043     QString format(bp->format);
0044     if (m_RawFormat != format)
0045     {
0046         format.remove('.');
0047         format.remove("stream_");
0048         m_RawFormatSupported = QImageReader::supportedImageFormats().contains(format.toLatin1());
0049         m_RawFormat = format;
0050     }
0051 
0052     if (m_RawFormatSupported)
0053         rc = streamImage->loadFromData(static_cast<uchar *>(bp->blob), bp->size);
0054     else if (static_cast<uint32_t>(bp->size) == totalBaseCount)
0055     {
0056         streamImage.reset(new QImage(static_cast<uchar *>(bp->blob), streamW, streamH, QImage::Format_Indexed8));
0057         streamImage->setColorTable(grayTable);
0058         rc = !streamImage->isNull();
0059     }
0060     else if (static_cast<uint32_t>(bp->size) == totalBaseCount * 3)
0061     {
0062         streamImage.reset(new QImage(static_cast<uchar *>(bp->blob), streamW, streamH, QImage::Format_RGB888));
0063         rc = !streamImage->isNull();
0064     }
0065 
0066     if (rc)
0067     {
0068         kPix = QPixmap::fromImage(streamImage->scaled(size(), Qt::KeepAspectRatio));
0069 
0070         paintOverlay(kPix);
0071 
0072         setPixmap(kPix);
0073     }
0074 
0075     emit imageChanged(streamImage);
0076 
0077     return rc;
0078 }
0079 
0080 bool VideoWG::save(const QString &filename, const char *format)
0081 {
0082     return kPix.save(filename, format);
0083 }
0084 
0085 void VideoWG::setSize(uint16_t w, uint16_t h)
0086 {
0087     streamW        = w;
0088     streamH        = h;
0089     totalBaseCount = w * h;
0090 }
0091 
0092 //void VideoWG::resizeEvent(QResizeEvent *ev)
0093 //{
0094 //    setPixmap(QPixmap::fromImage(streamImage->scaled(ev->size(), Qt::KeepAspectRatio)));
0095 //    ev->accept();
0096 //}
0097 
0098 void VideoWG::mousePressEvent(QMouseEvent *event)
0099 {
0100     origin = event->pos();
0101     if (!rubberBand)
0102         rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
0103     rubberBand->setGeometry(QRect(origin, QSize()));
0104     rubberBand->show();
0105 }
0106 
0107 void VideoWG::mouseMoveEvent(QMouseEvent *event)
0108 {
0109     rubberBand->setGeometry(QRect(origin, event->pos()).normalized());
0110 }
0111 
0112 void VideoWG::mouseReleaseEvent(QMouseEvent *event)
0113 {
0114     rubberBand->hide();
0115 
0116     if (event->button() == Qt::RightButton)
0117     {
0118         emit newSelection(QRect());
0119         return;
0120     }
0121 
0122     QRect rawSelection = rubberBand->geometry();
0123     int pixmapX        = (width() - kPix.width()) / 2;
0124     int pixmapY        = (height() - kPix.height()) / 2;
0125 
0126     QRect finalSelection;
0127 
0128     double scaleX = static_cast<double>(streamImage->width()) / kPix.width();
0129     double scaleY = static_cast<double>(streamImage->height()) / kPix.height();
0130 
0131     finalSelection.setX((rawSelection.x() - pixmapX) * scaleX);
0132     finalSelection.setY((rawSelection.y() - pixmapY) * scaleY);
0133     finalSelection.setWidth(rawSelection.width() * scaleX);
0134     finalSelection.setHeight(rawSelection.height() * scaleY);
0135 
0136     emit newSelection(finalSelection);
0137     // determine selection, for example using QRect::intersects()
0138     // and QRect::contains().
0139 }
0140 
0141 bool VideoWG::debayer(const IBLOB *bp, const BayerParams &params)
0142 {
0143     uint32_t rgb_size = streamW * streamH * 3;
0144     auto * destinationBuffer = new uint8_t[rgb_size];
0145 
0146     if (destinationBuffer == nullptr)
0147     {
0148         qCCritical(KSTARS) << "Unable to allocate memory for temporary bayer buffer.";
0149         return false;
0150     }
0151 
0152     int ds1394_height = streamH;
0153 
0154     uint8_t * dc1394_source = reinterpret_cast<uint8_t*>(bp->blob);
0155     if (params.offsetY == 1)
0156     {
0157         dc1394_source += streamW;
0158         ds1394_height--;
0159     }
0160     if (params.offsetX == 1)
0161     {
0162         dc1394_source++;
0163     }
0164     dc1394error_t error_code = dc1394_bayer_decoding_8bit(dc1394_source, destinationBuffer, streamW, ds1394_height,
0165                                params.filter, params.method);
0166 
0167     if (error_code != DC1394_SUCCESS)
0168     {
0169         qCCritical(KSTARS) << "Debayer failed" << error_code;
0170         delete[] destinationBuffer;
0171         return false;
0172     }
0173 
0174     streamImage.reset(new QImage(destinationBuffer, streamW, streamH, QImage::Format_RGB888));
0175     bool rc = !streamImage->isNull();
0176 
0177     if (rc)
0178     {
0179         kPix = QPixmap::fromImage(streamImage->scaled(size(), Qt::KeepAspectRatio));
0180 
0181         paintOverlay(kPix);
0182 
0183         setPixmap(kPix);
0184     }
0185 
0186     emit imageChanged(streamImage);
0187 
0188     delete[] destinationBuffer;
0189     return rc;
0190 }
0191 
0192 void VideoWG::paintOverlay(QPixmap &imagePix)
0193 {
0194     if (!overlayEnabled || m_EnabledOverlayElements.count() == 0) return;
0195 
0196     // Anchor - default to center of image
0197     QPointF m_anchor (static_cast<float>(kPix.width() / 2), static_cast<float>(kPix.height()/2));
0198     scale = (static_cast<float>(kPix.width()) / static_cast<float>(streamW));
0199 
0200     // Apply any offset from (only) the first enabled anchor element
0201     bool foundAnchor = false;
0202     for (auto &oneElement : m_EnabledOverlayElements) {
0203         if (oneElement["Type"] == "Anchor" && !foundAnchor) {
0204             m_anchor.setX(m_anchor.x() + oneElement["OffsetX"].toInt());
0205             m_anchor.setY(m_anchor.y() + oneElement["OffsetY"].toInt());
0206             foundAnchor = true;
0207         }
0208     }
0209 
0210     painter->begin(&imagePix);
0211     painter->translate(m_anchor);
0212     painter->scale(scale, scale);
0213 
0214     for (auto &currentElement : m_EnabledOverlayElements) {
0215 
0216         painter->save();
0217         QPen m_pen = QPen(QColor(currentElement["Colour"].toString()));
0218         m_pen.setWidth(currentElement["Thickness"].toUInt());
0219         m_pen.setCapStyle(Qt::FlatCap);
0220         m_pen.setJoinStyle(Qt::MiterJoin);
0221         painter->setPen(m_pen);
0222         painter->translate(currentElement["OffsetX"].toFloat(), currentElement["OffsetY"].toFloat());
0223 
0224         int m_count = currentElement["Count"].toUInt();
0225         float m_pcd = currentElement["PCD"].toFloat();
0226 
0227         if (m_count == 1) {
0228             PaintOneItem(currentElement["Type"].toString(), QPointF(0.0, 0.0), currentElement["SizeX"].toUInt(), currentElement["SizeY"].toUInt(), currentElement["Thickness"].toUInt());
0229         } else if (m_count > 1) {
0230             float slice = 360 / m_count;
0231             for (int i = 0; i < m_count; i++) {
0232                 painter->save();
0233                 painter->rotate((slice * i) + currentElement["Rotation"].toFloat());
0234                 PaintOneItem(currentElement["Type"].toString(), QPointF((m_pcd / 2), 0.0), currentElement["SizeX"].toUInt(), currentElement["SizeY"].toUInt(), currentElement["Thickness"].toUInt());
0235                 painter->restore();
0236             }
0237         }
0238 
0239         painter->restore();
0240      }
0241     painter->end();
0242 }
0243 
0244 void VideoWG::setupOverlay ()
0245 {
0246     if (overlayEnabled) {
0247         initOverlayModel();
0248 
0249         typeValues = new QStringList;
0250         collimationoverlaytype m_types;
0251         const QMetaObject *m_metaobject = m_types.metaObject();
0252         QMetaEnum m_metaEnum = m_metaobject->enumerator(m_metaobject->indexOfEnumerator("Types"));
0253         for (int i = 0; i < m_metaEnum.keyCount(); i++) {
0254             *typeValues << tr(m_metaEnum.key(i));
0255         }
0256 
0257         painter = new QPainter;
0258     }
0259 }
0260 
0261 void VideoWG::initOverlayModel()
0262 {
0263     m_CollimationOverlayElements.clear();
0264     auto userdb = QSqlDatabase::database(KStarsData::Instance()->userdb()->connectionName());
0265     m_CollimationOverlayElementsModel = new QSqlTableModel(this, userdb);
0266     modelChanged();
0267 }
0268 
0269 void VideoWG::modelChanged()
0270 {
0271     m_CollimationOverlayElements.clear();
0272     m_EnabledOverlayElements.clear();
0273     KStars::Instance()->data()->userdb()->GetCollimationOverlayElements(m_CollimationOverlayElements);
0274     for (auto &oneElement : m_CollimationOverlayElements)
0275         if (oneElement["Enabled"] == Qt::Checked)
0276             m_EnabledOverlayElements.append(oneElement);
0277 }
0278 
0279 void VideoWG::PaintOneItem (QString type, QPointF position, int sizeX, int sizeY, int thickness)
0280 {
0281     float m_sizeX = sizeX - (thickness / 2);
0282     float m_sizeY = sizeY - (thickness / 2);
0283 
0284     switch (typeValues->indexOf(type)) {
0285 case 0: // Anchor - ignore as we're not drawing it
0286     break;
0287 
0288 case 1: // Ellipse
0289     painter->drawEllipse(position, m_sizeX, m_sizeY);
0290     break;
0291 
0292 case 2: // Rectangle
0293 {
0294     QRect m_rect((position.x() - (m_sizeX / 2)), (position.y() - (m_sizeY / 2)), (m_sizeX - (thickness / 2)), (m_sizeY - (thickness / 2)));
0295     painter->drawRect(m_rect);
0296     break;
0297 }
0298 
0299 case 3: // Line
0300     painter->drawLine(position.x(), position.y(), sizeX, sizeY);
0301     break;
0302 
0303 default:
0304     break;
0305     };
0306 }
0307 
0308 void VideoWG::toggleOverlay()
0309 {
0310     if (overlayEnabled == false) {
0311         overlayEnabled = true;
0312         if (m_CollimationOverlayElementsModel == nullptr) {
0313             setupOverlay();
0314         }
0315     } else if (overlayEnabled == true) {
0316         overlayEnabled = false;
0317     }
0318 }
0319 
0320 VideoWG::~VideoWG()
0321 {
0322     delete m_CollimationOverlayElementsModel;
0323     delete m_CurrentElement;
0324     delete typeValues;
0325     delete painter;
0326 }