1 /*
2 SPDX-FileCopyrightText: 2021 Jasem Mutlaq <mutlaqja@ikarustech.com>
3
4 SPDX-License-Identifier: GPL-2.0-or-later
5 */
6
7 #include <zlib.h>
8 #include <QApplication>
9
10 #include "fitshistogramcommand.h"
11 #include "fitshistogrameditor.h"
12 #include "fitsviewer.h"
13 #include "fits_debug.h"
14
FITSHistogramCommand(const QSharedPointer<FITSData> & data,FITSHistogramEditor * inHisto,FITSScale newType,const QVector<double> & lmin,const QVector<double> & lmax)15 FITSHistogramCommand::FITSHistogramCommand(const QSharedPointer<FITSData> &data,
16 FITSHistogramEditor * inHisto,
17 FITSScale newType,
18 const QVector<double> &lmin,
19 const QVector<double> &lmax) : m_ImageData(data), histogram(inHisto),
20 type(newType), min(lmin), max(lmax)
21 {
22 }
23
~FITSHistogramCommand()24 FITSHistogramCommand::~FITSHistogramCommand()
25 {
26 delete[] delta;
27 }
28
calculateDelta(const uint8_t * buffer)29 bool FITSHistogramCommand::calculateDelta(const uint8_t * buffer)
30 {
31 uint8_t const * image_buffer = m_ImageData->getImageBuffer();
32 uint32_t totalPixels = m_ImageData->samplesPerChannel() * m_ImageData->channels();
33 unsigned long totalBytes = totalPixels * m_ImageData->getBytesPerPixel();
34
35 auto * raw_delta = new uint8_t[totalBytes];
36
37 if (raw_delta == nullptr)
38 {
39 qCWarning(KSTARS_FITS) << "Error! not enough memory to create image delta";
40 return false;
41 }
42
43 for (uint32_t i = 0; i < totalBytes; i++)
44 raw_delta[i] = buffer[i] ^ image_buffer[i];
45
46 compressedBytes = sizeof(uint8_t) * totalBytes + totalBytes / 64 + 16 + 3;
47 delete[] delta;
48 delta = new uint8_t[compressedBytes];
49
50 if (delta == nullptr)
51 {
52 delete[] raw_delta;
53 qCCritical(KSTARS_FITS)
54 << "FITSHistogram Error: Ran out of memory compressing delta";
55 return false;
56 }
57
58 int r = compress2(delta, &compressedBytes, raw_delta, totalBytes, 5);
59
60 if (r != Z_OK)
61 {
62 delete[] raw_delta;
63 /* this should NEVER happen */
64 qCCritical(KSTARS_FITS)
65 << "FITSHistogram Error: Failed to compress raw_delta";
66 return false;
67 }
68
69 delete[] raw_delta;
70 return true;
71 }
72
reverseDelta()73 bool FITSHistogramCommand::reverseDelta()
74 {
75 uint8_t const * image_buffer = m_ImageData->getImageBuffer();
76 uint32_t totalPixels = m_ImageData->samplesPerChannel() * m_ImageData->channels();
77 unsigned long totalBytes = totalPixels * m_ImageData->getBytesPerPixel();
78
79 auto * output_image = new uint8_t[totalBytes];
80
81 if (output_image == nullptr)
82 {
83 qCWarning(KSTARS_FITS) << "Error! not enough memory to create output image";
84 return false;
85 }
86
87 auto * raw_delta = new uint8_t[totalBytes];
88
89 if (raw_delta == nullptr)
90 {
91 delete[] output_image;
92 qCWarning(KSTARS_FITS) << "Error! not enough memory to create image delta";
93 return false;
94 }
95
96 int r = uncompress(raw_delta, &totalBytes, delta, compressedBytes);
97 if (r != Z_OK)
98 {
99 qCCritical(KSTARS_FITS)
100 << "FITSHistogram compression error in reverseDelta()";
101 delete[] output_image;
102 delete[] raw_delta;
103 return false;
104 }
105
106 for (unsigned int i = 0; i < totalBytes; i++)
107 output_image[i] = raw_delta[i] ^ image_buffer[i];
108
109 m_ImageData->setImageBuffer(output_image);
110
111 delete[] raw_delta;
112
113 return true;
114 }
115
redo()116 void FITSHistogramCommand::redo()
117 {
118 uint8_t const * image_buffer = m_ImageData->getImageBuffer();
119 uint8_t * buffer = nullptr;
120 uint32_t totalPixels = m_ImageData->samplesPerChannel() * m_ImageData->channels();
121 int BBP = m_ImageData->getBytesPerPixel();
122
123 QApplication::setOverrideCursor(Qt::WaitCursor);
124
125 if (delta != nullptr)
126 {
127 FITSImage::Statistic prevStats;
128 m_ImageData->saveStatistics(prevStats);
129
130 reverseDelta();
131
132 m_ImageData->restoreStatistics(stats);
133
134 stats = prevStats;
135 }
136 else
137 {
138 m_ImageData->saveStatistics(stats);
139
140 // If it's rotation of flip, no need to calculate delta
141 if (type >= FITS_ROTATE_CW && type <= FITS_FLIP_V)
142 {
143 m_ImageData->applyFilter(type);
144 }
145 else
146 {
147 buffer = new uint8_t[totalPixels * BBP];
148
149 if (buffer == nullptr)
150 {
151 qWarning(KSTARS_FITS()) << "Error! not enough memory to create image buffer in redo()";
152 QApplication::restoreOverrideCursor();
153 return;
154 }
155
156 memcpy(buffer, image_buffer, totalPixels * BBP);
157
158 QVector<double> dataMin = min, dataMax = max;
159 switch (type)
160 {
161 case FITS_AUTO:
162 case FITS_LINEAR:
163 m_ImageData->applyFilter(FITS_LINEAR, nullptr, &dataMin, &dataMax);
164 break;
165
166 case FITS_LOG:
167 m_ImageData->applyFilter(FITS_LOG, nullptr, &dataMin, &dataMax);
168 break;
169
170 case FITS_SQRT:
171 m_ImageData->applyFilter(FITS_SQRT, nullptr, &dataMin, &dataMax);
172 break;
173
174 default:
175 m_ImageData->applyFilter(type);
176 break;
177 }
178
179 calculateDelta(buffer);
180 delete[] buffer;
181 }
182 }
183
184 // if (histogram != nullptr)
185 // {
186 // histogram->construct();
187
188 // // if (tab->getViewer()->isStarsMarked())
189 // // imageData->findStars().waitForFinished();
190 // }
191
192 // image->pushFilter(type);
193 // image->rescale(ZOOM_KEEP_LEVEL);
194 // image->updateFrame();
195
196 QApplication::restoreOverrideCursor();
197 }
198
undo()199 void FITSHistogramCommand::undo()
200 {
201 QApplication::setOverrideCursor(Qt::WaitCursor);
202
203 if (delta != nullptr)
204 {
205 FITSImage::Statistic prevStats;
206 m_ImageData->saveStatistics(prevStats);
207
208 reverseDelta();
209
210 m_ImageData->restoreStatistics(stats);
211
212 stats = prevStats;
213 }
214 else
215 {
216 switch (type)
217 {
218 case FITS_ROTATE_CW:
219 m_ImageData->applyFilter(FITS_ROTATE_CCW);
220 break;
221 case FITS_ROTATE_CCW:
222 m_ImageData->applyFilter(FITS_ROTATE_CW);
223 break;
224 case FITS_FLIP_H:
225 case FITS_FLIP_V:
226 m_ImageData->applyFilter(type);
227 break;
228 default:
229 break;
230 }
231 }
232
233 // if (histogram != nullptr)
234 // {
235 // histogram->construct();
236
237 // // if (tab->getViewer()->isStarsMarked())
238 // // imageData->findStars().waitForFinished();
239 // }
240
241 // image->popFilter();
242 // image->rescale(ZOOM_KEEP_LEVEL);
243 // image->updateFrame();
244
245 QApplication::restoreOverrideCursor();
246 }
247
text() const248 QString FITSHistogramCommand::text() const
249 {
250 switch (type)
251 {
252 case FITS_AUTO:
253 return i18n("Auto Scale");
254 case FITS_LINEAR:
255 return i18n("Linear Scale");
256 case FITS_LOG:
257 return i18n("Logarithmic Scale");
258 case FITS_SQRT:
259 return i18n("Square Root Scale");
260
261 default:
262 if (type - 1 <= FITSViewer::filterTypes.count())
263 return FITSViewer::filterTypes.at(type - 1);
264 break;
265 }
266
267 return i18n("Unknown");
268 }
269
270