1 /*
2     SPDX-FileCopyrightText: 2003-2017 Jasem Mutlaq <mutlaqja@ikarustech.com>
3     SPDX-FileCopyrightText: 2016-2017 Robert Lancaster <rlancaste@gmail.com>
4 
5     SPDX-License-Identifier: GPL-2.0-or-later
6 */
7 
8 #pragma once
9 
10 #include "fitscommon.h"
11 
12 #include <config-kstars.h>
13 #include "stretch.h"
14 
15 #ifdef HAVE_DATAVISUALIZATION
16 #include "starprofileviewer.h"
17 #endif
18 
19 #include <QFutureWatcher>
20 #include <QPixmap>
21 #include <QScrollArea>
22 #include <QStack>
23 #include <QTimer>
24 #include <QPointer>
25 
26 #ifdef WIN32
27 // avoid compiler warning when windows.h is included after fitsio.h
28 #include <windows.h>
29 #endif
30 
31 #include <fitsio.h>
32 
33 #include <memory>
34 
35 class QAction;
36 class QEvent;
37 class QGestureEvent;
38 class QImage;
39 class QLabel;
40 class QPinchGesture;
41 class QResizeEvent;
42 class QToolBar;
43 
44 class FITSData;
45 class FITSLabel;
46 
47 class FITSView : public QScrollArea
48 {
49         Q_OBJECT
50     public:
51         explicit FITSView(QWidget *parent = nullptr, FITSMode fitsMode = FITS_NORMAL, FITSScale filterType = FITS_NONE);
52         virtual ~FITSView() override;
53 
54         typedef enum {dragCursor, selectCursor, scopeCursor, crosshairCursor } CursorMode;
55 
56         /**
57          * @brief loadFITS Loads FITS data and displays it in a FITSView frame
58          * @param inFilename FITS File name
59          * @param silent if set, error popups are suppressed.
60          * @note If image is successfully, loaded() signal is emitted, otherwise failed() signal is emitted.
61          * Obtain error by calling lastError()
62          */
63         void loadFile(const QString &inFilename, bool silent = true);
64 
65         /**
66          * @brief loadFITSFromData Takes ownership of the FITSData instance passed in and displays it in a FITSView frame
67          * @param data pointer to FITSData objects
68          */
69         bool loadData(const QSharedPointer<FITSData> &data);
70 
71         /**
72          * @brief clearView Reset view to NO IMAGE
73          */
74         void clearData();
75 
76         // Save FITS
77         bool saveImage(const QString &newFilename);
78         // Rescale image lineary from image_buffer, fit to window if desired
79         bool rescale(FITSZoom type);
80 
imageData()81         const QSharedPointer<FITSData> &imageData() const
82         {
83             return m_ImageData;
84         }
85 
getCurrentZoom()86         double getCurrentZoom() const
87         {
88             return currentZoom;
89         }
getDisplayImage()90         const QImage &getDisplayImage() const
91         {
92             return rawImage;
93         }
getDisplayPixmap()94         const QPixmap &getDisplayPixmap() const
95         {
96             return displayPixmap;
97         }
98 
99         // Tracking square
100         void setTrackingBoxEnabled(bool enable);
isTrackingBoxEnabled()101         bool isTrackingBoxEnabled() const
102         {
103             return trackingBoxEnabled;
104         }
105         QPixmap &getTrackingBoxPixmap(uint8_t margin = 0);
106         void setTrackingBox(const QRect &rect);
getTrackingBox()107         const QRect &getTrackingBox() const
108         {
109             return trackingBox;
110         }
111 
112         // last error
lastError()113         const QString &lastError() const
114         {
115             return m_LastError;
116         }
117 
118         // Overlay
119         virtual void drawOverlay(QPainter *, double scale);
120 
121         // Overlay objects
122         void drawStarFilter(QPainter *, double scale);
123         void drawStarCentroid(QPainter *, double scale);
124         void drawClipping(QPainter *);
125         void drawTrackingBox(QPainter *, double scale);
126         void drawMarker(QPainter *, double scale);
127         void drawCrosshair(QPainter *, double scale);
128 
129 #if !defined(KSTARS_LITE) && defined(HAVE_WCSLIB)
130         void drawEQGrid(QPainter *, double scale);
131 #endif
132         void drawObjectNames(QPainter *painter, double scale);
133         void drawPixelGrid(QPainter *painter, double scale);
134         void drawMagnifyingGlass(QPainter *painter, double scale);
135 
136         bool isImageStretched();
137         bool isCrosshairShown();
138         bool isClippingShown();
139         bool areObjectsShown();
140         bool isEQGridShown();
141         bool isPixelGridShown();
142         bool imageHasWCS();
143 
144         // Setup the graphics.
145         void updateFrame(bool now = false);
146 
147         bool isTelescopeActive();
148 
149         void enterEvent(QEvent *event) override;
150         void leaveEvent(QEvent *event) override;
151         CursorMode getCursorMode();
152         void setCursorMode(CursorMode mode);
153         void updateMouseCursor();
154 
155         void updateScopeButton();
setScopeButton(QAction * action)156         void setScopeButton(QAction *action)
157         {
158             centerTelescopeAction = action;
159         }
160 
161         // Zoom related
162         void cleanUpZoom(QPoint viewCenter = QPoint());
163         QPoint getImagePoint(QPoint viewPortPoint);
zoomedWidth()164         uint16_t zoomedWidth()
165         {
166             return currentWidth;
167         }
zoomedHeight()168         uint16_t zoomedHeight()
169         {
170             return currentHeight;
171         }
ZoomFactor()172         double ZoomFactor() const
173         {
174             return m_ZoomFactor;
175         }
176 
177 
178         // Star Detection
179         QFuture<bool> findStars(StarAlgorithm algorithm = ALGORITHM_CENTROID, const QRect &searchBox = QRect());
180         void toggleStars(bool enable);
181         void searchStars();
182         void setStarsEnabled(bool enable);
183         void setStarsHFREnabled(bool enable);
184         void setStarFilterRange(float const innerRadius, float const outerRadius);
185         int filterStars();
186 
187         // FITS Mode
188         void updateMode(FITSMode fmode);
getMode()189         FITSMode getMode()
190         {
191             return mode;
192         }
193 
setFilter(FITSScale newFilter)194         void setFilter(FITSScale newFilter)
195         {
196             filter = newFilter;
197         }
198 
199         void setFirstLoad(bool value);
200 
pushFilter(FITSScale value)201         void pushFilter(FITSScale value)
202         {
203             filterStack.push(value);
204         }
popFilter()205         FITSScale popFilter()
206         {
207             return filterStack.pop();
208         }
209 
210         CursorMode lastMouseMode { selectCursor };
isStarProfileShown()211         bool isStarProfileShown()
212         {
213             return showStarProfile;
214         }
215         // Floating toolbar
216         void createFloatingToolBar();
217 
218         //void setLoadWCSEnabled(bool value);
219 
220         // Returns the params set to stretch the image.
getStretchParams()221         StretchParams getStretchParams() const
222         {
223             return stretchParams;
224         }
225 
226         // Returns true if we're automatically generating stretch parameters.
227         // Note: this is not whether we're stretching, that's controlled by stretchImage.
getAutoStretch()228         bool getAutoStretch() const
229         {
230             return autoStretch;
231         }
232 
233         // Sets the params for stretching. Will also stretch and re-display the image.
234         // This only sets the first channel stretch params. For RGB images, the G&B channel
235         // stretch parameters are a function of the Red input param and the existing RGB params.
236         void setStretchParams(const StretchParams &params);
237 
238         // Sets whether to stretch the image or not.
239         // Will also re-display the image if onOff != stretchImage.
240         void setStretch(bool onOff);
241 
242         // Automatically generates stretch parameters and use them to re-display the image.
243         void setAutoStretchParams();
244 
245         // When sampling is > 1, we will display the image at a lower resolution.
246         // When sampling = 0, reset to the adaptive sampling value
setPreviewSampling(uint8_t value)247         void setPreviewSampling(uint8_t value)
248         {
249             if (value == 0)
250             {
251                 m_PreviewSampling = m_AdaptiveSampling;
252                 m_StretchingInProgress = false;
253             }
254             else
255             {
256                 m_PreviewSampling = value * m_AdaptiveSampling;
257                 m_StretchingInProgress = true;
258             }
259         }
260 
261     public slots:
262         void wheelEvent(QWheelEvent *event) override;
263         void resizeEvent(QResizeEvent *event) override;
264         void ZoomIn();
265         void ZoomOut();
266         void ZoomDefault();
267         void ZoomToFit();
268         void updateMagnifyingGlass(int x, int y);
269 
270         // Grids
271         void toggleEQGrid();
272         void toggleObjects();
273         void togglePixelGrid();
274         void toggleCrosshair();
275 
276         // Stars
277         void toggleStars();
278         void toggleStarProfile();
279         void viewStarProfile();
280 
281         void centerTelescope();
282 
283         void toggleStretch();
284         void toggleClipping();
285 
286         virtual void processPointSelection(int x, int y);
287         virtual void processMarkerSelection(int x, int y);
288         void move3DTrackingBox(int x, int y);
289         void resizeTrackingBox(int newSize);
290 
291     protected slots:
292         /**
293              * @brief syncWCSState Update toolbar and actions depending on whether WCS is available or not
294              */
295         void syncWCSState();
296 
297         bool event(QEvent *event) override;
298         bool gestureEvent(QGestureEvent *event);
299         void pinchTriggered(QPinchGesture *gesture);
300 
301     protected:
302         double average();
303         double stddev();
304         void calculateMaxPixel(double min, double max);
305         void initDisplayImage();
306 
307         QPointF getPointForGridLabel(QPainter *painter, const QString &str, double scale);
308         bool pointIsInImage(QPointF pt, double scale);
309 
310         void loadInFrame();
311 
312         double getScale();
313 
314         /// Floating toolbar
315         QToolBar *floatingToolBar { nullptr };
316         /// WCS Future Watcher
317         QFutureWatcher<bool> wcsWatcher;
318         /// FITS Future Watcher
319         QFutureWatcher<bool> fitsWatcher;
320         /// Cross hair
321         QPointF markerCrosshair;
322         /// Pointer to FITSData object
323         QSharedPointer<FITSData> m_ImageData;
324         /// Current zoom level
325         double currentZoom { 0 };
326         // The maximum percent zoom. The value is recalculated in the constructor
327         // based on the amount of physical memory.
328         int zoomMax { 400 };
329 
330     private:
331         bool processData();
332         void doStretch(QImage *outputImage);
333         double scaleSize(double size);
334         bool isLargeImage();
335         void updateFrameLargeImage();
336         void updateFrameSmallImage();
337         bool drawHFR(QPainter * painter, const QString &hfr, int x, int y);
338 
339         QPointer<QLabel> noImageLabel;
340         QPixmap noImage;
341         QPointer<FITSLabel> m_ImageFrame;
342         QVector<QPointF> eqGridPoints;
343 
344         /// Current width due to zoom
345         uint16_t currentWidth { 0 };
346         /// Current height due to zoom
347         uint16_t currentHeight { 0 };
348         /// Image zoom factor
349         const double m_ZoomFactor;
350 
351         // Original full-size image
352         QImage rawImage;
353         // Actual pixmap after all the overlays
354         QPixmap displayPixmap;
355 
356         bool firstLoad { true };
357         bool markStars { false };
358         bool showStarProfile { false };
359         bool showCrosshair { false };
360         bool showObjects { false };
361         bool showEQGrid { false };
362         bool showPixelGrid { false };
363         bool showStarsHFR { false };
364         bool showClipping { false };
365 
366         // Should the image be displayed in linear (false) or stretched (true).
367         // Initial value controlled by Options::autoStretch.
368         bool stretchImage { false };
369 
370         // When stretching, should we automatically compute parameters.
371         // When first displaying, this should be true, but may be set to false
372         // if the user has overridden the automatically set parameters.
373         bool autoStretch { true };
374 
375         // Params for stretching image.
376         StretchParams stretchParams;
377 
378         // Resolution for display. Sampling=2 means display every other sample.
379         uint8_t m_PreviewSampling { 1 };
380         bool m_StretchingInProgress { false};
381         // Adaptive sampling is based on available RAM
382         uint8_t m_AdaptiveSampling {1};
383 
384         struct
385         {
used__anonf5e7b99c0208386             bool used() const
387             {
388                 return innerRadius != 0.0f || outerRadius != 1.0f;
389             }
390             float innerRadius { 0.0f };
391             float outerRadius { 1.0f };
392         } starFilter;
393 
394         CursorMode cursorMode { selectCursor };
395         bool zooming { false };
396         int zoomTime { 0 };
397         QPoint zoomLocation;
398 
399         QString filename;
400         FITSMode mode;
401         FITSScale filter;
402         QString m_LastError;
403         QTimer m_UpdateFrameTimer;
404 
405         QStack<FITSScale> filterStack;
406 
407         // Tracking box
408         bool trackingBoxEnabled { false };
409         QRect trackingBox;
410         QPixmap trackingBoxPixmap;
411 
412         // Scope pixmap
413         QPixmap redScopePixmap;
414         // Magenta Scope Pixmap
415         QPixmap magentaScopePixmap;
416 
417         QAction *centerTelescopeAction { nullptr };
418         QAction *toggleEQGridAction { nullptr };
419         QAction *toggleObjectsAction { nullptr };
420         QAction *toggleStarsAction { nullptr };
421         QAction *toggleProfileAction { nullptr };
422         QAction *toggleStretchAction { nullptr };
423 
424         // State for the magnifying glass overlay.
425         int magnifyingGlassX { -1 };
426         int magnifyingGlassY { -1 };
427         bool showMagnifyingGlass { false };
428 
429         //Star Profile Viewer
430 #ifdef HAVE_DATAVISUALIZATION
431         QPointer<StarProfileViewer> starProfileWidget;
432 #endif
433 
434     signals:
435         void newStatus(const QString &msg, FITSBar id);
436         void debayerToggled(bool);
437         void wcsToggled(bool);
438         void actionUpdated(const QString &name, bool enable);
439         void trackingStarSelected(int x, int y);
440         void loaded();
441         void failed();
442         void starProfileWindowClosed();
443 
444         friend class FITSLabel;
445 };
446