File indexing completed on 2024-04-21 03:44:13
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 ¶ms) 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 ¶ms) 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 ¤tElement : 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 }