diff --git a/importer/thumbnailpage.cpp b/importer/thumbnailpage.cpp index dde7625f..f4e3cd34 100644 --- a/importer/thumbnailpage.cpp +++ b/importer/thumbnailpage.cpp @@ -1,470 +1,470 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2009 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Cambridge, MA 02110-1301, USA. */ // Self #include "thumbnailpage.h" #include "dialogguard.h" // Qt #include #include #include #include #include // KDE #include #include #include #include #include #include #include // Local #include #include #include #include #include #include #include #include #include #include #include #include namespace Gwenview { static const int DEFAULT_THUMBNAIL_SIZE = 128; static const qreal DEFAULT_THUMBNAIL_ASPECT_RATIO = 3. / 2.; static const char* URL_FOR_BASE_URL_GROUP = "UrlForBaseUrl"; class ImporterThumbnailViewHelper : public AbstractThumbnailViewHelper { public: ImporterThumbnailViewHelper(QObject* parent) : AbstractThumbnailViewHelper(parent) {} void showContextMenu(QWidget*) override {} void showMenuForUrlDroppedOnViewport(QWidget*, const QList&) override {} void showMenuForUrlDroppedOnDir(QWidget*, const QList&, const QUrl&) override {} }; inline KFileItem itemForIndex(const QModelIndex& index) { return index.data(KDirModel::FileItemRole).value(); } struct ThumbnailPagePrivate : public Ui_ThumbnailPage { ThumbnailPage* q; SerializedUrlMap mUrlMap; QIcon mSrcBaseIcon; QString mSrcBaseName; QUrl mSrcBaseUrl; QUrl mSrcUrl; KModelIndexProxyMapper* mSrcUrlModelProxyMapper; RecursiveDirModel* mRecursiveDirModel; QAbstractItemModel* mFinalModel; ThumbnailProvider mThumbnailProvider; QPushButton* mImportSelectedButton; QPushButton* mImportAllButton; QList mUrlList; void setupDirModel() { mRecursiveDirModel = new RecursiveDirModel(q); KindProxyModel* kindProxyModel = new KindProxyModel(q); kindProxyModel->setKindFilter( MimeTypeUtils::KIND_RASTER_IMAGE | MimeTypeUtils::KIND_SVG_IMAGE | MimeTypeUtils::KIND_VIDEO); kindProxyModel->setSourceModel(mRecursiveDirModel); QSortFilterProxyModel *sortModel = new QSortFilterProxyModel(q); sortModel->setDynamicSortFilter(true); sortModel->setSourceModel(kindProxyModel); sortModel->sort(0); mFinalModel = sortModel; QObject::connect( mFinalModel, SIGNAL(rowsInserted(QModelIndex,int,int)), q, SLOT(updateImportButtons())); QObject::connect( mFinalModel, SIGNAL(rowsRemoved(QModelIndex,int,int)), q, SLOT(updateImportButtons())); QObject::connect( mFinalModel, SIGNAL(modelReset()), q, SLOT(updateImportButtons())); } void setupIcons() { const KIconLoader::Group group = KIconLoader::NoGroup; const int size = KIconLoader::SizeHuge; mSrcIconLabel->setPixmap(KIconLoader::global()->loadIcon("camera-photo", group, size)); mDstIconLabel->setPixmap(KIconLoader::global()->loadIcon("computer", group, size)); } void setupSrcUrlWidgets() { mSrcUrlModelProxyMapper = nullptr; QObject::connect(mSrcUrlButton, SIGNAL(clicked()), q, SLOT(setupSrcUrlTreeView())); QObject::connect(mSrcUrlButton, SIGNAL(clicked()), q, SLOT(toggleSrcUrlTreeView())); mSrcUrlTreeView->hide(); KAcceleratorManager::setNoAccel(mSrcUrlButton); } void setupDstUrlRequester() { mDstUrlRequester->setMode(KFile::Directory | KFile::LocalOnly); } void setupThumbnailView() { mThumbnailView->setModel(mFinalModel); mThumbnailView->setSelectionMode(QAbstractItemView::ExtendedSelection); mThumbnailView->setThumbnailViewHelper(new ImporterThumbnailViewHelper(q)); PreviewItemDelegate* delegate = new PreviewItemDelegate(mThumbnailView); delegate->setThumbnailDetails(PreviewItemDelegate::FileNameDetail); PreviewItemDelegate::ContextBarActions actions; switch (GwenviewConfig::thumbnailActions()) { case ThumbnailActions::None: actions = PreviewItemDelegate::NoAction; break; case ThumbnailActions::ShowSelectionButtonOnly: case ThumbnailActions::AllButtons: actions = PreviewItemDelegate::SelectionAction; break; } delegate->setContextBarActions(actions); mThumbnailView->setItemDelegate(delegate); // Colors int value = GwenviewConfig::viewBackgroundValue(); QColor bgColor = QColor::fromHsv(0, 0, value); QColor fgColor = value > 128 ? Qt::black : Qt::white; QPalette pal = mThumbnailView->palette(); pal.setColor(QPalette::Base, bgColor); pal.setColor(QPalette::Text, fgColor); mThumbnailView->setPalette(pal); QObject::connect(mSlider, SIGNAL(valueChanged(int)), mThumbnailView, SLOT(setThumbnailWidth(int))); QObject::connect(mThumbnailView, SIGNAL(thumbnailWidthChanged(int)), mSlider, SLOT(setValue(int))); int thumbnailSize = DEFAULT_THUMBNAIL_SIZE; mSlider->setValue(thumbnailSize); mSlider->updateToolTip(); mThumbnailView->setThumbnailAspectRatio(DEFAULT_THUMBNAIL_ASPECT_RATIO); mThumbnailView->setThumbnailWidth(thumbnailSize); mThumbnailView->setThumbnailProvider(&mThumbnailProvider); QObject::connect( mThumbnailView->selectionModel(), SIGNAL(selectionChanged(QItemSelection,QItemSelection)), q, SLOT(updateImportButtons())); } void setupButtonBox() { QObject::connect(mConfigureButton, SIGNAL(clicked()), q, SLOT(showConfigDialog())); mImportSelectedButton = mButtonBox->addButton(i18n("Import Selected"), QDialogButtonBox::AcceptRole); QObject::connect(mImportSelectedButton, SIGNAL(clicked(bool)), q, SLOT(slotImportSelected())); mImportAllButton = mButtonBox->addButton(i18n("Import All"), QDialogButtonBox::AcceptRole); QObject::connect(mImportAllButton, SIGNAL(clicked(bool)), q, SLOT(slotImportAll())); QObject::connect( mButtonBox, SIGNAL(rejected()), q, SIGNAL(rejected())); } QUrl urlForBaseUrl() const { QUrl url = mUrlMap.value(mSrcBaseUrl); if (!url.isValid()) { return QUrl(); } KIO::StatJob *job = KIO::stat(url); KJobWidgets::setWindow(job, q); if (!job->exec()) { return QUrl(); } KFileItem item(job->statResult(), url, true /* delayedMimeTypes */); return item.isDir() ? url : QUrl(); } void rememberUrl(const QUrl& url) { mUrlMap.insert(mSrcBaseUrl, url); } }; ThumbnailPage::ThumbnailPage() : d(new ThumbnailPagePrivate) { d->q = this; d->mUrlMap.setConfigGroup(KConfigGroup(KSharedConfig::openConfig(), URL_FOR_BASE_URL_GROUP)); d->setupUi(this); d->setupIcons(); d->setupDirModel(); d->setupSrcUrlWidgets(); d->setupDstUrlRequester(); d->setupThumbnailView(); d->setupButtonBox(); updateImportButtons(); } ThumbnailPage::~ThumbnailPage() { delete d; } void ThumbnailPage::setSourceUrl(const QUrl& srcBaseUrl, const QString& iconName, const QString& name) { d->mSrcBaseIcon = QIcon::fromTheme(iconName); d->mSrcBaseName = name; const int size = KIconLoader::SizeHuge; d->mSrcIconLabel->setPixmap(d->mSrcBaseIcon.pixmap(size)); d->mSrcBaseUrl = srcBaseUrl; if (!d->mSrcBaseUrl.path().endsWith('/')) { d->mSrcBaseUrl.setPath(d->mSrcBaseUrl.path() + '/'); } QUrl url = d->urlForBaseUrl(); if (url.isValid()) { openUrl(url); } else { DocumentDirFinder* finder = new DocumentDirFinder(srcBaseUrl); connect(finder, SIGNAL(done(QUrl,DocumentDirFinder::Status)), SLOT(slotDocumentDirFinderDone(QUrl,DocumentDirFinder::Status))); finder->start(); } } void ThumbnailPage::slotDocumentDirFinderDone(const QUrl& url, DocumentDirFinder::Status /*status*/) { d->rememberUrl(url); openUrl(url); } void ThumbnailPage::openUrl(const QUrl& url) { d->mSrcUrl = url; QString path = QDir(d->mSrcBaseUrl.path()).relativeFilePath(d->mSrcUrl.path()); QString text; if (path.isEmpty() || path == ".") { text = d->mSrcBaseName; } else { path = QUrl::fromPercentEncoding(path.toUtf8()); - path.replace("/", QString::fromUtf8(" › ")); + path.replace('/', QString::fromUtf8(" › ")); text = QString::fromUtf8("%1 › %2").arg(d->mSrcBaseName).arg(path); } d->mSrcUrlButton->setText(text); d->mRecursiveDirModel->setUrl(url); } QList ThumbnailPage::urlList() const { return d->mUrlList; } void ThumbnailPage::setDestinationUrl(const QUrl& url) { d->mDstUrlRequester->setUrl(url); } QUrl ThumbnailPage::destinationUrl() const { return d->mDstUrlRequester->url(); } void ThumbnailPage::slotImportSelected() { importList(d->mThumbnailView->selectionModel()->selectedIndexes()); } void ThumbnailPage::slotImportAll() { QModelIndexList list; QAbstractItemModel* model = d->mThumbnailView->model(); for (int row = model->rowCount() - 1; row >= 0; --row) { list << model->index(row, 0); } importList(list); } void ThumbnailPage::importList(const QModelIndexList& list) { d->mUrlList.clear(); Q_FOREACH(const QModelIndex & index, list) { KFileItem item = itemForIndex(index); if (!ArchiveUtils::fileItemIsDirOrArchive(item)) { d->mUrlList << item.url(); } // FIXME: Handle dirs (do we want to import recursively?) } emit importRequested(); } void ThumbnailPage::updateImportButtons() { d->mImportSelectedButton->setEnabled(d->mThumbnailView->selectionModel()->hasSelection()); d->mImportAllButton->setEnabled(d->mThumbnailView->model()->rowCount(QModelIndex()) > 0); } void ThumbnailPage::showConfigDialog() { DialogGuard dialog(this); dialog->exec(); } /** * This model allows only the url passed in the constructor to appear at the root * level. This makes it possible to select the url, but not its siblings. * It also provides custom role values for the root item. */ class OnlyBaseUrlProxyModel : public QSortFilterProxyModel { public: OnlyBaseUrlProxyModel(const QUrl& url, const QIcon& icon, const QString& name, QObject* parent) : QSortFilterProxyModel(parent) , mUrl(url) , mIcon(icon) , mName(name) {} bool filterAcceptsRow(int sourceRow, const QModelIndex& sourceParent) const override { if (sourceParent.isValid()) { return true; } QModelIndex index = sourceModel()->index(sourceRow, 0); KFileItem item = itemForIndex(index); return item.url().matches(mUrl, QUrl::StripTrailingSlash); } QVariant data(const QModelIndex& index, int role) const override { if (index.parent().isValid()) { return QSortFilterProxyModel::data(index, role); } switch (role) { case Qt::DisplayRole: return mName; case Qt::DecorationRole: return mIcon; case Qt::ToolTipRole: return mUrl.toDisplayString(QUrl::PreferLocalFile); default: return QSortFilterProxyModel::data(index, role); } } private: QUrl mUrl; QIcon mIcon; QString mName; }; void ThumbnailPage::setupSrcUrlTreeView() { if (d->mSrcUrlTreeView->model()) { // Already initialized return; } KDirModel* dirModel = new KDirModel(this); dirModel->dirLister()->setDirOnlyMode(true); dirModel->dirLister()->openUrl(KIO::upUrl(d->mSrcBaseUrl)); OnlyBaseUrlProxyModel* onlyBaseUrlModel = new OnlyBaseUrlProxyModel(d->mSrcBaseUrl, d->mSrcBaseIcon, d->mSrcBaseName, this); onlyBaseUrlModel->setSourceModel(dirModel); QSortFilterProxyModel* sortModel = new QSortFilterProxyModel(this); sortModel->setDynamicSortFilter(true); sortModel->setSourceModel(onlyBaseUrlModel); sortModel->sort(0); d->mSrcUrlModelProxyMapper = new KModelIndexProxyMapper(dirModel, sortModel, this); d->mSrcUrlTreeView->setModel(sortModel); for(int i = 1; i < dirModel->columnCount(); ++i) { d->mSrcUrlTreeView->hideColumn(i); } connect(d->mSrcUrlTreeView, SIGNAL(activated(QModelIndex)), SLOT(openUrlFromIndex(QModelIndex))); connect(d->mSrcUrlTreeView, SIGNAL(clicked(QModelIndex)), SLOT(openUrlFromIndex(QModelIndex))); dirModel->expandToUrl(d->mSrcUrl); connect(dirModel, SIGNAL(expand(QModelIndex)), SLOT(slotSrcUrlModelExpand(QModelIndex))); } void ThumbnailPage::slotSrcUrlModelExpand(const QModelIndex& index) { QModelIndex viewIndex = d->mSrcUrlModelProxyMapper->mapLeftToRight(index); d->mSrcUrlTreeView->expand(viewIndex); KFileItem item = itemForIndex(index); if (item.url() == d->mSrcUrl) { d->mSrcUrlTreeView->selectionModel()->select(viewIndex, QItemSelectionModel::ClearAndSelect); } } void ThumbnailPage::toggleSrcUrlTreeView() { d->mSrcUrlTreeView->setVisible(!d->mSrcUrlTreeView->isVisible()); } void ThumbnailPage::openUrlFromIndex(const QModelIndex& index) { KFileItem item = itemForIndex(index); if (item.isNull()) { return; } QUrl url = item.url(); d->rememberUrl(url); openUrl(url); } } // namespace diff --git a/lib/imageformats/fitsformat/fitsdata.cpp b/lib/imageformats/fitsformat/fitsdata.cpp index 76bdb578..a137ecca 100644 --- a/lib/imageformats/fitsformat/fitsdata.cpp +++ b/lib/imageformats/fitsformat/fitsdata.cpp @@ -1,697 +1,697 @@ /*************************************************************************** Gwenview: an image viewer fitsimage.cpp - FITS Image ------------------- begin : Tue Feb 24 2004 copyright : (C) 2004 by Jasem Mutlaq copyright : (C) 2017 by Csaba Kertesz email : mutlaqja@ikarustech.com ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * * Some code fragments were adapted from Peter Kirchgessner's FITS plugin* * See http://members.aol.com/pkirchg for more details. * ***************************************************************************/ #include "fitsdata.h" #include #include #include FITSData::FITSData() { mode = FITS_NORMAL; debayerParams.method = DC1394_BAYER_METHOD_NEAREST; debayerParams.filter = DC1394_COLOR_FILTER_RGGB; debayerParams.offsetX = debayerParams.offsetY = 0; } FITSData::~FITSData() { int status = 0; clearImageBuffers(); if (fptr) { fits_close_file(fptr, &status); } } bool FITSData::loadFITS(QIODevice &buffer) { int status = 0, anynull = 0; long naxes[3]; char error_status[512]; QString errMessage; qint64 oldPos = buffer.pos(); QByteArray imageData; char* imageDataBuf = nullptr; size_t imageDataSize = 0; buffer.seek(0); imageData = buffer.readAll(); imageDataBuf = imageData.data(); imageDataSize = (size_t)imageData.size(); if (fptr) { fits_close_file(fptr, &status); } if (fits_open_memfile(&fptr, "", READONLY, reinterpret_cast(&imageDataBuf), &imageDataSize, 3000, nullptr, &status)) { fits_report_error(stderr, status); fits_get_errstatus(status, error_status); errMessage = QString("Could not open file %1. Error %2").arg(filename, QString::fromUtf8(error_status)); buffer.seek(oldPos); return false; } if (fits_get_img_param(fptr, 3, &(stats.bitpix), &(stats.ndim), naxes, &status)) { fits_report_error(stderr, status); fits_get_errstatus(status, error_status); errMessage = QString("FITS file open error (fits_get_img_param): %1").arg(QString::fromUtf8(error_status)); buffer.seek(oldPos); return false; } if (stats.ndim < 2) { errMessage = "1D FITS images are not supported."; buffer.seek(oldPos); return false; } switch (stats.bitpix) { case BYTE_IMG: data_type = TBYTE; stats.bytesPerPixel = sizeof(uint8_t); break; case SHORT_IMG: // Read SHORT image as USHORT data_type = TUSHORT; stats.bytesPerPixel = sizeof(int16_t); break; case USHORT_IMG: data_type = TUSHORT; stats.bytesPerPixel = sizeof(uint16_t); break; case LONG_IMG: // Read LONG image as ULONG data_type = TULONG; stats.bytesPerPixel = sizeof(int32_t); break; case ULONG_IMG: data_type = TULONG; stats.bytesPerPixel = sizeof(uint32_t); break; case FLOAT_IMG: data_type = TFLOAT; stats.bytesPerPixel = sizeof(float); break; case LONGLONG_IMG: data_type = TLONGLONG; stats.bytesPerPixel = sizeof(int64_t); break; case DOUBLE_IMG: data_type = TDOUBLE; stats.bytesPerPixel = sizeof(double); break; default: errMessage = QString("Bit depth %1 is not supported.").arg(stats.bitpix); return false; break; } if (stats.ndim < 3) { naxes[2] = 1; } if (naxes[0] == 0 || naxes[1] == 0) { errMessage = QString("Image has invalid dimensions %1x%2").arg(naxes[0], naxes[1]); buffer.seek(oldPos); return false; } stats.width = naxes[0]; stats.height = naxes[1]; stats.samples_per_channel = stats.width * stats.height; clearImageBuffers(); channels = naxes[2]; imageBuffer = new uint8_t[stats.samples_per_channel * channels * stats.bytesPerPixel]; long nelements = stats.samples_per_channel * channels; if (fits_read_img(fptr, data_type, 1, nelements, 0, imageBuffer, &anynull, &status)) { char errmsg[512]; fits_get_errstatus(status, errmsg); errMessage = QString("Error reading image: %1").arg(errmsg); fits_report_error(stderr, status); buffer.seek(oldPos); return false; } calculateStats(); if (checkDebayer()) { bayerBuffer = imageBuffer; debayer(); } return true; } void FITSData::clearImageBuffers() { delete[] imageBuffer; imageBuffer = nullptr; bayerBuffer = nullptr; } void FITSData::calculateStats(bool refresh) { // Calculate min max calculateMinMax(refresh); // Get standard deviation and mean in one run switch (data_type) { case TBYTE: runningAverageStdDev(); break; case TSHORT: runningAverageStdDev(); break; case TUSHORT: runningAverageStdDev(); break; case TLONG: runningAverageStdDev(); break; case TULONG: runningAverageStdDev(); break; case TFLOAT: runningAverageStdDev(); break; case TLONGLONG: runningAverageStdDev(); break; case TDOUBLE: runningAverageStdDev(); break; default: return; } stats.SNR = stats.mean[0] / stats.stddev[0]; } int FITSData::calculateMinMax(bool refresh) { int status, nfound = 0; status = 0; if (fptr && refresh == false) { if (fits_read_key_dbl(fptr, "DATAMIN", &(stats.min[0]), nullptr, &status) == 0) { nfound++; } if (fits_read_key_dbl(fptr, "DATAMAX", &(stats.max[0]), nullptr, &status) == 0) { nfound++; } // If we found both keywords, no need to calculate them, unless they are both zeros if (nfound == 2 && !(stats.min[0] == 0 && stats.max[0] == 0)) { return 0; } } stats.min[0] = 1.0E30; stats.max[0] = -1.0E30; stats.min[1] = 1.0E30; stats.max[1] = -1.0E30; stats.min[2] = 1.0E30; stats.max[2] = -1.0E30; switch (data_type) { case TBYTE: calculateMinMax(); break; case TSHORT: calculateMinMax(); break; case TUSHORT: calculateMinMax(); break; case TLONG: calculateMinMax(); break; case TULONG: calculateMinMax(); break; case TFLOAT: calculateMinMax(); break; case TLONGLONG: calculateMinMax(); break; case TDOUBLE: calculateMinMax(); break; default: break; } //qDebug() << "DATAMIN: " << stats.min << " - DATAMAX: " << stats.max; return 0; } template void FITSData::calculateMinMax() { T *buffer = reinterpret_cast(imageBuffer); if (channels == 1) { for (unsigned int i = 0; i < stats.samples_per_channel; i++) { if (buffer[i] < stats.min[0]) { stats.min[0] = buffer[i]; } else if (buffer[i] > stats.max[0]) { stats.max[0] = buffer[i]; } } } else { int g_offset = stats.samples_per_channel; int b_offset = stats.samples_per_channel * 2; for (unsigned int i = 0; i < stats.samples_per_channel; i++) { if (buffer[i] < stats.min[0]) { stats.min[0] = buffer[i]; } else if (buffer[i] > stats.max[0]) { stats.max[0] = buffer[i]; } if (buffer[i + g_offset] < stats.min[1]) { stats.min[1] = buffer[i + g_offset]; } else if (buffer[i + g_offset] > stats.max[1]) { stats.max[1] = buffer[i + g_offset]; } if (buffer[i + b_offset] < stats.min[2]) { stats.min[2] = buffer[i + b_offset]; } else if (buffer[i + b_offset] > stats.max[2]) { stats.max[2] = buffer[i + b_offset]; } } } } template void FITSData::runningAverageStdDev() { T *buffer = reinterpret_cast(imageBuffer); int m_n = 2; double m_oldM = 0, m_newM = 0, m_oldS = 0, m_newS = 0; m_oldM = m_newM = buffer[0]; for (unsigned int i = 1; i < stats.samples_per_channel; i++) { m_newM = m_oldM + (buffer[i] - m_oldM) / m_n; m_newS = m_oldS + (buffer[i] - m_oldM) * (buffer[i] - m_newM); m_oldM = m_newM; m_oldS = m_newS; m_n++; } double variance = (m_n == 2 ? 0 : m_newS / (m_n - 2)); stats.mean[0] = m_newM; stats.stddev[0] = sqrt(variance); } int FITSData::getFITSRecord(QString &recordList, int &nkeys) { char *header = nullptr; int status = 0; if (fits_hdr2str(fptr, 0, nullptr, 0, &header, &nkeys, &status)) { fits_report_error(stderr, status); free(header); return -1; } recordList = QString(header); free(header); return 0; } uint8_t *FITSData::getImageBuffer() { return imageBuffer; } bool FITSData::checkDebayer() { int status = 0; char bayerPattern[64]; // Let's search for BAYERPAT keyword, if it's not found we return as there is no bayer pattern in this image if (fits_read_keyword(fptr, "BAYERPAT", bayerPattern, nullptr, &status)) { return false; } if (stats.bitpix != 16 && stats.bitpix != 8) { return false; } QString pattern(bayerPattern); - pattern = pattern.remove("'").trimmed(); + pattern = pattern.remove('\'').trimmed(); if (pattern == "RGGB") { debayerParams.filter = DC1394_COLOR_FILTER_RGGB; } else if (pattern == "GBRG") { debayerParams.filter = DC1394_COLOR_FILTER_GBRG; } else if (pattern == "GRBG") { debayerParams.filter = DC1394_COLOR_FILTER_GRBG; } else if (pattern == "BGGR") { debayerParams.filter = DC1394_COLOR_FILTER_BGGR; // We return unless we find a valid pattern } else { return false; } fits_read_key(fptr, TINT, "XBAYROFF", &debayerParams.offsetX, nullptr, &status); fits_read_key(fptr, TINT, "YBAYROFF", &debayerParams.offsetY, nullptr, &status); return true; } bool FITSData::debayer() { if (!bayerBuffer) { int anynull = 0, status = 0; bayerBuffer = imageBuffer; if (fits_read_img(fptr, data_type, 1, stats.samples_per_channel, 0, bayerBuffer, &anynull, &status)) { char errmsg[512]; fits_get_errstatus(status, errmsg); return false; } } switch (data_type) { case TBYTE: return debayer_8bit(); case TUSHORT: return debayer_16bit(); default: return false; } return false; } bool FITSData::debayer_8bit() { dc1394error_t error_code; int rgb_size = stats.samples_per_channel * 3 * stats.bytesPerPixel; uint8_t *destinationBuffer = new uint8_t[rgb_size]; int ds1394_height = stats.height; uint8_t *dc1394_source = bayerBuffer; if (debayerParams.offsetY == 1) { dc1394_source += stats.width; ds1394_height--; } if (debayerParams.offsetX == 1) { dc1394_source++; } error_code = dc1394_bayer_decoding_8bit(dc1394_source, destinationBuffer, stats.width, ds1394_height, debayerParams.filter, debayerParams.method); if (error_code != DC1394_SUCCESS) { channels = 1; delete[] destinationBuffer; return false; } if (channels == 1) { delete[] imageBuffer; imageBuffer = new uint8_t[rgb_size]; } // Data in R1G1B1, we need to copy them into 3 layers for FITS uint8_t *rBuff = imageBuffer; uint8_t *gBuff = imageBuffer + (stats.width * stats.height); uint8_t *bBuff = imageBuffer + (stats.width * stats.height * 2); int imax = stats.samples_per_channel * 3 - 3; for (int i = 0; i <= imax; i += 3) { *rBuff++ = destinationBuffer[i]; *gBuff++ = destinationBuffer[i + 1]; *bBuff++ = destinationBuffer[i + 2]; } channels = 3; delete[] destinationBuffer; bayerBuffer = nullptr; return true; } bool FITSData::debayer_16bit() { dc1394error_t error_code; int rgb_size = stats.samples_per_channel * 3 * stats.bytesPerPixel; uint8_t *destinationBuffer = new uint8_t[rgb_size]; uint16_t *buffer = reinterpret_cast(bayerBuffer); uint16_t *dstBuffer = reinterpret_cast(destinationBuffer); int ds1394_height = stats.height; uint16_t *dc1394_source = buffer; if (debayerParams.offsetY == 1) { dc1394_source += stats.width; ds1394_height--; } if (debayerParams.offsetX == 1) { dc1394_source++; } error_code = dc1394_bayer_decoding_16bit(dc1394_source, dstBuffer, stats.width, ds1394_height, debayerParams.filter, debayerParams.method, 16); if (error_code != DC1394_SUCCESS) { channels = 1; delete[] destinationBuffer; return false; } if (channels == 1) { delete[] imageBuffer; imageBuffer = new uint8_t[rgb_size]; if (!imageBuffer) { delete[] destinationBuffer; return false; } } buffer = reinterpret_cast(imageBuffer); // Data in R1G1B1, we need to copy them into 3 layers for FITS uint16_t *rBuff = buffer; uint16_t *gBuff = buffer + (stats.width * stats.height); uint16_t *bBuff = buffer + (stats.width * stats.height * 2); int imax = stats.samples_per_channel * 3 - 3; for (int i = 0; i <= imax; i += 3) { *rBuff++ = dstBuffer[i]; *gBuff++ = dstBuffer[i + 1]; *bBuff++ = dstBuffer[i + 2]; } channels = 3; delete[] destinationBuffer; bayerBuffer = nullptr; return true; } QString FITSData::getLastError() const { return lastError; } template void FITSData::convertToQImage(double dataMin, double dataMax, double scale, double zero, QImage &image) { T *buffer = (T*)getImageBuffer(); const T limit = std::numeric_limits::max(); T bMin = dataMin < 0 ? 0 : dataMin; T bMax = dataMax > limit ? limit : dataMax; uint16_t w = getWidth(); uint16_t h = getHeight(); uint32_t size = getSize(); double val; if (getNumOfChannels() == 1) { /* Fill in pixel values using indexed map, linear scale */ for (int j = 0; j < h; j++) { unsigned char *scanLine = image.scanLine(j); for (int i = 0; i < w; i++) { val = qBound(bMin, buffer[j * w + i], bMax); val = val * scale + zero; scanLine[i] = qBound(0, (unsigned char)val, 255); } } } else { double rval = 0, gval = 0, bval = 0; QRgb value; /* Fill in pixel values using indexed map, linear scale */ for (int j = 0; j < h; j++) { QRgb *scanLine = reinterpret_cast((image.scanLine(j))); for (int i = 0; i < w; i++) { rval = qBound(bMin, buffer[j * w + i], bMax); gval = qBound(bMin, buffer[j * w + i + size], bMax); bval = qBound(bMin, buffer[j * w + i + size * 2], bMax); value = qRgb(rval * scale + zero, gval * scale + zero, bval * scale + zero); scanLine[i] = value; } } } } QImage FITSData::FITSToImage(QIODevice &buffer) { QImage fitsImage; double min, max; FITSData data; bool rc = data.loadFITS(buffer); if (rc == false) { return fitsImage; } data.getMinMax(&min, &max); if (min == max) { fitsImage.fill(Qt::white); return fitsImage; } if (data.getNumOfChannels() == 1) { fitsImage = QImage(data.getWidth(), data.getHeight(), QImage::Format_Indexed8); fitsImage.setColorCount(256); for (int i = 0; i < 256; i++) { fitsImage.setColor(i, qRgb(i, i, i)); } } else { fitsImage = QImage(data.getWidth(), data.getHeight(), QImage::Format_RGB32); } double dataMin = data.stats.mean[0] - data.stats.stddev[0]; double dataMax = data.stats.mean[0] + data.stats.stddev[0] * 3; double bscale = 255. / (dataMax - dataMin); double bzero = (-dataMin) * (255. / (dataMax - dataMin)); // Long way to do this since we do not want to use templated functions here switch (data.getDataType()) { case TBYTE: data.convertToQImage(dataMin, dataMax, bscale, bzero, fitsImage); break; case TSHORT: data.convertToQImage(dataMin, dataMax, bscale, bzero, fitsImage); break; case TUSHORT: data.convertToQImage(dataMin, dataMax, bscale, bzero, fitsImage); break; case TLONG: data.convertToQImage(dataMin, dataMax, bscale, bzero, fitsImage); break; case TULONG: data.convertToQImage(dataMin, dataMax, bscale, bzero, fitsImage); break; case TFLOAT: data.convertToQImage(dataMin, dataMax, bscale, bzero, fitsImage); break; case TLONGLONG: data.convertToQImage(dataMin, dataMax, bscale, bzero, fitsImage); break; case TDOUBLE: data.convertToQImage(dataMin, dataMax, bscale, bzero, fitsImage); break; default: break; } return fitsImage; } diff --git a/tests/auto/contextmanagertest.cpp b/tests/auto/contextmanagertest.cpp index c24bcac7..80c7f31e 100644 --- a/tests/auto/contextmanagertest.cpp +++ b/tests/auto/contextmanagertest.cpp @@ -1,117 +1,117 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2013 Aurélien Gâteau This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ // Self #include "contextmanagertest.h" // Local #include #include #include // Qt #include #include // KDE #include #include #include using namespace Gwenview; using namespace TestUtils; QTEST_MAIN(ContextManagerTest) void ContextManagerTest::testRemove() { // When the current image is removed Gwenview must go to the next image if // there is any, otherwise to the previous image. SandBoxDir sandBox; sandBox.fill(QStringList() << "a" << "b" << "c"); QUrl dirUrl = QUrl::fromLocalFile(sandBox.absolutePath()); SortedDirModel dirModel; { QEventLoop loop; connect(dirModel.dirLister(), SIGNAL(completed()), &loop, SLOT(quit())); dirModel.dirLister()->openUrl(dirUrl); loop.exec(); } QCOMPARE(dirModel.rowCount(), 3); ContextManager manager(&dirModel, 0); // Select second row manager.selectionModel()->setCurrentIndex(dirModel.index(1, 0), QItemSelectionModel::Select); // Remove "b", `manager` should select "c" - sandBox.remove("b"); + sandBox.remove(QStringLiteral("b")); dirModel.dirLister()->updateDirectory(dirUrl); while (dirModel.rowCount() == 3) { QTest::qWait(100); } QModelIndex currentIndex = manager.selectionModel()->currentIndex(); QCOMPARE(currentIndex.row(), 1); QCOMPARE(currentIndex.data(Qt::DisplayRole).toString(), QStringLiteral("c")); // Remove "c", `manager` should select "a" - sandBox.remove("c"); + sandBox.remove(QStringLiteral("c")); dirModel.dirLister()->updateDirectory(dirUrl); while (dirModel.rowCount() == 2) { QTest::qWait(100); } currentIndex = manager.selectionModel()->currentIndex(); QCOMPARE(currentIndex.row(), 0); QCOMPARE(currentIndex.data(Qt::DisplayRole).toString(), QStringLiteral("a")); } void ContextManagerTest::testInvalidDirUrl() { class DirLister : public KDirLister { public: DirLister() : mOpenUrlCalled(false) { setAutoErrorHandlingEnabled(false, nullptr); } bool openUrl(const QUrl &url, OpenUrlFlags flags = NoFlags) override { mOpenUrlCalled = true; return KDirLister::openUrl(url, flags); } bool mOpenUrlCalled; }; SortedDirModel dirModel; DirLister* dirLister = new DirLister; dirModel.setDirLister(dirLister); ContextManager manager(&dirModel, nullptr); manager.setCurrentDirUrl(QUrl()); QVERIFY(!dirLister->mOpenUrlCalled); }