Kstars

videowg.cpp
1/*
2 SPDX-FileCopyrightText: 2017 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5*/
6
7#include "videowg.h"
8#include "collimationoverlaytypes.h"
9
10#include "kstars_debug.h"
11#include "kstarsdata.h"
12#include "kstars.h"
13
14#include <QImageReader>
15#include <QMouseEvent>
16#include <QResizeEvent>
17#include <QRubberBand>
18#include <QSqlTableModel>
19#include <QSqlRecord>
20#include <QtMath>
21
22VideoWG::VideoWG(QWidget *parent) : QLabel(parent)
23{
24 streamImage.reset(new QImage());
25
26 grayTable.resize(256);
27
28 for (int i = 0; i < 256; i++)
29 grayTable[i] = qRgb(i, i, i);
30}
31
32bool VideoWG::newBayerFrame(IBLOB *bp, const BayerParams &params)
33{
34 return debayer(bp, params);
35}
36
37bool VideoWG::newFrame(IBLOB *bp)
38{
39 if (bp->size <= 0)
40 return false;
41
42 bool rc = false;
43 QString format(bp->format);
44 if (m_RawFormat != format)
45 {
46 format.remove('.');
47 format.remove("stream_");
48 m_RawFormatSupported = QImageReader::supportedImageFormats().contains(format.toLatin1());
49 m_RawFormat = format;
50 }
51
52 if (m_RawFormatSupported)
53 rc = streamImage->loadFromData(static_cast<uchar *>(bp->blob), bp->size);
54 else if (static_cast<uint32_t>(bp->size) == totalBaseCount)
55 {
56 streamImage.reset(new QImage(static_cast<uchar *>(bp->blob), streamW, streamH, QImage::Format_Indexed8));
57 streamImage->setColorTable(grayTable);
58 rc = !streamImage->isNull();
59 }
60 else if (static_cast<uint32_t>(bp->size) == totalBaseCount * 3)
61 {
62 streamImage.reset(new QImage(static_cast<uchar *>(bp->blob), streamW, streamH, QImage::Format_RGB888));
63 rc = !streamImage->isNull();
64 }
65
66 if (rc)
67 {
68 kPix = QPixmap::fromImage(streamImage->scaled(size(), Qt::KeepAspectRatio));
69
70 paintOverlay(kPix);
71
72 setPixmap(kPix);
73 }
74
75 emit imageChanged(streamImage);
76
77 return rc;
78}
79
80bool VideoWG::save(const QString &filename, const char *format)
81{
82 return kPix.save(filename, format);
83}
84
85void VideoWG::setSize(uint16_t w, uint16_t h)
86{
87 streamW = w;
88 streamH = h;
89 totalBaseCount = w * h;
90}
91
92//void VideoWG::resizeEvent(QResizeEvent *ev)
93//{
94// setPixmap(QPixmap::fromImage(streamImage->scaled(ev->size(), Qt::KeepAspectRatio)));
95// ev->accept();
96//}
97
98void VideoWG::mousePressEvent(QMouseEvent *event)
99{
100 origin = event->pos();
101 if (!rubberBand)
102 rubberBand = new QRubberBand(QRubberBand::Rectangle, this);
103 rubberBand->setGeometry(QRect(origin, QSize()));
104 rubberBand->show();
105}
106
107void VideoWG::mouseMoveEvent(QMouseEvent *event)
108{
109 rubberBand->setGeometry(QRect(origin, event->pos()).normalized());
110}
111
112void VideoWG::mouseReleaseEvent(QMouseEvent *event)
113{
114 rubberBand->hide();
115
116 if (event->button() == Qt::RightButton)
117 {
118 emit newSelection(QRect());
119 return;
120 }
121
122 QRect rawSelection = rubberBand->geometry();
123 int pixmapX = (width() - kPix.width()) / 2;
124 int pixmapY = (height() - kPix.height()) / 2;
125
126 QRect finalSelection;
127
128 double scaleX = static_cast<double>(streamImage->width()) / kPix.width();
129 double scaleY = static_cast<double>(streamImage->height()) / kPix.height();
130
131 finalSelection.setX((rawSelection.x() - pixmapX) * scaleX);
132 finalSelection.setY((rawSelection.y() - pixmapY) * scaleY);
133 finalSelection.setWidth(rawSelection.width() * scaleX);
134 finalSelection.setHeight(rawSelection.height() * scaleY);
135
136 emit newSelection(finalSelection);
137 // determine selection, for example using QRect::intersects()
138 // and QRect::contains().
139}
140
141bool VideoWG::debayer(const IBLOB *bp, const BayerParams &params)
142{
143 uint32_t rgb_size = streamW * streamH * 3;
144 auto * destinationBuffer = new uint8_t[rgb_size];
145
146 if (destinationBuffer == nullptr)
147 {
148 qCCritical(KSTARS) << "Unable to allocate memory for temporary bayer buffer.";
149 return false;
150 }
151
152 int ds1394_height = streamH;
153
154 uint8_t * dc1394_source = reinterpret_cast<uint8_t*>(bp->blob);
155 if (params.offsetY == 1)
156 {
157 dc1394_source += streamW;
158 ds1394_height--;
159 }
160 if (params.offsetX == 1)
161 {
162 dc1394_source++;
163 }
164 dc1394error_t error_code = dc1394_bayer_decoding_8bit(dc1394_source, destinationBuffer, streamW, ds1394_height,
165 params.filter, params.method);
166
167 if (error_code != DC1394_SUCCESS)
168 {
169 qCCritical(KSTARS) << "Debayer failed" << error_code;
170 delete[] destinationBuffer;
171 return false;
172 }
173
174 streamImage.reset(new QImage(destinationBuffer, streamW, streamH, QImage::Format_RGB888));
175 bool rc = !streamImage->isNull();
176
177 if (rc)
178 {
179 kPix = QPixmap::fromImage(streamImage->scaled(size(), Qt::KeepAspectRatio));
180
181 paintOverlay(kPix);
182
183 setPixmap(kPix);
184 }
185
186 emit imageChanged(streamImage);
187
188 delete[] destinationBuffer;
189 return rc;
190}
191
192void VideoWG::paintOverlay(QPixmap &imagePix)
193{
194 if (!overlayEnabled || m_EnabledOverlayElements.count() == 0) return;
195
196 // Anchor - default to center of image
197 QPointF m_anchor (static_cast<float>(kPix.width() / 2), static_cast<float>(kPix.height()/2));
198 scale = (static_cast<float>(kPix.width()) / static_cast<float>(streamW));
199
200 // Apply any offset from (only) the first enabled anchor element
201 bool foundAnchor = false;
202 for (auto &oneElement : m_EnabledOverlayElements) {
203 if (oneElement["Type"] == "Anchor" && !foundAnchor) {
204 m_anchor.setX(m_anchor.x() + oneElement["OffsetX"].toInt());
205 m_anchor.setY(m_anchor.y() + oneElement["OffsetY"].toInt());
206 foundAnchor = true;
207 }
208 }
209
210 painter->begin(&imagePix);
211 painter->translate(m_anchor);
212 painter->scale(scale, scale);
213
214 for (auto &currentElement : m_EnabledOverlayElements) {
215
216 painter->save();
217 QPen m_pen = QPen(QColor(currentElement["Colour"].toString()));
218 m_pen.setWidth(currentElement["Thickness"].toUInt());
221 painter->setPen(m_pen);
222 painter->translate(currentElement["OffsetX"].toFloat(), currentElement["OffsetY"].toFloat());
223
224 int m_count = currentElement["Count"].toUInt();
225 float m_pcd = currentElement["PCD"].toFloat();
226
227 if (m_count == 1) {
228 PaintOneItem(currentElement["Type"].toString(), QPointF(0.0, 0.0), currentElement["SizeX"].toUInt(), currentElement["SizeY"].toUInt(), currentElement["Thickness"].toUInt());
229 } else if (m_count > 1) {
230 float slice = 360 / m_count;
231 for (int i = 0; i < m_count; i++) {
232 painter->save();
233 painter->rotate((slice * i) + currentElement["Rotation"].toFloat());
234 PaintOneItem(currentElement["Type"].toString(), QPointF((m_pcd / 2), 0.0), currentElement["SizeX"].toUInt(), currentElement["SizeY"].toUInt(), currentElement["Thickness"].toUInt());
235 painter->restore();
236 }
237 }
238
239 painter->restore();
240 }
241 painter->end();
242}
243
244void VideoWG::setupOverlay ()
245{
246 if (overlayEnabled) {
247 initOverlayModel();
248
249 typeValues = new QStringList;
250 collimationoverlaytype m_types;
251 const QMetaObject *m_metaobject = m_types.metaObject();
252 QMetaEnum m_metaEnum = m_metaobject->enumerator(m_metaobject->indexOfEnumerator("Types"));
253 for (int i = 0; i < m_metaEnum.keyCount(); i++) {
254 *typeValues << tr(m_metaEnum.key(i));
255 }
256
257 painter = new QPainter;
258 }
259}
260
261void VideoWG::initOverlayModel()
262{
263 m_CollimationOverlayElements.clear();
264 auto userdb = QSqlDatabase::database(KStarsData::Instance()->userdb()->connectionName());
265 m_CollimationOverlayElementsModel = new QSqlTableModel(this, userdb);
266 modelChanged();
267}
268
269void VideoWG::modelChanged()
270{
271 m_CollimationOverlayElements.clear();
272 m_EnabledOverlayElements.clear();
273 KStars::Instance()->data()->userdb()->GetCollimationOverlayElements(m_CollimationOverlayElements);
274 for (auto &oneElement : m_CollimationOverlayElements)
275 if (oneElement["Enabled"] == Qt::Checked)
276 m_EnabledOverlayElements.append(oneElement);
277}
278
279void VideoWG::PaintOneItem (QString type, QPointF position, int sizeX, int sizeY, int thickness)
280{
281 float m_sizeX = sizeX - (thickness / 2);
282 float m_sizeY = sizeY - (thickness / 2);
283
284 switch (typeValues->indexOf(type)) {
285case 0: // Anchor - ignore as we're not drawing it
286 break;
287
288case 1: // Ellipse
289 painter->drawEllipse(position, m_sizeX, m_sizeY);
290 break;
291
292case 2: // Rectangle
293{
294 QRect m_rect((position.x() - (m_sizeX / 2)), (position.y() - (m_sizeY / 2)), (m_sizeX - (thickness / 2)), (m_sizeY - (thickness / 2)));
295 painter->drawRect(m_rect);
296 break;
297}
298
299case 3: // Line
300 painter->drawLine(position.x(), position.y(), sizeX, sizeY);
301 break;
302
303default:
304 break;
305 };
306}
307
308void VideoWG::toggleOverlay()
309{
310 if (overlayEnabled == false) {
311 overlayEnabled = true;
312 if (m_CollimationOverlayElementsModel == nullptr) {
313 setupOverlay();
314 }
315 } else if (overlayEnabled == true) {
316 overlayEnabled = false;
317 }
318}
319
320VideoWG::~VideoWG()
321{
322 delete m_CollimationOverlayElementsModel;
323 delete m_CurrentElement;
324 delete typeValues;
325 delete painter;
326}
bool GetCollimationOverlayElements(QList< QVariantMap > &collimationOverlayElements)
Populate the reference passed with all collimation overlay elements.
KSUserDB * userdb()
Definition kstarsdata.h:215
static KStars * Instance()
Definition kstars.h:123
KStarsData * data() const
Definition kstars.h:135
char * toString(const EngineQuery &query)
QList< QByteArray > supportedImageFormats()
virtual bool event(QEvent *e) override
void setPixmap(const QPixmap &)
void clear()
bool contains(const AT &value) const const
qsizetype count() const const
const char * key(int index) const const
int keyCount() const const
QMetaEnum enumerator(int index) const const
int indexOfEnumerator(const char *name) const const
QString tr(const char *sourceText, const char *disambiguation, int n)
bool begin(QPaintDevice *device)
void drawEllipse(const QPoint &center, int rx, int ry)
void drawLine(const QLine &line)
void drawRect(const QRect &rectangle)
bool end()
void restore()
void rotate(qreal angle)
void save()
void scale(qreal sx, qreal sy)
void setPen(Qt::PenStyle style)
void translate(const QPoint &offset)
void setCapStyle(Qt::PenCapStyle style)
void setJoinStyle(Qt::PenJoinStyle style)
void setWidth(int width)
QPixmap fromImage(QImage &&image, Qt::ImageConversionFlags flags)
int height() const const
bool save(QIODevice *device, const char *format, int quality) const const
int width() const const
qreal x() const const
qreal y() const const
int height() const const
QRect normalized() const const
void setHeight(int height)
void setWidth(int width)
void setX(int x)
void setY(int y)
int width() const const
int x() const const
int y() const const
void setGeometry(const QRect &rect)
QSqlDatabase database(const QString &connectionName, bool open)
qsizetype indexOf(const QRegularExpression &re, qsizetype from) const const
KeepAspectRatio
RightButton
MiterJoin
void hide()
void show()
void resize(const QSize &)
This file is part of the KDE documentation.
Documentation copyright © 1996-2024 The KDE developers.
Generated on Fri Jul 26 2024 11:59:52 by doxygen 1.11.0 written by Dimitri van Heesch, © 1997-2006

KDE's Doxygen guidelines are available online.