File indexing completed on 2024-04-21 14:45:49

0001 /*
0002     SPDX-FileCopyrightText: 2021 Jasem Mutlaq <mutlaqja@ikarustech.com>
0003 
0004     SPDX-License-Identifier: GPL-2.0-or-later
0005 */
0006 
0007 #include <zlib.h>
0008 #include <QApplication>
0009 
0010 #include "fitshistogramcommand.h"
0011 #include "fitshistogrameditor.h"
0012 #include "fitsviewer.h"
0013 #include "fits_debug.h"
0014 
0015 FITSHistogramCommand::FITSHistogramCommand(const QSharedPointer<FITSData> &data,
0016         FITSHistogramEditor * inHisto,
0017         FITSScale newType,
0018         const QVector<double> &lmin,
0019         const QVector<double> &lmax) : m_ImageData(data), histogram(inHisto),
0020     type(newType), min(lmin), max(lmax)
0021 {
0022 }
0023 
0024 FITSHistogramCommand::~FITSHistogramCommand()
0025 {
0026     delete[] delta;
0027 }
0028 
0029 bool FITSHistogramCommand::calculateDelta(const uint8_t * buffer)
0030 {
0031     uint8_t const * image_buffer = m_ImageData->getImageBuffer();
0032     uint32_t totalPixels = m_ImageData->samplesPerChannel() * m_ImageData->channels();
0033     unsigned long totalBytes = totalPixels * m_ImageData->getBytesPerPixel();
0034 
0035     auto * raw_delta = new uint8_t[totalBytes];
0036 
0037     if (raw_delta == nullptr)
0038     {
0039         qCWarning(KSTARS_FITS) << "Error! not enough memory to create image delta";
0040         return false;
0041     }
0042 
0043     for (uint32_t i = 0; i < totalBytes; i++)
0044         raw_delta[i] = buffer[i] ^ image_buffer[i];
0045 
0046     compressedBytes = sizeof(uint8_t) * totalBytes + totalBytes / 64 + 16 + 3;
0047     delete[] delta;
0048     delta = new uint8_t[compressedBytes];
0049 
0050     if (delta == nullptr)
0051     {
0052         delete[] raw_delta;
0053         qCCritical(KSTARS_FITS)
0054                 << "FITSHistogram Error: Ran out of memory compressing delta";
0055         return false;
0056     }
0057 
0058     int r = compress2(delta, &compressedBytes, raw_delta, totalBytes, 5);
0059 
0060     if (r != Z_OK)
0061     {
0062         delete[] raw_delta;
0063         /* this should NEVER happen */
0064         qCCritical(KSTARS_FITS)
0065                 << "FITSHistogram Error: Failed to compress raw_delta";
0066         return false;
0067     }
0068 
0069     delete[] raw_delta;
0070     return true;
0071 }
0072 
0073 bool FITSHistogramCommand::reverseDelta()
0074 {
0075     uint8_t const * image_buffer = m_ImageData->getImageBuffer();
0076     uint32_t totalPixels = m_ImageData->samplesPerChannel() * m_ImageData->channels();
0077     unsigned long totalBytes = totalPixels * m_ImageData->getBytesPerPixel();
0078 
0079     auto * output_image = new uint8_t[totalBytes];
0080 
0081     if (output_image == nullptr)
0082     {
0083         qCWarning(KSTARS_FITS) << "Error! not enough memory to create output image";
0084         return false;
0085     }
0086 
0087     auto * raw_delta = new uint8_t[totalBytes];
0088 
0089     if (raw_delta == nullptr)
0090     {
0091         delete[] output_image;
0092         qCWarning(KSTARS_FITS) << "Error! not enough memory to create image delta";
0093         return false;
0094     }
0095 
0096     int r = uncompress(raw_delta, &totalBytes, delta, compressedBytes);
0097     if (r != Z_OK)
0098     {
0099         qCCritical(KSTARS_FITS)
0100                 << "FITSHistogram compression error in reverseDelta()";
0101         delete[] output_image;
0102         delete[] raw_delta;
0103         return false;
0104     }
0105 
0106     for (unsigned int i = 0; i < totalBytes; i++)
0107         output_image[i] = raw_delta[i] ^ image_buffer[i];
0108 
0109     m_ImageData->setImageBuffer(output_image);
0110 
0111     delete[] raw_delta;
0112 
0113     return true;
0114 }
0115 
0116 void FITSHistogramCommand::redo()
0117 {
0118     uint8_t const * image_buffer = m_ImageData->getImageBuffer();
0119     uint8_t * buffer = nullptr;
0120     uint32_t totalPixels = m_ImageData->samplesPerChannel() * m_ImageData->channels();
0121     int BBP = m_ImageData->getBytesPerPixel();
0122 
0123     QApplication::setOverrideCursor(Qt::WaitCursor);
0124 
0125     if (delta != nullptr)
0126     {
0127         FITSImage::Statistic prevStats;
0128         m_ImageData->saveStatistics(prevStats);
0129 
0130         reverseDelta();
0131 
0132         m_ImageData->restoreStatistics(stats);
0133 
0134         stats = prevStats;
0135     }
0136     else
0137     {
0138         m_ImageData->saveStatistics(stats);
0139 
0140         // If it's rotation of flip, no need to calculate delta
0141         if (type >= FITS_ROTATE_CW && type <= FITS_MOUNT_FLIP_V)
0142         {
0143             m_ImageData->applyFilter(type);
0144         }
0145         else
0146         {
0147             buffer = new uint8_t[totalPixels * BBP];
0148 
0149             if (buffer == nullptr)
0150             {
0151                 qWarning(KSTARS_FITS()) << "Error! not enough memory to create image buffer in redo()";
0152                 QApplication::restoreOverrideCursor();
0153                 return;
0154             }
0155 
0156             memcpy(buffer, image_buffer, totalPixels * BBP);
0157 
0158             QVector<double> dataMin = min, dataMax = max;
0159             switch (type)
0160             {
0161                 case FITS_AUTO:
0162                 case FITS_LINEAR:
0163                     m_ImageData->applyFilter(FITS_LINEAR, nullptr, &dataMin, &dataMax);
0164                     break;
0165 
0166                 case FITS_LOG:
0167                     m_ImageData->applyFilter(FITS_LOG, nullptr, &dataMin, &dataMax);
0168                     break;
0169 
0170                 case FITS_SQRT:
0171                     m_ImageData->applyFilter(FITS_SQRT, nullptr, &dataMin, &dataMax);
0172                     break;
0173 
0174                 default:
0175                     m_ImageData->applyFilter(type);
0176                     break;
0177             }
0178 
0179             calculateDelta(buffer);
0180             delete[] buffer;
0181         }
0182     }
0183 
0184     //    if (histogram != nullptr)
0185     //    {
0186     //        histogram->construct();
0187 
0188     //        //        if (tab->getViewer()->isStarsMarked())
0189     //        //            imageData->findStars().waitForFinished();
0190     //    }
0191 
0192     //    image->pushFilter(type);
0193     //    image->rescale(ZOOM_KEEP_LEVEL);
0194     //    image->updateFrame();
0195 
0196     QApplication::restoreOverrideCursor();
0197 }
0198 
0199 void FITSHistogramCommand::undo()
0200 {
0201     QApplication::setOverrideCursor(Qt::WaitCursor);
0202 
0203     if (delta != nullptr)
0204     {
0205         FITSImage::Statistic prevStats;
0206         m_ImageData->saveStatistics(prevStats);
0207 
0208         reverseDelta();
0209 
0210         m_ImageData->restoreStatistics(stats);
0211 
0212         stats = prevStats;
0213     }
0214     else
0215     {
0216         switch (type)
0217         {
0218             case FITS_ROTATE_CW:
0219                 m_ImageData->applyFilter(FITS_ROTATE_CCW);
0220                 break;
0221             case FITS_ROTATE_CCW:
0222                 m_ImageData->applyFilter(FITS_ROTATE_CW);
0223                 break;
0224             case FITS_MOUNT_FLIP_H:
0225             case FITS_MOUNT_FLIP_V:
0226                 m_ImageData->applyFilter(type);
0227                 break;
0228             default:
0229                 break;
0230         }
0231     }
0232 
0233     //    if (histogram != nullptr)
0234     //    {
0235     //        histogram->construct();
0236 
0237     //        //        if (tab->getViewer()->isStarsMarked())
0238     //        //            imageData->findStars().waitForFinished();
0239     //    }
0240 
0241     //    image->popFilter();
0242     //    image->rescale(ZOOM_KEEP_LEVEL);
0243     //    image->updateFrame();
0244 
0245     QApplication::restoreOverrideCursor();
0246 }
0247 
0248 QString FITSHistogramCommand::text() const
0249 {
0250     switch (type)
0251     {
0252         case FITS_AUTO:
0253             return i18n("Auto Scale");
0254         case FITS_LINEAR:
0255             return i18n("Linear Scale");
0256         case FITS_LOG:
0257             return i18n("Logarithmic Scale");
0258         case FITS_SQRT:
0259             return i18n("Square Root Scale");
0260 
0261         default:
0262             if (type - 1 <= FITSViewer::filterTypes.count())
0263                 return FITSViewer::filterTypes.at(type - 1);
0264             break;
0265     }
0266 
0267     return i18n("Unknown");
0268 }
0269