diff --git a/CMakeLists.txt b/CMakeLists.txt index 5e204eb1..d201c275 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,198 +1,198 @@ cmake_minimum_required (VERSION 3.0 FATAL_ERROR) # KDE Application Version, managed by release script set (KDE_APPLICATIONS_VERSION_MAJOR "20") set (KDE_APPLICATIONS_VERSION_MINOR "03") set (KDE_APPLICATIONS_VERSION_MICRO "70") set (KDE_APPLICATIONS_VERSION "${KDE_APPLICATIONS_VERSION_MAJOR}.${KDE_APPLICATIONS_VERSION_MINOR}.${KDE_APPLICATIONS_VERSION_MICRO}") project(gwenview VERSION ${KDE_APPLICATIONS_VERSION}) set(CMAKE_MODULE_PATH ${CMAKE_SOURCE_DIR}/cmake ) set (QT_MIN_VERSION "5.9.0") set (KF5_MIN_VERSION "5.43.0") find_package(ECM ${KF5_MIN_VERSION} REQUIRED NO_MODULE) set(CMAKE_MODULE_PATH ${CMAKE_MODULE_PATH} ${ECM_MODULE_PATH}) include(KDEInstallDirs) include(KDECMakeSettings) include(KDECompilerSettings NO_POLICY_SCOPE) include(ECMOptionalAddSubdirectory) include(ECMInstallIcons) include(ECMSetupVersion) include(ECMMarkNonGuiExecutable) include(ECMGenerateHeaders) include(ECMAddAppIcon) include(GenerateExportHeader) include(FeatureSummary) ## Generate header with version number ecm_setup_version(${KDE_APPLICATIONS_VERSION} VARIABLE_PREFIX GWENVIEW VERSION_HEADER "${CMAKE_CURRENT_BINARY_DIR}/lib/gwenview_version.h" ) ## CMake options set(GWENVIEW_SEMANTICINFO_BACKEND_NONE OFF) set(GWENVIEW_SEMANTICINFO_BACKEND_FAKE OFF) set(GWENVIEW_SEMANTICINFO_BACKEND_BALOO OFF) set(GWENVIEW_SEMANTICINFO_BACKEND "Baloo" CACHE STRING "Semantic info backend for Gwenview (Baloo/Fake/None)") # Init GWENVIEW_SEMANTICINFO_BACKEND_* vars if(GWENVIEW_SEMANTICINFO_BACKEND STREQUAL "None") set(GWENVIEW_SEMANTICINFO_BACKEND_NONE ON) elseif(GWENVIEW_SEMANTICINFO_BACKEND STREQUAL "Fake") set(GWENVIEW_SEMANTICINFO_BACKEND_FAKE ON) else() set(GWENVIEW_SEMANTICINFO_BACKEND_BALOO ON) endif() find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED Core Widgets Concurrent Svg OpenGL PrintSupport) find_package(Qt5DBus ${QT_MIN_VERSION} CONFIG QUIET) set(HAVE_QTDBUS ${Qt5DBus_FOUND}) find_package(Phonon4Qt5 4.6.60 NO_MODULE REQUIRED) include_directories(BEFORE ${PHONON_INCLUDES}) add_definitions(-DPHONON_LIB_SONAME=\"${PHONON_LIB_SONAME}\") find_package(KF5 ${KF5_MIN_VERSION} REQUIRED COMPONENTS KIO ItemModels I18n DocTools Parts WindowSystem IconThemes Notifications WidgetsAddons ) find_package(KF5 ${KF5_MIN_VERSION} OPTIONAL_COMPONENTS Activities Purpose ) if (KF5ConfigWidgets_VERSION VERSION_GREATER "5.56.0" AND ${Phonon4Qt5_VERSION} VERSION_GREATER "4.10.59") add_definitions(-DQT_NO_FOREACH) MESSAGE(STATUS "compile without foreach") endif() ## Dependencies find_package(JPEG) set_package_properties(JPEG PROPERTIES URL "http://libjpeg.sourceforge.net/" DESCRIPTION "JPEG image manipulation support" TYPE REQUIRED) find_package(PNG) set_package_properties(PNG PROPERTIES URL "http://www.libpng.org" DESCRIPTION "PNG image manipulation support" TYPE REQUIRED) find_package(Exiv2) -set_package_properties(Exiv2 PROPERTIES URL "http://www.exiv2.org" DESCRIPTION "image metadata support" TYPE REQUIRED) +set_package_properties(Exiv2 PROPERTIES URL "https://www.exiv2.org" DESCRIPTION "image metadata support" TYPE REQUIRED) find_package(CFitsio) -set_package_properties(CFitsio PROPERTIES URL "http://heasarc.gsfc.nasa.gov/fitsio/fitsio.html" DESCRIPTION "FITS format support" TYPE OPTIONAL) +set_package_properties(CFitsio PROPERTIES URL "https://heasarc.gsfc.nasa.gov/fitsio/fitsio.html" DESCRIPTION "FITS format support" TYPE OPTIONAL) if(CFITSIO_FOUND) set(HAVE_FITS true) endif() find_package(KF5Kipi) if (KF5Kipi_FOUND) set(KIPI_FOUND true) endif() set_package_properties(KF5Kipi PROPERTIES URL "https://phabricator.kde.org/source/kipi-plugins/" DESCRIPTION "Provides various image manipulation and export features" TYPE OPTIONAL) find_package(LCMS2) set_package_properties(LCMS2 PROPERTIES URL "http://www.littlecms.com" DESCRIPTION "Color management engine" TYPE REQUIRED) if (GWENVIEW_SEMANTICINFO_BACKEND_BALOO) find_package(KF5Baloo 5.1.90) set_package_properties(KF5Baloo PROPERTIES URL "https://commits.kde.org/baloo" DESCRIPTION "Desktop-wide semantic information support" TYPE OPTIONAL) if (NOT KF5Baloo_FOUND) message (STATUS "You have selected Baloo for semantic backend, but required version was not found. Overriding the backend to None") unset(GWENVIEW_SEMANTICINFO_BACKEND_BALOO) set(GWENVIEW_SEMANTICINFO_BACKEND_NONE ON) endif () endif () find_package(KF5KDcraw) if (KF5KDcraw_FOUND) add_definitions(-DKDCRAW_FOUND) endif() set_package_properties(KF5KDcraw PROPERTIES URL "https://phabricator.kde.org/source/libkdcraw/" DESCRIPTION "C++ interface around LibRaw library used to decode RAW picture files" TYPE OPTIONAL) find_package(X11) if(X11_FOUND) find_package(Qt5 ${QT_MIN_VERSION} CONFIG REQUIRED X11Extras) set(HAVE_X11 1) endif() configure_file(config-gwenview.h.cmake ${CMAKE_CURRENT_BINARY_DIR}/config-gwenview.h) include_directories(${CMAKE_CURRENT_BINARY_DIR}) add_definitions(-DQT_NO_URL_CAST_FROM_STRING) function(JoinListAsString VALUES GLUE OUTPUT) string(REPLACE ";" "${GLUE}" _TMP_STR "${VALUES}") set(${OUTPUT} "${_TMP_STR}" PARENT_SCOPE) endfunction() set(IMAGE_MIME_TYPES_LIST image/gif image/jpeg image/png image/bmp image/x-eps image/x-icns image/x-ico image/x-portable-bitmap image/x-portable-graymap image/x-portable-pixmap image/x-xbitmap image/x-xpixmap image/tiff image/x-psd image/x-webp image/webp image/x-tga application/x-krita ) if (KF5KDcraw_FOUND) list(APPEND IMAGE_MIME_TYPES_LIST image/x-kde-raw image/x-canon-cr2 image/x-canon-crw image/x-kodak-dcr image/x-adobe-dng image/x-kodak-k25 image/x-kodak-kdc image/x-minolta-mrw image/x-nikon-nef image/x-olympus-orf image/x-pentax-pef image/x-fuji-raf image/x-panasonic-rw image/x-sony-sr2 image/x-sony-srf image/x-sigma-x3f image/x-sony-arw image/x-panasonic-rw2 ) endif() JoinListAsString("${IMAGE_MIME_TYPES_LIST}" ";" IMAGE_MIME_TYPES) ## dirs to build add_subdirectory(lib) add_subdirectory(app) add_subdirectory(importer) add_subdirectory(part) add_subdirectory(tests) add_subdirectory(icons) add_subdirectory(images) add_subdirectory(cursors) add_subdirectory(color-schemes) add_subdirectory(doc) add_subdirectory(kconf_update) feature_summary(WHAT ALL INCLUDE_QUIET_PACKAGES FATAL_ON_MISSING_REQUIRED_PACKAGES) diff --git a/cmake/FindExiv2.cmake b/cmake/FindExiv2.cmake index fc729d41..0701b896 100644 --- a/cmake/FindExiv2.cmake +++ b/cmake/FindExiv2.cmake @@ -1,117 +1,117 @@ #.rst: # FindExiv2 # --------- # # Try to find the Exiv2 library. # # This will define the following variables: # # ``Exiv2_FOUND`` # System has Exiv2. # # ``Exiv2_VERSION`` # The version of Exiv2. # # ``Exiv2_INCLUDE_DIRS`` # This should be passed to target_include_directories() if # the target is not used for linking. # # ``Exiv2_LIBRARIES`` # The Exiv2 library. # This can be passed to target_link_libraries() instead of # the ``Exiv2::Exiv2`` target # # If ``Exiv2_FOUND`` is TRUE, the following imported target # will be available: # # ``Exiv2::Exiv2`` # The Exiv2 library # # #============================================================================= # Copyright (c) 2010, Alexander Neundorf, # Copyright (c) 2008, Gilles Caulier, # # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # 3. The name of the author may not be used to endorse or promote products # derived from this software without specific prior written permission. # # THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR # IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES # OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. # IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT # NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, # DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY # THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT # (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF # THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. #============================================================================= find_package(PkgConfig QUIET) pkg_check_modules(PC_EXIV2 QUIET exiv2) find_path(Exiv2_INCLUDE_DIRS NAMES exiv2/exif.hpp HINTS ${PC_EXIV2_INCLUDEDIR} ) find_library(Exiv2_LIBRARIES NAMES exiv2 libexiv2 HINTS ${PC_EXIV2_LIBRARY_DIRS} ) set(Exiv2_VERSION ${PC_EXIV2_VERSION}) if(NOT Exiv2_VERSION AND DEFINED Exiv2_INCLUDE_DIRS) # With exiv >= 0.27, the version #defines are in exv_conf.h instead of version.hpp foreach(_exiv2_version_file "version.hpp" "exv_conf.h") if(EXISTS "${Exiv2_INCLUDE_DIRS}/exiv2/${_exiv2_version_file}") file(READ "${Exiv2_INCLUDE_DIRS}/exiv2/${_exiv2_version_file}" _exiv_version_file_content) string(REGEX MATCH "#define EXIV2_MAJOR_VERSION[ ]+\\([0-9]+\\)" EXIV2_MAJOR_VERSION_MATCH ${_exiv_version_file_content}) string(REGEX MATCH "#define EXIV2_MINOR_VERSION[ ]+\\([0-9]+\\)" EXIV2_MINOR_VERSION_MATCH ${_exiv_version_file_content}) string(REGEX MATCH "#define EXIV2_PATCH_VERSION[ ]+\\([0-9]+\\)" EXIV2_PATCH_VERSION_MATCH ${_exiv_version_file_content}) if(EXIV2_MAJOR_VERSION_MATCH) string(REGEX REPLACE ".*_MAJOR_VERSION[ ]+\\((.*)\\)" "\\1" EXIV2_MAJOR_VERSION ${EXIV2_MAJOR_VERSION_MATCH}) string(REGEX REPLACE ".*_MINOR_VERSION[ ]+\\((.*)\\)" "\\1" EXIV2_MINOR_VERSION ${EXIV2_MINOR_VERSION_MATCH}) string(REGEX REPLACE ".*_PATCH_VERSION[ ]+\\((.*)\\)" "\\1" EXIV2_PATCH_VERSION ${EXIV2_PATCH_VERSION_MATCH}) endif() endif() endforeach() set(Exiv2_VERSION "${EXIV2_MAJOR_VERSION}.${EXIV2_MINOR_VERSION}.${EXIV2_PATCH_VERSION}") endif() # Deprecated, for backward compatibility set(EXIV2_INCLUDE_DIR "${Exiv2_INCLUDE_DIRS}") set(EXIV2_LIBRARIES "${Exiv2_LIBRARIES}") include(FindPackageHandleStandardArgs) find_package_handle_standard_args(Exiv2 FOUND_VAR Exiv2_FOUND REQUIRED_VARS Exiv2_LIBRARIES Exiv2_INCLUDE_DIRS VERSION_VAR Exiv2_VERSION ) mark_as_advanced(Exiv2_INCLUDE_DIRS Exiv2_LIBRARIES) if(Exiv2_FOUND AND NOT TARGET Exiv2::Exiv2) add_library(Exiv2::Exiv2 UNKNOWN IMPORTED) set_target_properties(Exiv2::Exiv2 PROPERTIES IMPORTED_LOCATION "${Exiv2_LIBRARIES}" INTERFACE_INCLUDE_DIRECTORIES "${Exiv2_INCLUDE_DIRS}" ) endif() include(FeatureSummary) set_package_properties(Exiv2 PROPERTIES - URL "http://www.exiv2.org" + URL "https://www.exiv2.org" DESCRIPTION "Image metadata support" ) diff --git a/lib/crop/cropwidget.cpp b/lib/crop/cropwidget.cpp index 13874e98..447fee7f 100644 --- a/lib/crop/cropwidget.cpp +++ b/lib/crop/cropwidget.cpp @@ -1,575 +1,575 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2007 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 // Qt #include #include #include #include #include #include #include #include #include #include #include #include // KDE #include // Local #include #include #include "croptool.h" #include "cropwidget.h" #include "flowlayout.h" namespace Gwenview { // Euclidean algorithm to compute the greatest common divisor of two integers. // Found at: -// http://en.wikipedia.org/wiki/Euclidean_algorithm +// https://en.wikipedia.org/wiki/Euclidean_algorithm static int gcd(int a, int b) { return b == 0 ? a : gcd(b, a % b); } static QSize ratio(const QSize &size) { const int divisor = gcd(size.width(), size.height()); return size / divisor; } struct CropWidgetPrivate : public QWidget { CropWidget* q; QList mAdvancedWidgets; QWidget* mPreserveAspectRatioWidget; QCheckBox* advancedCheckBox; QComboBox* ratioComboBox; QSpinBox* widthSpinBox; QSpinBox* heightSpinBox; QSpinBox* leftSpinBox; QSpinBox* topSpinBox; QCheckBox* preserveAspectRatioCheckBox; QDialogButtonBox* dialogButtonBox; Document::Ptr mDocument; CropTool* mCropTool; bool mUpdatingFromCropTool; int mCurrentImageComboBoxIndex; int mCropRatioComboBoxCurrentIndex; bool ratioIsConstrained() const { return cropRatio() > 0; } QSizeF chosenRatio() const { // A size of 0 represents no ratio, i.e. the combobox is empty if (ratioComboBox->currentText().isEmpty()) { return QSizeF(0, 0); } // A preset ratio is selected const int index = ratioComboBox->currentIndex(); if (index != -1 && ratioComboBox->currentText() == ratioComboBox->itemText(index)) { return ratioComboBox->currentData().toSizeF(); } // A custom ratio has been entered, extract ratio from the text // If invalid, return zero size instead const QStringList lst = ratioComboBox->currentText().split(QLatin1Char(':')); if (lst.size() != 2) { return QSizeF(0, 0); } bool ok; const double width = lst[0].toDouble(&ok); if (!ok) { return QSizeF(0, 0); } const double height = lst[1].toDouble(&ok); if (!ok) { return QSizeF(0, 0); } // Valid custom value return QSizeF(width, height); } void setChosenRatio(QSizeF size) const { // Size matches preset ratio, let's set the combobox to that const int index = ratioComboBox->findData(size); if (index >= 0) { ratioComboBox->setCurrentIndex(index); return; } // Deselect whatever was selected if anything ratioComboBox->setCurrentIndex(-1); // If size is 0 (represents blank combobox, i.e., unrestricted) if (size.isEmpty()) { ratioComboBox->clearEditText(); return; } // Size must be custom ratio, convert to text and add to combobox QString ratioString = QStringLiteral("%1:%2").arg(size.width()).arg(size.height()); ratioComboBox->setCurrentText(ratioString); } double cropRatio() const { if (q->advancedSettingsEnabled()) { QSizeF size = chosenRatio(); if (size.isEmpty()) { return 0; } return size.height() / size.width(); } if (q->preserveAspectRatio()) { QSizeF size = ratio(mDocument->size()); return size.height() / size.width(); } return 0; } void addRatioToComboBox(const QSizeF& size, const QString& label = QString()) { QString text = label.isEmpty() ? QStringLiteral("%1:%2").arg(size.width()).arg(size.height()) : label; ratioComboBox->addItem(text, QVariant(size)); } void addSectionHeaderToComboBox(const QString& title) { // Insert a line ratioComboBox->insertSeparator(ratioComboBox->count()); // Insert our section header // This header is made of a separator with a text. We reset // Qt::AccessibleDescriptionRole to the header text otherwise QComboBox // delegate will draw a separator line instead of our text. int index = ratioComboBox->count(); ratioComboBox->insertSeparator(index); ratioComboBox->setItemText(index, title); ratioComboBox->setItemData(index, title, Qt::AccessibleDescriptionRole); ratioComboBox->setItemData(index, Qt::AlignHCenter, Qt::TextAlignmentRole); } void initRatioComboBox() { QList ratioList; const qreal sqrt2 = qSqrt(2.); ratioList << QSizeF(16, 9) << QSizeF(7, 5) << QSizeF(3, 2) << QSizeF(4, 3) << QSizeF(5, 4); addRatioToComboBox(ratio(mDocument->size()), i18n("Current Image")); mCurrentImageComboBoxIndex = ratioComboBox->count() - 1; // We need to refer to this ratio later addRatioToComboBox(QSizeF(1, 1), i18n("Square")); addRatioToComboBox(ratio(QApplication::desktop()->screenGeometry().size()), i18n("This Screen")); // The previous string should be changed to // addRatioToComboBox(ratio(QGuiApplication::screenAt(QCursor::pos())->geometry().size()), i18n("This Screen")); // after switching to Qt > 5.9 addSectionHeaderToComboBox(i18n("Landscape")); for (const QSizeF& size : qAsConst(ratioList)) { addRatioToComboBox(size); } addRatioToComboBox(QSizeF(sqrt2, 1), i18n("ISO (A4, A3...)")); addRatioToComboBox(QSizeF(11, 8.5), i18n("US Letter")); addSectionHeaderToComboBox(i18n("Portrait")); for (QSizeF size : qAsConst(ratioList)) { size.transpose(); addRatioToComboBox(size); } addRatioToComboBox(QSizeF(1, sqrt2), i18n("ISO (A4, A3...)")); addRatioToComboBox(QSizeF(8.5, 11), i18n("US Letter")); ratioComboBox->setMaxVisibleItems(ratioComboBox->count()); ratioComboBox->clearEditText(); QLineEdit* edit = qobject_cast(ratioComboBox->lineEdit()); Q_ASSERT(edit); // Do not use i18n("%1:%2") because ':' should not be translated, it is // used to parse the ratio string. edit->setPlaceholderText(QStringLiteral("%1:%2").arg(i18n("Width"), i18n("Height"))); // Enable clear button edit->setClearButtonEnabled(true); // Must manually adjust minimum width because the auto size adjustment doesn't take the // clear button into account const int width = ratioComboBox->minimumSizeHint().width(); ratioComboBox->setMinimumWidth(width + 24); mCropRatioComboBoxCurrentIndex = -1; ratioComboBox->setCurrentIndex(mCropRatioComboBoxCurrentIndex); } QRect cropRect() const { QRect rect( leftSpinBox->value(), topSpinBox->value(), widthSpinBox->value(), heightSpinBox->value() ); return rect; } void initSpinBoxes() { QSize size = mDocument->size(); leftSpinBox->setMaximum(size.width()); widthSpinBox->setMaximum(size.width()); topSpinBox->setMaximum(size.height()); heightSpinBox->setMaximum(size.height()); // When users change the crop rectangle, QSpinBox::setMaximum will be called // again, which then adapts the sizeHint due to a different maximum number // of digits, leading to horizontal movement in the layout. This can be // avoided by setting the minimum width so it fits the largest value possible. leftSpinBox->setMinimumWidth(leftSpinBox->sizeHint().width()); widthSpinBox->setMinimumWidth(widthSpinBox->sizeHint().width()); topSpinBox->setMinimumWidth(topSpinBox->sizeHint().width()); heightSpinBox->setMinimumWidth(heightSpinBox->sizeHint().width()); } void initDialogButtonBox() { QPushButton* cropButton = dialogButtonBox->button(QDialogButtonBox::Ok); cropButton->setIcon(QIcon::fromTheme(QStringLiteral("transform-crop-and-resize"))); cropButton->setText(i18n("Crop")); QObject::connect(dialogButtonBox, &QDialogButtonBox::accepted, q, &CropWidget::cropRequested); QObject::connect(dialogButtonBox, &QDialogButtonBox::rejected, q, &CropWidget::done); } QWidget* boxWidget(QWidget* parent = nullptr) { QWidget* widget = new QWidget(parent); QHBoxLayout* layout = new QHBoxLayout(widget); layout->setContentsMargins(0, 0, 0, 0); layout->setSpacing(2); return widget; } void setupUi(QWidget* cropWidget) { cropWidget->setObjectName(QStringLiteral("CropWidget")); FlowLayout* flowLayout = new FlowLayout(cropWidget, 6, 0); flowLayout->setObjectName(QStringLiteral("CropWidgetFlowLayout")); flowLayout->setAlignment(Qt::AlignCenter); flowLayout->setVerticalSpacing(6); // (1) Checkbox QWidget* box = boxWidget(cropWidget); advancedCheckBox = new QCheckBox(i18nc("@option:check", "Advanced settings"), box); advancedCheckBox->setFocusPolicy(Qt::NoFocus); box->layout()->addWidget(advancedCheckBox); flowLayout->addWidget(box); flowLayout->addSpacing(14); // (2) Ratio combobox (Advanced settings) box = boxWidget(cropWidget); mAdvancedWidgets << box; QLabel* label = new QLabel(i18nc("@label:listbox", "Aspect ratio:"), box); label->setContentsMargins(4, 4, 4, 4); box->layout()->addWidget(label); ratioComboBox = new QComboBox(box); ratioComboBox->setEditable(true); ratioComboBox->setInsertPolicy(QComboBox::NoInsert); box->layout()->addWidget(ratioComboBox); flowLayout->addWidget(box); flowLayout->addSpacing(8); // (3) Size spinboxes (Advanced settings) box = boxWidget(cropWidget); mAdvancedWidgets << box; label = new QLabel(i18nc("@label:spinbox", "Size:"), box); label->setContentsMargins(4, 4, 4, 4); box->layout()->addWidget(label); QHBoxLayout* innerLayout = new QHBoxLayout(); innerLayout->setSpacing(3); widthSpinBox = new QSpinBox(box); widthSpinBox->setAlignment(Qt::AlignCenter); innerLayout->addWidget(widthSpinBox); heightSpinBox = new QSpinBox(box); heightSpinBox->setAlignment(Qt::AlignCenter); innerLayout->addWidget(heightSpinBox); box->layout()->addItem(innerLayout); flowLayout->addWidget(box); flowLayout->addSpacing(8); // (4) Position spinboxes (Advanced settings) box = boxWidget(cropWidget); mAdvancedWidgets << box; label = new QLabel(i18nc("@label:spinbox", "Position:"), box); label->setContentsMargins(4, 4, 4, 4); box->layout()->addWidget(label); innerLayout = new QHBoxLayout(); innerLayout->setSpacing(3); leftSpinBox = new QSpinBox(box); leftSpinBox->setAlignment(Qt::AlignCenter); innerLayout->addWidget(leftSpinBox); topSpinBox = new QSpinBox(box); topSpinBox->setAlignment(Qt::AlignCenter); innerLayout->addWidget(topSpinBox); box->layout()->addItem(innerLayout); flowLayout->addWidget(box); flowLayout->addSpacing(18); // (5) Preserve ratio checkbox mPreserveAspectRatioWidget = boxWidget(cropWidget); preserveAspectRatioCheckBox = new QCheckBox(i18nc("@option:check", "Preserve aspect ratio"), mPreserveAspectRatioWidget); mPreserveAspectRatioWidget->layout()->addWidget(preserveAspectRatioCheckBox); flowLayout->addWidget(mPreserveAspectRatioWidget); flowLayout->addSpacing(18); // (6) Dialog buttons box = boxWidget(cropWidget); dialogButtonBox = new QDialogButtonBox(QDialogButtonBox::Cancel | QDialogButtonBox::Ok, box); box->layout()->addWidget(dialogButtonBox); flowLayout->addWidget(box); } }; CropWidget::CropWidget(QWidget* parent, RasterImageView* imageView, CropTool* cropTool) : QWidget(parent) , d(new CropWidgetPrivate) { setWindowFlags(Qt::Tool); d->q = this; d->mDocument = imageView->document(); d->mUpdatingFromCropTool = false; d->mCropTool = cropTool; d->setupUi(this); setFont(QFontDatabase::systemFont(QFontDatabase::SmallestReadableFont)); connect(d->advancedCheckBox, &QCheckBox::toggled, this, &CropWidget::slotAdvancedCheckBoxToggled); for (auto w : d->mAdvancedWidgets) { w->setVisible(false); } connect(d->preserveAspectRatioCheckBox, &QCheckBox::toggled, this, &CropWidget::applyRatioConstraint); d->initRatioComboBox(); connect(d->mCropTool, &CropTool::rectUpdated, this, &CropWidget::setCropRect); connect(d->leftSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &CropWidget::slotPositionChanged); connect(d->topSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &CropWidget::slotPositionChanged); connect(d->widthSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &CropWidget::slotWidthChanged); connect(d->heightSpinBox, QOverload::of(&QSpinBox::valueChanged), this, &CropWidget::slotHeightChanged); d->initDialogButtonBox(); // We need to listen for both signals because the combobox is multi-function: // Text Changed: required so that manual ratio entry is detected (index doesn't change) // Index Changed: required so that choosing an item with the same text is detected (e.g. going from US Letter portrait // to US Letter landscape) connect(d->ratioComboBox, &QComboBox::editTextChanged, this, &CropWidget::slotRatioComboBoxChanged); connect(d->ratioComboBox, QOverload::of(&QComboBox::currentIndexChanged), this, &CropWidget::slotRatioComboBoxChanged); // Don't do this before signals are connected, otherwise the tool won't get // initialized d->initSpinBoxes(); setCropRect(d->mCropTool->rect()); } CropWidget::~CropWidget() { delete d; } void CropWidget::setAdvancedSettingsEnabled(bool enable) { d->advancedCheckBox->setChecked(enable); } bool CropWidget::advancedSettingsEnabled() const { return d->advancedCheckBox->isChecked(); } void CropWidget::setPreserveAspectRatio(bool preserve) { d->preserveAspectRatioCheckBox->setChecked(preserve); } bool CropWidget::preserveAspectRatio() const { return d->preserveAspectRatioCheckBox->isChecked(); } void CropWidget::setCropRatio(QSizeF size) { d->setChosenRatio(size); } QSizeF CropWidget::cropRatio() const { return d->chosenRatio(); } void CropWidget::setCropRatioIndex(int index) { d->ratioComboBox->setCurrentIndex(index); } int CropWidget::cropRatioIndex() const { return d->mCropRatioComboBoxCurrentIndex; } void CropWidget::setCropRect(const QRect& rect) { d->mUpdatingFromCropTool = true; d->leftSpinBox->setValue(rect.left()); d->topSpinBox->setValue(rect.top()); d->widthSpinBox->setValue(rect.width()); d->heightSpinBox->setValue(rect.height()); d->mUpdatingFromCropTool = false; } void CropWidget::slotPositionChanged() { const QSize size = d->mDocument->size(); d->widthSpinBox->setMaximum(size.width() - d->leftSpinBox->value()); d->heightSpinBox->setMaximum(size.height() - d->topSpinBox->value()); if (d->mUpdatingFromCropTool) { return; } d->mCropTool->setRect(d->cropRect()); } void CropWidget::slotWidthChanged() { d->leftSpinBox->setMaximum(d->mDocument->width() - d->widthSpinBox->value()); if (d->mUpdatingFromCropTool) { return; } if (d->ratioIsConstrained()) { int height = int(d->widthSpinBox->value() * d->cropRatio()); d->heightSpinBox->setValue(height); } d->mCropTool->setRect(d->cropRect()); } void CropWidget::slotHeightChanged() { d->topSpinBox->setMaximum(d->mDocument->height() - d->heightSpinBox->value()); if (d->mUpdatingFromCropTool) { return; } if (d->ratioIsConstrained()) { int width = int(d->heightSpinBox->value() / d->cropRatio()); d->widthSpinBox->setValue(width); } d->mCropTool->setRect(d->cropRect()); } void CropWidget::applyRatioConstraint() { double ratio = d->cropRatio(); d->mCropTool->setCropRatio(ratio); if (!d->ratioIsConstrained()) { return; } QRect rect = d->cropRect(); rect.setHeight(int(rect.width() * ratio)); d->mCropTool->setRect(rect); } void CropWidget::slotAdvancedCheckBoxToggled(bool checked) { for (auto w : d->mAdvancedWidgets) { w->setVisible(checked); } d->mPreserveAspectRatioWidget->setVisible(!checked); applyRatioConstraint(); } void CropWidget::slotRatioComboBoxChanged() { const QString text = d->ratioComboBox->currentText(); // If text cleared, clear the current item as well if (text.isEmpty()) { d->ratioComboBox->setCurrentIndex(-1); } // We want to keep track of the selected ratio, including when the user has entered a custom ratio // or cleared the text. We can't simply use currentIndex() because this stays >= 0 when the user manually // enters text. We also can't set the current index to -1 when there is no match like above because that // interferes when manually entering text. // Furthermore, since there can be duplicate text items, we can't rely on findText() as it will stop on // the first match it finds. Therefore we must check if there's a match, and if so, get the index directly. if (d->ratioComboBox->findText(text) >= 0) { d->mCropRatioComboBoxCurrentIndex = d->ratioComboBox->currentIndex(); } else { d->mCropRatioComboBoxCurrentIndex = -1; } applyRatioConstraint(); } void CropWidget::updateCropRatio() { // First we need to re-calculate the "Current Image" ratio in case the user rotated the image d->ratioComboBox->setItemData(d->mCurrentImageComboBoxIndex, QVariant(ratio(d->mDocument->size()))); // Always re-apply the constraint, even though we only need to when the user has "Current Image" // selected or the "Preserve aspect ratio" checked, since there's no harm applyRatioConstraint(); // If the ratio is unrestricted, calling applyRatioConstraint doesn't update the rect, so we call // this manually to make sure the rect is adjusted to fit within the image d->mCropTool->setRect(d->mCropTool->rect()); } } // namespace diff --git a/lib/flowlayout.cpp b/lib/flowlayout.cpp index d4f565b3..a885dff0 100644 --- a/lib/flowlayout.cpp +++ b/lib/flowlayout.cpp @@ -1,212 +1,212 @@ /**************************************************************************** ** ** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** ** This file is part of the example classes of the Qt Toolkit. ** ** Commercial Usage ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License versions 2.0 or 3.0 as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information ** to ensure GNU General Public Licensing requirements will be met: -** http://www.fsf.org/licensing/licenses/info/GPLv2.html and -** http://www.gnu.org/copyleft/gpl.html. In addition, as a special +** https://www.fsf.org/licensing/licenses/info/GPLv2.html and +** https://www.gnu.org/copyleft/gpl.html. In addition, as a special ** exception, Nokia gives you certain additional rights. These rights ** are described in the Nokia Qt GPL Exception version 1.3, included in ** the file GPL_EXCEPTION.txt in this package. ** ** Qt for Windows(R) Licensees ** As a special exception, Nokia, as the sole copyright holder for Qt ** Designer, grants users of the Qt/Eclipse Integration plug-in the ** right for the Qt/Eclipse Integration to link to functionality ** provided by Qt Designer and its related libraries. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at qt-sales@nokia.com. ** ****************************************************************************/ // Self #include "flowlayout.h" // Qt #include FlowLayout::FlowLayout(QWidget *parent, int margin, int spacing) : QLayout(parent) { setContentsMargins(margin, margin, margin, margin); setHorizontalSpacing(spacing); setVerticalSpacing(spacing); } FlowLayout::FlowLayout(int spacing) { setHorizontalSpacing(spacing); setVerticalSpacing(spacing); } FlowLayout::~FlowLayout() { QLayoutItem *item; while ((item = takeAt(0))) delete item; } int FlowLayout::horizontalSpacing() const { return mHorizontalSpacing; } void FlowLayout::setHorizontalSpacing(const int spacing) { mHorizontalSpacing = spacing; } int FlowLayout::verticalSpacing() const { return mVerticalSpacing; } void FlowLayout::setVerticalSpacing(const int spacing) { mVerticalSpacing = spacing; } void FlowLayout::addItem(QLayoutItem *item) { itemList.append(item); } int FlowLayout::count() const { return itemList.size(); } QLayoutItem *FlowLayout::itemAt(int index) const { return itemList.value(index); } QLayoutItem *FlowLayout::takeAt(int index) { if (index >= 0 && index < itemList.size()) return itemList.takeAt(index); else return nullptr; } Qt::Orientations FlowLayout::expandingDirections() const { return 0; } bool FlowLayout::hasHeightForWidth() const { return true; } int FlowLayout::heightForWidth(int width) const { int height = doLayout(QRect(0, 0, width, 0), true); return height; } void FlowLayout::setGeometry(const QRect &rect) { QLayout::setGeometry(rect); doLayout(rect, false); } QSize FlowLayout::sizeHint() const { return minimumSize(); } QSize FlowLayout::minimumSize() const { QSize size; for (QLayoutItem *item : qAsConst(itemList)) size = size.expandedTo(item->minimumSize()); size += QSize(2 * margin(), 2 * margin()); return size; } void FlowLayout::addSpacing(const int size) { addItem(new QSpacerItem(size, 0, QSizePolicy::Fixed, QSizePolicy::Minimum)); } int FlowLayout::doLayout(const QRect &rect, bool testOnly) const { const int left = rect.x() + margin(); int x = left; int y = rect.y() + margin(); int lineHeight = 0; bool lastItemIsSpacer = false; QHash widthForY; for (QLayoutItem *item : qAsConst(itemList)) { const bool itemIsSpacer = item->spacerItem() != nullptr; // Don't add invisible items or succeeding spacer items if (item->sizeHint().width() == 0 || (itemIsSpacer && lastItemIsSpacer)) { continue; } int nextX = x + item->sizeHint().width() + horizontalSpacing(); if (nextX - horizontalSpacing() > rect.right() - margin() && lineHeight > 0) { x = left; y = y + lineHeight + verticalSpacing(); nextX = x + item->sizeHint().width() + horizontalSpacing(); lineHeight = 0; } // Don't place spacer items at start of line if (itemIsSpacer && x == left) { continue; } if (!testOnly) item->setGeometry(QRect(QPoint(x, y), item->sizeHint())); x = nextX; // Don't add spacer items at end of line if (!itemIsSpacer) { widthForY[y] = x - margin(); } lineHeight = qMax(lineHeight, item->sizeHint().height()); lastItemIsSpacer = itemIsSpacer; } if (!testOnly) { const int contentWidth = rect.width() - 2 * margin(); for (auto item : itemList) { QRect itemRect = item->geometry(); // Center lines horizontally if flag AlignHCenter is set if (alignment() & Qt::AlignHCenter) { if (widthForY.contains(itemRect.y())) { const int offset = (contentWidth - widthForY[itemRect.y()]) / 2; itemRect.translate(offset, 0); } } // Center items vertically if flag AlignVCenter is set if (alignment() & Qt::AlignVCenter) { const int offset = (lineHeight - itemRect.height()) / 2; itemRect.translate(0, offset); } item->setGeometry(itemRect); } } return y + lineHeight - rect.y() + margin(); } diff --git a/lib/flowlayout.h b/lib/flowlayout.h index 73a21776..bc0a6b28 100644 --- a/lib/flowlayout.h +++ b/lib/flowlayout.h @@ -1,78 +1,78 @@ /**************************************************************************** ** ** Copyright (C) 2008 Nokia Corporation and/or its subsidiary(-ies). ** Contact: Qt Software Information (qt-info@nokia.com) ** ** This file is part of the example classes of the Qt Toolkit. ** ** Commercial Usage ** Licensees holding valid Qt Commercial licenses may use this file in ** accordance with the Qt Commercial License Agreement provided with the ** Software or, alternatively, in accordance with the terms contained in ** a written agreement between you and Nokia. ** ** ** GNU General Public License Usage ** Alternatively, this file may be used under the terms of the GNU ** General Public License versions 2.0 or 3.0 as published by the Free ** Software Foundation and appearing in the file LICENSE.GPL included in ** the packaging of this file. Please review the following information ** to ensure GNU General Public Licensing requirements will be met: -** http://www.fsf.org/licensing/licenses/info/GPLv2.html and -** http://www.gnu.org/copyleft/gpl.html. In addition, as a special +** https://www.fsf.org/licensing/licenses/info/GPLv2.html and +** https://www.gnu.org/copyleft/gpl.html. In addition, as a special ** exception, Nokia gives you certain additional rights. These rights ** are described in the Nokia Qt GPL Exception version 1.3, included in ** the file GPL_EXCEPTION.txt in this package. ** ** Qt for Windows(R) Licensees ** As a special exception, Nokia, as the sole copyright holder for Qt ** Designer, grants users of the Qt/Eclipse Integration plug-in the ** right for the Qt/Eclipse Integration to link to functionality ** provided by Qt Designer and its related libraries. ** ** If you are unsure which license is appropriate for your use, please ** contact the sales department at qt-sales@nokia.com. ** ****************************************************************************/ #ifndef FLOWLAYOUT_H #define FLOWLAYOUT_H #include #include #include class GWENVIEWLIB_EXPORT FlowLayout : public QLayout { public: explicit FlowLayout(QWidget *parent, int margin = 0, int spacing = -1); FlowLayout(int spacing = -1); ~FlowLayout() override; int horizontalSpacing() const; void setHorizontalSpacing(const int spacing); int verticalSpacing() const; void setVerticalSpacing(const int spacing); void addItem(QLayoutItem *item) override; Qt::Orientations expandingDirections() const override; bool hasHeightForWidth() const override; int heightForWidth(int) const override; int count() const override; QLayoutItem *itemAt(int index) const override; QSize minimumSize() const override; void setGeometry(const QRect &rect) override; QSize sizeHint() const override; QLayoutItem *takeAt(int index) override; void addSpacing(const int size); private: int doLayout(const QRect &rect, bool testOnly) const; QList itemList; int mHorizontalSpacing; int mVerticalSpacing; }; #endif diff --git a/lib/imageformats/fitsformat/bayer.c b/lib/imageformats/fitsformat/bayer.c index 9fcc9aaa..6ffce161 100644 --- a/lib/imageformats/fitsformat/bayer.c +++ b/lib/imageformats/fitsformat/bayer.c @@ -1,2485 +1,2485 @@ /* * 1394-Based Digital Camera Control Library * * Bayer pattern decoding functions * * Written by Damien Douxchamps and Frederic Devernay * The original VNG and AHD Bayer decoding are from Dave Coffin's DCRAW. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "bayer.h" #include #include #include #include #include #define CLIP(in, out) \ in = in < 0 ? 0 : in; \ in = in > 255 ? 255 : in; \ out = in; #define CLIP16(in, out, bits) \ in = in < 0 ? 0 : in; \ in = in > ((1 << bits) - 1) ? ((1 << bits) - 1) : in; \ out = in; void ClearBorders(uint8_t *rgb, int sx, int sy, int w) { int i, j; /* black edges are added with a width w: */ i = 3 * sx * w - 1; j = 3 * sx * sy - 1; while (i >= 0) { rgb[i--] = 0; rgb[j--] = 0; } int low = sx * (w - 1) * 3 - 1 + w * 3; i = low + sx * (sy - w * 2 + 1) * 3; while (i > low) { j = 6 * w; while (j > 0) { rgb[i--] = 0; j--; } i -= (sx - 2 * w) * 3; } } void ClearBorders_uint16(uint16_t *rgb, int sx, int sy, int w) { int i, j; /* black edges: */ i = 3 * sx * w - 1; j = 3 * sx * sy - 1; while (i >= 0) { rgb[i--] = 0; rgb[j--] = 0; } int low = sx * (w - 1) * 3 - 1 + w * 3; i = low + sx * (sy - w * 2 + 1) * 3; while (i > low) { j = 6 * w; while (j > 0) { rgb[i--] = 0; j--; } i -= (sx - 2 * w) * 3; } } /************************************************************** * Color conversion functions for cameras that can * * output raw-Bayer pattern images, such as some Basler and * * Point Grey camera. Most of the algos presented here come * * from http://www-ise.stanford.edu/~tingchen/ and have been * * converted from Matlab to C and extended to all elementary * * patterns. * **************************************************************/ /* 8-bits versions */ /* insprired by OpenCV's Bayer decoding */ dc1394error_t dc1394_bayer_NearestNeighbor(const uint8_t *bayer, uint8_t *rgb, int sx, int sy, int tile) { const int bayerStep = sx; const int rgbStep = 3 * sx; int width = sx; int height = sy; int blue = tile == DC1394_COLOR_FILTER_BGGR || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; int start_with_green = tile == DC1394_COLOR_FILTER_GBRG || tile == DC1394_COLOR_FILTER_GRBG; int i, imax, iinc; if ((tile > DC1394_COLOR_FILTER_MAX) || (tile < DC1394_COLOR_FILTER_MIN)) return DC1394_INVALID_COLOR_FILTER; /* add black border */ imax = sx * sy * 3; for (i = sx * (sy - 1) * 3; i < imax; i++) { rgb[i] = 0; } iinc = (sx - 1) * 3; for (i = (sx - 1) * 3; i < imax; i += iinc) { rgb[i++] = 0; rgb[i++] = 0; rgb[i++] = 0; } rgb += 1; width -= 1; height -= 1; for (; height--; bayer += bayerStep, rgb += rgbStep) { const uint8_t *bayerEnd = bayer + width; if (start_with_green) { rgb[-blue] = bayer[1]; rgb[0] = bayer[bayerStep + 1]; rgb[blue] = bayer[bayerStep]; bayer++; rgb += 3; } if (blue > 0) { for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { rgb[-1] = bayer[0]; rgb[0] = bayer[1]; rgb[1] = bayer[bayerStep + 1]; rgb[2] = bayer[2]; rgb[3] = bayer[bayerStep + 2]; rgb[4] = bayer[bayerStep + 1]; } } else { for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { rgb[1] = bayer[0]; rgb[0] = bayer[1]; rgb[-1] = bayer[bayerStep + 1]; rgb[4] = bayer[2]; rgb[3] = bayer[bayerStep + 2]; rgb[2] = bayer[bayerStep + 1]; } } if (bayer < bayerEnd) { rgb[-blue] = bayer[0]; rgb[0] = bayer[1]; rgb[blue] = bayer[bayerStep + 1]; bayer++; rgb += 3; } bayer -= width; rgb -= width * 3; blue = -blue; start_with_green = !start_with_green; } return DC1394_SUCCESS; } /* OpenCV's Bayer decoding */ dc1394error_t dc1394_bayer_Bilinear(const uint8_t *bayer, uint8_t *rgb, int sx, int sy, int tile) { const int bayerStep = sx; const int rgbStep = 3 * sx; int width = sx; int height = sy; /* the two letters of the OpenCV name are respectively the 4th and 3rd letters from the blinky name, and we also have to switch R and B (OpenCV is BGR) CV_BayerBG2BGR <-> DC1394_COLOR_FILTER_BGGR CV_BayerGB2BGR <-> DC1394_COLOR_FILTER_GBRG CV_BayerGR2BGR <-> DC1394_COLOR_FILTER_GRBG int blue = tile == CV_BayerBG2BGR || tile == CV_BayerGB2BGR ? -1 : 1; int start_with_green = tile == CV_BayerGB2BGR || tile == CV_BayerGR2BGR; */ int blue = tile == DC1394_COLOR_FILTER_BGGR || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; int start_with_green = tile == DC1394_COLOR_FILTER_GBRG || tile == DC1394_COLOR_FILTER_GRBG; if ((tile > DC1394_COLOR_FILTER_MAX) || (tile < DC1394_COLOR_FILTER_MIN)) return DC1394_INVALID_COLOR_FILTER; ClearBorders(rgb, sx, sy, 1); rgb += rgbStep + 3 + 1; height -= 2; width -= 2; for (; height--; bayer += bayerStep, rgb += rgbStep) { int t0, t1; const uint8_t *bayerEnd = bayer + width; if (start_with_green) { /* OpenCV has a bug in the next line, which was t0 = (bayer[0] + bayer[bayerStep * 2] + 1) >> 1; */ t0 = (bayer[1] + bayer[bayerStep * 2 + 1] + 1) >> 1; t1 = (bayer[bayerStep] + bayer[bayerStep + 2] + 1) >> 1; rgb[-blue] = (uint8_t)t0; rgb[0] = bayer[bayerStep + 1]; rgb[blue] = (uint8_t)t1; bayer++; rgb += 3; } if (blue > 0) { for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + bayer[bayerStep * 2 + 2] + 2) >> 2; t1 = (bayer[1] + bayer[bayerStep] + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + 2) >> 2; rgb[-1] = (uint8_t)t0; rgb[0] = (uint8_t)t1; rgb[1] = bayer[bayerStep + 1]; t0 = (bayer[2] + bayer[bayerStep * 2 + 2] + 1) >> 1; t1 = (bayer[bayerStep + 1] + bayer[bayerStep + 3] + 1) >> 1; rgb[2] = (uint8_t)t0; rgb[3] = bayer[bayerStep + 2]; rgb[4] = (uint8_t)t1; } } else { for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + bayer[bayerStep * 2 + 2] + 2) >> 2; t1 = (bayer[1] + bayer[bayerStep] + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + 2) >> 2; rgb[1] = (uint8_t)t0; rgb[0] = (uint8_t)t1; rgb[-1] = bayer[bayerStep + 1]; t0 = (bayer[2] + bayer[bayerStep * 2 + 2] + 1) >> 1; t1 = (bayer[bayerStep + 1] + bayer[bayerStep + 3] + 1) >> 1; rgb[4] = (uint8_t)t0; rgb[3] = bayer[bayerStep + 2]; rgb[2] = (uint8_t)t1; } } if (bayer < bayerEnd) { t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + bayer[bayerStep * 2 + 2] + 2) >> 2; t1 = (bayer[1] + bayer[bayerStep] + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + 2) >> 2; rgb[-blue] = (uint8_t)t0; rgb[0] = (uint8_t)t1; rgb[blue] = bayer[bayerStep + 1]; bayer++; rgb += 3; } bayer -= width; rgb -= width * 3; blue = -blue; start_with_green = !start_with_green; } return DC1394_SUCCESS; } /* High-Quality Linear Interpolation For Demosaicing Of Bayer-Patterned Color Images, by Henrique S. Malvar, Li-wei He, and Ross Cutler, in ICASSP'04 */ dc1394error_t dc1394_bayer_HQLinear(const uint8_t *bayer, uint8_t *rgb, int sx, int sy, int tile) { const int bayerStep = sx; const int rgbStep = 3 * sx; int width = sx; int height = sy; int blue = tile == DC1394_COLOR_FILTER_BGGR || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; int start_with_green = tile == DC1394_COLOR_FILTER_GBRG || tile == DC1394_COLOR_FILTER_GRBG; if ((tile > DC1394_COLOR_FILTER_MAX) || (tile < DC1394_COLOR_FILTER_MIN)) return DC1394_INVALID_COLOR_FILTER; ClearBorders(rgb, sx, sy, 2); rgb += 2 * rgbStep + 6 + 1; height -= 4; width -= 4; /* We begin with a (+1 line,+1 column) offset with respect to bilinear decoding, so start_with_green is the same, but blue is opposite */ blue = -blue; for (; height--; bayer += bayerStep, rgb += rgbStep) { int t0, t1; const uint8_t *bayerEnd = bayer + width; const int bayerStep2 = bayerStep * 2; const int bayerStep3 = bayerStep * 3; const int bayerStep4 = bayerStep * 4; if (start_with_green) { /* at green pixel */ rgb[0] = bayer[bayerStep2 + 2]; t0 = rgb[0] * 5 + ((bayer[bayerStep + 2] + bayer[bayerStep3 + 2]) << 2) - bayer[2] - bayer[bayerStep + 1] - bayer[bayerStep + 3] - bayer[bayerStep3 + 1] - bayer[bayerStep3 + 3] - bayer[bayerStep4 + 2] + ((bayer[bayerStep2] + bayer[bayerStep2 + 4] + 1) >> 1); t1 = rgb[0] * 5 + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3]) << 2) - bayer[bayerStep2] - bayer[bayerStep + 1] - bayer[bayerStep + 3] - bayer[bayerStep3 + 1] - bayer[bayerStep3 + 3] - bayer[bayerStep2 + 4] + ((bayer[2] + bayer[bayerStep4 + 2] + 1) >> 1); t0 = (t0 + 4) >> 3; CLIP(t0, rgb[-blue]); t1 = (t1 + 4) >> 3; CLIP(t1, rgb[blue]); bayer++; rgb += 3; } if (blue > 0) { for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { /* B at B */ rgb[1] = bayer[bayerStep2 + 2]; /* R at B */ t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + bayer[bayerStep3 + 1] + bayer[bayerStep3 + 3]) << 1) - (((bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) * 3 + 1) >> 1) + rgb[1] * 6; /* G at B */ t1 = ((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3] + bayer[bayerStep3 + 2]) << 1) - (bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + (rgb[1] << 2); t0 = (t0 + 4) >> 3; CLIP(t0, rgb[-1]); t1 = (t1 + 4) >> 3; CLIP(t1, rgb[0]); /* at green pixel */ rgb[3] = bayer[bayerStep2 + 3]; t0 = rgb[3] * 5 + ((bayer[bayerStep + 3] + bayer[bayerStep3 + 3]) << 2) - bayer[3] - bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - bayer[bayerStep4 + 3] + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 5] + 1) >> 1); t1 = rgb[3] * 5 + ((bayer[bayerStep2 + 2] + bayer[bayerStep2 + 4]) << 2) - bayer[bayerStep2 + 1] - bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - bayer[bayerStep2 + 5] + ((bayer[3] + bayer[bayerStep4 + 3] + 1) >> 1); t0 = (t0 + 4) >> 3; CLIP(t0, rgb[2]); t1 = (t1 + 4) >> 3; CLIP(t1, rgb[4]); } } else { for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { /* R at R */ rgb[-1] = bayer[bayerStep2 + 2]; /* B at R */ t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + bayer[bayerStep3 + 1] + bayer[bayerStep3 + 3]) << 1) - (((bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) * 3 + 1) >> 1) + rgb[-1] * 6; /* G at R */ t1 = ((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3] + bayer[bayerStep * 3 + 2]) << 1) - (bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + (rgb[-1] << 2); t0 = (t0 + 4) >> 3; CLIP(t0, rgb[1]); t1 = (t1 + 4) >> 3; CLIP(t1, rgb[0]); /* at green pixel */ rgb[3] = bayer[bayerStep2 + 3]; t0 = rgb[3] * 5 + ((bayer[bayerStep + 3] + bayer[bayerStep3 + 3]) << 2) - bayer[3] - bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - bayer[bayerStep4 + 3] + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 5] + 1) >> 1); t1 = rgb[3] * 5 + ((bayer[bayerStep2 + 2] + bayer[bayerStep2 + 4]) << 2) - bayer[bayerStep2 + 1] - bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - bayer[bayerStep2 + 5] + ((bayer[3] + bayer[bayerStep4 + 3] + 1) >> 1); t0 = (t0 + 4) >> 3; CLIP(t0, rgb[4]); t1 = (t1 + 4) >> 3; CLIP(t1, rgb[2]); } } if (bayer < bayerEnd) { /* B at B */ rgb[blue] = bayer[bayerStep2 + 2]; /* R at B */ t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + bayer[bayerStep3 + 1] + bayer[bayerStep3 + 3]) << 1) - (((bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) * 3 + 1) >> 1) + rgb[blue] * 6; /* G at B */ t1 = (((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3] + bayer[bayerStep3 + 2])) << 1) - (bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + (rgb[blue] << 2); t0 = (t0 + 4) >> 3; CLIP(t0, rgb[-blue]); t1 = (t1 + 4) >> 3; CLIP(t1, rgb[0]); bayer++; rgb += 3; } bayer -= width; rgb -= width * 3; blue = -blue; start_with_green = !start_with_green; } return DC1394_SUCCESS; } /* coriander's Bayer decoding */ /* Edge Sensing Interpolation II from http://www-ise.stanford.edu/~tingchen/ */ /* (Laroche,Claude A. "Apparatus and method for adaptively interpolating a full color image utilizing chrominance gradients" U.S. Patent 5,373,322) */ dc1394error_t dc1394_bayer_EdgeSense(const uint8_t *bayer, uint8_t *rgb, int sx, int sy, int tile) { uint8_t *outR, *outG, *outB; register int i3, j3, base; int i, j; int dh, dv; int tmp; int sx3 = sx * 3; /* sx and sy should be even */ switch (tile) { case DC1394_COLOR_FILTER_GRBG: case DC1394_COLOR_FILTER_BGGR: outR = &rgb[0]; outG = &rgb[1]; outB = &rgb[2]; break; case DC1394_COLOR_FILTER_GBRG: case DC1394_COLOR_FILTER_RGGB: outR = &rgb[2]; outG = &rgb[1]; outB = &rgb[0]; break; default: return DC1394_INVALID_COLOR_FILTER; } switch (tile) { case DC1394_COLOR_FILTER_GRBG: case DC1394_COLOR_FILTER_GBRG: /* copy original RGB data to output images */ for (i = 0, i3 = 0; i < sy * sx; i += (sx << 1), i3 += (sx3 << 1)) { for (j = 0, j3 = 0; j < sx; j += 2, j3 += 6) { base = i3 + j3; outG[base] = bayer[i + j]; outG[base + sx3 + 3] = bayer[i + j + sx + 1]; outR[base + 3] = bayer[i + j + 1]; outB[base + sx3] = bayer[i + j + sx]; } } /* process GREEN channel */ for (i3 = 3 * sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) { for (j3 = 6; j3 < sx3 - 9; j3 += 6) { base = i3 + j3; dh = abs(((outB[base - 6] + outB[base + 6]) >> 1) - outB[base]); dv = abs(((outB[base - (sx3 << 1)] + outB[base + (sx3 << 1)]) >> 1) - outB[base]); tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); CLIP(tmp, outG[base]); } } for (i3 = 2 * sx3; i3 < (sy - 3) * sx3; i3 += (sx3 << 1)) { for (j3 = 9; j3 < sx3 - 6; j3 += 6) { base = i3 + j3; dh = abs(((outR[base - 6] + outR[base + 6]) >> 1) - outR[base]); dv = abs(((outR[base - (sx3 << 1)] + outR[base + (sx3 << 1)]) >> 1) - outR[base]); tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); CLIP(tmp, outG[base]); } } /* process RED channel */ for (i3 = 0; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) { for (j3 = 6; j3 < sx3 - 3; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outR[base - 3] - outG[base - 3] + outR[base + 3] - outG[base + 3]) >> 1); CLIP(tmp, outR[base]); } } for (i3 = sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) { for (j3 = 3; j3 < sx3; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outR[base - sx3] - outG[base - sx3] + outR[base + sx3] - outG[base + sx3]) >> 1); CLIP(tmp, outR[base]); } for (j3 = 6; j3 < sx3 - 3; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outR[base - sx3 - 3] - outG[base - sx3 - 3] + outR[base - sx3 + 3] - outG[base - sx3 + 3] + outR[base + sx3 - 3] - outG[base + sx3 - 3] + outR[base + sx3 + 3] - outG[base + sx3 + 3]) >> 2); CLIP(tmp, outR[base]); } } /* process BLUE channel */ for (i3 = sx3; i3 < sy * sx3; i3 += (sx3 << 1)) { for (j3 = 3; j3 < sx3 - 6; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outB[base - 3] - outG[base - 3] + outB[base + 3] - outG[base + 3]) >> 1); CLIP(tmp, outB[base]); } } for (i3 = 2 * sx3; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) { for (j3 = 0; j3 < sx3 - 3; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outB[base - sx3] - outG[base - sx3] + outB[base + sx3] - outG[base + sx3]) >> 1); CLIP(tmp, outB[base]); } for (j3 = 3; j3 < sx3 - 6; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outB[base - sx3 - 3] - outG[base - sx3 - 3] + outB[base - sx3 + 3] - outG[base - sx3 + 3] + outB[base + sx3 - 3] - outG[base + sx3 - 3] + outB[base + sx3 + 3] - outG[base + sx3 + 3]) >> 2); CLIP(tmp, outB[base]); } } break; case DC1394_COLOR_FILTER_BGGR: case DC1394_COLOR_FILTER_RGGB: /* copy original RGB data to output images */ for (i = 0, i3 = 0; i < sy * sx; i += (sx << 1), i3 += (sx3 << 1)) { for (j = 0, j3 = 0; j < sx; j += 2, j3 += 6) { base = i3 + j3; outB[base] = bayer[i + j]; outR[base + sx3 + 3] = bayer[i + sx + (j + 1)]; outG[base + 3] = bayer[i + j + 1]; outG[base + sx3] = bayer[i + sx + j]; } } /* process GREEN channel */ for (i3 = 2 * sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) { for (j3 = 6; j3 < sx3 - 9; j3 += 6) { base = i3 + j3; dh = abs(((outB[base - 6] + outB[base + 6]) >> 1) - outB[base]); dv = abs(((outB[base - (sx3 << 1)] + outB[base + (sx3 << 1)]) >> 1) - outB[base]); tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); CLIP(tmp, outG[base]); } } for (i3 = 3 * sx3; i3 < (sy - 3) * sx3; i3 += (sx3 << 1)) { for (j3 = 9; j3 < sx3 - 6; j3 += 6) { base = i3 + j3; dh = abs(((outR[base - 6] + outR[base + 6]) >> 1) - outR[base]); dv = abs(((outR[base - (sx3 << 1)] + outR[base + (sx3 << 1)]) >> 1) - outR[base]); tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); CLIP(tmp, outG[base]); } } /* process RED channel */ for (i3 = sx3; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) { /* G-points (1/2) */ for (j3 = 6; j3 < sx3 - 3; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outR[base - 3] - outG[base - 3] + outR[base + 3] - outG[base + 3]) >> 1); CLIP(tmp, outR[base]); } } for (i3 = 2 * sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) { for (j3 = 3; j3 < sx3; j3 += 6) { /* G-points (2/2) */ base = i3 + j3; tmp = outG[base] + ((outR[base - sx3] - outG[base - sx3] + outR[base + sx3] - outG[base + sx3]) >> 1); CLIP(tmp, outR[base]); } for (j3 = 6; j3 < sx3 - 3; j3 += 6) { /* B-points */ base = i3 + j3; tmp = outG[base] + ((outR[base - sx3 - 3] - outG[base - sx3 - 3] + outR[base - sx3 + 3] - outG[base - sx3 + 3] + outR[base + sx3 - 3] - outG[base + sx3 - 3] + outR[base + sx3 + 3] - outG[base + sx3 + 3]) >> 2); CLIP(tmp, outR[base]); } } /* process BLUE channel */ for (i = 0, i3 = 0; i < sy * sx; i += (sx << 1), i3 += (sx3 << 1)) { for (j = 1, j3 = 3; j < sx - 2; j += 2, j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outB[base - 3] - outG[base - 3] + outB[base + 3] - outG[base + 3]) >> 1); CLIP(tmp, outB[base]); } } for (i3 = sx3; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) { for (j3 = 0; j3 < sx3 - 3; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outB[base - sx3] - outG[base - sx3] + outB[base + sx3] - outG[base + sx3]) >> 1); CLIP(tmp, outB[base]); } for (j3 = 3; j3 < sx3 - 6; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outB[base - sx3 - 3] - outG[base - sx3 - 3] + outB[base - sx3 + 3] - outG[base - sx3 + 3] + outB[base + sx3 - 3] - outG[base + sx3 - 3] + outB[base + sx3 + 3] - outG[base + sx3 + 3]) >> 2); CLIP(tmp, outB[base]); } } break; } ClearBorders(rgb, sx, sy, 3); return DC1394_SUCCESS; } /* coriander's Bayer decoding */ dc1394error_t dc1394_bayer_Downsample(const uint8_t *bayer, uint8_t *rgb, int sx, int sy, int tile) { uint8_t *outR, *outG, *outB; register int i, j; int tmp; switch (tile) { case DC1394_COLOR_FILTER_GRBG: case DC1394_COLOR_FILTER_BGGR: outR = &rgb[0]; outG = &rgb[1]; outB = &rgb[2]; break; case DC1394_COLOR_FILTER_GBRG: case DC1394_COLOR_FILTER_RGGB: outR = &rgb[2]; outG = &rgb[1]; outB = &rgb[0]; break; default: return DC1394_INVALID_COLOR_FILTER; } switch (tile) { case DC1394_COLOR_FILTER_GRBG: case DC1394_COLOR_FILTER_GBRG: for (i = 0; i < sy * sx; i += (sx << 1)) { for (j = 0; j < sx; j += 2) { tmp = ((bayer[i + j] + bayer[i + sx + j + 1]) >> 1); CLIP(tmp, outG[((i >> 2) + (j >> 1)) * 3]); tmp = bayer[i + j + 1]; CLIP(tmp, outR[((i >> 2) + (j >> 1)) * 3]); tmp = bayer[i + sx + j]; CLIP(tmp, outB[((i >> 2) + (j >> 1)) * 3]); } } break; case DC1394_COLOR_FILTER_BGGR: case DC1394_COLOR_FILTER_RGGB: for (i = 0; i < sy * sx; i += (sx << 1)) { for (j = 0; j < sx; j += 2) { tmp = ((bayer[i + sx + j] + bayer[i + j + 1]) >> 1); CLIP(tmp, outG[((i >> 2) + (j >> 1)) * 3]); tmp = bayer[i + sx + j + 1]; CLIP(tmp, outR[((i >> 2) + (j >> 1)) * 3]); tmp = bayer[i + j]; CLIP(tmp, outB[((i >> 2) + (j >> 1)) * 3]); } } break; } return DC1394_SUCCESS; } /* this is the method used inside AVT cameras. See AVT docs. */ dc1394error_t dc1394_bayer_Simple(const uint8_t *bayer, uint8_t *rgb, int sx, int sy, int tile) { const int bayerStep = sx; const int rgbStep = 3 * sx; int width = sx; int height = sy; int blue = tile == DC1394_COLOR_FILTER_BGGR || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; int start_with_green = tile == DC1394_COLOR_FILTER_GBRG || tile == DC1394_COLOR_FILTER_GRBG; int i, imax, iinc; if ((tile > DC1394_COLOR_FILTER_MAX) || (tile < DC1394_COLOR_FILTER_MIN)) return DC1394_INVALID_COLOR_FILTER; /* add black border */ imax = sx * sy * 3; for (i = sx * (sy - 1) * 3; i < imax; i++) { rgb[i] = 0; } iinc = (sx - 1) * 3; for (i = (sx - 1) * 3; i < imax; i += iinc) { rgb[i++] = 0; rgb[i++] = 0; rgb[i++] = 0; } rgb += 1; width -= 1; height -= 1; for (; height--; bayer += bayerStep, rgb += rgbStep) { const uint8_t *bayerEnd = bayer + width; if (start_with_green) { rgb[-blue] = bayer[1]; rgb[0] = (bayer[0] + bayer[bayerStep + 1] + 1) >> 1; rgb[blue] = bayer[bayerStep]; bayer++; rgb += 3; } if (blue > 0) { for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { rgb[-1] = bayer[0]; rgb[0] = (bayer[1] + bayer[bayerStep] + 1) >> 1; rgb[1] = bayer[bayerStep + 1]; rgb[2] = bayer[2]; rgb[3] = (bayer[1] + bayer[bayerStep + 2] + 1) >> 1; rgb[4] = bayer[bayerStep + 1]; } } else { for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { rgb[1] = bayer[0]; rgb[0] = (bayer[1] + bayer[bayerStep] + 1) >> 1; rgb[-1] = bayer[bayerStep + 1]; rgb[4] = bayer[2]; rgb[3] = (bayer[1] + bayer[bayerStep + 2] + 1) >> 1; rgb[2] = bayer[bayerStep + 1]; } } if (bayer < bayerEnd) { rgb[-blue] = bayer[0]; rgb[0] = (bayer[1] + bayer[bayerStep] + 1) >> 1; rgb[blue] = bayer[bayerStep + 1]; bayer++; rgb += 3; } bayer -= width; rgb -= width * 3; blue = -blue; start_with_green = !start_with_green; } return DC1394_SUCCESS; } /* 16-bits versions */ /* insprired by OpenCV's Bayer decoding */ dc1394error_t dc1394_bayer_NearestNeighbor_uint16(const uint16_t *bayer, uint16_t *rgb, int sx, int sy, int tile, int bits) { (void)bits; const int bayerStep = sx; const int rgbStep = 3 * sx; int width = sx; int height = sy; int blue = tile == DC1394_COLOR_FILTER_BGGR || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; int start_with_green = tile == DC1394_COLOR_FILTER_GBRG || tile == DC1394_COLOR_FILTER_GRBG; int i, iinc, imax; if ((tile > DC1394_COLOR_FILTER_MAX) || (tile < DC1394_COLOR_FILTER_MIN)) return DC1394_INVALID_COLOR_FILTER; /* add black border */ imax = sx * sy * 3; for (i = sx * (sy - 1) * 3; i < imax; i++) { rgb[i] = 0; } iinc = (sx - 1) * 3; for (i = (sx - 1) * 3; i < imax; i += iinc) { rgb[i++] = 0; rgb[i++] = 0; rgb[i++] = 0; } rgb += 1; height -= 1; width -= 1; for (; height--; bayer += bayerStep, rgb += rgbStep) { const uint16_t *bayerEnd = bayer + width; if (start_with_green) { rgb[-blue] = bayer[1]; rgb[0] = bayer[bayerStep + 1]; rgb[blue] = bayer[bayerStep]; bayer++; rgb += 3; } if (blue > 0) { for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { rgb[-1] = bayer[0]; rgb[0] = bayer[1]; rgb[1] = bayer[bayerStep + 1]; rgb[2] = bayer[2]; rgb[3] = bayer[bayerStep + 2]; rgb[4] = bayer[bayerStep + 1]; } } else { for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { rgb[1] = bayer[0]; rgb[0] = bayer[1]; rgb[-1] = bayer[bayerStep + 1]; rgb[4] = bayer[2]; rgb[3] = bayer[bayerStep + 2]; rgb[2] = bayer[bayerStep + 1]; } } if (bayer < bayerEnd) { rgb[-blue] = bayer[0]; rgb[0] = bayer[1]; rgb[blue] = bayer[bayerStep + 1]; bayer++; rgb += 3; } bayer -= width; rgb -= width * 3; blue = -blue; start_with_green = !start_with_green; } return DC1394_SUCCESS; } /* OpenCV's Bayer decoding */ dc1394error_t dc1394_bayer_Bilinear_uint16(const uint16_t *bayer, uint16_t *rgb, int sx, int sy, int tile, int bits) { (void)bits; const int bayerStep = sx; const int rgbStep = 3 * sx; int width = sx; int height = sy; int blue = tile == DC1394_COLOR_FILTER_BGGR || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; int start_with_green = tile == DC1394_COLOR_FILTER_GBRG || tile == DC1394_COLOR_FILTER_GRBG; if ((tile > DC1394_COLOR_FILTER_MAX) || (tile < DC1394_COLOR_FILTER_MIN)) return DC1394_INVALID_COLOR_FILTER; rgb += rgbStep + 3 + 1; height -= 2; width -= 2; for (; height--; bayer += bayerStep, rgb += rgbStep) { int t0, t1; const uint16_t *bayerEnd = bayer + width; if (start_with_green) { /* OpenCV has a bug in the next line, which was t0 = (bayer[0] + bayer[bayerStep * 2] + 1) >> 1; */ t0 = (bayer[1] + bayer[bayerStep * 2 + 1] + 1) >> 1; t1 = (bayer[bayerStep] + bayer[bayerStep + 2] + 1) >> 1; rgb[-blue] = (uint16_t)t0; rgb[0] = bayer[bayerStep + 1]; rgb[blue] = (uint16_t)t1; bayer++; rgb += 3; } if (blue > 0) { for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + bayer[bayerStep * 2 + 2] + 2) >> 2; t1 = (bayer[1] + bayer[bayerStep] + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + 2) >> 2; rgb[-1] = (uint16_t)t0; rgb[0] = (uint16_t)t1; rgb[1] = bayer[bayerStep + 1]; t0 = (bayer[2] + bayer[bayerStep * 2 + 2] + 1) >> 1; t1 = (bayer[bayerStep + 1] + bayer[bayerStep + 3] + 1) >> 1; rgb[2] = (uint16_t)t0; rgb[3] = bayer[bayerStep + 2]; rgb[4] = (uint16_t)t1; } } else { for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + bayer[bayerStep * 2 + 2] + 2) >> 2; t1 = (bayer[1] + bayer[bayerStep] + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + 2) >> 2; rgb[1] = (uint16_t)t0; rgb[0] = (uint16_t)t1; rgb[-1] = bayer[bayerStep + 1]; t0 = (bayer[2] + bayer[bayerStep * 2 + 2] + 1) >> 1; t1 = (bayer[bayerStep + 1] + bayer[bayerStep + 3] + 1) >> 1; rgb[4] = (uint16_t)t0; rgb[3] = bayer[bayerStep + 2]; rgb[2] = (uint16_t)t1; } } if (bayer < bayerEnd) { t0 = (bayer[0] + bayer[2] + bayer[bayerStep * 2] + bayer[bayerStep * 2 + 2] + 2) >> 2; t1 = (bayer[1] + bayer[bayerStep] + bayer[bayerStep + 2] + bayer[bayerStep * 2 + 1] + 2) >> 2; rgb[-blue] = (uint16_t)t0; rgb[0] = (uint16_t)t1; rgb[blue] = bayer[bayerStep + 1]; bayer++; rgb += 3; } bayer -= width; rgb -= width * 3; blue = -blue; start_with_green = !start_with_green; } return DC1394_SUCCESS; } /* High-Quality Linear Interpolation For Demosaicing Of Bayer-Patterned Color Images, by Henrique S. Malvar, Li-wei He, and Ross Cutler, in ICASSP'04 */ dc1394error_t dc1394_bayer_HQLinear_uint16(const uint16_t *bayer, uint16_t *rgb, int sx, int sy, int tile, int bits) { const int bayerStep = sx; const int rgbStep = 3 * sx; int width = sx; int height = sy; /* the two letters of the OpenCV name are respectively the 4th and 3rd letters from the blinky name, and we also have to switch R and B (OpenCV is BGR) CV_BayerBG2BGR <-> DC1394_COLOR_FILTER_BGGR CV_BayerGB2BGR <-> DC1394_COLOR_FILTER_GBRG CV_BayerGR2BGR <-> DC1394_COLOR_FILTER_GRBG int blue = tile == CV_BayerBG2BGR || tile == CV_BayerGB2BGR ? -1 : 1; int start_with_green = tile == CV_BayerGB2BGR || tile == CV_BayerGR2BGR; */ int blue = tile == DC1394_COLOR_FILTER_BGGR || tile == DC1394_COLOR_FILTER_GBRG ? -1 : 1; int start_with_green = tile == DC1394_COLOR_FILTER_GBRG || tile == DC1394_COLOR_FILTER_GRBG; if ((tile > DC1394_COLOR_FILTER_MAX) || (tile < DC1394_COLOR_FILTER_MIN)) return DC1394_INVALID_COLOR_FILTER; ClearBorders_uint16(rgb, sx, sy, 2); rgb += 2 * rgbStep + 6 + 1; height -= 4; width -= 4; /* We begin with a (+1 line,+1 column) offset with respect to bilinear decoding, so start_with_green is the same, but blue is opposite */ blue = -blue; for (; height--; bayer += bayerStep, rgb += rgbStep) { int t0, t1; const uint16_t *bayerEnd = bayer + width; const int bayerStep2 = bayerStep * 2; const int bayerStep3 = bayerStep * 3; const int bayerStep4 = bayerStep * 4; if (start_with_green) { /* at green pixel */ rgb[0] = bayer[bayerStep2 + 2]; t0 = rgb[0] * 5 + ((bayer[bayerStep + 2] + bayer[bayerStep3 + 2]) << 2) - bayer[2] - bayer[bayerStep + 1] - bayer[bayerStep + 3] - bayer[bayerStep3 + 1] - bayer[bayerStep3 + 3] - bayer[bayerStep4 + 2] + ((bayer[bayerStep2] + bayer[bayerStep2 + 4] + 1) >> 1); t1 = rgb[0] * 5 + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3]) << 2) - bayer[bayerStep2] - bayer[bayerStep + 1] - bayer[bayerStep + 3] - bayer[bayerStep3 + 1] - bayer[bayerStep3 + 3] - bayer[bayerStep2 + 4] + ((bayer[2] + bayer[bayerStep4 + 2] + 1) >> 1); t0 = (t0 + 4) >> 3; CLIP16(t0, rgb[-blue], bits); t1 = (t1 + 4) >> 3; CLIP16(t1, rgb[blue], bits); bayer++; rgb += 3; } if (blue > 0) { for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { /* B at B */ rgb[1] = bayer[bayerStep2 + 2]; /* R at B */ t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + bayer[bayerStep3 + 1] + bayer[bayerStep3 + 3]) << 1) - (((bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) * 3 + 1) >> 1) + rgb[1] * 6; /* G at B */ t1 = ((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3] + bayer[bayerStep * 3 + 2]) << 1) - (bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + (rgb[1] << 2); t0 = (t0 + 4) >> 3; CLIP16(t0, rgb[-1], bits); t1 = (t1 + 4) >> 3; CLIP16(t1, rgb[0], bits); /* at green pixel */ rgb[3] = bayer[bayerStep2 + 3]; t0 = rgb[3] * 5 + ((bayer[bayerStep + 3] + bayer[bayerStep3 + 3]) << 2) - bayer[3] - bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - bayer[bayerStep4 + 3] + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 5] + 1) >> 1); t1 = rgb[3] * 5 + ((bayer[bayerStep2 + 2] + bayer[bayerStep2 + 4]) << 2) - bayer[bayerStep2 + 1] - bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - bayer[bayerStep2 + 5] + ((bayer[3] + bayer[bayerStep4 + 3] + 1) >> 1); t0 = (t0 + 4) >> 3; CLIP16(t0, rgb[2], bits); t1 = (t1 + 4) >> 3; CLIP16(t1, rgb[4], bits); } } else { for (; bayer <= bayerEnd - 2; bayer += 2, rgb += 6) { /* R at R */ rgb[-1] = bayer[bayerStep2 + 2]; /* B at R */ t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + bayer[bayerStep * 3 + 1] + bayer[bayerStep3 + 3]) << 1) - (((bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) * 3 + 1) >> 1) + rgb[-1] * 6; /* G at R */ t1 = ((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3] + bayer[bayerStep3 + 2]) << 1) - (bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + (rgb[-1] << 2); t0 = (t0 + 4) >> 3; CLIP16(t0, rgb[1], bits); t1 = (t1 + 4) >> 3; CLIP16(t1, rgb[0], bits); /* at green pixel */ rgb[3] = bayer[bayerStep2 + 3]; t0 = rgb[3] * 5 + ((bayer[bayerStep + 3] + bayer[bayerStep3 + 3]) << 2) - bayer[3] - bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - bayer[bayerStep4 + 3] + ((bayer[bayerStep2 + 1] + bayer[bayerStep2 + 5] + 1) >> 1); t1 = rgb[3] * 5 + ((bayer[bayerStep2 + 2] + bayer[bayerStep2 + 4]) << 2) - bayer[bayerStep2 + 1] - bayer[bayerStep + 2] - bayer[bayerStep + 4] - bayer[bayerStep3 + 2] - bayer[bayerStep3 + 4] - bayer[bayerStep2 + 5] + ((bayer[3] + bayer[bayerStep4 + 3] + 1) >> 1); t0 = (t0 + 4) >> 3; CLIP16(t0, rgb[4], bits); t1 = (t1 + 4) >> 3; CLIP16(t1, rgb[2], bits); } } if (bayer < bayerEnd) { /* B at B */ rgb[blue] = bayer[bayerStep2 + 2]; /* R at B */ t0 = ((bayer[bayerStep + 1] + bayer[bayerStep + 3] + bayer[bayerStep3 + 1] + bayer[bayerStep3 + 3]) << 1) - (((bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) * 3 + 1) >> 1) + rgb[blue] * 6; /* G at B */ t1 = (((bayer[bayerStep + 2] + bayer[bayerStep2 + 1] + bayer[bayerStep2 + 3] + bayer[bayerStep3 + 2])) << 1) - (bayer[2] + bayer[bayerStep2] + bayer[bayerStep2 + 4] + bayer[bayerStep4 + 2]) + (rgb[blue] << 2); t0 = (t0 + 4) >> 3; CLIP16(t0, rgb[-blue], bits); t1 = (t1 + 4) >> 3; CLIP16(t1, rgb[0], bits); bayer++; rgb += 3; } bayer -= width; rgb -= width * 3; blue = -blue; start_with_green = !start_with_green; } return DC1394_SUCCESS; } /* coriander's Bayer decoding */ dc1394error_t dc1394_bayer_EdgeSense_uint16(const uint16_t *bayer, uint16_t *rgb, int sx, int sy, int tile, int bits) { uint16_t *outR, *outG, *outB; register int i3, j3, base; int i, j; int dh, dv; int tmp; int sx3 = sx * 3; /* sx and sy should be even */ switch (tile) { case DC1394_COLOR_FILTER_GRBG: case DC1394_COLOR_FILTER_BGGR: outR = &rgb[0]; outG = &rgb[1]; outB = &rgb[2]; break; case DC1394_COLOR_FILTER_GBRG: case DC1394_COLOR_FILTER_RGGB: outR = &rgb[2]; outG = &rgb[1]; outB = &rgb[0]; break; default: return DC1394_INVALID_COLOR_FILTER; } switch (tile) { case DC1394_COLOR_FILTER_GRBG: case DC1394_COLOR_FILTER_GBRG: /* copy original RGB data to output images */ for (i = 0, i3 = 0; i < sy * sx; i += (sx << 1), i3 += (sx3 << 1)) { for (j = 0, j3 = 0; j < sx; j += 2, j3 += 6) { base = i3 + j3; outG[base] = bayer[i + j]; outG[base + sx3 + 3] = bayer[i + j + sx + 1]; outR[base + 3] = bayer[i + j + 1]; outB[base + sx3] = bayer[i + j + sx]; } } /* process GREEN channel */ for (i3 = 3 * sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) { for (j3 = 6; j3 < sx3 - 9; j3 += 6) { base = i3 + j3; dh = abs(((outB[base - 6] + outB[base + 6]) >> 1) - outB[base]); dv = abs(((outB[base - (sx3 << 1)] + outB[base + (sx3 << 1)]) >> 1) - outB[base]); tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); CLIP16(tmp, outG[base], bits); } } for (i3 = 2 * sx3; i3 < (sy - 3) * sx3; i3 += (sx3 << 1)) { for (j3 = 9; j3 < sx3 - 6; j3 += 6) { base = i3 + j3; dh = abs(((outR[base - 6] + outR[base + 6]) >> 1) - outR[base]); dv = abs(((outR[base - (sx3 << 1)] + outR[base + (sx3 << 1)]) >> 1) - outR[base]); tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); CLIP16(tmp, outG[base], bits); } } /* process RED channel */ for (i3 = 0; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) { for (j3 = 6; j3 < sx3 - 3; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outR[base - 3] - outG[base - 3] + outR[base + 3] - outG[base + 3]) >> 1); CLIP16(tmp, outR[base], bits); } } for (i3 = sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) { for (j3 = 3; j3 < sx3; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outR[base - sx3] - outG[base - sx3] + outR[base + sx3] - outG[base + sx3]) >> 1); CLIP16(tmp, outR[base], bits); } for (j3 = 6; j3 < sx3 - 3; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outR[base - sx3 - 3] - outG[base - sx3 - 3] + outR[base - sx3 + 3] - outG[base - sx3 + 3] + outR[base + sx3 - 3] - outG[base + sx3 - 3] + outR[base + sx3 + 3] - outG[base + sx3 + 3]) >> 2); CLIP16(tmp, outR[base], bits); } } /* process BLUE channel */ for (i3 = sx3; i3 < sy * sx3; i3 += (sx3 << 1)) { for (j3 = 3; j3 < sx3 - 6; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outB[base - 3] - outG[base - 3] + outB[base + 3] - outG[base + 3]) >> 1); CLIP16(tmp, outB[base], bits); } } for (i3 = 2 * sx3; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) { for (j3 = 0; j3 < sx3 - 3; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outB[base - sx3] - outG[base - sx3] + outB[base + sx3] - outG[base + sx3]) >> 1); CLIP16(tmp, outB[base], bits); } for (j3 = 3; j3 < sx3 - 6; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outB[base - sx3 - 3] - outG[base - sx3 - 3] + outB[base - sx3 + 3] - outG[base - sx3 + 3] + outB[base + sx3 - 3] - outG[base + sx3 - 3] + outB[base + sx3 + 3] - outG[base + sx3 + 3]) >> 2); CLIP16(tmp, outB[base], bits); } } break; case DC1394_COLOR_FILTER_BGGR: case DC1394_COLOR_FILTER_RGGB: /* copy original RGB data to output images */ for (i = 0, i3 = 0; i < sy * sx; i += (sx << 1), i3 += (sx3 << 1)) { for (j = 0, j3 = 0; j < sx; j += 2, j3 += 6) { base = i3 + j3; outB[base] = bayer[i + j]; outR[base + sx3 + 3] = bayer[i + sx + (j + 1)]; outG[base + 3] = bayer[i + j + 1]; outG[base + sx3] = bayer[i + sx + j]; } } /* process GREEN channel */ for (i3 = 2 * sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) { for (j3 = 6; j3 < sx3 - 9; j3 += 6) { base = i3 + j3; dh = abs(((outB[base - 6] + outB[base + 6]) >> 1) - outB[base]); dv = abs(((outB[base - (sx3 << 1)] + outB[base + (sx3 << 1)]) >> 1) - outB[base]); tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); CLIP16(tmp, outG[base], bits); } } for (i3 = 3 * sx3; i3 < (sy - 3) * sx3; i3 += (sx3 << 1)) { for (j3 = 9; j3 < sx3 - 6; j3 += 6) { base = i3 + j3; dh = abs(((outR[base - 6] + outR[base + 6]) >> 1) - outR[base]); dv = abs(((outR[base - (sx3 << 1)] + outR[base + (sx3 << 1)]) >> 1) - outR[base]); tmp = (((outG[base - 3] + outG[base + 3]) >> 1) * (dh <= dv) + ((outG[base - sx3] + outG[base + sx3]) >> 1) * (dh > dv)); CLIP16(tmp, outG[base], bits); } } /* process RED channel */ for (i3 = sx3; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) { /* G-points (1/2) */ for (j3 = 6; j3 < sx3 - 3; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outR[base - 3] - outG[base - 3] + outR[base + 3] - outG[base + 3]) >> 1); CLIP16(tmp, outR[base], bits); } } for (i3 = 2 * sx3; i3 < (sy - 2) * sx3; i3 += (sx3 << 1)) { for (j3 = 3; j3 < sx3; j3 += 6) { /* G-points (2/2) */ base = i3 + j3; tmp = outG[base] + ((outR[base - sx3] - outG[base - sx3] + outR[base + sx3] - outG[base + sx3]) >> 1); CLIP16(tmp, outR[base], bits); } for (j3 = 6; j3 < sx3 - 3; j3 += 6) { /* B-points */ base = i3 + j3; tmp = outG[base] + ((outR[base - sx3 - 3] - outG[base - sx3 - 3] + outR[base - sx3 + 3] - outG[base - sx3 + 3] + outR[base + sx3 - 3] - outG[base + sx3 - 3] + outR[base + sx3 + 3] - outG[base + sx3 + 3]) >> 2); CLIP16(tmp, outR[base], bits); } } /* process BLUE channel */ for (i = 0, i3 = 0; i < sy * sx; i += (sx << 1), i3 += (sx3 << 1)) { for (j = 1, j3 = 3; j < sx - 2; j += 2, j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outB[base - 3] - outG[base - 3] + outB[base + 3] - outG[base + 3]) >> 1); CLIP16(tmp, outB[base], bits); } } for (i3 = sx3; i3 < (sy - 1) * sx3; i3 += (sx3 << 1)) { for (j3 = 0; j3 < sx3 - 3; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outB[base - sx3] - outG[base - sx3] + outB[base + sx3] - outG[base + sx3]) >> 1); CLIP16(tmp, outB[base], bits); } for (j3 = 3; j3 < sx3 - 6; j3 += 6) { base = i3 + j3; tmp = outG[base] + ((outB[base - sx3 - 3] - outG[base - sx3 - 3] + outB[base - sx3 + 3] - outG[base - sx3 + 3] + outB[base + sx3 - 3] - outG[base + sx3 - 3] + outB[base + sx3 + 3] - outG[base + sx3 + 3]) >> 2); CLIP16(tmp, outB[base], bits); } } break; } ClearBorders_uint16(rgb, sx, sy, 3); return DC1394_SUCCESS; } /* coriander's Bayer decoding */ dc1394error_t dc1394_bayer_Downsample_uint16(const uint16_t *bayer, uint16_t *rgb, int sx, int sy, int tile, int bits) { uint16_t *outR, *outG, *outB; register int i, j; int tmp; switch (tile) { case DC1394_COLOR_FILTER_GRBG: case DC1394_COLOR_FILTER_BGGR: outR = &rgb[0]; outG = &rgb[1]; outB = &rgb[2]; break; case DC1394_COLOR_FILTER_GBRG: case DC1394_COLOR_FILTER_RGGB: outR = &rgb[2]; outG = &rgb[1]; outB = &rgb[0]; break; default: return DC1394_INVALID_COLOR_FILTER; } switch (tile) { case DC1394_COLOR_FILTER_GRBG: case DC1394_COLOR_FILTER_GBRG: for (i = 0; i < sy * sx; i += (sx << 1)) { for (j = 0; j < sx; j += 2) { tmp = ((bayer[i + j] + bayer[i + sx + j + 1]) >> 1); CLIP16(tmp, outG[((i >> 2) + (j >> 1)) * 3], bits); tmp = bayer[i + sx + j + 1]; CLIP16(tmp, outR[((i >> 2) + (j >> 1)) * 3], bits); tmp = bayer[i + sx + j]; CLIP16(tmp, outB[((i >> 2) + (j >> 1)) * 3], bits); } } break; case DC1394_COLOR_FILTER_BGGR: case DC1394_COLOR_FILTER_RGGB: for (i = 0; i < sy * sx; i += (sx << 1)) { for (j = 0; j < sx; j += 2) { tmp = ((bayer[i + sx + j] + bayer[i + j + 1]) >> 1); CLIP16(tmp, outG[((i >> 2) + (j >> 1)) * 3], bits); tmp = bayer[i + sx + j + 1]; CLIP16(tmp, outR[((i >> 2) + (j >> 1)) * 3], bits); tmp = bayer[i + j]; CLIP16(tmp, outB[((i >> 2) + (j >> 1)) * 3], bits); } } break; } return DC1394_SUCCESS; } /* coriander's Bayer decoding */ dc1394error_t dc1394_bayer_Simple_uint16(const uint16_t *bayer, uint16_t *rgb, int sx, int sy, int tile, int bits) { uint16_t *outR, *outG, *outB; register int i, j; int tmp, base; /* sx and sy should be even */ switch (tile) { case DC1394_COLOR_FILTER_GRBG: case DC1394_COLOR_FILTER_BGGR: outR = &rgb[0]; outG = &rgb[1]; outB = &rgb[2]; break; case DC1394_COLOR_FILTER_GBRG: case DC1394_COLOR_FILTER_RGGB: outR = &rgb[2]; outG = &rgb[1]; outB = &rgb[0]; break; default: return DC1394_INVALID_COLOR_FILTER; } switch (tile) { case DC1394_COLOR_FILTER_GRBG: case DC1394_COLOR_FILTER_BGGR: outR = &rgb[0]; outG = &rgb[1]; outB = &rgb[2]; break; case DC1394_COLOR_FILTER_GBRG: case DC1394_COLOR_FILTER_RGGB: outR = &rgb[2]; outG = &rgb[1]; outB = &rgb[0]; break; default: outR = NULL; outG = NULL; outB = NULL; break; } switch (tile) { case DC1394_COLOR_FILTER_GRBG: case DC1394_COLOR_FILTER_GBRG: for (i = 0; i < sy - 1; i += 2) { for (j = 0; j < sx - 1; j += 2) { base = i * sx + j; tmp = ((bayer[base] + bayer[base + sx + 1]) >> 1); CLIP16(tmp, outG[base * 3], bits); tmp = bayer[base + 1]; CLIP16(tmp, outR[base * 3], bits); tmp = bayer[base + sx]; CLIP16(tmp, outB[base * 3], bits); } } for (i = 0; i < sy - 1; i += 2) { for (j = 1; j < sx - 1; j += 2) { base = i * sx + j; tmp = ((bayer[base + 1] + bayer[base + sx]) >> 1); CLIP16(tmp, outG[(base)*3], bits); tmp = bayer[base]; CLIP16(tmp, outR[(base)*3], bits); tmp = bayer[base + 1 + sx]; CLIP16(tmp, outB[(base)*3], bits); } } for (i = 1; i < sy - 1; i += 2) { for (j = 0; j < sx - 1; j += 2) { base = i * sx + j; tmp = ((bayer[base + sx] + bayer[base + 1]) >> 1); CLIP16(tmp, outG[base * 3], bits); tmp = bayer[base + sx + 1]; CLIP16(tmp, outR[base * 3], bits); tmp = bayer[base]; CLIP16(tmp, outB[base * 3], bits); } } for (i = 1; i < sy - 1; i += 2) { for (j = 1; j < sx - 1; j += 2) { base = i * sx + j; tmp = ((bayer[base] + bayer[base + 1 + sx]) >> 1); CLIP16(tmp, outG[(base)*3], bits); tmp = bayer[base + sx]; CLIP16(tmp, outR[(base)*3], bits); tmp = bayer[base + 1]; CLIP16(tmp, outB[(base)*3], bits); } } break; case DC1394_COLOR_FILTER_BGGR: case DC1394_COLOR_FILTER_RGGB: for (i = 0; i < sy - 1; i += 2) { for (j = 0; j < sx - 1; j += 2) { base = i * sx + j; tmp = ((bayer[base + sx] + bayer[base + 1]) >> 1); CLIP16(tmp, outG[base * 3], bits); tmp = bayer[base + sx + 1]; CLIP16(tmp, outR[base * 3], bits); tmp = bayer[base]; CLIP16(tmp, outB[base * 3], bits); } } for (i = 1; i < sy - 1; i += 2) { for (j = 0; j < sx - 1; j += 2) { base = i * sx + j; tmp = ((bayer[base] + bayer[base + 1 + sx]) >> 1); CLIP16(tmp, outG[(base)*3], bits); tmp = bayer[base + 1]; CLIP16(tmp, outR[(base)*3], bits); tmp = bayer[base + sx]; CLIP16(tmp, outB[(base)*3], bits); } } for (i = 0; i < sy - 1; i += 2) { for (j = 1; j < sx - 1; j += 2) { base = i * sx + j; tmp = ((bayer[base] + bayer[base + sx + 1]) >> 1); CLIP16(tmp, outG[base * 3], bits); tmp = bayer[base + sx]; CLIP16(tmp, outR[base * 3], bits); tmp = bayer[base + 1]; CLIP16(tmp, outB[base * 3], bits); } } for (i = 1; i < sy - 1; i += 2) { for (j = 1; j < sx - 1; j += 2) { base = i * sx + j; tmp = ((bayer[base + 1] + bayer[base + sx]) >> 1); CLIP16(tmp, outG[(base)*3], bits); tmp = bayer[base]; CLIP16(tmp, outR[(base)*3], bits); tmp = bayer[base + 1 + sx]; CLIP16(tmp, outB[(base)*3], bits); } } break; } /* add black border */ for (i = sx * (sy - 1) * 3; i < sx * sy * 3; i++) { rgb[i] = 0; } for (i = (sx - 1) * 3; i < sx * sy * 3; i += (sx - 1) * 3) { rgb[i++] = 0; rgb[i++] = 0; rgb[i++] = 0; } return DC1394_SUCCESS; } -/* Variable Number of Gradients, from dcraw */ +/* Variable Number of Gradients, from dcraw */ /* Ported to libdc1394 by Frederic Devernay */ #define FORC3 for (c = 0; c < 3; c++) #define SQR(x) ((x) * (x)) #define ABS(x) (((int)(x) ^ ((int)(x) >> 31)) - ((int)(x) >> 31)) #ifndef MIN #define MIN(a, b) ((a) < (b) ? (a) : (b)) #endif #ifndef MAX #define MAX(a, b) ((a) > (b) ? (a) : (b)) #endif #define LIM(x, min, max) MAX(min, MIN(x, max)) #define ULIM(x, y, z) ((y) < (z) ? LIM(x, y, z) : LIM(x, z, y)) /* In order to inline this calculation, I make the risky assumption that all filter patterns can be described by a repeating pattern of eight rows and two columns Return values are either 0/1/2/3 = G/M/C/Y or 0/1/2/3 = R/G1/B/G2 */ #define FC(row, col) (filters >> ((((row) << 1 & 14) + ((col)&1)) << 1) & 3) /* This algorithm is officially called: "Interpolation using a Threshold-based variable number of gradients" described in http://www-ise.stanford.edu/~tingchen/algodep/vargra.html I've extended the basic idea to work with non-Bayer filter arrays. Gradients are numbered clockwise from NW=0 to W=7. */ static const signed char bayervng_terms[] = { -2, -2, +0, -1, 0, 0x01, -2, -2, +0, +0, 1, 0x01, -2, -1, -1, +0, 0, 0x01, -2, -1, +0, -1, 0, 0x02, -2, -1, +0, +0, 0, 0x03, -2, -1, +0, +1, 1, 0x01, -2, +0, +0, -1, 0, 0x06, -2, +0, +0, +0, 1, 0x02, -2, +0, +0, +1, 0, 0x03, -2, +1, -1, +0, 0, 0x04, -2, +1, +0, -1, 1, 0x04, -2, +1, +0, +0, 0, 0x06, -2, +1, +0, +1, 0, 0x02, -2, +2, +0, +0, 1, 0x04, -2, +2, +0, +1, 0, 0x04, -1, -2, -1, +0, 0, 0x80, -1, -2, +0, -1, 0, 0x01, -1, -2, +1, -1, 0, 0x01, -1, -2, +1, +0, 1, 0x01, -1, -1, -1, +1, 0, 0x88, -1, -1, +1, -2, 0, 0x40, -1, -1, +1, -1, 0, 0x22, -1, -1, +1, +0, 0, 0x33, -1, -1, +1, +1, 1, 0x11, -1, +0, -1, +2, 0, 0x08, -1, +0, +0, -1, 0, 0x44, -1, +0, +0, +1, 0, 0x11, -1, +0, +1, -2, 1, 0x40, -1, +0, +1, -1, 0, 0x66, -1, +0, +1, +0, 1, 0x22, -1, +0, +1, +1, 0, 0x33, -1, +0, +1, +2, 1, 0x10, -1, +1, +1, -1, 1, 0x44, -1, +1, +1, +0, 0, 0x66, -1, +1, +1, +1, 0, 0x22, -1, +1, +1, +2, 0, 0x10, -1, +2, +0, +1, 0, 0x04, -1, +2, +1, +0, 1, 0x04, -1, +2, +1, +1, 0, 0x04, +0, -2, +0, +0, 1, 0x80, +0, -1, +0, +1, 1, 0x88, +0, -1, +1, -2, 0, 0x40, +0, -1, +1, +0, 0, 0x11, +0, -1, +2, -2, 0, 0x40, +0, -1, +2, -1, 0, 0x20, +0, -1, +2, +0, 0, 0x30, +0, -1, +2, +1, 1, 0x10, +0, +0, +0, +2, 1, 0x08, +0, +0, +2, -2, 1, 0x40, +0, +0, +2, -1, 0, 0x60, +0, +0, +2, +0, 1, 0x20, +0, +0, +2, +1, 0, 0x30, +0, +0, +2, +2, 1, 0x10, +0, +1, +1, +0, 0, 0x44, +0, +1, +1, +2, 0, 0x10, +0, +1, +2, -1, 1, 0x40, +0, +1, +2, +0, 0, 0x60, +0, +1, +2, +1, 0, 0x20, +0, +1, +2, +2, 0, 0x10, +1, -2, +1, +0, 0, 0x80, +1, -1, +1, +1, 0, 0x88, +1, +0, +1, +2, 0, 0x08, +1, +0, +2, -1, 0, 0x40, +1, +0, +2, +1, 0, 0x10 }, bayervng_chood[] = { -1, -1, -1, 0, -1, +1, 0, +1, +1, +1, +1, 0, +1, -1, 0, -1 }; dc1394error_t dc1394_bayer_VNG(const uint8_t *bayer, uint8_t *dst, int sx, int sy, dc1394color_filter_t pattern) { const int height = sy, width = sx; static const signed char *cp; /* the following has the same type as the image */ uint8_t(*brow[5])[3], *pix; /* [FD] */ int code[8][2][320], *ip, gval[8], gmin, gmax, sum[4]; int row, col, x, y, x1, x2, y1, y2, t, weight, grads, color, diag; int g, diff, thold, num, c; uint32_t filters; /* [FD] */ /* first, use bilinear bayer decoding */ dc1394_bayer_Bilinear(bayer, dst, sx, sy, pattern); switch (pattern) { case DC1394_COLOR_FILTER_BGGR: filters = 0x16161616; break; case DC1394_COLOR_FILTER_GRBG: filters = 0x61616161; break; case DC1394_COLOR_FILTER_RGGB: filters = 0x94949494; break; case DC1394_COLOR_FILTER_GBRG: filters = 0x49494949; break; default: return DC1394_INVALID_COLOR_FILTER; } for (row = 0; row < 8; row++) { /* Precalculate for VNG */ for (col = 0; col < 2; col++) { ip = code[row][col]; for (cp = bayervng_terms, t = 0; t < 64; t++) { y1 = *cp++; x1 = *cp++; y2 = *cp++; x2 = *cp++; weight = *cp++; grads = *cp++; color = FC(row + y1, col + x1); if ((int)FC(row + y2, col + x2) != color) continue; diag = ((int)FC(row, col + 1) == color && (int)FC(row + 1, col) == color) ? 2 : 1; if (abs(y1 - y2) == diag && abs(x1 - x2) == diag) continue; *ip++ = (y1 * width + x1) * 3 + color; /* [FD] */ *ip++ = (y2 * width + x2) * 3 + color; /* [FD] */ *ip++ = weight; for (g = 0; g < 8; g++) if (grads & 1 << g) *ip++ = g; *ip++ = -1; } *ip++ = INT_MAX; for (cp = bayervng_chood, g = 0; g < 8; g++) { y = *cp++; x = *cp++; *ip++ = (y * width + x) * 3; /* [FD] */ color = FC(row, col); if ((int)FC(row + y, col + x) != color && (int)FC(row + y * 2, col + x * 2) == color) *ip++ = (y * width + x) * 6 + color; /* [FD] */ else *ip++ = 0; } } } brow[4] = calloc(width * 3, sizeof **brow); for (row = 0; row < 3; row++) brow[row] = brow[4] + row * width; for (row = 2; row < height - 2; row++) { /* Do VNG interpolation */ for (col = 2; col < width - 2; col++) { pix = dst + (row * width + col) * 3; /* [FD] */ ip = code[row & 7][col & 1]; memset(gval, 0, sizeof gval); while ((g = ip[0]) != INT_MAX) { /* Calculate gradients */ diff = ABS(pix[g] - pix[ip[1]]) << ip[2]; gval[ip[3]] += diff; ip += 5; if ((g = ip[-1]) == -1) continue; gval[g] += diff; while ((g = *ip++) != -1) gval[g] += diff; } ip++; gmin = gmax = gval[0]; /* Choose a threshold */ for (g = 1; g < 8; g++) { if (gmin > gval[g]) gmin = gval[g]; if (gmax < gval[g]) gmax = gval[g]; } if (gmax == 0) { memcpy(brow[2][col], pix, 3 * sizeof *dst); /* [FD] */ continue; } thold = gmin + (gmax >> 1); memset(sum, 0, sizeof sum); color = FC(row, col); for (num = g = 0; g < 8; g++, ip += 2) { /* Average the neighbors */ if (gval[g] <= thold) { for (c = 0; c < 3; c++) /* [FD] */ if (c == color && ip[1]) sum[c] += (pix[c] + pix[ip[1]]) >> 1; else sum[c] += pix[ip[0] + c]; num++; } } for (c = 0; c < 3; c++) { /* [FD] Save to buffer */ t = pix[color]; if (c != color) t += (sum[c] - sum[color]) / num; CLIP(t, brow[2][col][c]); /* [FD] */ } } if (row > 3) /* Write buffer to image */ memcpy(dst + 3 * ((row - 2) * width + 2), brow[0] + 2, (width - 4) * 3 * sizeof *dst); /* [FD] */ for (g = 0; g < 4; g++) brow[(g - 1) & 3] = brow[g]; } memcpy(dst + 3 * ((row - 2) * width + 2), brow[0] + 2, (width - 4) * 3 * sizeof *dst); memcpy(dst + 3 * ((row - 1) * width + 2), brow[1] + 2, (width - 4) * 3 * sizeof *dst); free(brow[4]); return DC1394_SUCCESS; } dc1394error_t dc1394_bayer_VNG_uint16(const uint16_t *bayer, uint16_t *dst, int sx, int sy, dc1394color_filter_t pattern, int bits) { const int height = sy, width = sx; static const signed char *cp; /* the following has the same type as the image */ uint16_t(*brow[5])[3], *pix; /* [FD] */ int code[8][2][320], *ip, gval[8], gmin, gmax, sum[4]; int row, col, x, y, x1, x2, y1, y2, t, weight, grads, color, diag; int g, diff, thold, num, c; uint32_t filters; /* [FD] */ /* first, use bilinear bayer decoding */ dc1394_bayer_Bilinear_uint16(bayer, dst, sx, sy, pattern, bits); switch (pattern) { case DC1394_COLOR_FILTER_BGGR: filters = 0x16161616; break; case DC1394_COLOR_FILTER_GRBG: filters = 0x61616161; break; case DC1394_COLOR_FILTER_RGGB: filters = 0x94949494; break; case DC1394_COLOR_FILTER_GBRG: filters = 0x49494949; break; default: return DC1394_INVALID_COLOR_FILTER; } for (row = 0; row < 8; row++) { /* Precalculate for VNG */ for (col = 0; col < 2; col++) { ip = code[row][col]; for (cp = bayervng_terms, t = 0; t < 64; t++) { y1 = *cp++; x1 = *cp++; y2 = *cp++; x2 = *cp++; weight = *cp++; grads = *cp++; color = FC(row + y1, col + x1); if ((int)FC(row + y2, col + x2) != color) continue; diag = ((int)FC(row, col + 1) == color && (int)FC(row + 1, col) == color) ? 2 : 1; if (abs(y1 - y2) == diag && abs(x1 - x2) == diag) continue; *ip++ = (y1 * width + x1) * 3 + color; /* [FD] */ *ip++ = (y2 * width + x2) * 3 + color; /* [FD] */ *ip++ = weight; for (g = 0; g < 8; g++) if (grads & 1 << g) *ip++ = g; *ip++ = -1; } *ip++ = INT_MAX; for (cp = bayervng_chood, g = 0; g < 8; g++) { y = *cp++; x = *cp++; *ip++ = (y * width + x) * 3; /* [FD] */ color = FC(row, col); if ((int)FC(row + y, col + x) != color && (int)FC(row + y * 2, col + x * 2) == color) *ip++ = (y * width + x) * 6 + color; /* [FD] */ else *ip++ = 0; } } } brow[4] = calloc(width * 3, sizeof **brow); for (row = 0; row < 3; row++) brow[row] = brow[4] + row * width; for (row = 2; row < height - 2; row++) { /* Do VNG interpolation */ for (col = 2; col < width - 2; col++) { pix = dst + (row * width + col) * 3; /* [FD] */ ip = code[row & 7][col & 1]; memset(gval, 0, sizeof gval); while ((g = ip[0]) != INT_MAX) { /* Calculate gradients */ diff = ABS(pix[g] - pix[ip[1]]) << ip[2]; gval[ip[3]] += diff; ip += 5; if ((g = ip[-1]) == -1) continue; gval[g] += diff; while ((g = *ip++) != -1) gval[g] += diff; } ip++; gmin = gmax = gval[0]; /* Choose a threshold */ for (g = 1; g < 8; g++) { if (gmin > gval[g]) gmin = gval[g]; if (gmax < gval[g]) gmax = gval[g]; } if (gmax == 0) { memcpy(brow[2][col], pix, 3 * sizeof *dst); /* [FD] */ continue; } thold = gmin + (gmax >> 1); memset(sum, 0, sizeof sum); color = FC(row, col); for (num = g = 0; g < 8; g++, ip += 2) { /* Average the neighbors */ if (gval[g] <= thold) { for (c = 0; c < 3; c++) /* [FD] */ if (c == color && ip[1]) sum[c] += (pix[c] + pix[ip[1]]) >> 1; else sum[c] += pix[ip[0] + c]; num++; } } for (c = 0; c < 3; c++) { /* [FD] Save to buffer */ t = pix[color]; if (c != color) t += (sum[c] - sum[color]) / num; CLIP16(t, brow[2][col][c], bits); /* [FD] */ } } if (row > 3) /* Write buffer to image */ memcpy(dst + 3 * ((row - 2) * width + 2), brow[0] + 2, (width - 4) * 3 * sizeof *dst); /* [FD] */ for (g = 0; g < 4; g++) brow[(g - 1) & 3] = brow[g]; } memcpy(dst + 3 * ((row - 2) * width + 2), brow[0] + 2, (width - 4) * 3 * sizeof *dst); memcpy(dst + 3 * ((row - 1) * width + 2), brow[1] + 2, (width - 4) * 3 * sizeof *dst); free(brow[4]); return DC1394_SUCCESS; } /* AHD interpolation ported from dcraw to libdc1394 by Samuel Audet */ static dc1394bool_t ahd_inited = DC1394_FALSE; /* WARNING: not multi-processor safe */ #define CLIPOUT(x) LIM(x, 0, 255) #define CLIPOUT16(x, bits) LIM(x, 0, ((1 << bits) - 1)) static const double xyz_rgb[3][3] = { /* XYZ from RGB */ { 0.412453, 0.357580, 0.180423 }, { 0.212671, 0.715160, 0.072169 }, { 0.019334, 0.119193, 0.950227 } }; static const float d65_white[3] = { 0.950456, 1, 1.088754 }; static void cam_to_cielab(uint16_t cam[3], float lab[3]) /* [SA] */ { int c, i, j; float r, xyz[3]; static float cbrt[0x10000], xyz_cam[3][4]; if (cam == NULL) { for (i = 0; i < 0x10000; i++) { r = i / 65535.0; cbrt[i] = r > 0.008856 ? pow(r, 1 / 3.0) : 7.787 * r + 16 / 116.0; } for (i = 0; i < 3; i++) for (j = 0; j < 3; j++) /* [SA] */ xyz_cam[i][j] = xyz_rgb[i][j] / d65_white[i]; /* [SA] */ } else { xyz[0] = xyz[1] = xyz[2] = 0.5; FORC3 { /* [SA] */ xyz[0] += xyz_cam[0][c] * cam[c]; xyz[1] += xyz_cam[1][c] * cam[c]; xyz[2] += xyz_cam[2][c] * cam[c]; } xyz[0] = cbrt[CLIPOUT16((int)xyz[0], 16)]; /* [SA] */ xyz[1] = cbrt[CLIPOUT16((int)xyz[1], 16)]; /* [SA] */ xyz[2] = cbrt[CLIPOUT16((int)xyz[2], 16)]; /* [SA] */ lab[0] = 116 * xyz[1] - 16; lab[1] = 500 * (xyz[0] - xyz[1]); lab[2] = 200 * (xyz[1] - xyz[2]); } } /* Adaptive Homogeneity-Directed interpolation is based on the work of Keigo Hirakawa, Thomas Parks, and Paul Lee. */ #define TS 256 /* Tile Size */ dc1394error_t dc1394_bayer_AHD(const uint8_t *bayer, uint8_t *dst, int sx, int sy, dc1394color_filter_t pattern) { int i, j, top, left, row, col, tr, tc, fc, c, d, val, hm[2]; /* the following has the same type as the image */ uint8_t(*pix)[3], (*rix)[3]; /* [SA] */ uint16_t rix16[3]; /* [SA] */ static const int dir[4] = { -1, 1, -TS, TS }; unsigned ldiff[2][4], abdiff[2][4], leps, abeps; float flab[3]; /* [SA] */ uint8_t(*rgb)[TS][TS][3]; short(*lab)[TS][TS][3]; char(*homo)[TS][TS], *buffer; /* start - new code for libdc1394 */ uint32_t filters; const int height = sy, width = sx; int x, y; if (ahd_inited == DC1394_FALSE) { /* WARNING: this might not be multi-processor safe */ cam_to_cielab(NULL, NULL); ahd_inited = DC1394_TRUE; } switch (pattern) { case DC1394_COLOR_FILTER_BGGR: filters = 0x16161616; break; case DC1394_COLOR_FILTER_GRBG: filters = 0x61616161; break; case DC1394_COLOR_FILTER_RGGB: filters = 0x94949494; break; case DC1394_COLOR_FILTER_GBRG: filters = 0x49494949; break; default: return DC1394_INVALID_COLOR_FILTER; } /* fill-in destination with known exact values */ for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { int channel = FC(y, x); dst[(y * width + x) * 3 + channel] = bayer[y * width + x]; } } /* end - new code for libdc1394 */ /* start - code from border_interpolate (int border) */ { int border = 3; int row, col, y, x; unsigned f, c, sum[8]; for (row = 0; row < height; row++) for (col = 0; col < width; col++) { if (col == border && row >= border && row < height - border) col = width - border; memset(sum, 0, sizeof sum); for (y = row - 1; y != row + 2; y++) for (x = col - 1; x != col + 2; x++) if (y < height && x < width) { f = FC(y, x); sum[f] += dst[(y * width + x) * 3 + f]; /* [SA] */ sum[f + 4]++; } f = FC(row, col); FORC3 if (c != f && sum[c + 4]) /* [SA] */ dst[(row * width + col) * 3 + c] = sum[c] / sum[c + 4]; /* [SA] */ } } /* end - code from border_interpolate (int border) */ buffer = (char *)malloc(26 * TS * TS); /* 1664 kB */ /* merror (buffer, "ahd_interpolate()"); */ rgb = (uint8_t(*)[TS][TS][3])buffer; /* [SA] */ lab = (short(*)[TS][TS][3])(buffer + 12 * TS * TS); homo = (char(*)[TS][TS])(buffer + 24 * TS * TS); for (top = 0; top < height; top += TS - 6) for (left = 0; left < width; left += TS - 6) { memset(rgb, 0, 12 * TS * TS); /* Interpolate green horizontally and vertically: */ for (row = top < 2 ? 2 : top; row < top + TS && row < height - 2; row++) { col = left + (FC(row, left) == 1); if (col < 2) col += 2; for (fc = FC(row, col); col < left + TS && col < width - 2; col += 2) { pix = (uint8_t(*)[3])dst + (row * width + col); /* [SA] */ val = ((pix[-1][1] + pix[0][fc] + pix[1][1]) * 2 - pix[-2][fc] - pix[2][fc]) >> 2; rgb[0][row - top][col - left][1] = ULIM(val, pix[-1][1], pix[1][1]); val = ((pix[-width][1] + pix[0][fc] + pix[width][1]) * 2 - pix[-2 * width][fc] - pix[2 * width][fc]) >> 2; rgb[1][row - top][col - left][1] = ULIM(val, pix[-width][1], pix[width][1]); } } /* Interpolate red and blue, and convert to CIELab: */ for (d = 0; d < 2; d++) for (row = top + 1; row < top + TS - 1 && row < height - 1; row++) for (col = left + 1; col < left + TS - 1 && col < width - 1; col++) { pix = (uint8_t(*)[3])dst + (row * width + col); /* [SA] */ rix = &rgb[d][row - top][col - left]; if ((c = 2 - FC(row, col)) == 1) { c = FC(row + 1, col); val = pix[0][1] + ((pix[-1][2 - c] + pix[1][2 - c] - rix[-1][1] - rix[1][1]) >> 1); rix[0][2 - c] = CLIPOUT(val); /* [SA] */ val = pix[0][1] + ((pix[-width][c] + pix[width][c] - rix[-TS][1] - rix[TS][1]) >> 1); } else val = rix[0][1] + ((pix[-width - 1][c] + pix[-width + 1][c] + pix[+width - 1][c] + pix[+width + 1][c] - rix[-TS - 1][1] - rix[-TS + 1][1] - rix[+TS - 1][1] - rix[+TS + 1][1] + 1) >> 2); rix[0][c] = CLIPOUT(val); /* [SA] */ c = FC(row, col); rix[0][c] = pix[0][c]; rix16[0] = rix[0][0]; /* [SA] */ rix16[1] = rix[0][1]; /* [SA] */ rix16[2] = rix[0][2]; /* [SA] */ cam_to_cielab(rix16, flab); /* [SA] */ FORC3 lab[d][row - top][col - left][c] = 64 * flab[c]; } /* Build homogeneity maps from the CIELab images: */ memset(homo, 0, 2 * TS * TS); for (row = top + 2; row < top + TS - 2 && row < height; row++) { tr = row - top; for (col = left + 2; col < left + TS - 2 && col < width; col++) { tc = col - left; for (d = 0; d < 2; d++) for (i = 0; i < 4; i++) ldiff[d][i] = ABS(lab[d][tr][tc][0] - lab[d][tr][tc + dir[i]][0]); leps = MIN(MAX(ldiff[0][0], ldiff[0][1]), MAX(ldiff[1][2], ldiff[1][3])); for (d = 0; d < 2; d++) for (i = 0; i < 4; i++) if (i >> 1 == d || ldiff[d][i] <= leps) abdiff[d][i] = SQR(lab[d][tr][tc][1] - lab[d][tr][tc + dir[i]][1]) + SQR(lab[d][tr][tc][2] - lab[d][tr][tc + dir[i]][2]); abeps = MIN(MAX(abdiff[0][0], abdiff[0][1]), MAX(abdiff[1][2], abdiff[1][3])); for (d = 0; d < 2; d++) for (i = 0; i < 4; i++) if (ldiff[d][i] <= leps && abdiff[d][i] <= abeps) homo[d][tr][tc]++; } } /* Combine the most homogenous pixels for the final result: */ for (row = top + 3; row < top + TS - 3 && row < height - 3; row++) { tr = row - top; for (col = left + 3; col < left + TS - 3 && col < width - 3; col++) { tc = col - left; for (d = 0; d < 2; d++) for (hm[d] = 0, i = tr - 1; i <= tr + 1; i++) for (j = tc - 1; j <= tc + 1; j++) hm[d] += homo[d][i][j]; if (hm[0] != hm[1]) FORC3 dst[(row * width + col) * 3 + c] = CLIPOUT(rgb[hm[1] > hm[0]][tr][tc][c]); /* [SA] */ else FORC3 dst[(row * width + col) * 3 + c] = CLIPOUT((rgb[0][tr][tc][c] + rgb[1][tr][tc][c]) >> 1); /* [SA] */ } } } free(buffer); return DC1394_SUCCESS; } dc1394error_t dc1394_bayer_AHD_uint16(const uint16_t *bayer, uint16_t *dst, int sx, int sy, dc1394color_filter_t pattern, int bits) { int i, j, top, left, row, col, tr, tc, fc, c, d, val, hm[2]; /* the following has the same type as the image */ uint16_t(*pix)[3], (*rix)[3]; /* [SA] */ static const int dir[4] = { -1, 1, -TS, TS }; unsigned ldiff[2][4], abdiff[2][4], leps, abeps; float flab[3]; uint16_t(*rgb)[TS][TS][3]; /* [SA] */ short(*lab)[TS][TS][3]; char(*homo)[TS][TS], *buffer; /* start - new code for libdc1394 */ uint32_t filters; const int height = sy, width = sx; int x, y; if (ahd_inited == DC1394_FALSE) { /* WARNING: this might not be multi-processor safe */ cam_to_cielab(NULL, NULL); ahd_inited = DC1394_TRUE; } switch (pattern) { case DC1394_COLOR_FILTER_BGGR: filters = 0x16161616; break; case DC1394_COLOR_FILTER_GRBG: filters = 0x61616161; break; case DC1394_COLOR_FILTER_RGGB: filters = 0x94949494; break; case DC1394_COLOR_FILTER_GBRG: filters = 0x49494949; break; default: return DC1394_INVALID_COLOR_FILTER; } /* fill-in destination with known exact values */ for (y = 0; y < height; y++) { for (x = 0; x < width; x++) { int channel = FC(y, x); dst[(y * width + x) * 3 + channel] = bayer[y * width + x]; } } /* end - new code for libdc1394 */ /* start - code from border_interpolate(int border) */ { int border = 3; int row, col, y, x; unsigned f, c, sum[8]; for (row = 0; row < height; row++) for (col = 0; col < width; col++) { if (col == border && row >= border && row < height - border) col = width - border; memset(sum, 0, sizeof sum); for (y = row - 1; y != row + 2; y++) for (x = col - 1; x != col + 2; x++) if (y < height && x < width) { f = FC(y, x); sum[f] += dst[(y * width + x) * 3 + f]; /* [SA] */ sum[f + 4]++; } f = FC(row, col); FORC3 if (c != f && sum[c + 4]) /* [SA] */ dst[(row * width + col) * 3 + c] = sum[c] / sum[c + 4]; /* [SA] */ } } /* end - code from border_interpolate(int border) */ buffer = (char *)malloc(26 * TS * TS); /* 1664 kB */ /* merror (buffer, "ahd_interpolate()"); */ rgb = (uint16_t(*)[TS][TS][3])buffer; /* [SA] */ lab = (short(*)[TS][TS][3])(buffer + 12 * TS * TS); homo = (char(*)[TS][TS])(buffer + 24 * TS * TS); for (top = 0; top < height; top += TS - 6) for (left = 0; left < width; left += TS - 6) { memset(rgb, 0, 12 * TS * TS); /* Interpolate green horizontally and vertically: */ for (row = top < 2 ? 2 : top; row < top + TS && row < height - 2; row++) { col = left + (FC(row, left) == 1); if (col < 2) col += 2; for (fc = FC(row, col); col < left + TS && col < width - 2; col += 2) { pix = (uint16_t(*)[3])dst + (row * width + col); /* [SA] */ val = ((pix[-1][1] + pix[0][fc] + pix[1][1]) * 2 - pix[-2][fc] - pix[2][fc]) >> 2; rgb[0][row - top][col - left][1] = ULIM(val, pix[-1][1], pix[1][1]); val = ((pix[-width][1] + pix[0][fc] + pix[width][1]) * 2 - pix[-2 * width][fc] - pix[2 * width][fc]) >> 2; rgb[1][row - top][col - left][1] = ULIM(val, pix[-width][1], pix[width][1]); } } /* Interpolate red and blue, and convert to CIELab: */ for (d = 0; d < 2; d++) for (row = top + 1; row < top + TS - 1 && row < height - 1; row++) for (col = left + 1; col < left + TS - 1 && col < width - 1; col++) { pix = (uint16_t(*)[3])dst + (row * width + col); /* [SA] */ rix = &rgb[d][row - top][col - left]; if ((c = 2 - FC(row, col)) == 1) { c = FC(row + 1, col); val = pix[0][1] + ((pix[-1][2 - c] + pix[1][2 - c] - rix[-1][1] - rix[1][1]) >> 1); rix[0][2 - c] = CLIPOUT16(val, bits); /* [SA] */ val = pix[0][1] + ((pix[-width][c] + pix[width][c] - rix[-TS][1] - rix[TS][1]) >> 1); } else val = rix[0][1] + ((pix[-width - 1][c] + pix[-width + 1][c] + pix[+width - 1][c] + pix[+width + 1][c] - rix[-TS - 1][1] - rix[-TS + 1][1] - rix[+TS - 1][1] - rix[+TS + 1][1] + 1) >> 2); rix[0][c] = CLIPOUT16(val, bits); /* [SA] */ c = FC(row, col); rix[0][c] = pix[0][c]; cam_to_cielab(rix[0], flab); FORC3 lab[d][row - top][col - left][c] = 64 * flab[c]; } /* Build homogeneity maps from the CIELab images: */ memset(homo, 0, 2 * TS * TS); for (row = top + 2; row < top + TS - 2 && row < height; row++) { tr = row - top; for (col = left + 2; col < left + TS - 2 && col < width; col++) { tc = col - left; for (d = 0; d < 2; d++) for (i = 0; i < 4; i++) ldiff[d][i] = ABS(lab[d][tr][tc][0] - lab[d][tr][tc + dir[i]][0]); leps = MIN(MAX(ldiff[0][0], ldiff[0][1]), MAX(ldiff[1][2], ldiff[1][3])); for (d = 0; d < 2; d++) for (i = 0; i < 4; i++) if (i >> 1 == d || ldiff[d][i] <= leps) abdiff[d][i] = SQR(lab[d][tr][tc][1] - lab[d][tr][tc + dir[i]][1]) + SQR(lab[d][tr][tc][2] - lab[d][tr][tc + dir[i]][2]); abeps = MIN(MAX(abdiff[0][0], abdiff[0][1]), MAX(abdiff[1][2], abdiff[1][3])); for (d = 0; d < 2; d++) for (i = 0; i < 4; i++) if (ldiff[d][i] <= leps && abdiff[d][i] <= abeps) homo[d][tr][tc]++; } } /* Combine the most homogenous pixels for the final result: */ for (row = top + 3; row < top + TS - 3 && row < height - 3; row++) { tr = row - top; for (col = left + 3; col < left + TS - 3 && col < width - 3; col++) { tc = col - left; for (d = 0; d < 2; d++) for (hm[d] = 0, i = tr - 1; i <= tr + 1; i++) for (j = tc - 1; j <= tc + 1; j++) hm[d] += homo[d][i][j]; if (hm[0] != hm[1]) FORC3 dst[(row * width + col) * 3 + c] = CLIPOUT16(rgb[hm[1] > hm[0]][tr][tc][c], bits); /* [SA] */ else FORC3 dst[(row * width + col) * 3 + c] = CLIPOUT16((rgb[0][tr][tc][c] + rgb[1][tr][tc][c]) >> 1, bits); /* [SA] */ } } } free(buffer); return DC1394_SUCCESS; } dc1394error_t dc1394_bayer_decoding_8bit(const uint8_t *bayer, uint8_t *rgb, uint32_t sx, uint32_t sy, dc1394color_filter_t tile, dc1394bayer_method_t method) { switch (method) { case DC1394_BAYER_METHOD_NEAREST: return dc1394_bayer_NearestNeighbor(bayer, rgb, sx, sy, tile); case DC1394_BAYER_METHOD_SIMPLE: return dc1394_bayer_Simple(bayer, rgb, sx, sy, tile); case DC1394_BAYER_METHOD_BILINEAR: return dc1394_bayer_Bilinear(bayer, rgb, sx, sy, tile); case DC1394_BAYER_METHOD_HQLINEAR: return dc1394_bayer_HQLinear(bayer, rgb, sx, sy, tile); case DC1394_BAYER_METHOD_DOWNSAMPLE: return dc1394_bayer_Downsample(bayer, rgb, sx, sy, tile); case DC1394_BAYER_METHOD_EDGESENSE: return dc1394_bayer_EdgeSense(bayer, rgb, sx, sy, tile); case DC1394_BAYER_METHOD_VNG: return dc1394_bayer_VNG(bayer, rgb, sx, sy, tile); case DC1394_BAYER_METHOD_AHD: return dc1394_bayer_AHD(bayer, rgb, sx, sy, tile); default: return DC1394_INVALID_BAYER_METHOD; } } dc1394error_t dc1394_bayer_decoding_16bit(const uint16_t *bayer, uint16_t *rgb, uint32_t sx, uint32_t sy, dc1394color_filter_t tile, dc1394bayer_method_t method, uint32_t bits) { switch (method) { case DC1394_BAYER_METHOD_NEAREST: return dc1394_bayer_NearestNeighbor_uint16(bayer, rgb, sx, sy, tile, bits); case DC1394_BAYER_METHOD_SIMPLE: return dc1394_bayer_Simple_uint16(bayer, rgb, sx, sy, tile, bits); case DC1394_BAYER_METHOD_BILINEAR: return dc1394_bayer_Bilinear_uint16(bayer, rgb, sx, sy, tile, bits); case DC1394_BAYER_METHOD_HQLINEAR: return dc1394_bayer_HQLinear_uint16(bayer, rgb, sx, sy, tile, bits); case DC1394_BAYER_METHOD_DOWNSAMPLE: return dc1394_bayer_Downsample_uint16(bayer, rgb, sx, sy, tile, bits); case DC1394_BAYER_METHOD_EDGESENSE: return dc1394_bayer_EdgeSense_uint16(bayer, rgb, sx, sy, tile, bits); case DC1394_BAYER_METHOD_VNG: return dc1394_bayer_VNG_uint16(bayer, rgb, sx, sy, tile, bits); case DC1394_BAYER_METHOD_AHD: return dc1394_bayer_AHD_uint16(bayer, rgb, sx, sy, tile, bits); default: return DC1394_INVALID_BAYER_METHOD; } } diff --git a/lib/imageformats/fitsformat/bayer.h b/lib/imageformats/fitsformat/bayer.h index 70554a33..fe783354 100644 --- a/lib/imageformats/fitsformat/bayer.h +++ b/lib/imageformats/fitsformat/bayer.h @@ -1,275 +1,275 @@ /* * 1394-Based Digital Camera Control Library * * Bayer pattern decoding functions * * Written by Damien Douxchamps and Frederic Devernay * The original VNG and AHD Bayer decoding are from Dave Coffin's DCRAW. * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either * version 2.1 of the License, or (at your option) any later version. * * This library 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 * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public * License along with this library; if not, write to the Free Software * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #pragma once #include /** * Error codes returned by most libdc1394 functions. * * General rule: 0 is success, negative denotes a problem. */ typedef enum { DC1394_SUCCESS = 0, DC1394_FAILURE = -1, DC1394_NOT_A_CAMERA = -2, DC1394_FUNCTION_NOT_SUPPORTED = -3, DC1394_CAMERA_NOT_INITIALIZED = -4, DC1394_MEMORY_ALLOCATION_FAILURE = -5, DC1394_TAGGED_REGISTER_NOT_FOUND = -6, DC1394_NO_ISO_CHANNEL = -7, DC1394_NO_BANDWIDTH = -8, DC1394_IOCTL_FAILURE = -9, DC1394_CAPTURE_IS_NOT_SET = -10, DC1394_CAPTURE_IS_RUNNING = -11, DC1394_RAW1394_FAILURE = -12, DC1394_FORMAT7_ERROR_FLAG_1 = -13, DC1394_FORMAT7_ERROR_FLAG_2 = -14, DC1394_INVALID_ARGUMENT_VALUE = -15, DC1394_REQ_VALUE_OUTSIDE_RANGE = -16, DC1394_INVALID_FEATURE = -17, DC1394_INVALID_VIDEO_FORMAT = -18, DC1394_INVALID_VIDEO_MODE = -19, DC1394_INVALID_FRAMERATE = -20, DC1394_INVALID_TRIGGER_MODE = -21, DC1394_INVALID_TRIGGER_SOURCE = -22, DC1394_INVALID_ISO_SPEED = -23, DC1394_INVALID_IIDC_VERSION = -24, DC1394_INVALID_COLOR_CODING = -25, DC1394_INVALID_COLOR_FILTER = -26, DC1394_INVALID_CAPTURE_POLICY = -27, DC1394_INVALID_ERROR_CODE = -28, DC1394_INVALID_BAYER_METHOD = -29, DC1394_INVALID_VIDEO1394_DEVICE = -30, DC1394_INVALID_OPERATION_MODE = -31, DC1394_INVALID_TRIGGER_POLARITY = -32, DC1394_INVALID_FEATURE_MODE = -33, DC1394_INVALID_LOG_TYPE = -34, DC1394_INVALID_BYTE_ORDER = -35, DC1394_INVALID_STEREO_METHOD = -36, DC1394_BASLER_NO_MORE_SFF_CHUNKS = -37, DC1394_BASLER_CORRUPTED_SFF_CHUNK = -38, DC1394_BASLER_UNKNOWN_SFF_CHUNK = -39 } dc1394error_t; #define DC1394_ERROR_MIN DC1394_BASLER_UNKNOWN_SFF_CHUNK #define DC1394_ERROR_MAX DC1394_SUCCESS #define DC1394_ERROR_NUM (DC1394_ERROR_MAX - DC1394_ERROR_MIN + 1) /** * Enumeration of video modes. Note that the notion of IIDC "format" is not present here, except in the format_7 name. */ typedef enum { DC1394_VIDEO_MODE_160x120_YUV444 = 64, DC1394_VIDEO_MODE_320x240_YUV422, DC1394_VIDEO_MODE_640x480_YUV411, DC1394_VIDEO_MODE_640x480_YUV422, DC1394_VIDEO_MODE_640x480_RGB8, DC1394_VIDEO_MODE_640x480_MONO8, DC1394_VIDEO_MODE_640x480_MONO16, DC1394_VIDEO_MODE_800x600_YUV422, DC1394_VIDEO_MODE_800x600_RGB8, DC1394_VIDEO_MODE_800x600_MONO8, DC1394_VIDEO_MODE_1024x768_YUV422, DC1394_VIDEO_MODE_1024x768_RGB8, DC1394_VIDEO_MODE_1024x768_MONO8, DC1394_VIDEO_MODE_800x600_MONO16, DC1394_VIDEO_MODE_1024x768_MONO16, DC1394_VIDEO_MODE_1280x960_YUV422, DC1394_VIDEO_MODE_1280x960_RGB8, DC1394_VIDEO_MODE_1280x960_MONO8, DC1394_VIDEO_MODE_1600x1200_YUV422, DC1394_VIDEO_MODE_1600x1200_RGB8, DC1394_VIDEO_MODE_1600x1200_MONO8, DC1394_VIDEO_MODE_1280x960_MONO16, DC1394_VIDEO_MODE_1600x1200_MONO16, DC1394_VIDEO_MODE_EXIF, DC1394_VIDEO_MODE_FORMAT7_0, DC1394_VIDEO_MODE_FORMAT7_1, DC1394_VIDEO_MODE_FORMAT7_2, DC1394_VIDEO_MODE_FORMAT7_3, DC1394_VIDEO_MODE_FORMAT7_4, DC1394_VIDEO_MODE_FORMAT7_5, DC1394_VIDEO_MODE_FORMAT7_6, DC1394_VIDEO_MODE_FORMAT7_7 } dc1394video_mode_t; #define DC1394_VIDEO_MODE_MIN DC1394_VIDEO_MODE_160x120_YUV444 #define DC1394_VIDEO_MODE_MAX DC1394_VIDEO_MODE_FORMAT7_7 #define DC1394_VIDEO_MODE_NUM (DC1394_VIDEO_MODE_MAX - DC1394_VIDEO_MODE_MIN + 1) /* Special min/max are defined for Format_7 */ #define DC1394_VIDEO_MODE_FORMAT7_MIN DC1394_VIDEO_MODE_FORMAT7_0 #define DC1394_VIDEO_MODE_FORMAT7_MAX DC1394_VIDEO_MODE_FORMAT7_7 #define DC1394_VIDEO_MODE_FORMAT7_NUM (DC1394_VIDEO_MODE_FORMAT7_MAX - DC1394_VIDEO_MODE_FORMAT7_MIN + 1) /** * Enumeration of colour codings. For details on the data format please read the IIDC specifications. */ typedef enum { DC1394_COLOR_CODING_MONO8 = 352, DC1394_COLOR_CODING_YUV411, DC1394_COLOR_CODING_YUV422, DC1394_COLOR_CODING_YUV444, DC1394_COLOR_CODING_RGB8, DC1394_COLOR_CODING_MONO16, DC1394_COLOR_CODING_RGB16, DC1394_COLOR_CODING_MONO16S, DC1394_COLOR_CODING_RGB16S, DC1394_COLOR_CODING_RAW8, DC1394_COLOR_CODING_RAW16 } dc1394color_coding_t; #define DC1394_COLOR_CODING_MIN DC1394_COLOR_CODING_MONO8 #define DC1394_COLOR_CODING_MAX DC1394_COLOR_CODING_RAW16 #define DC1394_COLOR_CODING_NUM (DC1394_COLOR_CODING_MAX - DC1394_COLOR_CODING_MIN + 1) /** * RAW sensor filters. These elementary tiles tesselate the image plane in RAW modes. RGGB should be interpreted in 2D as * * RG * GB * * and similarly for other filters. */ typedef enum { DC1394_COLOR_FILTER_RGGB = 512, DC1394_COLOR_FILTER_GBRG, DC1394_COLOR_FILTER_GRBG, DC1394_COLOR_FILTER_BGGR } dc1394color_filter_t; #define DC1394_COLOR_FILTER_MIN DC1394_COLOR_FILTER_RGGB #define DC1394_COLOR_FILTER_MAX DC1394_COLOR_FILTER_BGGR #define DC1394_COLOR_FILTER_NUM (DC1394_COLOR_FILTER_MAX - DC1394_COLOR_FILTER_MIN + 1) /** * Byte order for YUV formats (may be expanded to RGB in the future) * * IIDC cameras always return data in UYVY order, but conversion functions can change this if requested. */ typedef enum { DC1394_BYTE_ORDER_UYVY = 800, DC1394_BYTE_ORDER_YUYV } dc1394byte_order_t; #define DC1394_BYTE_ORDER_MIN DC1394_BYTE_ORDER_UYVY #define DC1394_BYTE_ORDER_MAX DC1394_BYTE_ORDER_YUYV #define DC1394_BYTE_ORDER_NUM (DC1394_BYTE_ORDER_MAX - DC1394_BYTE_ORDER_MIN + 1) /** * A struct containing a list of color codings */ typedef struct { uint32_t num; dc1394color_coding_t codings[DC1394_COLOR_CODING_NUM]; } dc1394color_codings_t; /** * A struct containing a list of video modes */ typedef struct { uint32_t num; dc1394video_mode_t modes[DC1394_VIDEO_MODE_NUM]; } dc1394video_modes_t; /** * Yet another boolean data type */ typedef enum { DC1394_FALSE = 0, DC1394_TRUE } dc1394bool_t; /** * Yet another boolean data type, a bit more oriented towards electrical-engineers */ typedef enum { DC1394_OFF = 0, DC1394_ON } dc1394switch_t; /** * A list of de-mosaicing techniques for Bayer-patterns. * * The speed of the techniques can vary greatly, as well as their quality. */ typedef enum { DC1394_BAYER_METHOD_NEAREST = 0, DC1394_BAYER_METHOD_SIMPLE, DC1394_BAYER_METHOD_BILINEAR, DC1394_BAYER_METHOD_HQLINEAR, DC1394_BAYER_METHOD_DOWNSAMPLE, DC1394_BAYER_METHOD_EDGESENSE, DC1394_BAYER_METHOD_VNG, DC1394_BAYER_METHOD_AHD } dc1394bayer_method_t; #define DC1394_BAYER_METHOD_MIN DC1394_BAYER_METHOD_NEAREST #define DC1394_BAYER_METHOD_MAX DC1394_BAYER_METHOD_AHD #define DC1394_BAYER_METHOD_NUM (DC1394_BAYER_METHOD_MAX - DC1394_BAYER_METHOD_MIN + 1) typedef struct { dc1394bayer_method_t method; /* Debayer method */ dc1394color_filter_t filter; /* Debayer pattern */ int offsetX, offsetY; /* Debayer offset */ } BayerParams; /************************************************************************************************ * * * Color conversion functions for cameras that can output raw Bayer pattern images (color * * codings DC1394_COLOR_CODING_RAW8 and DC1394_COLOR_CODING_RAW16). * * * * Credits and sources: * * - Nearest Neighbor : OpenCV library * * - Bilinear : OpenCV library * * - HQLinear : High-Quality Linear Interpolation For Demosaicing Of Bayer-Patterned * * Color Images, by Henrique S. Malvar, Li-wei He, and Ross Cutler, * * in Proceedings of the ICASSP'04 Conference. * * - Edge Sense II : Laroche, Claude A. "Apparatus and method for adaptively interpolating * * a full color image utilizing chrominance gradients" * * U.S. Patent 5,373,322. Based on the code found on the website * * http://www-ise.stanford.edu/~tingchen/ Converted to C and adapted to * * all four elementary patterns. * * - Downsample : "Known to the Ancients" * * - Simple : Implemented from the information found in the manual of Allied Vision * * Technologies (AVT) cameras. * * - VNG : Variable Number of Gradients, a method described in * * http://www-ise.stanford.edu/~tingchen/algodep/vargra.html * * Sources import from DCRAW by Frederic Devernay. DCRAW is a RAW * * converter program by Dave Coffin. URL: * - * http://www.cybercom.net/~dcoffin/dcraw/ * + * https://www.dechifro.org/dcraw/ * * - AHD : Adaptive Homogeneity-Directed Demosaicing Algorithm, by K. Hirakawa * * and T.W. Parks, IEEE Transactions on Image Processing, Vol. 14, Nr. 3, * * March 2005, pp. 360 - 369. * * * ************************************************************************************************/ #ifdef __cplusplus extern "C" { #endif /** * Perform de-mosaicing on an 8-bit image buffer */ dc1394error_t dc1394_bayer_decoding_8bit(const uint8_t *bayer, uint8_t *rgb, uint32_t width, uint32_t height, dc1394color_filter_t tile, dc1394bayer_method_t method); /** * Perform de-mosaicing on an 16-bit image buffer */ dc1394error_t dc1394_bayer_decoding_16bit(const uint16_t *bayer, uint16_t *rgb, uint32_t width, uint32_t height, dc1394color_filter_t tile, dc1394bayer_method_t method, uint32_t bits); #ifdef __cplusplus } #endif diff --git a/lib/jpegcontent.cpp b/lib/jpegcontent.cpp index 270ad688..0b79d15c 100644 --- a/lib/jpegcontent.cpp +++ b/lib/jpegcontent.cpp @@ -1,714 +1,714 @@ // vim: set tabstop=4 shiftwidth=4 expandtab: /* Gwenview: an image viewer Copyright 2007 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. */ #include "jpegcontent.h" // System #include #include #include #include extern "C" { #include #include "transupp.h" } // Qt #include #include #include #include #include #include // KDE #include // Exiv2 #include // Local #include "jpegerrormanager.h" #include "iodevicejpegsourcemanager.h" #include "exiv2imageloader.h" #include "gwenviewconfig.h" #include "imageutils.h" namespace Gwenview { const int INMEM_DST_DELTA = 4096; //----------------------------------------------- // // In-memory data destination manager for libjpeg // //----------------------------------------------- struct inmem_dest_mgr : public jpeg_destination_mgr { QByteArray* mOutput; void dump() { qDebug() << "dest_mgr:\n"; qDebug() << "- next_output_byte: " << next_output_byte; qDebug() << "- free_in_buffer: " << free_in_buffer; qDebug() << "- output size: " << mOutput->size(); } }; void inmem_init_destination(j_compress_ptr cinfo) { inmem_dest_mgr* dest = (inmem_dest_mgr*)(cinfo->dest); if (dest->mOutput->size() == 0) { dest->mOutput->resize(INMEM_DST_DELTA); } dest->free_in_buffer = dest->mOutput->size(); dest->next_output_byte = (JOCTET*)(dest->mOutput->data()); } boolean inmem_empty_output_buffer(j_compress_ptr cinfo) { inmem_dest_mgr* dest = (inmem_dest_mgr*)(cinfo->dest); dest->mOutput->resize(dest->mOutput->size() + INMEM_DST_DELTA); dest->next_output_byte = (JOCTET*)(dest->mOutput->data() + dest->mOutput->size() - INMEM_DST_DELTA); dest->free_in_buffer = INMEM_DST_DELTA; return true; } void inmem_term_destination(j_compress_ptr cinfo) { inmem_dest_mgr* dest = (inmem_dest_mgr*)(cinfo->dest); int finalSize = dest->next_output_byte - (JOCTET*)(dest->mOutput->data()); Q_ASSERT(finalSize >= 0); dest->mOutput->resize(finalSize); } //--------------------- // // JpegContent::Private // //--------------------- struct JpegContent::Private { // JpegContent usually stores the image pixels as compressed JPEG data in // mRawData. However if the image is set with setImage() because the user // performed a lossy image manipulation, mRawData is cleared and the image // pixels are kept in mImage until updateRawDataFromImage() is called. QImage mImage; // Store the input file, keep it open readOnly. This allows the file to be memory mapped // (i.e. mRawData may point to mFile.map()) rather than completely read on load. Postpone // QFile::readAll() as long as possible (currently in save()). QFile mFile; QByteArray mRawData; QSize mSize; QString mComment; bool mPendingTransformation; QTransform mTransformMatrix; Exiv2::ExifData mExifData; QString mErrorString; Private() { mPendingTransformation = false; } void setupInmemDestination(j_compress_ptr cinfo, QByteArray* outputData) { Q_ASSERT(!cinfo->dest); inmem_dest_mgr* dest = (inmem_dest_mgr*) (*cinfo->mem->alloc_small)((j_common_ptr) cinfo, JPOOL_PERMANENT, sizeof(inmem_dest_mgr)); cinfo->dest = (struct jpeg_destination_mgr*)(dest); dest->init_destination = inmem_init_destination; dest->empty_output_buffer = inmem_empty_output_buffer; dest->term_destination = inmem_term_destination; dest->mOutput = outputData; } bool readSize() { struct jpeg_decompress_struct srcinfo; // Init JPEG structs JPEGErrorManager errorManager; // Initialize the JPEG decompression object srcinfo.err = &errorManager; jpeg_create_decompress(&srcinfo); if (setjmp(errorManager.jmp_buffer)) { qCritical() << "libjpeg fatal error\n"; return false; } // Specify data source for decompression QBuffer buffer(&mRawData); buffer.open(QIODevice::ReadOnly); IODeviceJpegSourceManager::setup(&srcinfo, &buffer); // Read the header jcopy_markers_setup(&srcinfo, JCOPYOPT_ALL); int result = jpeg_read_header(&srcinfo, true); if (result != JPEG_HEADER_OK) { qCritical() << "Could not read jpeg header\n"; jpeg_destroy_decompress(&srcinfo); return false; } mSize = QSize(srcinfo.image_width, srcinfo.image_height); jpeg_destroy_decompress(&srcinfo); return true; } bool updateRawDataFromImage() { QBuffer buffer; QImageWriter writer(&buffer, "jpeg"); writer.setQuality(GwenviewConfig::jPEGQuality()); if (!writer.write(mImage)) { mErrorString = writer.errorString(); return false; } mRawData = buffer.data(); mImage = QImage(); return true; } }; //------------ // // JpegContent // //------------ JpegContent::JpegContent() { d = new JpegContent::Private(); } JpegContent::~JpegContent() { delete d; } bool JpegContent::load(const QString& path) { if (d->mFile.isOpen()) { d->mFile.unmap(reinterpret_cast(d->mRawData.data())); d->mFile.close(); d->mRawData.clear(); } d->mFile.setFileName(path); if (!d->mFile.open(QIODevice::ReadOnly)) { qCritical() << "Could not open '" << path << "' for reading\n"; return false; } QByteArray rawData; uchar* mappedFile = d->mFile.map(0, d->mFile.size(), QFileDevice::MapPrivateOption); if (mappedFile == nullptr) { // process' mapping limit exceeded, file is sealed or filesystem doesn't support it, etc. qDebug() << "Could not mmap '" << path << "', falling back to QFile::readAll()\n"; rawData = d->mFile.readAll(); // all read in, no need to keep it open d->mFile.close(); } else { rawData = QByteArray::fromRawData(reinterpret_cast(mappedFile), d->mFile.size()); } return loadFromData(rawData); } bool JpegContent::loadFromData(const QByteArray& data) { std::unique_ptr image; Exiv2ImageLoader loader; if (!loader.load(data)) { qCritical() << "Could not load image with Exiv2, reported error:" << loader.errorMessage(); } image.reset(loader.popImage().release()); return loadFromData(data, image.get()); } bool JpegContent::loadFromData(const QByteArray& data, Exiv2::Image* exiv2Image) { d->mPendingTransformation = false; d->mTransformMatrix.reset(); d->mRawData = data; if (d->mRawData.size() == 0) { qCritical() << "No data\n"; return false; } if (!d->readSize()) return false; d->mExifData = exiv2Image->exifData(); d->mComment = QString::fromUtf8(exiv2Image->comment().c_str()); if (!GwenviewConfig::applyExifOrientation()) { return true; } // Adjust the size according to the orientation switch (orientation()) { case TRANSPOSE: case ROT_90: case TRANSVERSE: case ROT_270: d->mSize.transpose(); break; default: break; } return true; } QByteArray JpegContent::rawData() const { return d->mRawData; } Orientation JpegContent::orientation() const { Exiv2::ExifKey key("Exif.Image.Orientation"); Exiv2::ExifData::iterator it = d->mExifData.findKey(key); // We do the same checks as in libexiv2's src/crwimage.cpp: - // http://dev.exiv2.org/projects/exiv2/repository/entry/trunk/src/crwimage.cpp?rev=2681#L1336 + // https://github.com/Exiv2/exiv2/blob/0d397b95c7b4a10819c0ea0f36fa20943e6a4ea5/src/crwimage.cpp#L1336 if (it == d->mExifData.end() || it->count() == 0 || it->typeId() != Exiv2::unsignedShort) { return NOT_AVAILABLE; } return Orientation(it->toLong()); } int JpegContent::dotsPerMeterX() const { return dotsPerMeter(QStringLiteral("XResolution")); } int JpegContent::dotsPerMeterY() const { return dotsPerMeter(QStringLiteral("YResolution")); } int JpegContent::dotsPerMeter(const QString& keyName) const { Exiv2::ExifKey keyResUnit("Exif.Image.ResolutionUnit"); Exiv2::ExifData::iterator it = d->mExifData.findKey(keyResUnit); if (it == d->mExifData.end()) { return 0; } int res = it->toLong(); QString keyVal = QLatin1String("Exif.Image.") + keyName; Exiv2::ExifKey keyResolution(keyVal.toLocal8Bit().data()); it = d->mExifData.findKey(keyResolution); if (it == d->mExifData.end()) { return 0; } // The unit for measuring XResolution and YResolution. The same unit is used for both XResolution and YResolution. // If the image resolution in unknown, 2 (inches) is designated. // Default = 2 // 2 = inches // 3 = centimeters // Other = reserved const float INCHESPERMETER = (100. / 2.54); switch (res) { case 3: // dots per cm return int(it->toLong() * 100); default: // dots per inch return int(it->toLong() * INCHESPERMETER); } return 0; } void JpegContent::resetOrientation() { Exiv2::ExifKey key("Exif.Image.Orientation"); Exiv2::ExifData::iterator it = d->mExifData.findKey(key); if (it == d->mExifData.end()) { return; } *it = uint16_t(NORMAL); } QSize JpegContent::size() const { return d->mSize; } QString JpegContent::comment() const { return d->mComment; } void JpegContent::setComment(const QString& comment) { d->mComment = comment; } static QTransform createRotMatrix(int angle) { QTransform matrix; matrix.rotate(angle); return matrix; } static QTransform createScaleMatrix(int dx, int dy) { QTransform matrix; matrix.scale(dx, dy); return matrix; } struct OrientationInfo { OrientationInfo() : orientation(NOT_AVAILABLE) , jxform(JXFORM_NONE) {} OrientationInfo(Orientation o, const QTransform &m, JXFORM_CODE j) : orientation(o), matrix(m), jxform(j) {} Orientation orientation; QTransform matrix; JXFORM_CODE jxform; }; typedef QList OrientationInfoList; static const OrientationInfoList& orientationInfoList() { static OrientationInfoList list; if (list.size() == 0) { QTransform rot90 = createRotMatrix(90); QTransform hflip = createScaleMatrix(-1, 1); QTransform vflip = createScaleMatrix(1, -1); list << OrientationInfo() << OrientationInfo(NORMAL, QTransform(), JXFORM_NONE) << OrientationInfo(HFLIP, hflip, JXFORM_FLIP_H) << OrientationInfo(ROT_180, createRotMatrix(180), JXFORM_ROT_180) << OrientationInfo(VFLIP, vflip, JXFORM_FLIP_V) << OrientationInfo(TRANSPOSE, hflip * rot90, JXFORM_TRANSPOSE) << OrientationInfo(ROT_90, rot90, JXFORM_ROT_90) << OrientationInfo(TRANSVERSE, vflip * rot90, JXFORM_TRANSVERSE) << OrientationInfo(ROT_270, createRotMatrix(270), JXFORM_ROT_270) ; } return list; } void JpegContent::transform(Orientation orientation) { if (orientation != NOT_AVAILABLE && orientation != NORMAL) { d->mPendingTransformation = true; OrientationInfoList::ConstIterator it(orientationInfoList().begin()), end(orientationInfoList().end()); for (; it != end; ++it) { if ((*it).orientation == orientation) { d->mTransformMatrix = (*it).matrix * d->mTransformMatrix; break; } } if (it == end) { qWarning() << "Could not find matrix for orientation\n"; } } } #if 0 static void dumpMatrix(const QTransform& matrix) { qDebug() << "matrix | " << matrix.m11() << ", " << matrix.m12() << " |\n"; qDebug() << " | " << matrix.m21() << ", " << matrix.m22() << " |\n"; qDebug() << " ( " << matrix.dx() << ", " << matrix.dy() << " )\n"; } #endif static bool matricesAreSame(const QTransform& m1, const QTransform& m2, double tolerance) { return fabs(m1.m11() - m2.m11()) < tolerance && fabs(m1.m12() - m2.m12()) < tolerance && fabs(m1.m21() - m2.m21()) < tolerance && fabs(m1.m22() - m2.m22()) < tolerance && fabs(m1.dx() - m2.dx()) < tolerance && fabs(m1.dy() - m2.dy()) < tolerance; } static JXFORM_CODE findJxform(const QTransform& matrix) { OrientationInfoList::ConstIterator it(orientationInfoList().begin()), end(orientationInfoList().end()); for (; it != end; ++it) { if (matricesAreSame((*it).matrix, matrix, 0.001)) { return (*it).jxform; } } qWarning() << "findJxform: failed\n"; return JXFORM_NONE; } void JpegContent::applyPendingTransformation() { if (d->mRawData.size() == 0) { qCritical() << "No data loaded\n"; return; } // The following code is inspired by jpegtran.c from the libjpeg // Init JPEG structs struct jpeg_decompress_struct srcinfo; struct jpeg_compress_struct dstinfo; jvirt_barray_ptr * src_coef_arrays; jvirt_barray_ptr * dst_coef_arrays; // Initialize the JPEG decompression object JPEGErrorManager srcErrorManager; srcinfo.err = &srcErrorManager; jpeg_create_decompress(&srcinfo); if (setjmp(srcErrorManager.jmp_buffer)) { qCritical() << "libjpeg error in src\n"; return; } // Initialize the JPEG compression object JPEGErrorManager dstErrorManager; dstinfo.err = &dstErrorManager; jpeg_create_compress(&dstinfo); if (setjmp(dstErrorManager.jmp_buffer)) { qCritical() << "libjpeg error in dst\n"; return; } // Specify data source for decompression QBuffer buffer(&d->mRawData); buffer.open(QIODevice::ReadOnly); IODeviceJpegSourceManager::setup(&srcinfo, &buffer); // Enable saving of extra markers that we want to copy jcopy_markers_setup(&srcinfo, JCOPYOPT_ALL); (void) jpeg_read_header(&srcinfo, true); // Init transformation jpeg_transform_info transformoption; memset(&transformoption, 0, sizeof(jpeg_transform_info)); transformoption.transform = findJxform(d->mTransformMatrix); jtransform_request_workspace(&srcinfo, &transformoption); /* Read source file as DCT coefficients */ src_coef_arrays = jpeg_read_coefficients(&srcinfo); /* Initialize destination compression parameters from source values */ jpeg_copy_critical_parameters(&srcinfo, &dstinfo); /* Adjust destination parameters if required by transform options; * also find out which set of coefficient arrays will hold the output. */ dst_coef_arrays = jtransform_adjust_parameters(&srcinfo, &dstinfo, src_coef_arrays, &transformoption); /* Specify data destination for compression */ QByteArray output; output.resize(d->mRawData.size()); d->setupInmemDestination(&dstinfo, &output); /* Start compressor (note no image data is actually written here) */ jpeg_write_coefficients(&dstinfo, dst_coef_arrays); /* Copy to the output file any extra markers that we want to preserve */ jcopy_markers_execute(&srcinfo, &dstinfo, JCOPYOPT_ALL); /* Execute image transformation, if any */ jtransform_execute_transformation(&srcinfo, &dstinfo, src_coef_arrays, &transformoption); /* Finish compression and release memory */ jpeg_finish_compress(&dstinfo); jpeg_destroy_compress(&dstinfo); (void) jpeg_finish_decompress(&srcinfo); jpeg_destroy_decompress(&srcinfo); // Set rawData to our new JPEG d->mRawData = output; } QImage JpegContent::thumbnail() const { QImage image; if (!d->mExifData.empty()) { #if(EXIV2_TEST_VERSION(0,17,91)) Exiv2::ExifThumbC thumb(d->mExifData); Exiv2::DataBuf thumbnail = thumb.copy(); #else Exiv2::DataBuf thumbnail = d->mExifData.copyThumbnail(); #endif image.loadFromData(thumbnail.pData_, thumbnail.size_); Exiv2::ExifData::iterator it = d->mExifData.findKey(Exiv2::ExifKey("Exif.Canon.ThumbnailImageValidArea")); // ensure ThumbnailImageValidArea actually specifies a rectangle, i.e. there must be 4 coordinates if (it != d->mExifData.end() && it->count() == 4) { QRect validArea(QPoint(it->toLong(0), it->toLong(2)), QPoint(it->toLong(1), it->toLong(3))); image = image.copy(validArea); } else { // Unfortunately, Sony does not provide an exif tag that specifies the valid area of the // embedded thumbnail. Need to derive it from the size of the preview image instead. it = d->mExifData.findKey(Exiv2::ExifKey("Exif.Sony1.PreviewImageSize")); if (it != d->mExifData.end() && it->count() == 2) { const long prevHeight = it->toLong(0); const long prevWidth = it->toLong(1); const double scale = prevWidth / image.width(); // the embedded thumb only needs to be cropped vertically const long validThumbAreaHeight = ceil(prevHeight / scale); const long totalHeightOfBlackArea = image.height() - validThumbAreaHeight; // black bars on top and bottom should be equal in height const long offsetFromTop = totalHeightOfBlackArea / 2; const QRect validArea(QPoint(0, offsetFromTop), QSize(image.width(), validThumbAreaHeight)); image = image.copy(validArea); } } Orientation o = orientation(); if (GwenviewConfig::applyExifOrientation() && o != NORMAL && o != NOT_AVAILABLE) { image = image.transformed(ImageUtils::transformMatrix(o)); } } return image; } void JpegContent::setThumbnail(const QImage& thumbnail) { if (d->mExifData.empty()) { return; } QByteArray array; QBuffer buffer(&array); buffer.open(QIODevice::WriteOnly); QImageWriter writer(&buffer, "JPEG"); if (!writer.write(thumbnail)) { qCritical() << "Could not write thumbnail\n"; return; } #if (EXIV2_TEST_VERSION(0,17,91)) Exiv2::ExifThumb thumb(d->mExifData); thumb.setJpegThumbnail((unsigned char*)array.data(), array.size()); #else d->mExifData.setJpegThumbnail((unsigned char*)array.data(), array.size()); #endif } bool JpegContent::save(const QString& path) { // we need to take ownership of the input file's data // if the input file is still open, data is still only mem-mapped if (d->mFile.isOpen()) { // backup the mmap() pointer auto* mappedFile = reinterpret_cast(d->mRawData.data()); // read the file to memory d->mRawData = d->mFile.readAll(); d->mFile.unmap(mappedFile); d->mFile.close(); } QFile file(path); if (!file.open(QIODevice::WriteOnly)) { d->mErrorString = i18nc("@info", "Could not open file for writing."); return false; } return save(&file); } bool JpegContent::save(QIODevice* device) { if (!d->mImage.isNull()) { if (!d->updateRawDataFromImage()) { return false; } } if (d->mRawData.size() == 0) { d->mErrorString = i18nc("@info", "No data to store."); return false; } if (d->mPendingTransformation) { applyPendingTransformation(); d->mPendingTransformation = false; } std::unique_ptr image; image.reset(Exiv2::ImageFactory::open((unsigned char*)d->mRawData.data(), d->mRawData.size()).release()); // Store Exif info image->setExifData(d->mExifData); image->setComment(d->mComment.toUtf8().toStdString()); image->writeMetadata(); // Update mRawData Exiv2::BasicIo& io = image->io(); d->mRawData.resize(io.size()); io.read((unsigned char*)d->mRawData.data(), io.size()); QDataStream stream(device); stream.writeRawData(d->mRawData.data(), d->mRawData.size()); // Make sure we are up to date loadFromData(d->mRawData); return true; } QString JpegContent::errorString() const { return d->mErrorString; } void JpegContent::setImage(const QImage& image) { d->mRawData.clear(); d->mImage = image; d->mSize = image.size(); d->mExifData["Exif.Photo.PixelXDimension"] = image.width(); d->mExifData["Exif.Photo.PixelYDimension"] = image.height(); resetOrientation(); d->mPendingTransformation = false; d->mTransformMatrix = QTransform(); } } // namespace