diff --git a/libs/pigment/KoColorProfile.h b/libs/pigment/KoColorProfile.h index f339ba60ed..aca3df8493 100644 --- a/libs/pigment/KoColorProfile.h +++ b/libs/pigment/KoColorProfile.h @@ -1,211 +1,215 @@ /* * Copyright (c) 2007 Cyrille Berger * * 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; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KO_COLOR_PROFILE_H_ #define _KO_COLOR_PROFILE_H_ #include #include #include #include "kritapigment_export.h" /** * Contains information needed for color transformation. */ class KRITAPIGMENT_EXPORT KoColorProfile { public: /** * @param fileName file name to load or save that profile */ explicit KoColorProfile(const QString &fileName = QString()); KoColorProfile(const KoColorProfile& profile); virtual ~KoColorProfile(); /** * @return the type of this profile (icc, ctlcs etc) */ virtual QString type() const { return QString(); } /** * Create a copy of this profile. * Data that shall not change during the life time of the profile shouldn't be * duplicated but shared, like for instance ICC data. * * Data that shall be changed like a palette or hdr information such as exposure * must be duplicated while cloning. */ virtual KoColorProfile* clone() const = 0; /** * Load the profile in memory. * @return true if the profile has been successfully loaded */ virtual bool load(); /** * Override this function to save the profile. * @param fileName destination * @return true if the profile has been successfully saved */ virtual bool save(const QString &fileName); /** * @return true if the profile is valid, false if it isn't been loaded in memory yet, or * if the loaded memory is a bad profile */ virtual bool valid() const = 0; /** * @return the name of this profile */ QString name() const; /** * @return the info of this profile */ QString info() const; /** @return manufacturer of the profile */ QString manufacturer() const; /** * @return the copyright of the profile */ QString copyright() const; /** * @return the filename of the profile (it might be empty) */ QString fileName() const; /** * @param filename new filename */ void setFileName(const QString &filename); /** * Return version */ virtual float version() const = 0; /** * @return true if you can use this profile can be used to convert color from a different * profile to this one */ virtual bool isSuitableForOutput() const = 0; /** * @return true if this profile is suitable to use for printing */ virtual bool isSuitableForPrinting() const = 0; /** * @return true if this profile is suitable to use for display */ virtual bool isSuitableForDisplay() const = 0; /** * @return which rendering intents are supported */ virtual bool supportsPerceptual() const = 0; virtual bool supportsSaturation() const = 0; virtual bool supportsAbsolute() const = 0; virtual bool supportsRelative() const = 0; /** * @return if the profile has colorants. */ virtual bool hasColorants() const = 0; /** * @return a qvector (9) with the RGB colorants in XYZ */ virtual QVector getColorantsXYZ() const = 0; /** * @return a qvector (9) with the RGB colorants in xyY */ virtual QVector getColorantsxyY() const = 0; /** * @return a qvector (3) with the whitepoint in XYZ */ virtual QVector getWhitePointXYZ() const = 0; /** * @return a qvector (3) with the whitepoint in xyY */ virtual QVector getWhitePointxyY() const = 0; /** * @return estimated gamma for RGB and Grayscale profiles */ virtual QVector getEstimatedTRC() const = 0; /** * @return if the profile has a TRC(required for linearisation). */ virtual bool hasTRC() const = 0; + /** + * @return if the profile's TRCs are linear. + */ + virtual bool isLinear() const = 0; /** * Linearizes first 3 values of QVector, leaving other values unchanged. * Returns the same QVector if it is not possible to linearize. */ virtual void linearizeFloatValue(QVector & Value) const = 0; /** * Delinearizes first 3 values of QVector, leaving other values unchanged. * Returns the same QVector if it is not possible to delinearize. * Effectively undoes LinearizeFloatValue. */ virtual void delinearizeFloatValue(QVector & Value) const = 0; /** * More imprecise versions of the above(limited to 16bit, and can't * delinearize above 1.0.) Use this for filters and images. */ virtual void linearizeFloatValueFast(QVector & Value) const = 0; virtual void delinearizeFloatValueFast(QVector & Value) const = 0; virtual QByteArray uniqueId() const = 0; virtual bool operator==(const KoColorProfile&) const = 0; /** * @return an array with the raw data of the profile */ virtual QByteArray rawData() const { return QByteArray(); } protected: /** * Allows to define the name of this profile. */ void setName(const QString &name); /** * Allows to set the information string of that profile. */ void setInfo(const QString &info); /** * Allows to set the manufacturer string of that profile. */ void setManufacturer(const QString &manufacturer); /** * Allows to set the copyright string of that profile. */ void setCopyright(const QString ©right); private: struct Private; Private* const d; }; #endif diff --git a/libs/pigment/colorprofiles/KoDummyColorProfile.cpp b/libs/pigment/colorprofiles/KoDummyColorProfile.cpp index ec6b08a25a..c528dfef37 100644 --- a/libs/pigment/colorprofiles/KoDummyColorProfile.cpp +++ b/libs/pigment/colorprofiles/KoDummyColorProfile.cpp @@ -1,141 +1,145 @@ /* * Copyright (c) 2010 Cyrille Berger * * 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; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "KoDummyColorProfile.h" KoDummyColorProfile::KoDummyColorProfile() { setName("default"); } KoDummyColorProfile::~KoDummyColorProfile() { } KoColorProfile* KoDummyColorProfile::clone() const { return new KoDummyColorProfile(); } bool KoDummyColorProfile::valid() const { return true; } float KoDummyColorProfile::version() const { return 0.0; } bool KoDummyColorProfile::isSuitableForOutput() const { return true; } bool KoDummyColorProfile::isSuitableForPrinting() const { return true; } bool KoDummyColorProfile::isSuitableForDisplay() const { return true; } bool KoDummyColorProfile::supportsPerceptual() const { return true; } bool KoDummyColorProfile::supportsSaturation() const { return true; } bool KoDummyColorProfile::supportsAbsolute() const { return true; } bool KoDummyColorProfile::supportsRelative() const { return true; } bool KoDummyColorProfile::hasColorants() const { return true; } bool KoDummyColorProfile::hasTRC() const { return true; } +bool KoDummyColorProfile::isLinear() const +{ + return true; +} QVector KoDummyColorProfile::getColorantsXYZ() const { QVector d50Dummy(3); d50Dummy<<0.34773<<0.35952<<1.0; return d50Dummy; } QVector KoDummyColorProfile::getColorantsxyY() const { QVector d50Dummy(3); d50Dummy<<0.34773<<0.35952<<1.0; return d50Dummy; } QVector KoDummyColorProfile::getWhitePointXYZ() const { QVector d50Dummy(3); d50Dummy<<0.9642<<1.0000<<0.8249; return d50Dummy; } QVector KoDummyColorProfile::getWhitePointxyY() const { QVector d50Dummy(3); d50Dummy<<0.34773<<0.35952<<1.0; return d50Dummy; } QVector KoDummyColorProfile::getEstimatedTRC() const { QVector Dummy(3); Dummy.fill(2.2); return Dummy; } void KoDummyColorProfile::linearizeFloatValue(QVector & ) const { } void KoDummyColorProfile::delinearizeFloatValue(QVector & ) const { } void KoDummyColorProfile::linearizeFloatValueFast(QVector & ) const { } void KoDummyColorProfile::delinearizeFloatValueFast(QVector & ) const { } bool KoDummyColorProfile::operator==(const KoColorProfile& rhs) const { return dynamic_cast(&rhs); } QByteArray KoDummyColorProfile::uniqueId() const { return QByteArray(); } diff --git a/libs/pigment/colorprofiles/KoDummyColorProfile.h b/libs/pigment/colorprofiles/KoDummyColorProfile.h index 798ae3c2c3..a3b15d5452 100644 --- a/libs/pigment/colorprofiles/KoDummyColorProfile.h +++ b/libs/pigment/colorprofiles/KoDummyColorProfile.h @@ -1,55 +1,56 @@ /* * Copyright (c) 2010 Cyrille Berger * * 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; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KO_DUMMY_COLOR_PROFILE_H_ #define _KO_DUMMY_COLOR_PROFILE_H_ #include "KoColorProfile.h" class KoDummyColorProfile : public KoColorProfile { public: KoDummyColorProfile(); ~KoDummyColorProfile() override; KoColorProfile* clone() const override; bool valid() const override; float version() const override; bool isSuitableForOutput() const override; bool isSuitableForPrinting() const override; bool isSuitableForDisplay() const override; bool supportsPerceptual() const override; bool supportsSaturation() const override; bool supportsAbsolute() const override; bool supportsRelative() const override; bool hasColorants() const override; bool hasTRC() const override; + bool isLinear() const override; QVector getColorantsXYZ() const override; QVector getColorantsxyY() const override; QVector getWhitePointXYZ() const override; QVector getWhitePointxyY() const override; QVector getEstimatedTRC() const override; void linearizeFloatValue(QVector & Value) const override; void delinearizeFloatValue(QVector & Value) const override; void linearizeFloatValueFast(QVector & Value) const override; void delinearizeFloatValueFast(QVector & Value) const override; bool operator==(const KoColorProfile&) const override; QByteArray uniqueId() const override; }; #endif diff --git a/libs/widgets/KisDlgInternalColorSelector.cpp b/libs/widgets/KisDlgInternalColorSelector.cpp index 3b8c7885ca..7f28ee6621 100644 --- a/libs/widgets/KisDlgInternalColorSelector.cpp +++ b/libs/widgets/KisDlgInternalColorSelector.cpp @@ -1,350 +1,356 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * 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 #include #include #include #include #include #include #include #include "KoColorSpaceRegistry.h" #include #include #include #include #include #include #include "kis_signal_compressor.h" #include "KoColorDisplayRendererInterface.h" #include "kis_spinbox_color_selector.h" #include "KisDlgInternalColorSelector.h" #include "ui_WdgDlgInternalColorSelector.h" #include "kis_config_notifier.h" #include "kis_color_input.h" #include "kis_icon_utils.h" #include "KisSqueezedComboBox.h" +#include "ui_WdgDlgInternalColorSelector.h" + std::function KisDlgInternalColorSelector::s_screenColorPickerFactory = 0; struct KisDlgInternalColorSelector::Private { bool allowUpdates = true; KoColor currentColor; KoColor previousColor; KoColor sRGB = KoColor(KoColorSpaceRegistry::instance()->rgb8()); const KoColorSpace *currentColorSpace; bool lockUsedCS = false; bool chooseAlpha = false; KisSignalCompressor *compressColorChanges; const KoColorDisplayRendererInterface *displayRenderer; KisHexColorInput *hexColorInput = 0; KisPaletteModel *paletteModel = 0; KisPaletteListWidget *paletteChooser = 0; KisScreenColorPickerBase *screenColorPicker = 0; }; KisDlgInternalColorSelector::KisDlgInternalColorSelector(QWidget *parent, KoColor color, Config config, const QString &caption, const KoColorDisplayRendererInterface *displayRenderer) : QDialog(parent) , m_d(new Private) { setModal(config.modal); setFocusPolicy(Qt::ClickFocus); m_ui = new Ui_WdgDlgInternalColorSelector(); m_ui->setupUi(this); setWindowTitle(caption); m_d->currentColor = color; m_d->currentColorSpace = m_d->currentColor.colorSpace(); m_d->displayRenderer = displayRenderer; m_ui->spinboxselector->slotSetColor(color); connect(m_ui->spinboxselector, SIGNAL(sigNewColor(KoColor)), this, SLOT(slotColorUpdated(KoColor))); m_ui->visualSelector->slotSetColor(color); m_ui->visualSelector->setDisplayRenderer(displayRenderer); m_ui->visualSelector->setConfig(false, config.modal); if (config.visualColorSelector) { connect(m_ui->visualSelector, SIGNAL(sigNewColor(KoColor)), this, SLOT(slotColorUpdated(KoColor))); connect(KisConfigNotifier::instance(), SIGNAL(configChanged()), m_ui->visualSelector, SLOT(configurationChanged())); } else { m_ui->visualSelector->hide(); } m_d->paletteChooser = new KisPaletteListWidget(this); m_d->paletteModel = new KisPaletteModel(this); m_ui->bnPaletteChooser->setIcon(KisIconUtils::loadIcon("hi16-palette_library")); m_ui->paletteBox->setPaletteModel(m_d->paletteModel); m_ui->paletteBox->setDisplayRenderer(displayRenderer); m_ui->cmbNameList->setCompanionView(m_ui->paletteBox); connect(m_d->paletteChooser, SIGNAL(sigPaletteSelected(KoColorSet*)), this, SLOT(slotChangePalette(KoColorSet*))); connect(m_ui->cmbNameList, SIGNAL(sigColorSelected(KoColor)), SLOT(slotColorUpdated(KoColor))); // For some bizare reason, the modal dialog doesn't like having the colorset set, so let's not. if (config.paletteBox) { //TODO: Add disable signal as well. Might be not necessary...? KConfigGroup cfg(KSharedConfig::openConfig()->group("")); QString paletteName = cfg.readEntry("internal_selector_active_color_set", QString()); KoResourceServer* rServer = KoResourceServerProvider::instance()->paletteServer(); KoColorSet *savedPal = rServer->resourceByName(paletteName); if (savedPal) { this->slotChangePalette(savedPal); } else { if (rServer->resources().count()) { savedPal = rServer->resources().first(); if (savedPal) { this->slotChangePalette(savedPal); } } } connect(m_ui->paletteBox, SIGNAL(sigColorSelected(KoColor)), this, SLOT(slotColorUpdated(KoColor))); m_ui->bnPaletteChooser->setPopupWidget(m_d->paletteChooser); } else { m_ui->paletteBox->setEnabled(false); m_ui->cmbNameList->setEnabled(false); m_ui->bnPaletteChooser->setEnabled(false); } if (config.prevNextButtons) { m_ui->currentColor->setColor(m_d->currentColor); m_ui->currentColor->setDisplayRenderer(displayRenderer); m_ui->previousColor->setColor(m_d->currentColor); m_ui->previousColor->setDisplayRenderer(displayRenderer); connect(m_ui->previousColor, SIGNAL(triggered(KoColorPatch*)), SLOT(slotSetColorFromPatch(KoColorPatch*))); } else { m_ui->currentColor->hide(); m_ui->previousColor->hide(); } if (config.hexInput) { m_d->sRGB.fromKoColor(m_d->currentColor); m_d->hexColorInput = new KisHexColorInput(this, &m_d->sRGB); m_d->hexColorInput->update(); connect(m_d->hexColorInput, SIGNAL(updated()), SLOT(slotSetColorFromHex())); m_ui->rightPane->addWidget(m_d->hexColorInput); m_d->hexColorInput->setToolTip(i18n("This is a hexcode input, for webcolors. It can only get colors in the sRGB space.")); } // KisScreenColorPicker is in the kritaui module, so dependency inversion is used to access it. m_ui->screenColorPickerWidget->setLayout(new QHBoxLayout(m_ui->screenColorPickerWidget)); if (s_screenColorPickerFactory) { m_d->screenColorPicker = s_screenColorPickerFactory(m_ui->screenColorPickerWidget); m_ui->screenColorPickerWidget->layout()->addWidget(m_d->screenColorPicker); if (config.screenColorPicker) { connect(m_d->screenColorPicker, SIGNAL(sigNewColorPicked(KoColor)),this, SLOT(slotColorUpdated(KoColor))); } else { m_d->screenColorPicker->hide(); } } - connect(this, SIGNAL(signalForegroundColorChosen(KoColor)), this, SLOT(slotLockSelector())); m_d->compressColorChanges = new KisSignalCompressor(100 /* ms */, KisSignalCompressor::POSTPONE, this); connect(m_d->compressColorChanges, SIGNAL(timeout()), this, SLOT(endUpdateWithNewColor())); connect(m_ui->buttonBox, SIGNAL(accepted()), this, SLOT(accept()), Qt::UniqueConnection); connect(m_ui->buttonBox, SIGNAL(rejected()), this, SLOT(reject()), Qt::UniqueConnection); connect(this, SIGNAL(finished(int)), SLOT(slotFinishUp())); } KisDlgInternalColorSelector::~KisDlgInternalColorSelector() { delete m_ui; } void KisDlgInternalColorSelector::slotColorUpdated(KoColor newColor) { - //if the update did not come from this selector... - if (m_d->allowUpdates || QObject::sender() == this->parent()) { + // not-so-nice solution: if someone calls this slot directly and that code was + // triggered by our compressor signal, our compressor is technically the sender()! + if (sender() == m_d->compressColorChanges) { + return; + } + // Do not accept external updates while a color update emit is pending; + // Note: Assumes external updates only come from parent(), a separate slot might be better + if (m_d->allowUpdates || (QObject::sender() && QObject::sender() != this->parent())) { // Enforce palette colors KConfigGroup group(KSharedConfig::openConfig(), ""); if (group.readEntry("colorsettings/forcepalettecolors", false)) { newColor = m_ui->paletteBox->closestColor(newColor); } if (m_d->lockUsedCS){ newColor.convertTo(m_d->currentColorSpace); m_d->currentColor = newColor; } else { m_d->currentColor = newColor; } updateAllElements(QObject::sender()); } } void KisDlgInternalColorSelector::slotSetColorFromPatch(KoColorPatch *patch) { slotColorUpdated(patch->color()); } void KisDlgInternalColorSelector::colorSpaceChanged(const KoColorSpace *cs) { if (cs == m_d->currentColorSpace) { return; } m_d->currentColorSpace = KoColorSpaceRegistry::instance()->colorSpace(cs->colorModelId().id(), cs->colorDepthId().id(), cs->profile()); m_ui->spinboxselector->slotSetColorSpace(m_d->currentColorSpace); m_ui->visualSelector->slotsetColorSpace(m_d->currentColorSpace); } void KisDlgInternalColorSelector::lockUsedColorSpace(const KoColorSpace *cs) { colorSpaceChanged(cs); + if (m_d->currentColor.colorSpace() != m_d->currentColorSpace) { + m_d->currentColor.convertTo(m_d->currentColorSpace); + } m_d->lockUsedCS = true; } void KisDlgInternalColorSelector::setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer) { if (displayRenderer) { m_d->displayRenderer = displayRenderer; m_ui->visualSelector->setDisplayRenderer(displayRenderer); m_ui->currentColor->setDisplayRenderer(displayRenderer); m_ui->previousColor->setDisplayRenderer(displayRenderer); m_ui->paletteBox->setDisplayRenderer(displayRenderer); } else { m_d->displayRenderer = KoDumbColorDisplayRenderer::instance(); } } KoColor KisDlgInternalColorSelector::getModalColorDialog(const KoColor color, QWidget* parent, QString caption) { Config config = Config(); KisDlgInternalColorSelector dialog(parent, color, config, caption); dialog.setPreviousColor(color); dialog.exec(); return dialog.getCurrentColor(); } KoColor KisDlgInternalColorSelector::getCurrentColor() { return m_d->currentColor; } void KisDlgInternalColorSelector::chooseAlpha(bool chooseAlpha) { m_d->chooseAlpha = chooseAlpha; } void KisDlgInternalColorSelector::slotConfigurationChanged() { //m_d->canvas->displayColorConverter()-> //slotColorSpaceChanged(m_d->canvas->image()->colorSpace()); } -void KisDlgInternalColorSelector::slotLockSelector() -{ - m_d->allowUpdates = false; -} - void KisDlgInternalColorSelector::setPreviousColor(KoColor c) { m_d->previousColor = c; } void KisDlgInternalColorSelector::reject() { slotColorUpdated(m_d->previousColor); QDialog::reject(); } void KisDlgInternalColorSelector::updateAllElements(QObject *source) { //update everything!!! if (source != m_ui->spinboxselector) { m_ui->spinboxselector->slotSetColor(m_d->currentColor); } if (source != m_ui->visualSelector) { m_ui->visualSelector->slotSetColor(m_d->currentColor); } if (source != m_d->hexColorInput) { m_d->sRGB.fromKoColor(m_d->currentColor); m_d->hexColorInput->update(); } if (source != m_ui->paletteBox) { m_ui->paletteBox->selectClosestColor(m_d->currentColor); } m_ui->previousColor->setColor(m_d->previousColor); m_ui->currentColor->setColor(m_d->currentColor); - if (source != this->parent()) { - emit(signalForegroundColorChosen(m_d->currentColor)); + if (source && source != this->parent()) { + m_d->allowUpdates = false; m_d->compressColorChanges->start(); } if (m_d->screenColorPicker) { m_d->screenColorPicker->updateIcons(); } } void KisDlgInternalColorSelector::endUpdateWithNewColor() { + emit signalForegroundColorChosen(m_d->currentColor); m_d->allowUpdates = true; } void KisDlgInternalColorSelector::focusInEvent(QFocusEvent *) { //setPreviousColor(); } void KisDlgInternalColorSelector::slotFinishUp() { setPreviousColor(m_d->currentColor); KConfigGroup cfg(KSharedConfig::openConfig()->group("")); if (m_d->paletteModel) { if (m_d->paletteModel->colorSet()) { cfg.writeEntry("internal_selector_active_color_set", m_d->paletteModel->colorSet()->name()); } } } void KisDlgInternalColorSelector::slotSetColorFromHex() { slotColorUpdated(m_d->sRGB); } void KisDlgInternalColorSelector::slotChangePalette(KoColorSet *set) { if (!set) { return; } m_d->paletteModel->setPalette(set); } void KisDlgInternalColorSelector::showEvent(QShowEvent *event) { updateAllElements(0); QDialog::showEvent(event); } diff --git a/libs/widgets/KisDlgInternalColorSelector.h b/libs/widgets/KisDlgInternalColorSelector.h index b0b63b4882..0536d0a42f 100644 --- a/libs/widgets/KisDlgInternalColorSelector.h +++ b/libs/widgets/KisDlgInternalColorSelector.h @@ -1,195 +1,192 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * 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. */ #ifndef KISDLGINTERNALCOLORSELECTOR_H #define KISDLGINTERNALCOLORSELECTOR_H #include "kritawidgets_export.h" #include "KoColor.h" #include "KoColorSpace.h" #include "KoColorDisplayRendererInterface.h" #include "KoColorSet.h" #include +#include #include "KisScreenColorPickerBase.h" -#include "ui_WdgDlgInternalColorSelector.h" + +class Ui_WdgDlgInternalColorSelector; +class KoColorPatch; /** * @brief The KisInternalColorSelector class * * A non-modal color selector dialog that is not a plugin and can thus be used for filters. */ class KRITAWIDGETS_EXPORT KisDlgInternalColorSelector : public QDialog { Q_OBJECT static std::function s_screenColorPickerFactory; public: static void setScreenColorPickerFactory(std::function f) { s_screenColorPickerFactory = f; } struct Config { Config() : modal(true), visualColorSelector(true), paletteBox(true), screenColorPicker(true), prevNextButtons(true), hexInput(true), useAlpha(false){} bool modal; bool visualColorSelector; bool paletteBox; bool screenColorPicker; bool prevNextButtons; bool hexInput; bool useAlpha; }; KisDlgInternalColorSelector(QWidget* parent, KoColor color, Config config, const QString &caption, const KoColorDisplayRendererInterface *displayRenderer = KoDumbColorDisplayRenderer::instance()); ~KisDlgInternalColorSelector() override; /** * @brief slotColorSpaceChanged * Color space has changed, use this dialog to change the colorspace. */ void colorSpaceChanged(const KoColorSpace *cs); /** * @brief lockUsedColorSpace * Lock the used colorspace of this selector. * @param cs */ void lockUsedColorSpace(const KoColorSpace *cs); /** * @brief setDisplayRenderer * Set the display renderer. This is necessary for HDR color manage support. * @param displayRenderer */ void setDisplayRenderer(const KoColorDisplayRendererInterface *displayRenderer); /** * @brief getModalColorDialog * Execute this dialog modally. The function returns * the KoColor you want. * @param color - The current color. Make sure this is in the color space you want your * end color to be in. * @param parent parent widget. * @param caption the dialog caption. */ static KoColor getModalColorDialog(const KoColor color, QWidget* parent = 0, QString caption = QString()); /** * @brief getCurrentColor * @return gives currently active color; */ KoColor getCurrentColor(); void chooseAlpha(bool chooseAlpha); Q_SIGNALS: /** * @brief signalForegroundColorChosen * The most important signal. This will sent out when a color has been picked from the selector. * There will be a small delay to make sure that the selector causes too many updates. * * Do not connect this to slotColorUpdated. * @param color The new color chosen */ void signalForegroundColorChosen(KoColor color); public Q_SLOTS: /** * @brief slotColorUpdated * Very important slot. Is connected to krita's resources to make sure it has * the currently active color. It's very important that this function is able to understand * when the signal came from itself. * @param newColor This is the new color. */ void slotColorUpdated(KoColor newColor); /** * @brief slotSetColorFromPatch * update current color from kocolorpatch. * @param patch */ void slotSetColorFromPatch(KoColorPatch* patch); /** * @brief setPreviousColor * set the previous color. */ void setPreviousColor(KoColor c); void reject() override; private Q_SLOTS: - /** - * @brief slotLockSelector - * This slot will prevent the color from being updated. - */ - void slotLockSelector(); - /** * @brief slotConfigurationChanged * Wrapper slot for changes to the colorspace. */ void slotConfigurationChanged(); void endUpdateWithNewColor(); /** * @brief slotFinishUp * This is called when the selector is closed, for saving the current palette. */ void slotFinishUp(); /** * @brief slotSetColorFromHex * Update from the hex color input. */ void slotSetColorFromHex(); void slotChangePalette(KoColorSet *set); protected: void showEvent(QShowEvent *event) override; private: void focusInEvent(QFocusEvent *) override; /** * @brief updateAllElements * Updates each widget with the new element, and if it's responsible for the update sents * a signal out that there's a new color. */ void updateAllElements(QObject *source); private: Ui_WdgDlgInternalColorSelector *m_ui; struct Private; //The private struct const QScopedPointer m_d; //the private pointer }; #endif // KISDLGINTERNALCOLORSELECTOR_H diff --git a/libs/widgets/KisPaletteDelegate.cpp b/libs/widgets/KisPaletteDelegate.cpp index 3f6b6f9d83..81eb068a6c 100644 --- a/libs/widgets/KisPaletteDelegate.cpp +++ b/libs/widgets/KisPaletteDelegate.cpp @@ -1,111 +1,110 @@ /* * Copyright (c) 2016 Dmitry Kazakov * * 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 #include #include // qBound #include #include #include "kis_debug.h" #include "KisPaletteDelegate.h" const int KisPaletteDelegate::BORDER_WIDTH = 3; KisPaletteDelegate::KisPaletteDelegate(QObject *parent) : QAbstractItemDelegate(parent) { } KisPaletteDelegate::~KisPaletteDelegate() { } void KisPaletteDelegate::paintCrossedLine(const QStyleOptionViewItem &option, QPainter *painter) const { QRect crossRect = kisGrowRect(option.rect, -qBound(2, option.rect.width() / 6, 4)); painter->save(); painter->setRenderHint(QPainter::Antialiasing, true); painter->setPen(QPen(Qt::white, 2.5)); painter->drawLine(crossRect.topLeft(), crossRect.bottomRight()); painter->setPen(QPen(Qt::red, 1.0)); painter->drawLine(crossRect.topLeft(), crossRect.bottomRight()); painter->restore(); } void KisPaletteDelegate::paintNonCrossed(QPainter */*painter*/, const QStyleOptionViewItem &/*option*/, const QModelIndex &/*index*/, const bool /*isSelected*/) const { } void KisPaletteDelegate::paintGroupName(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index, const bool isSelected) const { QString name = qvariant_cast(index.data(Qt::DisplayRole)); if (isSelected) { painter->fillRect(option.rect, option.palette.highlight()); } QRect paintRect = kisGrowRect(option.rect, -BORDER_WIDTH); painter->setBrush(QBrush(Qt::lightGray)); painter->drawText(paintRect, name); } void KisPaletteDelegate::paint(QPainter *painter, const QStyleOptionViewItem &option, const QModelIndex &index) const { - painter->save(); - if (!index.isValid()) return; + painter->save(); const bool isSelected = option.state & QStyle::State_Selected; if (qvariant_cast(index.data(KisPaletteModel::IsGroupNameRole))) { paintGroupName(painter, option, index, isSelected); } else { QRect paintRect = option.rect; if (isSelected) { painter->fillRect(option.rect, option.palette.highlight()); paintRect = kisGrowRect(option.rect, -BORDER_WIDTH); } if (qvariant_cast(index.data(KisPaletteModel::CheckSlotRole))) { QBrush brush = qvariant_cast(index.data(Qt::BackgroundRole)); painter->fillRect(paintRect, brush); } else { QBrush lightBrush(Qt::gray); QBrush darkBrush(Qt::darkGray); painter->fillRect(paintRect, lightBrush); painter->fillRect(QRect(paintRect.topLeft(), paintRect.center()), darkBrush); painter->fillRect(QRect(paintRect.center(), paintRect.bottomRight()), darkBrush); } QString name = qvariant_cast(index.data(Qt::DisplayRole)); if (!m_crossedKeyword.isNull() && name.toLower().contains(m_crossedKeyword)) { paintCrossedLine(option, painter); } } painter->restore(); } QSize KisPaletteDelegate::sizeHint(const QStyleOptionViewItem &option, const QModelIndex &) const { return option.decorationSize; } diff --git a/libs/widgets/KisVisualColorSelector.cpp b/libs/widgets/KisVisualColorSelector.cpp index 091adce0b5..d87947a2e1 100644 --- a/libs/widgets/KisVisualColorSelector.cpp +++ b/libs/widgets/KisVisualColorSelector.cpp @@ -1,440 +1,566 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * 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 "KisVisualColorSelector.h" #include #include #include #include #include #include +#include +#include #include #include #include #include #include #include #include "KoColorConversions.h" #include "KoColorDisplayRendererInterface.h" +#include "KoColorProfile.h" #include "KoChannelInfo.h" #include #include #include "kis_signal_compressor.h" #include "kis_debug.h" #include "KisVisualColorSelectorShape.h" #include "KisVisualRectangleSelectorShape.h" #include "KisVisualTriangleSelectorShape.h" #include "KisVisualEllipticalSelectorShape.h" struct KisVisualColorSelector::Private { KoColor currentcolor; const KoColorSpace *currentCS {0}; QList widgetlist; - bool updateSelf {false}; - bool updateLonesome {false}; // for modal dialogs. + bool updateLonesome {false}; // currently redundant; remove? bool circular {false}; + bool exposureSupported = false; + bool isRGBA = false; + bool isLinear = false; + int displayPosition[4]; // map channel index to storage index for display + int colorChannelCount; + QVector4D channelValues; + QVector4D channelMaxValues; + ColorModel model; const KoColorDisplayRendererInterface *displayRenderer {0}; KisColorSelectorConfiguration acs_config; KisSignalCompressor *updateTimer {0}; }; KisVisualColorSelector::KisVisualColorSelector(QWidget *parent) : KisColorSelectorInterface(parent) , m_d(new Private) { this->setSizePolicy(QSizePolicy::Expanding,QSizePolicy::Expanding); - QVBoxLayout *layout = new QVBoxLayout; - this->setLayout(layout); KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); m_d->acs_config = KisColorSelectorConfiguration::fromString(cfg.readEntry("colorSelectorConfiguration", KisColorSelectorConfiguration().toString())); m_d->updateTimer = new KisSignalCompressor(100 /* ms */, KisSignalCompressor::POSTPONE); connect(m_d->updateTimer, SIGNAL(timeout()), SLOT(slotRebuildSelectors()), Qt::UniqueConnection); } KisVisualColorSelector::~KisVisualColorSelector() { delete m_d->updateTimer; } void KisVisualColorSelector::slotSetColor(const KoColor &c) { - if (m_d->updateSelf == false) { - m_d->currentcolor = c; - if (m_d->currentCS != c.colorSpace()) { - slotsetColorSpace(c.colorSpace()); + m_d->currentcolor = c; + if (m_d->currentCS != c.colorSpace()) { + slotsetColorSpace(c.colorSpace()); + } + else { + m_d->channelValues = convertKoColorToShapeCoordinates(m_d->currentcolor); + Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) { + shape->setChannelValues(m_d->channelValues, true); } } - updateSelectorElements(QObject::sender()); } void KisVisualColorSelector::slotsetColorSpace(const KoColorSpace *cs) { if (m_d->currentCS != cs) { m_d->currentCS = cs; slotRebuildSelectors(); } } void KisVisualColorSelector::setConfig(bool forceCircular, bool forceSelfUpdate) { m_d->circular = forceCircular; m_d->updateLonesome = forceSelfUpdate; } KoColor KisVisualColorSelector::getCurrentColor() const { return m_d->currentcolor; } +QVector4D KisVisualColorSelector::getChannelValues() const +{ + return m_d->channelValues; +} + +KoColor KisVisualColorSelector::convertShapeCoordsToKoColor(const QVector4D &coordinates) const +{ + KoColor c(m_d->currentCS); + QVector4D baseValues(coordinates); + QVector channelValues(c.colorSpace()->channelCount()); + channelValues.fill(1.0); + + if (m_d->model != ColorModel::Channel && m_d->isRGBA == true) { + + if (m_d->model == ColorModel::HSV) { + HSVToRGB(coordinates.x()*360, coordinates.y(), coordinates.z(), &baseValues[0], &baseValues[1], &baseValues[2]); + } + else if (m_d->model == ColorModel::HSL) { + HSLToRGB(coordinates.x()*360, coordinates.y(), coordinates.z(), &baseValues[0], &baseValues[1], &baseValues[2]); + } + else if (m_d->model == ColorModel::HSI) { + // why suddenly qreal? + qreal temp[3]; + HSIToRGB(coordinates.x(), coordinates.y(), coordinates.z(), &temp[0], &temp[1], &temp[2]); + baseValues.setX(temp[0]); + baseValues.setY(temp[1]); + baseValues.setZ(temp[2]); + } + else /*if (m_d->model == ColorModel::HSY)*/ { + QVector luma= m_d->currentCS->lumaCoefficients(); + qreal temp[3]; + HSYToRGB(coordinates.x(), coordinates.y(), coordinates.z(), &temp[0], &temp[1], &temp[2], + luma[0], luma[1], luma[2]); + baseValues.setX(temp[0]); + baseValues.setY(temp[1]); + baseValues.setZ(temp[2]); + } + if (m_d->isLinear) { + for (int i=0; i<3; i++) { + baseValues[i] = pow(baseValues[i], 2.2); + } + } + } + + if (m_d->exposureSupported) { + baseValues *= m_d->channelMaxValues; + } + + for (int i=0; icolorChannelCount; i++) { + // TODO: proper exposure control + channelValues[m_d->displayPosition[i]] = baseValues[i] /* *(maxvalue[i]) */; + } + + c.colorSpace()->fromNormalisedChannelsValue(c.data(), channelValues); + + return c; + +} + +QVector4D KisVisualColorSelector::convertKoColorToShapeCoordinates(KoColor c) const +{ + if (c.colorSpace() != m_d->currentCS) { + c.convertTo(m_d->currentCS); + } + QVector channelValues (c.colorSpace()->channelCount()); + channelValues.fill(1.0); + m_d->currentCS->normalisedChannelsValue(c.data(), channelValues); + QVector4D channelValuesDisplay(0, 0, 0, 0), coordinates(0, 0, 0, 0); + // TODO: L*a*b is apparently not [0, 1]^3 as "normalized" values, needs extra transform (old bug) + for (int i =0; icolorChannelCount; i++) { + channelValuesDisplay[i] = channelValues[m_d->displayPosition[i]]; + } + + if (m_d->exposureSupported) { + channelValuesDisplay /= m_d->channelMaxValues; + } + if (m_d->model != ColorModel::Channel && m_d->isRGBA == true) { + if (m_d->isRGBA == true) { + if (m_d->isLinear) { + for (int i=0; i<3; i++) { + channelValuesDisplay[i] = pow(channelValuesDisplay[i], 1/2.2); + } + } + if (m_d->model == ColorModel::HSV){ + QVector3D hsv; + // TODO: handle undefined hue case (returns -1) + RGBToHSV(channelValuesDisplay[0], channelValuesDisplay[1], channelValuesDisplay[2], &hsv[0], &hsv[1], &hsv[2]); + hsv[0] /= 360; + coordinates = QVector4D(hsv, 0.f); + } else if (m_d->model == ColorModel::HSL) { + QVector3D hsl; + RGBToHSL(channelValuesDisplay[0], channelValuesDisplay[1], channelValuesDisplay[2], &hsl[0], &hsl[1], &hsl[2]); + hsl[0] /= 360; + coordinates = QVector4D(hsl, 0.f); + } else if (m_d->model == ColorModel::HSI) { + qreal hsi[3]; + RGBToHSI(channelValuesDisplay[0], channelValuesDisplay[1], channelValuesDisplay[2], &hsi[0], &hsi[1], &hsi[2]); + coordinates = QVector4D(hsi[0], hsi[1], hsi[2], 0.f); + } else if (m_d->model == ColorModel::HSY) { + QVector luma = m_d->currentCS->lumaCoefficients(); + qreal hsy[3]; + RGBToHSY(channelValuesDisplay[0], channelValuesDisplay[1], channelValuesDisplay[2], &hsy[0], &hsy[1], &hsy[2], luma[0], luma[1], luma[2]); + coordinates = QVector4D(hsy[0], hsy[1], hsy[2], 0.f); + } + for (int i=0; i<3; i++) { + coordinates[i] = qBound(0.f, coordinates[i], 1.f); + } + } + } else { + for (int i=0; i<4; i++) { + coordinates[i] = qBound(0.f, channelValuesDisplay[i], 1.f); + } + } + return coordinates; +} + void KisVisualColorSelector::configurationChanged() { if (m_d->updateTimer) { m_d->updateTimer->start(); } } +void KisVisualColorSelector::slotDisplayConfigurationChanged() +{ + Q_ASSERT(m_d->displayRenderer); + + if (m_d->currentCS) + { + m_d->channelMaxValues = QVector4D(1, 1, 1, 1); + QList channels = m_d->currentCS->channels(); + for (int i=0; icolorChannelCount; ++i) + { + m_d->channelMaxValues[i] = m_d->displayRenderer->maxVisibleFloatValue(channels[m_d->displayPosition[i]]); + } + // need to re-scale our normalized channel values on exposure changes: + m_d->channelValues = convertKoColorToShapeCoordinates(m_d->currentcolor); + Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) { + shape->setChannelValues(m_d->channelValues, true); + } + } +} + void KisVisualColorSelector::slotRebuildSelectors() { KConfigGroup cfg = KSharedConfig::openConfig()->group("advancedColorSelector"); m_d->acs_config = KisColorSelectorConfiguration::fromString(cfg.readEntry("colorSelectorConfiguration", KisColorSelectorConfiguration().toString())); + QList channelList = m_d->currentCS->channels(); + int cCount = 0; + Q_FOREACH(const KoChannelInfo *channel, channelList) + { + if (channel->channelType() != KoChannelInfo::ALPHA) + { + m_d->displayPosition[cCount] = channel->displayPosition(); + ++cCount; + } + } + Q_ASSERT_X(cCount < 5, "", "unsupported channel count!"); + m_d->colorChannelCount = cCount; + + // TODO: The following is done because the IDs are actually strings. Ideally, in the future, we + // refactor everything so that the IDs are actually proper enums or something faster. + if (m_d->displayRenderer + && (m_d->currentCS->colorDepthId() == Float16BitsColorDepthID + || m_d->currentCS->colorDepthId() == Float32BitsColorDepthID + || m_d->currentCS->colorDepthId() == Float64BitsColorDepthID) + && m_d->currentCS->colorModelId() != LABAColorModelID + && m_d->currentCS->colorModelId() != CMYKAColorModelID) { + m_d->exposureSupported = true; + } else { + m_d->exposureSupported = false; + } + m_d->isRGBA = (m_d->currentCS->colorModelId() == RGBAColorModelID); + const KoColorProfile *profile = m_d->currentCS->profile(); + m_d->isLinear = (profile && profile->isLinear()); + qDeleteAll(children()); m_d->widgetlist.clear(); + // TODO: Layout only used for monochrome selector currently, but always present QLayout *layout = new QHBoxLayout; - //redraw all the widgets. - int sizeValue = qMin(width(), height()); - int borderWidth = qMax(sizeValue*0.1, 20.0); + //recreate all the widgets. + m_d->model = KisVisualColorSelector::Channel; if (m_d->currentCS->colorChannelCount() == 1) { KisVisualColorSelectorShape *bar; if (m_d->circular==false) { - bar = new KisVisualRectangleSelectorShape(this, KisVisualColorSelectorShape::onedimensional,KisVisualColorSelectorShape::Channel, m_d->currentCS, 0, 0,m_d->displayRenderer, borderWidth); + bar = new KisVisualRectangleSelectorShape(this, KisVisualColorSelectorShape::onedimensional, m_d->currentCS, 0, 0,m_d->displayRenderer, 20); bar->setMaximumWidth(width()*0.1); bar->setMaximumHeight(height()); } else { - bar = new KisVisualEllipticalSelectorShape(this, KisVisualColorSelectorShape::onedimensional,KisVisualColorSelectorShape::Channel, m_d->currentCS, 0, 0,m_d->displayRenderer, borderWidth, KisVisualEllipticalSelectorShape::borderMirrored); + bar = new KisVisualEllipticalSelectorShape(this, KisVisualColorSelectorShape::onedimensional, m_d->currentCS, 0, 0,m_d->displayRenderer, 20, KisVisualEllipticalSelectorShape::borderMirrored); layout->setMargin(0); } - connect (bar, SIGNAL(sigNewColor(KoColor)), this, SLOT(updateFromWidgets(KoColor))); + connect(bar, SIGNAL(sigCursorMoved(QPointF)), SLOT(slotCursorMoved(QPointF))); layout->addWidget(bar); m_d->widgetlist.append(bar); } else if (m_d->currentCS->colorChannelCount() == 3) { - QRect newrect(0,0, this->geometry().width(), this->geometry().height()); - - KisVisualColorSelectorShape::ColorModel modelS = KisVisualColorSelectorShape::HSV; + KisVisualColorSelector::ColorModel modelS = KisVisualColorSelector::HSV; int channel1 = 0; int channel2 = 1; int channel3 = 2; switch(m_d->acs_config.subTypeParameter) { case KisColorSelectorConfiguration::H: channel1 = 0; break; case KisColorSelectorConfiguration::hsyS: case KisColorSelectorConfiguration::hsiS: case KisColorSelectorConfiguration::hslS: case KisColorSelectorConfiguration::hsvS: channel1 = 1; break; case KisColorSelectorConfiguration::V: case KisColorSelectorConfiguration::L: case KisColorSelectorConfiguration::I: case KisColorSelectorConfiguration::Y: channel1 = 2; break; default: Q_ASSERT_X(false, "", "Invalid acs_config.subTypeParameter"); } switch(m_d->acs_config.mainTypeParameter) { case KisColorSelectorConfiguration::hsySH: - modelS = KisVisualColorSelectorShape::HSY; + modelS = KisVisualColorSelector::HSY; channel2 = 0; channel3 = 1; break; case KisColorSelectorConfiguration::hsiSH: - modelS = KisVisualColorSelectorShape::HSI; + modelS = KisVisualColorSelector::HSI; channel2 = 0; channel3 = 1; break; case KisColorSelectorConfiguration::hslSH: - modelS = KisVisualColorSelectorShape::HSL; + modelS = KisVisualColorSelector::HSL; channel2 = 0; channel3 = 1; break; case KisColorSelectorConfiguration::hsvSH: - modelS = KisVisualColorSelectorShape::HSV; + modelS = KisVisualColorSelector::HSV; channel2 = 0; channel3 = 1; break; case KisColorSelectorConfiguration::YH: - modelS = KisVisualColorSelectorShape::HSY; + modelS = KisVisualColorSelector::HSY; channel2 = 0; channel3 = 2; break; case KisColorSelectorConfiguration::LH: - modelS = KisVisualColorSelectorShape::HSL; + modelS = KisVisualColorSelector::HSL; channel2 = 0; channel3 = 2; break; case KisColorSelectorConfiguration::IH: - modelS = KisVisualColorSelectorShape::HSL; + modelS = KisVisualColorSelector::HSL; channel2 = 0; channel3 = 2; break; case KisColorSelectorConfiguration::VH: - modelS = KisVisualColorSelectorShape::HSV; + modelS = KisVisualColorSelector::HSV; channel2 = 0; channel3 = 2; break; case KisColorSelectorConfiguration::SY: - modelS = KisVisualColorSelectorShape::HSY; + modelS = KisVisualColorSelector::HSY; channel2 = 1; channel3 = 2; break; case KisColorSelectorConfiguration::SI: - modelS = KisVisualColorSelectorShape::HSI; + modelS = KisVisualColorSelector::HSI; channel2 = 1; channel3 = 2; break; case KisColorSelectorConfiguration::SL: - modelS = KisVisualColorSelectorShape::HSL; + modelS = KisVisualColorSelector::HSL; channel2 = 1; channel3 = 2; break; case KisColorSelectorConfiguration::SV: case KisColorSelectorConfiguration::SV2: - modelS = KisVisualColorSelectorShape::HSV; + modelS = KisVisualColorSelector::HSV; channel2 = 1; channel3 = 2; break; default: Q_ASSERT_X(false, "", "Invalid acs_config.mainTypeParameter"); } if (m_d->acs_config.mainType == KisColorSelectorConfiguration::Triangle) { - modelS = KisVisualColorSelectorShape::HSV; + modelS = KisVisualColorSelector::HSV; //Triangle only really works in HSV mode. } + + m_d->model = modelS; KisVisualColorSelectorShape *bar; if (m_d->acs_config.subType == KisColorSelectorConfiguration::Ring) { bar = new KisVisualEllipticalSelectorShape(this, KisVisualColorSelectorShape::onedimensional, - modelS, m_d->currentCS, channel1, channel1, - m_d->displayRenderer, borderWidth,KisVisualEllipticalSelectorShape::border); - bar->resize(sizeValue, sizeValue); + m_d->displayRenderer, 20,KisVisualEllipticalSelectorShape::border); } else if (m_d->acs_config.subType == KisColorSelectorConfiguration::Slider && m_d->circular == false) { bar = new KisVisualRectangleSelectorShape(this, KisVisualColorSelectorShape::onedimensional, - modelS, m_d->currentCS, channel1, channel1, - m_d->displayRenderer, borderWidth); - bar->setMaximumWidth(borderWidth); - bar->setMinimumWidth(borderWidth); - bar->setMinimumHeight(sizeValue); + m_d->displayRenderer, 20); } else if (m_d->acs_config.subType == KisColorSelectorConfiguration::Slider && m_d->circular == true) { bar = new KisVisualEllipticalSelectorShape(this, KisVisualColorSelectorShape::onedimensional, - modelS, m_d->currentCS, channel1, channel1, - m_d->displayRenderer, borderWidth, KisVisualEllipticalSelectorShape::borderMirrored); - bar->resize(sizeValue, sizeValue); + m_d->displayRenderer, 20, KisVisualEllipticalSelectorShape::borderMirrored); } else { // Accessing bar below would crash since it's not initialized. // Hopefully this can never happen. warnUI << "Invalid subType, cannot initialize KisVisualColorSelectorShape"; Q_ASSERT_X(false, "", "Invalid subType, cannot initialize KisVisualColorSelectorShape"); return; } - bar->setColor(m_d->currentcolor); m_d->widgetlist.append(bar); KisVisualColorSelectorShape *block; if (m_d->acs_config.mainType == KisColorSelectorConfiguration::Triangle) { block = new KisVisualTriangleSelectorShape(this, KisVisualColorSelectorShape::twodimensional, - modelS, m_d->currentCS, channel2, channel3, m_d->displayRenderer); - block->setGeometry(bar->getSpaceForTriangle(newrect)); } else if (m_d->acs_config.mainType == KisColorSelectorConfiguration::Square) { block = new KisVisualRectangleSelectorShape(this, KisVisualColorSelectorShape::twodimensional, - modelS, m_d->currentCS, channel2, channel3, m_d->displayRenderer); - block->setGeometry(bar->getSpaceForSquare(newrect)); } else { block = new KisVisualEllipticalSelectorShape(this, KisVisualColorSelectorShape::twodimensional, - modelS, m_d->currentCS, channel2, channel3, m_d->displayRenderer); - block->setGeometry(bar->getSpaceForCircle(newrect)); - } - block->setColor(m_d->currentcolor); - connect (bar, SIGNAL(sigNewColor(KoColor)), block, SLOT(setColorFromSibling(KoColor))); - connect (block, SIGNAL(sigNewColor(KoColor)), SLOT(updateFromWidgets(KoColor))); - connect (bar, SIGNAL(sigHSXchange()), SLOT(HSXwrangler())); - connect (block, SIGNAL(sigHSXchange()), SLOT(HSXwrangler())); + connect(bar, SIGNAL(sigCursorMoved(QPointF)), SLOT(slotCursorMoved(QPointF))); + connect(block, SIGNAL(sigCursorMoved(QPointF)), SLOT(slotCursorMoved(QPointF))); m_d->widgetlist.append(block); } else if (m_d->currentCS->colorChannelCount() == 4) { - KisVisualRectangleSelectorShape *block = new KisVisualRectangleSelectorShape(this, KisVisualRectangleSelectorShape::twodimensional,KisVisualColorSelectorShape::Channel, m_d->currentCS, 0, 1); - KisVisualRectangleSelectorShape *block2 = new KisVisualRectangleSelectorShape(this, KisVisualRectangleSelectorShape::twodimensional,KisVisualColorSelectorShape::Channel, m_d->currentCS, 2, 3); - block->setMaximumWidth(width()*0.5); - block->setMaximumHeight(height()); - block2->setMaximumWidth(width()*0.5); - block2->setMaximumHeight(height()); - block->setColor(m_d->currentcolor); - block2->setColor(m_d->currentcolor); - connect (block, SIGNAL(sigNewColor(KoColor)), block2, SLOT(setColorFromSibling(KoColor))); - connect (block2, SIGNAL(sigNewColor(KoColor)), SLOT(updateFromWidgets(KoColor))); - layout->addWidget(block); - layout->addWidget(block2); + KisVisualRectangleSelectorShape *block = new KisVisualRectangleSelectorShape(this, KisVisualRectangleSelectorShape::twodimensional, m_d->currentCS, 0, 1); + KisVisualRectangleSelectorShape *block2 = new KisVisualRectangleSelectorShape(this, KisVisualRectangleSelectorShape::twodimensional, m_d->currentCS, 2, 3); + connect(block, SIGNAL(sigCursorMoved(QPointF)), SLOT(slotCursorMoved(QPointF))); + connect(block2, SIGNAL(sigCursorMoved(QPointF)), SLOT(slotCursorMoved(QPointF))); m_d->widgetlist.append(block); m_d->widgetlist.append(block2); } this->setLayout(layout); + // make sure we call "our" resize function + KisVisualColorSelector::resizeEvent(0); + + // finally recalculate channel values and update widgets + if (m_d->displayRenderer) { + slotDisplayConfigurationChanged(); + } + m_d->channelValues = convertKoColorToShapeCoordinates(m_d->currentcolor); + Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) { + shape->setChannelValues(m_d->channelValues, true); + // if this widget is currently visible, new children are hidden by default + shape->show(); + } } void KisVisualColorSelector::setDisplayRenderer (const KoColorDisplayRendererInterface *displayRenderer) { m_d->displayRenderer = displayRenderer; if (m_d->widgetlist.size()>0) { Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) { shape->setDisplayRenderer(displayRenderer); } } + connect(m_d->displayRenderer, SIGNAL(displayConfigurationChanged()), + SLOT(slotDisplayConfigurationChanged()), Qt::UniqueConnection); + slotDisplayConfigurationChanged(); } -void KisVisualColorSelector::updateSelectorElements(QObject *source) +void KisVisualColorSelector::slotCursorMoved(QPointF pos) { - //first lock all elements from sending updates, then update all elements. - Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) { - shape->blockSignals(true); + const KisVisualColorSelectorShape *shape = qobject_cast(sender()); + Q_ASSERT(shape); + QVector channels = shape->getChannels(); + m_d->channelValues[channels.at(0)] = pos.x(); + if (shape->getDimensions() == KisVisualColorSelectorShape::twodimensional) + { + m_d->channelValues[channels.at(1)] = pos.y(); } - - Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) { - if (shape!=source) { - if (m_d->updateSelf) { - shape->setColorFromSibling(m_d->currentcolor); - } else { - shape->setColor(m_d->currentcolor); + KoColor newColor = convertShapeCoordsToKoColor(m_d->channelValues); + if (newColor != m_d->currentcolor) + { + m_d->currentcolor = newColor; + + Q_FOREACH (KisVisualColorSelectorShape *widget, m_d->widgetlist) { + if (widget != shape){ + widget->setChannelValues(m_d->channelValues, false); } } + emit sigNewColor(m_d->currentcolor); } - Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) { - shape->blockSignals(false); - } - -} - -void KisVisualColorSelector::updateFromWidgets(KoColor c) -{ - m_d->currentcolor = c; - m_d->updateSelf = true; - if (m_d->updateLonesome) { - slotSetColor(c); - Q_EMIT sigNewColor(c); - - } else { - Q_EMIT sigNewColor(c); - } -} - -void KisVisualColorSelector::leaveEvent(QEvent *) -{ - m_d->updateSelf = false; } void KisVisualColorSelector::resizeEvent(QResizeEvent *) { int sizeValue = qMin(width(), height()); int borderWidth = qMax(sizeValue*0.1, 20.0); QRect newrect(0,0, this->geometry().width(), this->geometry().height()); if (!m_d->currentCS) { slotsetColorSpace(m_d->currentcolor.colorSpace()); } if (m_d->currentCS->colorChannelCount()==3) { - + // set border width first, else the resized painting may have happened already, and we'd have to re-render + m_d->widgetlist.at(0)->setBorderWidth(borderWidth); if (m_d->acs_config.subType == KisColorSelectorConfiguration::Ring) { m_d->widgetlist.at(0)->resize(sizeValue,sizeValue); } else if (m_d->acs_config.subType == KisColorSelectorConfiguration::Slider && m_d->circular==false) { - m_d->widgetlist.at(0)->setMaximumWidth(borderWidth); - m_d->widgetlist.at(0)->setMinimumWidth(borderWidth); - m_d->widgetlist.at(0)->setMinimumHeight(sizeValue); - m_d->widgetlist.at(0)->setMaximumHeight(sizeValue); + m_d->widgetlist.at(0)->resize(borderWidth, sizeValue); } else if (m_d->acs_config.subType == KisColorSelectorConfiguration::Slider && m_d->circular==true) { m_d->widgetlist.at(0)->resize(sizeValue,sizeValue); } - m_d->widgetlist.at(0)->setBorderWidth(borderWidth); if (m_d->acs_config.mainType == KisColorSelectorConfiguration::Triangle) { m_d->widgetlist.at(1)->setGeometry(m_d->widgetlist.at(0)->getSpaceForTriangle(newrect)); } else if (m_d->acs_config.mainType == KisColorSelectorConfiguration::Square) { m_d->widgetlist.at(1)->setGeometry(m_d->widgetlist.at(0)->getSpaceForSquare(newrect)); } else if (m_d->acs_config.mainType == KisColorSelectorConfiguration::Wheel) { m_d->widgetlist.at(1)->setGeometry(m_d->widgetlist.at(0)->getSpaceForCircle(newrect)); } } - Q_FOREACH (KisVisualColorSelectorShape *shape, m_d->widgetlist) { - shape->update(); + else if (m_d->currentCS->colorChannelCount() == 4) { + int sizeBlock = qMin(width()/2 - 8, height()); + m_d->widgetlist.at(0)->setGeometry(0, 0, sizeBlock, sizeBlock); + m_d->widgetlist.at(1)->setGeometry(sizeBlock + 8, 0, sizeBlock, sizeBlock); } } - -void KisVisualColorSelector::HSXwrangler() -{ - //qDebug() << this << "HSXWrangler"; - - QVector currentCoordinates = QVector(3); - - QVector w1 = m_d->widgetlist.at(0)->getHSX(currentCoordinates, true); - QVector w2 = m_d->widgetlist.at(1)->getHSX(currentCoordinates, true); - QVector ch(3); - - ch[0] = m_d->widgetlist.at(0)->getChannels().at(0); - ch[1] = m_d->widgetlist.at(1)->getChannels().at(0); - ch[2] = m_d->widgetlist.at(1)->getChannels().at(1); - - currentCoordinates[ch[0]] = w1[ch[0]]; - currentCoordinates[ch[1]] = w2[ch[1]]; - currentCoordinates[ch[2]] = w2[ch[2]]; - - m_d->widgetlist.at(0)->setHSX(currentCoordinates, true); - m_d->widgetlist.at(1)->setHSX(currentCoordinates, true); -} diff --git a/libs/widgets/KisVisualColorSelector.h b/libs/widgets/KisVisualColorSelector.h index 04ad5eebd6..fe5eb1c40b 100644 --- a/libs/widgets/KisVisualColorSelector.h +++ b/libs/widgets/KisVisualColorSelector.h @@ -1,86 +1,88 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * 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. */ #ifndef KIS_VISUAL_COLOR_SELECTOR_H #define KIS_VISUAL_COLOR_SELECTOR_H #include #include #include #include #include #include #include #include "KoColorDisplayRendererInterface.h" #include "KisColorSelectorConfiguration.h" #include "KisColorSelectorInterface.h" #include "kritawidgets_export.h" /** * @brief The KisVisualColorSelector class * * This gives a color selector box that draws gradients and everything. * * Unlike other color selectors, this one draws the full gamut of the given * colorspace. */ class KRITAWIDGETS_EXPORT KisVisualColorSelector : public KisColorSelectorInterface { Q_OBJECT public: + enum ColorModel{Channel, HSV, HSL, HSI, HSY, YUV}; explicit KisVisualColorSelector(QWidget *parent = 0); ~KisVisualColorSelector() override; /** * @brief setConfig * @param forceCircular * Force circular is for space where you only have room for a circular selector. * @param forceSelfUpdate * force self-update is for making it update itself when using a modal dialog. */ void setConfig(bool forceCircular, bool forceSelfUpdate) override; KoColor getCurrentColor() const override; + QVector4D getChannelValues() const; + KoColor convertShapeCoordsToKoColor(const QVector4D &coordinates) const; + QVector4D convertKoColorToShapeCoordinates(KoColor c) const; public Q_SLOTS: void slotSetColor(const KoColor &c) override; void slotsetColorSpace(const KoColorSpace *cs); - void slotRebuildSelectors(); void configurationChanged(); void setDisplayRenderer (const KoColorDisplayRendererInterface *displayRenderer) override; private Q_SLOTS: - void updateFromWidgets(KoColor c); - void HSXwrangler(); + void slotCursorMoved(QPointF pos); + void slotDisplayConfigurationChanged(); + void slotRebuildSelectors(); protected: - void leaveEvent(QEvent *) override; void resizeEvent(QResizeEvent *) override; private: struct Private; const QScopedPointer m_d; - void updateSelectorElements(QObject *source); void drawGradients(); }; #endif diff --git a/libs/widgets/KisVisualColorSelectorShape.cpp b/libs/widgets/KisVisualColorSelectorShape.cpp index 0dbf241f86..045c5fc642 100644 --- a/libs/widgets/KisVisualColorSelectorShape.cpp +++ b/libs/widgets/KisVisualColorSelectorShape.cpp @@ -1,619 +1,286 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * 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 "KisVisualColorSelectorShape.h" #include #include #include #include #include #include +#include #include #include #include #include #include #include #include "KoColorConversions.h" #include "KoColorDisplayRendererInterface.h" #include "KoChannelInfo.h" #include #include #include "kis_signal_compressor.h" #include "kis_debug.h" struct KisVisualColorSelectorShape::Private { QImage gradient; QImage fullSelector; bool imagesNeedUpdate {true}; - QPointF currentCoordinates; + QPointF currentCoordinates; // somewhat redundant? + QVector4D currentChannelValues; Dimensions dimension; - ColorModel model; const KoColorSpace *colorSpace; - KoColor currentColor; int channel1; int channel2; - KisSignalCompressor *updateTimer {0}; bool mousePressActive = false; const KoColorDisplayRendererInterface *displayRenderer = 0; - qreal hue = 0.0; - qreal sat = 0.0; - qreal tone = 0.0; - bool usesOCIO = false; - bool isRGBA = false; - bool is8Bit = false; - }; KisVisualColorSelectorShape::KisVisualColorSelectorShape(QWidget *parent, KisVisualColorSelectorShape::Dimensions dimension, - KisVisualColorSelectorShape::ColorModel model, const KoColorSpace *cs, int channel1, int channel2, const KoColorDisplayRendererInterface *displayRenderer): QWidget(parent), m_d(new Private) { m_d->dimension = dimension; - m_d->model = model; m_d->colorSpace = cs; - - // TODO: The following is done because the IDs are actually strings. Ideally, in the future, we - // refactor everything so that the IDs are actually proper enums or something faster. - if (m_d->displayRenderer - && (m_d->colorSpace->colorDepthId() == Float16BitsColorDepthID - || m_d->colorSpace->colorDepthId() == Float32BitsColorDepthID - || m_d->colorSpace->colorDepthId() == Float64BitsColorDepthID) - && m_d->colorSpace->colorModelId() != LABAColorModelID - && m_d->colorSpace->colorModelId() != CMYKAColorModelID) { - m_d->usesOCIO = true; - } else { - m_d->usesOCIO = false; - } - if (m_d->colorSpace->colorModelId() == RGBAColorModelID) { - m_d->isRGBA = true; - } else { - m_d->isRGBA = false; - } - if (m_d->colorSpace->colorDepthId() == Integer8BitsColorDepthID) { - m_d->is8Bit = true; - } else { - m_d->is8Bit = false; - } - m_d->currentColor = KoColor(); - m_d->currentColor.setOpacity(1.0); - m_d->currentColor.convertTo(cs); int maxchannel = m_d->colorSpace->colorChannelCount()-1; m_d->channel1 = qBound(0, channel1, maxchannel); m_d->channel2 = qBound(0, channel2, maxchannel); this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); - // HACK: the updateTimer isn't connected to anything, we only check whether it's still active - // and running in order to determine whether we will emit a certain signal. - m_d->updateTimer = new KisSignalCompressor(100 /* ms */, KisSignalCompressor::POSTPONE, this); setDisplayRenderer(displayRenderer); - show(); - } KisVisualColorSelectorShape::~KisVisualColorSelectorShape() { } -void KisVisualColorSelectorShape::updateCursor() -{ - QPointF point1 = convertKoColorToShapeCoordinate(m_d->currentColor); - if (point1 != m_d->currentCoordinates) { - m_d->currentCoordinates = point1; - } -} - QPointF KisVisualColorSelectorShape::getCursorPosition() { return m_d->currentCoordinates; } -void KisVisualColorSelectorShape::setColor(KoColor c) +void KisVisualColorSelectorShape::setCursorPosition(QPointF position, bool signal) { - //qDebug() << this << "KisVisualColorSelectorShape::setColor"; - if (c.colorSpace() != m_d->colorSpace) { - c.convertTo(m_d->colorSpace); + QPointF newPos(qBound(0.0, position.x(), 1.0), qBound(0.0, position.y(), 1.0)); + if (newPos != m_d->currentCoordinates) + { + m_d->currentCoordinates = newPos; + // for internal consistency, because we have a bit of redundancy here + m_d->currentChannelValues[m_d->channel1] = newPos.x(); + if (m_d->dimension == Dimensions::twodimensional){ + m_d->currentChannelValues[m_d->channel2] = newPos.y(); + } + update(); + if (signal){ + emit sigCursorMoved(newPos); + } } - m_d->currentColor = c; - updateCursor(); - - m_d->imagesNeedUpdate = true; - update(); } -void KisVisualColorSelectorShape::setColorFromSibling(KoColor c) +void KisVisualColorSelectorShape::setChannelValues(QVector4D channelValues, bool setCursor) { - //qDebug() << this << "setColorFromSibling"; - if (c.colorSpace() != m_d->colorSpace) { - c.convertTo(m_d->colorSpace); + //qDebug() << this << "setChannelValues"; + m_d->currentChannelValues = channelValues; + if (setCursor) { + m_d->currentCoordinates = QPointF(qBound(0.f, channelValues[m_d->channel1], 1.f), + qBound(0.f, channelValues[m_d->channel2], 1.f)); + } + else { + // for internal consistency, because we have a bit of redundancy here + m_d->currentChannelValues[m_d->channel1] = m_d->currentCoordinates.x(); + if (m_d->dimension == Dimensions::twodimensional){ + m_d->currentChannelValues[m_d->channel2] = m_d->currentCoordinates.y(); + } } - m_d->currentColor = c; - Q_EMIT sigNewColor(c); m_d->imagesNeedUpdate = true; update(); } void KisVisualColorSelectorShape::setDisplayRenderer (const KoColorDisplayRendererInterface *displayRenderer) { if (displayRenderer) { if (m_d->displayRenderer) { m_d->displayRenderer->disconnect(this); } m_d->displayRenderer = displayRenderer; } else { m_d->displayRenderer = KoDumbColorDisplayRenderer::instance(); } - connect(m_d->displayRenderer, SIGNAL(displayConfigurationChanged()), - SLOT(updateFromChangedDisplayRenderer()), Qt::UniqueConnection); - -} - -void KisVisualColorSelectorShape::updateFromChangedDisplayRenderer() -{ - //qDebug() << this << "updateFromChangedDisplayRenderer();"; - m_d->imagesNeedUpdate = true; - updateCursor(); - //m_d->currentColor = convertShapeCoordinateToKoColor(getCursorPosition()); - update(); } void KisVisualColorSelectorShape::forceImageUpdate() { //qDebug() << this << "forceImageUpdate"; m_d->imagesNeedUpdate = true; } QColor KisVisualColorSelectorShape::getColorFromConverter(KoColor c){ QColor col; KoColor color = c; if (m_d->displayRenderer) { color.convertTo(m_d->displayRenderer->getPaintingColorSpace()); col = m_d->displayRenderer->toQColor(c); } else { col = c.toQColor(); } return col; } +// currently unused? void KisVisualColorSelectorShape::slotSetActiveChannels(int channel1, int channel2) { //qDebug() << this << "slotSetActiveChannels"; int maxchannel = m_d->colorSpace->colorChannelCount()-1; m_d->channel1 = qBound(0, channel1, maxchannel); m_d->channel2 = qBound(0, channel2, maxchannel); m_d->imagesNeedUpdate = true; update(); } bool KisVisualColorSelectorShape::imagesNeedUpdate() const { return m_d->imagesNeedUpdate; } QImage KisVisualColorSelectorShape::getImageMap() { //qDebug() << this << ">>>>>>>>> getImageMap()" << m_d->imagesNeedUpdate; - if (m_d->imagesNeedUpdate == true) { - m_d->gradient = QImage(width(), height(), QImage::Format_ARGB32); - m_d->gradient.fill(Qt::transparent); -// KoColor c = m_d->currentColor; - + if (m_d->imagesNeedUpdate) { // Fill a buffer with the right kocolors - quint8 *data = new quint8[width() * height() * height()]; - quint8 *dataPtr = data; - for (int y = 0; y < m_d->gradient.height(); y++) { - for (int x=0; x < m_d->gradient.width(); x++) { - QPointF newcoordinate = convertWidgetCoordinateToShapeCoordinate(QPoint(x, y)); - KoColor c = convertShapeCoordinateToKoColor(newcoordinate); - memcpy(dataPtr, c.data(), m_d->currentColor.colorSpace()->pixelSize()); - dataPtr += m_d->currentColor.colorSpace()->pixelSize(); - } - } - // Convert the buffer to a qimage - if (m_d->displayRenderer) { - m_d->gradient = m_d->displayRenderer->convertToQImage(m_d->currentColor.colorSpace(), data, width(), height()); - } - else { - m_d->gradient = m_d->currentColor.colorSpace()->convertToQImage(data, width(), height(), 0, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); - } - delete[] data; - + m_d->gradient = renderBackground(m_d->currentChannelValues, m_d->colorSpace->pixelSize()); m_d->imagesNeedUpdate = false; } return m_d->gradient; } -KoColor KisVisualColorSelectorShape::convertShapeCoordinateToKoColor(QPointF coordinates, bool cursor) +QImage KisVisualColorSelectorShape::convertImageMap(const quint8 *rawColor, quint32 size) const { - //qDebug() << this << ">>>>>>>>> convertShapeCoordinateToKoColor()" << coordinates; - - KoColor c = m_d->currentColor; - QVector channelValues (c.colorSpace()->channelCount()); - channelValues.fill(1.0); - c.colorSpace()->normalisedChannelsValue(c.data(), channelValues); - QVector channelValuesDisplay = channelValues; - QVector maxvalue(c.colorSpace()->channelCount()); - maxvalue.fill(1.0); - - if (m_d->usesOCIO == true) { - - for (int ch = 0; ch < maxvalue.size(); ch++) { - KoChannelInfo *channel = m_d->colorSpace->channels()[ch]; - maxvalue[ch] = m_d->displayRenderer->maxVisibleFloatValue(channel); - channelValues[ch] = channelValues[ch]/(maxvalue[ch]); - channelValuesDisplay[KoChannelInfo::displayPositionToChannelIndex(ch, m_d->colorSpace->channels())] = channelValues[ch]; - } - } - else { - for (int i =0; i < channelValues.size();i++) { - channelValuesDisplay[KoChannelInfo::displayPositionToChannelIndex(i, m_d->colorSpace->channels())] = qBound((float)0.0,channelValues[i], (float)1.0); - } - } - - qreal huedivider = 1.0; - qreal huedivider2 = 1.0; - - if (m_d->channel1 == 0) { - huedivider = 360.0; - } - - if (m_d->channel2 == 0) { - huedivider2 = 360.0; - } - - if (m_d->model != ColorModel::Channel && m_d->isRGBA == true) { - - if (m_d->model == ColorModel::HSV) { - /* - * RGBToHSV has a undefined hue possibility. This means that hue will be -1. - * This can be annoying for dealing with a selector, but I understand it is being - * used for the KoColorSelector... For now implement a qMax here. - */ - QVector inbetween(3); - RGBToHSV(channelValuesDisplay[0],channelValuesDisplay[1], channelValuesDisplay[2], &inbetween[0], &inbetween[1], &inbetween[2]); - inbetween = convertvectorqrealTofloat(getHSX(convertvectorfloatToqreal(inbetween))); - inbetween[m_d->channel1] = coordinates.x()*huedivider; - if (m_d->dimension == Dimensions::twodimensional) { - inbetween[m_d->channel2] = coordinates.y()*huedivider2; - } - if (cursor) { - setHSX(convertvectorfloatToqreal(inbetween)); - Q_EMIT sigHSXchange(); - } - HSVToRGB(qMax(inbetween[0],(float)0.0), inbetween[1], inbetween[2], &channelValuesDisplay[0], &channelValuesDisplay[1], &channelValuesDisplay[2]); - } - else if (m_d->model == ColorModel::HSL) { - /* - * HSLToRGB can give negative values on the grey. I fixed the fromNormalisedChannel function to clamp, - * but you might want to manually clamp for floating point values. - */ - QVector inbetween(3); - RGBToHSL(channelValuesDisplay[0],channelValuesDisplay[1], channelValuesDisplay[2], &inbetween[0], &inbetween[1], &inbetween[2]); - inbetween = convertvectorqrealTofloat(getHSX(convertvectorfloatToqreal(inbetween))); - inbetween[m_d->channel1] = fmod(coordinates.x()*huedivider, 360.0); - if (m_d->dimension == Dimensions::twodimensional) { - inbetween[m_d->channel2] = coordinates.y()*huedivider2; - } - if (cursor) { - setHSX(convertvectorfloatToqreal(inbetween)); - Q_EMIT sigHSXchange(); - } - HSLToRGB(qMax(inbetween[0], (float)0.0), inbetween[1], inbetween[2], &channelValuesDisplay[0], &channelValuesDisplay[1], &channelValuesDisplay[2]); - } - else if (m_d->model == ColorModel::HSI) { - /* - * HSI is a modified HSY function. - */ - QVector chan2 = convertvectorfloatToqreal(channelValuesDisplay); - QVector inbetween(3); - RGBToHSI(chan2[0],chan2[1], chan2[2], &inbetween[0], &inbetween[1], &inbetween[2]); - inbetween = getHSX(inbetween); - inbetween[m_d->channel1] = coordinates.x(); - if (m_d->dimension == Dimensions::twodimensional) { - inbetween[m_d->channel2] = coordinates.y(); - } - if (cursor) { - setHSX(inbetween); - Q_EMIT sigHSXchange(); - } - HSIToRGB(inbetween[0], inbetween[1], inbetween[2],&chan2[0],&chan2[1], &chan2[2]); - channelValuesDisplay = convertvectorqrealTofloat(chan2); - } - else /*if (m_d->model == ColorModel::HSY)*/ { - /* - * HSY is pretty slow to render due being a pretty over-the-top function. - * Might be worth investigating whether HCY can be used instead, but I have had - * some weird results with that. - */ - QVector luma= m_d->colorSpace->lumaCoefficients(); - QVector chan2 = convertvectorfloatToqreal(channelValuesDisplay); - QVector inbetween(3); - RGBToHSY(chan2[0],chan2[1], chan2[2], &inbetween[0], &inbetween[1], &inbetween[2], - luma[0], luma[1], luma[2]); - inbetween = getHSX(inbetween); - inbetween[m_d->channel1] = coordinates.x(); - if (m_d->dimension == Dimensions::twodimensional) { - inbetween[m_d->channel2] = coordinates.y(); - } - if (cursor) { - setHSX(inbetween); - Q_EMIT sigHSXchange(); - } - HSYToRGB(inbetween[0], inbetween[1], inbetween[2],&chan2[0],&chan2[1], &chan2[2], - luma[0], luma[1], luma[2]); - channelValuesDisplay = convertvectorqrealTofloat(chan2); - } - + Q_ASSERT(size == width()*height()*m_d->colorSpace->pixelSize()); + QImage image; + // Convert the buffer to a qimage + if (m_d->displayRenderer) { + image = m_d->displayRenderer->convertToQImage(m_d->colorSpace, rawColor, width(), height()); } else { - channelValuesDisplay[m_d->channel1] = coordinates.x(); - if (m_d->dimension == Dimensions::twodimensional) { - channelValuesDisplay[m_d->channel2] = coordinates.y(); - } + image = m_d->colorSpace->convertToQImage(rawColor, width(), height(), 0, KoColorConversionTransformation::internalRenderingIntent(), KoColorConversionTransformation::internalConversionFlags()); } - - for (int i=0; icolorSpace->channels())]*(maxvalue[i]); + // safeguard: + if (image.isNull()) + { + image = QImage(width(), height(), QImage::Format_ARGB32); + image.fill(Qt::black); } - c.colorSpace()->fromNormalisedChannelsValue(c.data(), channelValues); - - return c; + return image; } -QPointF KisVisualColorSelectorShape::convertKoColorToShapeCoordinate(KoColor c) +QImage KisVisualColorSelectorShape::renderBackground(const QVector4D &channelValues, quint32 pixelSize) const { - ////qDebug() << this << ">>>>>>>>> convertKoColorToShapeCoordinate()"; - - if (c.colorSpace() != m_d->colorSpace) { - c.convertTo(m_d->colorSpace); - } - QVector channelValues (m_d->currentColor.colorSpace()->channelCount()); - channelValues.fill(1.0); - m_d->colorSpace->normalisedChannelsValue(c.data(), channelValues); - QVector channelValuesDisplay = channelValues; - QVector maxvalue(c.colorSpace()->channelCount()); - maxvalue.fill(1.0); - if (m_d->usesOCIO == true) { - for (int ch = 0; chcolorSpace->channels()[ch]; - maxvalue[ch] = m_d->displayRenderer->maxVisibleFloatValue(channel); - channelValues[ch] = channelValues[ch]/(maxvalue[ch]); - channelValuesDisplay[KoChannelInfo::displayPositionToChannelIndex(ch, m_d->colorSpace->channels())] = channelValues[ch]; - } - } else { - for (int i =0; icolorSpace->channels())] = qBound((float)0.0,channelValues[i], (float)1.0); - } - } - QPointF coordinates(0.0,0.0); - qreal huedivider = 1.0; - qreal huedivider2 = 1.0; - if (m_d->channel1==0) { - huedivider = 360.0; - } - if (m_d->channel2==0) { - huedivider2 = 360.0; - } - if (m_d->model != ColorModel::Channel && m_d->isRGBA == true) { - if (m_d->isRGBA == true) { - if (m_d->model == ColorModel::HSV){ - QVector inbetween(3); - RGBToHSV(channelValuesDisplay[0],channelValuesDisplay[1], channelValuesDisplay[2], &inbetween[0], &inbetween[1], &inbetween[2]); - inbetween = convertvectorqrealTofloat(getHSX(convertvectorfloatToqreal(inbetween))); - coordinates.setX(inbetween[m_d->channel1]/huedivider); - if (m_d->dimension == Dimensions::twodimensional) { - coordinates.setY(inbetween[m_d->channel2]/huedivider2); - } - } else if (m_d->model == ColorModel::HSL) { - QVector inbetween(3); - RGBToHSL(channelValuesDisplay[0],channelValuesDisplay[1], channelValuesDisplay[2], &inbetween[0], &inbetween[1], &inbetween[2]); - inbetween = convertvectorqrealTofloat(getHSX(convertvectorfloatToqreal(inbetween))); - coordinates.setX(inbetween[m_d->channel1]/huedivider); - if (m_d->dimension == Dimensions::twodimensional) { - coordinates.setY(inbetween[m_d->channel2]/huedivider2); - } - } else if (m_d->model == ColorModel::HSI) { - QVector chan2 = convertvectorfloatToqreal(channelValuesDisplay); - QVector inbetween(3); - RGBToHSI(channelValuesDisplay[0],channelValuesDisplay[1], channelValuesDisplay[2], &inbetween[0], &inbetween[1], &inbetween[2]); - inbetween = getHSX(inbetween); - coordinates.setX(inbetween[m_d->channel1]); - if (m_d->dimension == Dimensions::twodimensional) { - coordinates.setY(inbetween[m_d->channel2]); - } - } else if (m_d->model == ColorModel::HSY) { - QVector luma = m_d->colorSpace->lumaCoefficients(); - QVector chan2 = convertvectorfloatToqreal(channelValuesDisplay); - QVector inbetween(3); - RGBToHSY(channelValuesDisplay[0],channelValuesDisplay[1], channelValuesDisplay[2], &inbetween[0], &inbetween[1], &inbetween[2], luma[0], luma[1], luma[2]); - inbetween = getHSX(inbetween); - coordinates.setX(inbetween[m_d->channel1]); - if (m_d->dimension == Dimensions::twodimensional) { - coordinates.setY(inbetween[m_d->channel2]); - } + const KisVisualColorSelector *selector = qobject_cast(parent()); + Q_ASSERT(selector); + + quint32 imageSize = width() * height() * m_d->colorSpace->pixelSize(); + QScopedArrayPointer raw(new quint8[imageSize] {}); + quint8 *dataPtr = raw.data(); + QVector4D coordinates = channelValues; + for (int y = 0; y < height(); y++) { + for (int x=0; x < width(); x++) { + QPointF newcoordinate = convertWidgetCoordinateToShapeCoordinate(QPoint(x, y)); + coordinates[m_d->channel1] = newcoordinate.x(); + if (m_d->dimension == Dimensions::twodimensional){ + coordinates[m_d->channel2] = newcoordinate.y(); } + KoColor c = selector->convertShapeCoordsToKoColor(coordinates); + memcpy(dataPtr, c.data(), pixelSize); + dataPtr += pixelSize; } - } else { - coordinates.setX(qBound((float)0.0, channelValuesDisplay[m_d->channel1], (float)1.0)); - if (m_d->dimension == Dimensions::twodimensional) { - coordinates.setY(qBound((float)0.0, channelValuesDisplay[m_d->channel2], (float)1.0)); - } - } - return coordinates; -} - -QVector KisVisualColorSelectorShape::convertvectorqrealTofloat(QVector real) -{ - QVector vloat(real.size()); - for (int i=0; i KisVisualColorSelectorShape::convertvectorfloatToqreal(QVector vloat) -{ - QVector real(vloat.size()); - for (int i=0; ibutton()==Qt::LeftButton) { m_d->mousePressActive = true; QPointF coordinates = convertWidgetCoordinateToShapeCoordinate(e->pos()); - KoColor col = convertShapeCoordinateToKoColor(coordinates, true); - setColor(col); - Q_EMIT sigNewColor(col); - m_d->updateTimer->start(); + setCursorPosition(coordinates, true); } } void KisVisualColorSelectorShape::mouseMoveEvent(QMouseEvent *e) { - if (m_d->mousePressActive==true && this->mask().contains(e->pos())) { + if (m_d->mousePressActive==true) { QPointF coordinates = convertWidgetCoordinateToShapeCoordinate(e->pos()); - quint8* oldData = m_d->currentColor.data(); - KoColor col = convertShapeCoordinateToKoColor(coordinates, true); - QRect offsetrect(this->geometry().topLeft() + QPoint(7.0, 7.0), this->geometry().bottomRight() - QPoint(7.0, 7.0)); - if (offsetrect.contains(e->pos()) || (m_d->colorSpace->difference(col.data(), oldData) > 5)) { - setColor(col); - if (!m_d->updateTimer->isActive()) { - Q_EMIT sigNewColor(col); - m_d->updateTimer->start(); - } - } - + setCursorPosition(coordinates, true); } else { e->ignore(); } } -void KisVisualColorSelectorShape::mouseReleaseEvent(QMouseEvent *) +void KisVisualColorSelectorShape::mouseReleaseEvent(QMouseEvent *e) { - m_d->mousePressActive = false; + if (e->button()==Qt::LeftButton) { + m_d->mousePressActive = false; + } } void KisVisualColorSelectorShape::paintEvent(QPaintEvent*) { QPainter painter(this); - //check if old and new colors differ. - - if (m_d->imagesNeedUpdate) { - setMask(getMaskMap()); - } drawCursor(); painter.drawImage(0,0,m_d->fullSelector); } -KisVisualColorSelectorShape::Dimensions KisVisualColorSelectorShape::getDimensions() +void KisVisualColorSelectorShape::resizeEvent(QResizeEvent *) { - return m_d->dimension; + forceImageUpdate(); + setMask(getMaskMap()); } -KisVisualColorSelectorShape::ColorModel KisVisualColorSelectorShape::getColorModel() +KisVisualColorSelectorShape::Dimensions KisVisualColorSelectorShape::getDimensions() const { - return m_d->model; + return m_d->dimension; } void KisVisualColorSelectorShape::setFullImage(QImage full) { m_d->fullSelector = full; } KoColor KisVisualColorSelectorShape::getCurrentColor() { - return m_d->currentColor; -} - -QVector KisVisualColorSelectorShape::getHSX(QVector hsx, bool wrangler) -{ - QVector ihsx = hsx; - if (!wrangler){ - //Ok, so this docker will not update luminosity if there's not at the least 3% more variation. - //This is necessary for 8bit. - if (m_d->is8Bit == true){ - if (hsx[2]>m_d->tone-0.03 && hsx[2]tone+0.03) { - ihsx[2] = m_d->tone; - } - } else { - if (hsx[2]>m_d->tone-0.005 && hsx[2]tone+0.005) { - ihsx[2] = m_d->tone; - } - } - if (m_d->model==HSV){ - if (hsx[2]<=0.0) { - ihsx[1] = m_d->sat; - } - } else { - if ((hsx[2]<=0.0 || hsx[2]>=1.0)) { - ihsx[1] = m_d->sat; - } - } - if ((hsx[1]<=0.0 || hsx[0]<0.0)){ - ihsx[0]=m_d->hue; - } - } else { - ihsx[0]=m_d->hue; - ihsx[1]=m_d->sat; - ihsx[2]=m_d->tone; - } - return ihsx; -} - -void KisVisualColorSelectorShape::setHSX(QVector hsx, bool wrangler) -{ - if (wrangler){ - m_d->tone = hsx[2]; - m_d->sat = hsx[1]; - m_d->hue = hsx[0]; - } else { - if (m_d->channel1==2 || m_d->channel2==2){ - m_d->tone=hsx[2]; - } - if (m_d->model==HSV){ - if (hsx[2]>0.0) { - m_d->sat = hsx[1]; - } - } else { - if ((hsx[2]>0.0 || hsx[2]<1.0)) { - m_d->sat = hsx[1]; - } - } - if ((hsx[1]>0.0 && hsx[0]>=0.0)){ - m_d->hue = hsx[0]; - } + const KisVisualColorSelector *selector = qobject_cast(parent()); + if (selector) + { + return selector->convertShapeCoordsToKoColor(m_d->currentChannelValues); } + return KoColor(m_d->colorSpace); } -QVector KisVisualColorSelectorShape::getChannels() +QVector KisVisualColorSelectorShape::getChannels() const { QVector channels(2); channels[0] = m_d->channel1; channels[1] = m_d->channel2; return channels; } diff --git a/libs/widgets/KisVisualColorSelectorShape.h b/libs/widgets/KisVisualColorSelectorShape.h index a24219a26c..5852f2333f 100644 --- a/libs/widgets/KisVisualColorSelectorShape.h +++ b/libs/widgets/KisVisualColorSelectorShape.h @@ -1,236 +1,220 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * 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. */ #ifndef KIS_VISUAL_COLOR_SELECTOR_SHAPE_H #define KIS_VISUAL_COLOR_SELECTOR_SHAPE_H #include #include #include #include #include #include #include #include "KoColorDisplayRendererInterface.h" #include "KisVisualColorSelector.h" #include "KisColorSelectorConfiguration.h" /** * @brief The KisVisualColorSelectorShape class * A 2d widget can represent at maximum 2 coordinates. * So first decide howmany coordinates you need. (onedimensional, or twodimensional) * Then the model, (Channel, HSV, HSL, HSI, YUV). Channel is the raw color channels. * When it finds a non-implemented feature it'll return to Channel. * Then, select the channels you wish to be affected. This uses the model, so for cmyk * the channel is c=0, m=1, y=2, k=3, but for hsv, hue=0, sat=1, and val=2 * These can also be set with 'slotsetactive channels'. * Then finally, connect the displayrenderer, you can also do this with 'setdisplayrenderer' * * Either way, this class is made to be subclassed, with a few virtuals so that the geometry * can be calculated properly. */ class KisVisualColorSelectorShape : public QWidget { Q_OBJECT public: /** * @brief The Dimensions enum * Whether or not the shape is single or two dimensional. **/ enum Dimensions{onedimensional, twodimensional}; enum ColorModel{Channel, HSV, HSL, HSI, HSY, YUV}; explicit KisVisualColorSelectorShape(QWidget *parent, KisVisualColorSelectorShape::Dimensions dimension, - KisVisualColorSelectorShape::ColorModel model, const KoColorSpace *cs, int channel1, int channel2, const KoColorDisplayRendererInterface *displayRenderer = KoDumbColorDisplayRenderer::instance()); ~KisVisualColorSelectorShape() override; /** * @brief getCursorPosition * @return current cursor position in shape-coordinates. */ QPointF getCursorPosition(); /** * @brief getDimensions * @return whether this is a single or twodimensional widget. */ - Dimensions getDimensions(); - /** - * @brief getColorModel - * @return the model of this widget. - */ - ColorModel getColorModel(); + Dimensions getDimensions() const; /** * @brief getPixmap * @return the pixmap of the gradient, for drawing on with a subclass. * the pixmap will not change unless 'm_d->setPixmap=true' which is toggled by * refresh and update functions. */ bool imagesNeedUpdate() const; QImage getImageMap(); /** * @brief setFullImage * Set the full widget image to be painted. * @param full this should be the full image. */ void setFullImage(QImage full); /** * @brief getCurrentColor * @return the current kocolor */ KoColor getCurrentColor(); /** * @brief setDisplayRenderer * disconnect the old display renderer if needed and connect the new one. * @param displayRenderer */ void setDisplayRenderer (const KoColorDisplayRendererInterface *displayRenderer); /** * @brief getColorFromConverter * @param c a koColor. * @return get the qcolor from the given kocolorusing this widget's display renderer. */ QColor getColorFromConverter(KoColor c); /** * @brief getSpaceForSquare * @param geom the full widget rectangle * @return rectangle with enough space for second widget */ virtual QRect getSpaceForSquare(QRect geom) = 0; virtual QRect getSpaceForCircle(QRect geom) = 0; virtual QRect getSpaceForTriangle(QRect geom) = 0; /** * @brief forceImageUpdate * force the image to recache. */ void forceImageUpdate(); /** * @brief setBorderWidth * set the border of the single dimensional selector. * @param width */ virtual void setBorderWidth(int width) = 0; /** * @brief getChannels * get used channels * @return */ - QVector getChannels(); + QVector getChannels() const; /** - * @brief setHSX - * This is for the cursor not to change when selecting - * black, white, and desaturated values. Will not change the non-native values. - * @param hsx the hsx value. - * @param wrangler defines whether this docker will update luminosity if there's not at the least 3\% more variation - */ - void setHSX(QVector hsx, bool wrangler=false); + * @brief setCursorPosition + * Set the cursor to normalized shape coordinates. This will only repaint the cursor. + * @param position normalized shape coordinates ([0,1] range, not yet transformed to actual channel values!) + * @param signal if true, emit a sigCursorMoved signal + */ + void setCursorPosition(QPointF position, bool signal = false); + /** - * @brief getHSX sets the sat and hue so they won't - * switch around much. - * @param hsx the hsx values. - * @param wrangler defines whether this docker will update luminosity if there's not at the least 3\% more variation - * @return returns hsx, corrected. - */ - QVector getHSX(QVector hsx, bool wrangler= false); + * @brief setChannelValues + * Set the current channel values; + * Note that channel values controlled by the shape itself have no effect unless setCursor is true. + * This will trigger a full widget repaint. + * @param position normalized shape coordinates ([0,1] range) + * these are not yet transformed to color space specific ranges! + * @param setCursor if true, sets the cursor too, otherwise the shape-controlled channels are not set + */ + void setChannelValues(QVector4D channelValues, bool setCursor); Q_SIGNALS: - void sigNewColor(KoColor col); - void sigHSXchange(); + void sigCursorMoved(QPointF pos); public Q_SLOTS: - /** - * @brief setColor - * Set this widget's current color and change the cursor position. - * @param c - */ - void setColor(KoColor c); - /** - * @brief setColorFromSibling - * set this widget's current color, but don't change the cursor position, - * instead sent out a signal of the new color. - * @param c - */ - void setColorFromSibling(KoColor c); /** * @brief slotSetActiveChannels * Change the active channels if necessary. * @param channel1 used by single and twodimensional widgets. * @param channel2 only used by twodimensional widgets. */ void slotSetActiveChannels(int channel1, int channel2); - /** - * @brief updateFromChangedDisplayRenderer - * for updating from the display renderer... not sure why this one is public. - */ - void updateFromChangedDisplayRenderer(); protected: + /** + * @brief convertImageMap + * convert image data containing raw KoColor data into a QImage + * @param data must point to memory of size width()*height()*pixelSize + * @param size the number of bytes to read from data, must match aforementioned cirteria + * @return the converted QImage guaranteed to match the widget size (black content on failure) + */ + QImage convertImageMap(const quint8 *rawColor, quint32 size) const; + /** + * @brief renderBackground + * Render the widget background visible inside the widget's mask in current color space + * Rendering shall be done with the conversion functions of KisVisualColorSelector + * @param data points to zero-initialized memory of size width()*height()*pixelSize + * @param pixelSize the data size to transfer from KoColor::data() to data per pixel + * in the current color space + * @param channelValues the normalized channel values of the currently picked color + */ + virtual QImage renderBackground(const QVector4D &channelValues, quint32 pixelSize) const; void mousePressEvent(QMouseEvent *e) override; void mouseMoveEvent(QMouseEvent *e) override; void mouseReleaseEvent(QMouseEvent *e) override; void paintEvent(QPaintEvent*) override; + void resizeEvent(QResizeEvent *) override; private: struct Private; const QScopedPointer m_d; /** * @brief convertShapeCoordinateToWidgetCoordinate * @return take the position in the shape and convert it to screen coordinates. */ - virtual QPointF convertShapeCoordinateToWidgetCoordinate(QPointF) = 0; + virtual QPointF convertShapeCoordinateToWidgetCoordinate(QPointF) const = 0; /** * @brief convertWidgetCoordinateToShapeCoordinate * Convert a coordinate in the widget's height/width to a shape coordinate. * @param coordinate the position your wish to have the shape coordinates of. */ - virtual QPointF convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) = 0; - - /** - * @brief updateCursor - * Update the cursor position. - */ - void updateCursor(); - - QPointF convertKoColorToShapeCoordinate(KoColor c); - KoColor convertShapeCoordinateToKoColor(QPointF coordinates, bool cursor = false); + virtual QPointF convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) const = 0; /** * @brief getPixmap * @return the pixmap of this shape. */ virtual QRegion getMaskMap() = 0; virtual void drawCursor() = 0; - - QVector convertvectorqrealTofloat(QVector real); - QVector convertvectorfloatToqreal(QVector vloat); }; #endif diff --git a/libs/widgets/KisVisualEllipticalSelectorShape.cpp b/libs/widgets/KisVisualEllipticalSelectorShape.cpp index b6a0077208..f6e7ed63fc 100644 --- a/libs/widgets/KisVisualEllipticalSelectorShape.cpp +++ b/libs/widgets/KisVisualEllipticalSelectorShape.cpp @@ -1,243 +1,283 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * 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 "KisVisualEllipticalSelectorShape.h" #include #include #include #include #include #include +#include #include #include #include #include #include #include #include "KoColorConversions.h" #include "KoColorDisplayRendererInterface.h" #include "KoChannelInfo.h" #include #include #include "kis_signal_compressor.h" #include "kis_debug.h" #include "kis_global.h" KisVisualEllipticalSelectorShape::KisVisualEllipticalSelectorShape(QWidget *parent, Dimensions dimension, - ColorModel model, const KoColorSpace *cs, int channel1, int channel2, const KoColorDisplayRendererInterface *displayRenderer, int barWidth, singelDTypes d) - : KisVisualColorSelectorShape(parent, dimension, model, cs, channel1, channel2, displayRenderer) + : KisVisualColorSelectorShape(parent, dimension, cs, channel1, channel2, displayRenderer) { //qDebug() << "creating KisVisualEllipticalSelectorShape" << this; m_type = d; m_barWidth = barWidth; } KisVisualEllipticalSelectorShape::~KisVisualEllipticalSelectorShape() { //qDebug() << "deleting KisVisualEllipticalSelectorShape" << this; } QSize KisVisualEllipticalSelectorShape::sizeHint() const { return QSize(180,180); } void KisVisualEllipticalSelectorShape::setBorderWidth(int width) { m_barWidth = width; + forceImageUpdate(); + update(); } QRect KisVisualEllipticalSelectorShape::getSpaceForSquare(QRect geom) { int sizeValue = qMin(width(),height()); QRect b(geom.left(), geom.top(), sizeValue, sizeValue); QLineF radius(b.center(), QPointF(b.left()+m_barWidth, b.center().y()) ); radius.setAngle(135); QPointF tl = radius.p2(); radius.setAngle(315); QPointF br = radius.p2(); QRect r(tl.toPoint(), br.toPoint()); return r; } QRect KisVisualEllipticalSelectorShape::getSpaceForCircle(QRect geom) { int sizeValue = qMin(width(),height()); QRect b(geom.left(), geom.top(), sizeValue, sizeValue); QPointF tl = QPointF (b.topLeft().x()+m_barWidth, b.topLeft().y()+m_barWidth); QPointF br = QPointF (b.bottomRight().x()-m_barWidth, b.bottomRight().y()-m_barWidth); QRect r(tl.toPoint(), br.toPoint()); return r; } QRect KisVisualEllipticalSelectorShape::getSpaceForTriangle(QRect geom) { int sizeValue = qMin(width(),height()); QRect b(geom.left(), geom.top(), sizeValue, sizeValue); QLineF radius(b.center(), QPointF(b.left()+m_barWidth, b.center().y()) ); radius.setAngle(90);//point at yellowgreen :) QPointF t = radius.p2(); radius.setAngle(330);//point to purple :) QPointF br = radius.p2(); radius.setAngle(210);//point to cerulean :) QPointF bl = radius.p2(); QPointF tl = QPoint(bl.x(),t.y()); QRect r(tl.toPoint(), br.toPoint()); return r; } -QPointF KisVisualEllipticalSelectorShape::convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) +QPointF KisVisualEllipticalSelectorShape::convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) const { qreal x; qreal y; qreal offset=7.0; qreal a = (qreal)width()*0.5; QPointF center(a, a); QLineF line(center, QPoint((m_barWidth*0.5),a)); qreal angle = coordinate.x()*360.0; angle = fmod(angle+180.0,360.0); angle = 180.0-angle; angle = angle+180.0; if (m_type==KisVisualEllipticalSelectorShape::borderMirrored) { angle = (coordinate.x()/2)*360.0; angle = fmod((angle+270.0), 360.0); } line.setAngle(angle); if (getDimensions()!=KisVisualColorSelectorShape::onedimensional) { line.setLength(qMin(coordinate.y()*(a-offset), a-offset)); } x = qRound(line.p2().x()); y = qRound(line.p2().y()); return QPointF(x,y); } -QPointF KisVisualEllipticalSelectorShape::convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) +QPointF KisVisualEllipticalSelectorShape::convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) const { //default implementation: qreal x = 0.5; qreal y = 1.0; qreal offset = 7.0; QPointF center = QRectF(QPointF(0.0, 0.0), this->size()).center(); qreal a = (this->width()/2); qreal xRel = center.x()-coordinate.x(); qreal yRel = center.y()-coordinate.y(); - qreal angle = atan2(xRel, yRel); qreal radius = sqrt(xRel*xRel+yRel*yRel); - angle = kisRadiansToDegrees(angle); if (m_type!=KisVisualEllipticalSelectorShape::borderMirrored){ - angle = fmod(angle-90, 360.0); - angle = 180.0-angle; - angle = angle+180.0; + qreal angle = atan2(yRel, xRel); + angle = kisRadiansToDegrees(angle); + angle = fmod(angle+360, 360.0); x = angle/360.0; if (getDimensions()==KisVisualColorSelectorShape::twodimensional) { y = qBound(0.0,radius/(a-offset), 1.0); } } else { + qreal angle = atan2(xRel, yRel); + angle = kisRadiansToDegrees(angle); angle = fmod(angle+180, 360.0); if (angle>180.0) { - angle = 180.0-angle; - angle = angle+180; + angle = 360.0-angle; } x = (angle/360.0)*2; if (getDimensions()==KisVisualColorSelectorShape::twodimensional) { y = qBound(0.0,(radius+offset)/a, 1.0); } } return QPointF(x, y); } QRegion KisVisualEllipticalSelectorShape::getMaskMap() { QRegion mask = QRegion(0,0,width(),height(), QRegion::Ellipse); if (getDimensions()==KisVisualColorSelectorShape::onedimensional) { mask = mask.subtracted(QRegion(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2), QRegion::Ellipse)); } return mask; } -void KisVisualEllipticalSelectorShape::resizeEvent(QResizeEvent *) +QImage KisVisualEllipticalSelectorShape::renderBackground(const QVector4D &channelValues, quint32 pixelSize) const { - //qDebug() << this << "KisVisualEllipticalSelectorShape::resizeEvent"; - forceImageUpdate(); -} + const KisVisualColorSelector *selector = qobject_cast(parent()); + Q_ASSERT(selector); + // optimization assumes widget is (close to) square, but should still render correctly as ellipse + int rMaxSquare = qRound(qMax(width(), height()) * 0.5f + 0.5f); + rMaxSquare *= rMaxSquare; + int rMinSquare = 0; + if (getDimensions() == Dimensions::onedimensional) + { + rMinSquare = qMax(0, qRound(qMin(width(), height()) * 0.5f - m_barWidth)); + rMinSquare *= rMinSquare; + } + int cx = width()/2; + int cy = height()/2; -void KisVisualEllipticalSelectorShape::drawCursor() -{ - //qDebug() << this << "KisVisualEllipticalSelectorShape::drawCursor: image needs update" << imagesNeedUpdate(); - QPointF cursorPoint = convertShapeCoordinateToWidgetCoordinate(getCursorPosition()); - QImage fullSelector = getImageMap(); - QColor col = getColorFromConverter(getCurrentColor()); - QPainter painter; - painter.begin(&fullSelector); + // Fill a buffer with the right kocolors + quint32 imageSize = width() * height() * pixelSize; + QScopedArrayPointer raw(new quint8[imageSize] {}); + quint8 *dataPtr = raw.data(); + bool is2D = (getDimensions() == Dimensions::twodimensional); + QVector4D coordinates = channelValues; + QVector channels = getChannels(); + for (int y = 0; y < height(); y++) { + int dy = y - cy; + for (int x=0; x < width(); x++) { + int dx = x - cx; + int radSquare = dx*dx + dy*dy; + if (radSquare >= rMinSquare && radSquare < rMaxSquare) + { + QPointF newcoordinate = convertWidgetCoordinateToShapeCoordinate(QPoint(x, y)); + coordinates[channels.at(0)] = newcoordinate.x(); + if (is2D){ + coordinates[channels.at(1)] = newcoordinate.y(); + } + KoColor c = selector->convertShapeCoordsToKoColor(coordinates); + memcpy(dataPtr, c.data(), pixelSize); + } + dataPtr += pixelSize; + } + } + QImage image = convertImageMap(raw.data(), imageSize); + // cleanup edges by erasing with antialiased circles + QPainter painter(&image); painter.setRenderHint(QPainter::Antialiasing); - QRect innerRect(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2)); - - painter.save(); painter.setCompositionMode(QPainter::CompositionMode_Clear); QPen pen; pen.setWidth(5); painter.setPen(pen); painter.drawEllipse(QRect(0,0,width(),height())); if (getDimensions()==KisVisualColorSelectorShape::onedimensional) { + QRect innerRect(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2)); painter.setBrush(Qt::SolidPattern); painter.drawEllipse(innerRect); } - painter.restore(); + return image; +} +void KisVisualEllipticalSelectorShape::drawCursor() +{ + //qDebug() << this << "KisVisualEllipticalSelectorShape::drawCursor: image needs update" << imagesNeedUpdate(); + QPointF cursorPoint = convertShapeCoordinateToWidgetCoordinate(getCursorPosition()); + QImage fullSelector = getImageMap(); + QColor col = getColorFromConverter(getCurrentColor()); + QPainter painter; + painter.begin(&fullSelector); + painter.setRenderHint(QPainter::Antialiasing); + QRect innerRect(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2)); QBrush fill; fill.setStyle(Qt::SolidPattern); int cursorwidth = 5; if (m_type==KisVisualEllipticalSelectorShape::borderMirrored) { painter.setPen(Qt::white); fill.setColor(Qt::white); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth); QPoint mirror(innerRect.center().x()+(innerRect.center().x()-cursorPoint.x()),cursorPoint.y()); painter.drawEllipse(mirror, cursorwidth, cursorwidth); fill.setColor(col); painter.setPen(Qt::black); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth-1, cursorwidth-1); painter.drawEllipse(mirror, cursorwidth-1, cursorwidth-1); } else { painter.setPen(Qt::white); fill.setColor(Qt::white); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth); fill.setColor(col); painter.setPen(Qt::black); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth-1.0, cursorwidth-1.0); } painter.end(); setFullImage(fullSelector); } diff --git a/libs/widgets/KisVisualEllipticalSelectorShape.h b/libs/widgets/KisVisualEllipticalSelectorShape.h index a09ecc156f..e50c37bb3e 100644 --- a/libs/widgets/KisVisualEllipticalSelectorShape.h +++ b/libs/widgets/KisVisualEllipticalSelectorShape.h @@ -1,73 +1,73 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * 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. */ #ifndef KISVISUALCOLORSELECTOR_H #define KISVISUALCOLORSELECTOR_H #include #include #include #include #include #include #include #include "KoColorDisplayRendererInterface.h" #include "KisColorSelectorConfiguration.h" #include "KisVisualColorSelectorShape.h" class KisVisualEllipticalSelectorShape : public KisVisualColorSelectorShape { Q_OBJECT public: enum singelDTypes{border, borderMirrored}; explicit KisVisualEllipticalSelectorShape(QWidget *parent, Dimensions dimension, - ColorModel model, const KoColorSpace *cs, int channel1, int channel2, const KoColorDisplayRendererInterface *displayRenderer = KoDumbColorDisplayRenderer::instance(), int barWidth=20, KisVisualEllipticalSelectorShape::singelDTypes d = KisVisualEllipticalSelectorShape::border ); ~KisVisualEllipticalSelectorShape() override; void setBorderWidth(int width) override; /** * @brief getSpaceForSquare * @param geom the full widget rectangle * @return rectangle with enough space for second widget */ QRect getSpaceForSquare(QRect geom) override; QRect getSpaceForCircle(QRect geom) override; QRect getSpaceForTriangle(QRect geom) override; protected: - void resizeEvent(QResizeEvent *) override; + virtual QImage renderBackground(const QVector4D &channelValues, quint32 pixelSize) const override; + private: - QPointF convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) override; - QPointF convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) override; + QPointF convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) const override; + QPointF convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) const override; singelDTypes m_type; int m_barWidth; QRegion getMaskMap() override; void drawCursor() override; QSize sizeHint() const override; }; #endif // KISVISUALCOLORSELECTOR_H diff --git a/libs/widgets/KisVisualRectangleSelectorShape.cpp b/libs/widgets/KisVisualRectangleSelectorShape.cpp index 05c3e21c69..ddc800142c 100644 --- a/libs/widgets/KisVisualRectangleSelectorShape.cpp +++ b/libs/widgets/KisVisualRectangleSelectorShape.cpp @@ -1,372 +1,368 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * 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 "KisVisualRectangleSelectorShape.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "KoColorConversions.h" #include "KoColorDisplayRendererInterface.h" #include "KoChannelInfo.h" #include #include #include "kis_signal_compressor.h" #include "kis_debug.h" KisVisualRectangleSelectorShape::KisVisualRectangleSelectorShape(QWidget *parent, Dimensions dimension, - ColorModel model, const KoColorSpace *cs, int channel1, int channel2, const KoColorDisplayRendererInterface *displayRenderer, int width, singelDTypes d) - : KisVisualColorSelectorShape(parent, dimension, model, cs, channel1, channel2, displayRenderer) + : KisVisualColorSelectorShape(parent, dimension, cs, channel1, channel2, displayRenderer) { //qDebug() << "creating KisVisualRectangleSelectorShape" << this; m_type = d; m_barWidth = width; } KisVisualRectangleSelectorShape::~KisVisualRectangleSelectorShape() { //qDebug() << "deleting KisVisualRectangleSelectorShape" << this; } void KisVisualRectangleSelectorShape::setBorderWidth(int width) { m_barWidth = width; } QRect KisVisualRectangleSelectorShape::getSpaceForSquare(QRect geom) { QPointF tl; QPointF br; if (m_type==KisVisualRectangleSelectorShape::vertical) { br = geom.bottomRight(); tl = QPoint(geom.topLeft().x()+m_barWidth, geom.topLeft().y()); } else if (m_type==KisVisualRectangleSelectorShape::horizontal) { br = geom.bottomRight(); tl = QPoint(geom.topLeft().x(), geom.topLeft().y()+m_barWidth); } else { tl = QPointF (geom.topLeft().x()+m_barWidth, geom.topLeft().y()+m_barWidth); br = QPointF (geom.bottomRight().x()-m_barWidth, geom.bottomRight().y()-m_barWidth); } QRect a(tl.toPoint(), br.toPoint()); QRect r(a.left(), a.top(), qMin(a.height(), a.width()), qMin(a.height(), a.width())); return r; } QRect KisVisualRectangleSelectorShape::getSpaceForCircle(QRect geom) { return getSpaceForSquare(geom); } QRect KisVisualRectangleSelectorShape::getSpaceForTriangle(QRect geom) { return getSpaceForSquare(geom); } -QPointF KisVisualRectangleSelectorShape::convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) +QPointF KisVisualRectangleSelectorShape::convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) const { + // Reminder: in Qt widget space, origin is top-left, but we want zero y to be at the bottom qreal x = 0.5 * m_barWidth; qreal y = 0.5 * m_barWidth; qreal offset = 5.0; KisVisualColorSelectorShape::Dimensions dimension = getDimensions(); if (dimension == KisVisualColorSelectorShape::onedimensional) { if ( m_type == KisVisualRectangleSelectorShape::vertical) { - y = qMin(coordinate.x()*(height()-offset*2)+offset, (qreal)height()); + y = qMin((1.0 - coordinate.x())*(height()-offset*2)+offset, (qreal)height()); } else if (m_type == KisVisualRectangleSelectorShape::horizontal) { x = qMin(coordinate.x()*(width()-offset*2)+offset, (qreal)width()); } else if (m_type == KisVisualRectangleSelectorShape::border) { QRectF innerRect(m_barWidth/2, m_barWidth/2, width()-m_barWidth, height()-m_barWidth); QPointF left (innerRect.left(),innerRect.center().y()); QList polygonLines; polygonLines.append(QLineF(left, innerRect.topLeft())); polygonLines.append(QLineF(innerRect.topLeft(), innerRect.topRight())); polygonLines.append(QLineF(innerRect.topRight(), innerRect.bottomRight())); polygonLines.append(QLineF(innerRect.bottomRight(), innerRect.bottomLeft())); polygonLines.append(QLineF(innerRect.bottomLeft(), left)); qreal totalLength =0.0; Q_FOREACH(QLineF line, polygonLines) { totalLength += line.length(); } qreal length = coordinate.x()*totalLength; QPointF intersect(x,y); Q_FOREACH(QLineF line, polygonLines) { if (line.length()>length && length>0){ intersect = line.pointAt(length/line.length()); } length-=line.length(); } x = qRound(intersect.x()); y = qRound(intersect.y()); } else /*if (m_type == KisVisualRectangleSelectorShape::borderMirrored)*/ { QRectF innerRect(m_barWidth/2, m_barWidth/2, width()-m_barWidth, height()-m_barWidth); QPointF bottom (innerRect.center().x(), innerRect.bottom()); QList polygonLines; polygonLines.append(QLineF(bottom, innerRect.bottomLeft())); polygonLines.append(QLineF(innerRect.bottomLeft(), innerRect.topLeft())); polygonLines.append(QLineF(innerRect.topLeft(), innerRect.topRight())); polygonLines.append(QLineF(innerRect.topRight(), innerRect.bottomRight())); polygonLines.append(QLineF(innerRect.bottomRight(), bottom)); qreal totalLength =0.0; Q_FOREACH(QLineF line, polygonLines) { totalLength += line.length(); } qreal length = coordinate.x()*(totalLength/2); QPointF intersect(x,y); if (coordinate.y()==1) { for (int i = polygonLines.size()-1; i==0; i--) { QLineF line = polygonLines.at(i); if (line.length()>length && length>0){ intersect = line.pointAt(length/line.length()); } length-=line.length(); } } else { Q_FOREACH(QLineF line, polygonLines) { if (line.length()>length && length>0){ intersect = line.pointAt(length/line.length()); } length-=line.length(); } } x = qRound(intersect.x()); y = qRound(intersect.y()); } } else { - x = qMin(coordinate.x()*(height()-offset*2)+offset, (qreal)height()); - y = qMin(coordinate.y()*(width()-offset*2)+offset, (qreal)width()); + x = qMin(coordinate.x()*(width()-offset*2)+offset, (qreal)width()); + y = qMin((1.0 - coordinate.y())*(height()-offset*2)+offset, (qreal)height()); } return QPointF(x,y); } -QPointF KisVisualRectangleSelectorShape::convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) +QPointF KisVisualRectangleSelectorShape::convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) const { - //default implementation: + // Reminder: in Qt widget space, origin is top-left, but we want zero y to be at the bottom + //default values: qreal x = 0.5; qreal y = 0.5; qreal offset = 5.0; KisVisualColorSelectorShape::Dimensions dimension = getDimensions(); - if (getMaskMap().contains(coordinate)) { - if (dimension == KisVisualColorSelectorShape::onedimensional ) { - if (m_type == KisVisualRectangleSelectorShape::vertical) { - x = qMax(((qreal)coordinate.y()-offset)/((qreal)height()-offset*2), 0.0); - } else if (m_type == KisVisualRectangleSelectorShape::horizontal) { - x = qMax(((qreal)coordinate.x()-offset)/((qreal)width()-offset*2),0.0); - } else if (m_type == KisVisualRectangleSelectorShape::border) { - //border - - QRectF innerRect(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2)); - QPointF left (innerRect.left(),innerRect.center().y()); - QList polygonLines; - polygonLines.append(QLineF(left, innerRect.topLeft())); - polygonLines.append(QLineF(innerRect.topLeft(), innerRect.topRight())); - polygonLines.append(QLineF(innerRect.topRight(), innerRect.bottomRight())); - polygonLines.append(QLineF(innerRect.bottomRight(), innerRect.bottomLeft())); - polygonLines.append(QLineF(innerRect.bottomLeft(), left)); - - QLineF radius(coordinate, this->geometry().center()); - QPointF intersect(0.5,0.5); - qreal length = 0.0; - qreal totalLength = 0.0; - bool foundIntersect = false; - Q_FOREACH(QLineF line, polygonLines) { - if (line.intersect(radius,&intersect)==QLineF::BoundedIntersection && foundIntersect==false) - { - foundIntersect = true; - length+=QLineF(line.p1(), intersect).length(); + if (dimension == KisVisualColorSelectorShape::onedimensional ) { + if (m_type == KisVisualRectangleSelectorShape::vertical) { + x = 1.0 - (coordinate.y()-offset)/(height()-offset*2); + } else if (m_type == KisVisualRectangleSelectorShape::horizontal) { + x = (coordinate.x()-offset)/(width()-offset*2); + } else if (m_type == KisVisualRectangleSelectorShape::border) { + //border + + QRectF innerRect(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2)); + QPointF left (innerRect.left(),innerRect.center().y()); + QList polygonLines; + polygonLines.append(QLineF(left, innerRect.topLeft())); + polygonLines.append(QLineF(innerRect.topLeft(), innerRect.topRight())); + polygonLines.append(QLineF(innerRect.topRight(), innerRect.bottomRight())); + polygonLines.append(QLineF(innerRect.bottomRight(), innerRect.bottomLeft())); + polygonLines.append(QLineF(innerRect.bottomLeft(), left)); + + QLineF radius(coordinate, this->geometry().center()); + QPointF intersect(0.5,0.5); + qreal length = 0.0; + qreal totalLength = 0.0; + bool foundIntersect = false; + Q_FOREACH(QLineF line, polygonLines) { + if (line.intersect(radius,&intersect)==QLineF::BoundedIntersection && foundIntersect==false) + { + foundIntersect = true; + length+=QLineF(line.p1(), intersect).length(); - } - if (foundIntersect==false) { - length+=line.length(); - } - totalLength+=line.length(); } + if (foundIntersect==false) { + length+=line.length(); + } + totalLength+=line.length(); + } - x = length/totalLength; - - } else /*if (m_type == KisVisualRectangleSelectorShape::borderMirrored)*/ { - //border - - QRectF innerRect(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2)); - QPointF bottom (innerRect.center().x(), innerRect.bottom()); - QList polygonLines; - polygonLines.append(QLineF(bottom, innerRect.bottomLeft())); - polygonLines.append(QLineF(innerRect.bottomLeft(), innerRect.topLeft())); - polygonLines.append(QLineF(innerRect.topLeft(), innerRect.topRight())); - polygonLines.append(QLineF(innerRect.topRight(), innerRect.bottomRight())); - polygonLines.append(QLineF(innerRect.bottomRight(), bottom)); - - QLineF radius(coordinate, this->geometry().center()); - QPointF intersect(0.5,0.5); - qreal length = 0.0; - qreal totalLength = 0.0; - bool foundIntersect = false; - Q_FOREACH(QLineF line, polygonLines) { - if (line.intersect(radius,&intersect)==QLineF::BoundedIntersection && foundIntersect==false) - { - foundIntersect = true; - length+=QLineF(line.p1(), intersect).length(); + x = length/totalLength; + + } else /*if (m_type == KisVisualRectangleSelectorShape::borderMirrored)*/ { + //border + + QRectF innerRect(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2)); + QPointF bottom (innerRect.center().x(), innerRect.bottom()); + QList polygonLines; + polygonLines.append(QLineF(bottom, innerRect.bottomLeft())); + polygonLines.append(QLineF(innerRect.bottomLeft(), innerRect.topLeft())); + polygonLines.append(QLineF(innerRect.topLeft(), innerRect.topRight())); + polygonLines.append(QLineF(innerRect.topRight(), innerRect.bottomRight())); + polygonLines.append(QLineF(innerRect.bottomRight(), bottom)); + + QLineF radius(coordinate, this->geometry().center()); + QPointF intersect(0.5,0.5); + qreal length = 0.0; + qreal totalLength = 0.0; + bool foundIntersect = false; + Q_FOREACH(QLineF line, polygonLines) { + if (line.intersect(radius,&intersect)==QLineF::BoundedIntersection && foundIntersect==false) + { + foundIntersect = true; + length+=QLineF(line.p1(), intersect).length(); - } - if (foundIntersect==false) { - length+=line.length(); - } - totalLength+=line.length(); } - int halflength = totalLength/2; - - if (length>halflength) { - x = (halflength - (length-halflength))/halflength; - y = 1.0; - } else { - x = length/halflength; - y = 0.0; + if (foundIntersect==false) { + length+=line.length(); } + totalLength+=line.length(); + } + int halflength = totalLength/2; + + if (length>halflength) { + x = (halflength - (length-halflength))/halflength; + y = 1.0; + } else { + x = length/halflength; + y = 0.0; } } - else { - x = qMax(((qreal)coordinate.x()-offset)/((qreal)width()-offset*2), 0.0); - y = qMax(((qreal)coordinate.y()-offset)/((qreal)height()-offset*2), 0.0); - } } + else { + x = (coordinate.x()-offset)/(width()-offset*2); + y = 1.0 - (coordinate.y()-offset)/(height()-offset*2); + } + x = qBound((qreal)0.0, x, (qreal)1.0); + y = qBound((qreal)0.0, y, (qreal)1.0); return QPointF(x, y); } QRegion KisVisualRectangleSelectorShape::getMaskMap() { QRegion mask = QRegion(0,0,width(),height()); if (m_type==KisVisualRectangleSelectorShape::border || m_type==KisVisualRectangleSelectorShape::borderMirrored) { mask = mask.subtracted(QRegion(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2))); } return mask; } -void KisVisualRectangleSelectorShape::resizeEvent(QResizeEvent *) -{ - //qDebug() << this << "KisVisualRectangleSelectorShape::resizeEvent"; - forceImageUpdate(); -} void KisVisualRectangleSelectorShape::drawCursor() { //qDebug() << this << "KisVisualRectangleSelectorShape::drawCursor: image needs update" << imagesNeedUpdate(); QPointF cursorPoint = convertShapeCoordinateToWidgetCoordinate(getCursorPosition()); QImage fullSelector = getImageMap(); QColor col = getColorFromConverter(getCurrentColor()); QPainter painter; painter.begin(&fullSelector); painter.setRenderHint(QPainter::Antialiasing); //QPainterPath path; QBrush fill; fill.setStyle(Qt::SolidPattern); int cursorwidth = 5; QRect rect(cursorPoint.toPoint().x()-cursorwidth,cursorPoint.toPoint().y()-cursorwidth, cursorwidth*2,cursorwidth*2); if (m_type==KisVisualRectangleSelectorShape::vertical){ int x = ( cursorPoint.x()-(width()/2)+1 ); int y = ( cursorPoint.y()-cursorwidth ); rect.setCoords(x, y, x+width()-2, y+(cursorwidth*2)); painter.save(); painter.setCompositionMode(QPainter::CompositionMode_Clear); QPen pen; pen.setWidth(5); painter.setPen(pen); painter.drawLine(QLine(QPoint(0.0,0.0), QPoint(0.0,height()))); painter.drawLine(QLine(QPoint(width(),0.0), QPoint(width(),height()))); painter.restore(); } else { int x = cursorPoint.x()-cursorwidth; int y = cursorPoint.y()-(height()/2)+1; rect.setCoords(x, y, x+(cursorwidth*2), y+cursorwidth-2); } QRectF innerRect(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2)); if (getDimensions() == KisVisualColorSelectorShape::onedimensional && m_type!=KisVisualRectangleSelectorShape::border && m_type!=KisVisualRectangleSelectorShape::borderMirrored) { painter.setPen(Qt::white); fill.setColor(Qt::white); painter.setBrush(fill); painter.drawRect(rect); //set filter conversion! fill.setColor(col); painter.setPen(Qt::black); painter.setBrush(fill); rect.setCoords(rect.topLeft().x()+1, rect.topLeft().y()+1, rect.topLeft().x()+rect.width()-2, rect.topLeft().y()+rect.height()-2); painter.drawRect(rect); }else if(m_type==KisVisualRectangleSelectorShape::borderMirrored){ painter.setPen(Qt::white); fill.setColor(Qt::white); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth); QPoint mirror(innerRect.center().x()+(innerRect.center().x()-cursorPoint.x()),cursorPoint.y()); painter.drawEllipse(mirror, cursorwidth, cursorwidth); fill.setColor(col); painter.setPen(Qt::black); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth-1, cursorwidth-1); painter.drawEllipse(mirror, cursorwidth-1, cursorwidth-1); } else { painter.save(); painter.setCompositionMode(QPainter::CompositionMode_Clear); QPen pen; pen.setWidth(5); painter.setPen(pen); painter.drawRect(QRect(0,0,width(),height())); painter.restore(); painter.setPen(Qt::white); fill.setColor(Qt::white); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth); fill.setColor(col); painter.setPen(Qt::black); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth-1.0, cursorwidth-1.0); } painter.end(); setFullImage(fullSelector); } diff --git a/libs/widgets/KisVisualRectangleSelectorShape.h b/libs/widgets/KisVisualRectangleSelectorShape.h index 4fe72d216c..dada1fd036 100644 --- a/libs/widgets/KisVisualRectangleSelectorShape.h +++ b/libs/widgets/KisVisualRectangleSelectorShape.h @@ -1,71 +1,68 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * 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. */ #ifndef KIS_VISUAL_RECTANGLE_SELECTOR_SHAPE_H #define KIS_VISUAL_RECTANGLE_SELECTOR_SHAPE_H #include #include #include #include #include #include #include #include "KoColorDisplayRendererInterface.h" #include "KisColorSelectorConfiguration.h" #include "KisVisualColorSelectorShape.h" class KisVisualRectangleSelectorShape : public KisVisualColorSelectorShape { Q_OBJECT public: enum singelDTypes{vertical, horizontal, border, borderMirrored}; explicit KisVisualRectangleSelectorShape(QWidget *parent, Dimensions dimension, - ColorModel model, const KoColorSpace *cs, int channel1, int channel2, const KoColorDisplayRendererInterface *displayRenderer = KoDumbColorDisplayRenderer::instance(), int width=20, KisVisualRectangleSelectorShape::singelDTypes d = KisVisualRectangleSelectorShape::vertical ); ~KisVisualRectangleSelectorShape() override; void setBorderWidth(int width) override; /** * @brief getSpaceForSquare * @param geom the full widget rectangle * @return rectangle with enough space for second widget */ QRect getSpaceForSquare(QRect geom) override; QRect getSpaceForCircle(QRect geom) override; QRect getSpaceForTriangle(QRect geom) override; -protected: - void resizeEvent(QResizeEvent *) override; private: - QPointF convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) override; - QPointF convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) override; + QPointF convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) const override; + QPointF convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) const override; singelDTypes m_type; int m_barWidth; QRegion getMaskMap() override; void drawCursor() override; }; #endif diff --git a/libs/widgets/KisVisualTriangleSelectorShape.cpp b/libs/widgets/KisVisualTriangleSelectorShape.cpp index 91e4741801..18cc17c0f1 100644 --- a/libs/widgets/KisVisualTriangleSelectorShape.cpp +++ b/libs/widgets/KisVisualTriangleSelectorShape.cpp @@ -1,206 +1,205 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * 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 "KisVisualTriangleSelectorShape.h" #include #include #include #include #include #include #include #include #include #include #include #include #include "KoColorConversions.h" #include "KoColorDisplayRendererInterface.h" #include "KoChannelInfo.h" #include #include #include "kis_signal_compressor.h" #include "kis_debug.h" #include "kis_global.h" KisVisualTriangleSelectorShape::KisVisualTriangleSelectorShape(QWidget *parent, Dimensions dimension, - ColorModel model, const KoColorSpace *cs, int channel1, int channel2, const KoColorDisplayRendererInterface *displayRenderer, int barwidth) - : KisVisualColorSelectorShape(parent, dimension, model, cs, channel1, channel2, displayRenderer) + : KisVisualColorSelectorShape(parent, dimension, cs, channel1, channel2, displayRenderer) { //qDebug() << "creating KisVisualTriangleSelectorShape" << this; m_barWidth = barwidth; setTriangle(); } KisVisualTriangleSelectorShape::~KisVisualTriangleSelectorShape() { //qDebug() << "deleting KisVisualTriangleSelectorShape" << this; } void KisVisualTriangleSelectorShape::setBorderWidth(int width) { m_barWidth = width; } QRect KisVisualTriangleSelectorShape::getSpaceForSquare(QRect geom) { return geom; } QRect KisVisualTriangleSelectorShape::getSpaceForCircle(QRect geom) { return geom; } QRect KisVisualTriangleSelectorShape::getSpaceForTriangle(QRect geom) { return geom; } void KisVisualTriangleSelectorShape::setTriangle() { QPoint apex = QPoint (width()*0.5,0); QPolygon triangle; triangle<< QPoint(0,height()) << apex << QPoint(width(),height()) << QPoint(0,height()); m_triangle = triangle; QLineF a(triangle.at(0),triangle.at(1)); QLineF b(triangle.at(0),triangle.at(2)); QLineF ap(triangle.at(2), a.pointAt(0.5)); QLineF bp(triangle.at(1), b.pointAt(0.5)); QPointF intersect; ap.intersect(bp,&intersect); m_center = intersect; QLineF r(triangle.at(0), intersect); m_radius = r.length(); } -QPointF KisVisualTriangleSelectorShape::convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) +QPointF KisVisualTriangleSelectorShape::convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) const { qreal offset=7.0;//the offset is so we get a nice little border that allows selecting extreme colors better. qreal yOffset = (cos(kisDegreesToRadians(30))*offset)*2; qreal xOffset = qFloor(sin(kisDegreesToRadians(30))*offset); qreal y = qMax(qMin((coordinate.y()*(height()-yOffset-offset))+yOffset, (qreal)height()-offset),yOffset); qreal triWidth = width(); qreal horizontalLineLength = ((y-yOffset)*(2./sqrt(3.))); qreal horizontalLineStart = (triWidth*0.5)-(horizontalLineLength*0.5); qreal relativeX = qMin(coordinate.x()*(horizontalLineLength), horizontalLineLength); qreal x = qMax(relativeX + horizontalLineStart + xOffset, horizontalLineStart+xOffset); if (y<=yOffset){ x = 0.5*width(); } return QPointF(x,y); } -QPointF KisVisualTriangleSelectorShape::convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) +QPointF KisVisualTriangleSelectorShape::convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) const { //default implementation: gotten from the kotrianglecolorselector/kis_color_selector_triangle. qreal x = 0.5; qreal y = 0.5; qreal offset=7.0; //the offset is so we get a nice little border that allows selecting extreme colors better. qreal yOffset = (cos(kisDegreesToRadians(30))*offset)*2; qreal xOffset = qFloor(sin(kisDegreesToRadians(30))*offset); y = qMin(qMax((qreal)coordinate.y()-yOffset, 0.0)/(height()-yOffset-offset), 1.0); qreal triWidth = width(); qreal horizontalLineLength = ((qreal)coordinate.y()-yOffset)*(2./sqrt(3.)); qreal horizontalLineStart = (triWidth*0.5)-(horizontalLineLength*0.5); qreal relativeX = qMax((qreal)coordinate.x()-xOffset-horizontalLineStart,0.0); x = qMin(relativeX/horizontalLineLength, 1.0); if (coordinate.y()<=yOffset){ x = 0.5; } return QPointF(x, y); } QRegion KisVisualTriangleSelectorShape::getMaskMap() { QRegion mask = QRegion(m_triangle); //QRegion mask = QRegion(); //if (getDimensions()==KisVisualColorSelectorShape::onedimensional) { // mask = mask.subtracted(QRegion(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2))); //} return mask; } -void KisVisualTriangleSelectorShape::resizeEvent(QResizeEvent *) +void KisVisualTriangleSelectorShape::resizeEvent(QResizeEvent *e) { //qDebug() << this << "KisVisualTriangleSelectorShape::resizeEvent(QResizeEvent *)"; setTriangle(); - forceImageUpdate(); + KisVisualColorSelectorShape::resizeEvent(e); } void KisVisualTriangleSelectorShape::drawCursor() { //qDebug() << this << "KisVisualTriangleSelectorShape::drawCursor: image needs update" << imagesNeedUpdate(); QPointF cursorPoint = convertShapeCoordinateToWidgetCoordinate(getCursorPosition()); QImage fullSelector = getImageMap(); QColor col = getColorFromConverter(getCurrentColor()); QPainter painter; painter.begin(&fullSelector); painter.setRenderHint(QPainter::Antialiasing); painter.save(); painter.setCompositionMode(QPainter::CompositionMode_Clear); QPen pen; pen.setWidth(10); painter.setPen(pen); painter.drawPolygon(m_triangle); painter.restore(); //QPainterPath path; QBrush fill; fill.setStyle(Qt::SolidPattern); int cursorwidth = 5; //QRect innerRect(m_barWidth, m_barWidth, width()-(m_barWidth*2), height()-(m_barWidth*2)); /*if(m_type==KisVisualTriangleSelectorShape::borderMirrored){ painter.setPen(Qt::white); fill.setColor(Qt::white); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth); QPoint mirror(innerRect.center().x()+(innerRect.center().x()-cursorPoint.x()),cursorPoint.y()); painter.drawEllipse(mirror, cursorwidth, cursorwidth); fill.setColor(col); painter.setPen(Qt::black); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth-1, cursorwidth-1); painter.drawEllipse(mirror, cursorwidth-1, cursorwidth-1); } else {*/ painter.setPen(Qt::white); fill.setColor(Qt::white); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth, cursorwidth); fill.setColor(col); painter.setPen(Qt::black); painter.setBrush(fill); painter.drawEllipse(cursorPoint, cursorwidth-1.0, cursorwidth-1.0); //} painter.end(); setFullImage(fullSelector); } diff --git a/libs/widgets/KisVisualTriangleSelectorShape.h b/libs/widgets/KisVisualTriangleSelectorShape.h index fc4b68e82e..8e7ce92526 100644 --- a/libs/widgets/KisVisualTriangleSelectorShape.h +++ b/libs/widgets/KisVisualTriangleSelectorShape.h @@ -1,77 +1,76 @@ /* * Copyright (C) Wolthera van Hovell tot Westerflier , (C) 2016 * * 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. */ #ifndef KIS_VISUAL_TRIANGLE_SELECTOR_SHAPE_H #define KIS_VISUAL_TRIANGLE_SELECTOR_SHAPE_H #include #include #include #include #include #include #include #include "KoColorDisplayRendererInterface.h" #include "KisColorSelectorConfiguration.h" #include "KisVisualColorSelectorShape.h" class KisVisualTriangleSelectorShape : public KisVisualColorSelectorShape { Q_OBJECT public: enum singelDTypes{border, borderMirrored}; explicit KisVisualTriangleSelectorShape(QWidget *parent, Dimensions dimension, - ColorModel model, const KoColorSpace *cs, int channel1, int channel2, const KoColorDisplayRendererInterface *displayRenderer = KoDumbColorDisplayRenderer::instance(), int barwidth=20 ); ~KisVisualTriangleSelectorShape() override; void setBorderWidth(int width) override; void setTriangle(); /** * @brief getSpaceForSquare * @param geom the full widget rectangle * @return rectangle with enough space for second widget */ QRect getSpaceForSquare(QRect geom) override; QRect getSpaceForCircle(QRect geom) override; QRect getSpaceForTriangle(QRect geom) override; protected: - void resizeEvent(QResizeEvent *) override; + void resizeEvent(QResizeEvent *e) override; private: - QPointF convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) override; - QPointF convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) override; + QPointF convertShapeCoordinateToWidgetCoordinate(QPointF coordinate) const override; + QPointF convertWidgetCoordinateToShapeCoordinate(QPoint coordinate) const override; singelDTypes m_type; int m_barWidth; QPolygon m_triangle; QPointF m_center; qreal m_radius; QRegion getMaskMap() override; void drawCursor() override; }; #endif diff --git a/libs/widgets/WdgDlgInternalColorSelector.ui b/libs/widgets/WdgDlgInternalColorSelector.ui index 2e56498ffc..6a17a05e6a 100644 --- a/libs/widgets/WdgDlgInternalColorSelector.ui +++ b/libs/widgets/WdgDlgInternalColorSelector.ui @@ -1,279 +1,273 @@ WdgDlgInternalColorSelector 0 0 505 483 Dialog 0 0 90 90 0 0 - - - 0 - 50 - - QFrame::StyledPanel QFrame::Sunken 2 2 0 0 0 0 0 0 0 50 70 0 0 25 25 0 0 90 0 0 0 0 50 0 0 0 true 0 0 50 50 Qt::Horizontal QDialogButtonBox::Cancel|QDialogButtonBox::Ok KisPopupButton QPushButton
kis_popup_button.h
KisPaletteView QTableView
kis_palette_view.h
KisSpinboxColorSelector QWidget
kis_spinbox_color_selector.h
1
KisVisualColorSelector QWidget
KisVisualColorSelector.h
1
KoColorPatch QWidget
KoColorPatch.h
1
KisPaletteComboBox QComboBox
KisPaletteComboBox.h
buttonBox accepted() WdgDlgInternalColorSelector accept() 248 254 157 274 buttonBox rejected() WdgDlgInternalColorSelector reject() 316 260 286 274
diff --git a/plugins/color/lcms2engine/colorprofiles/IccColorProfile.cpp b/plugins/color/lcms2engine/colorprofiles/IccColorProfile.cpp index 7a4802dc53..f463f52438 100644 --- a/plugins/color/lcms2engine/colorprofiles/IccColorProfile.cpp +++ b/plugins/color/lcms2engine/colorprofiles/IccColorProfile.cpp @@ -1,383 +1,389 @@ /* * Copyright (c) 2007 Cyrille Berger * * 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; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "IccColorProfile.h" #include #include #include #include #include "QDebug" #include "LcmsColorProfileContainer.h" #include "lcms2.h" struct IccColorProfile::Data::Private { QByteArray rawData; }; IccColorProfile::Data::Data() : d(new Private) { } IccColorProfile::Data::Data(const QByteArray &rawData) : d(new Private) { d->rawData = rawData; } IccColorProfile::Data::~Data() { } QByteArray IccColorProfile::Data::rawData() { return d->rawData; } void IccColorProfile::Data::setRawData(const QByteArray &rawData) { d->rawData = rawData; } IccColorProfile::Container::Container() { } IccColorProfile::Container::~Container() { } struct IccColorProfile::Private { struct Shared { QScopedPointer data; QScopedPointer lcmsProfile; QVector uiMinMaxes; }; QSharedPointer shared; }; IccColorProfile::IccColorProfile(const QString &fileName) : KoColorProfile(fileName), d(new Private) { // QSharedPointer lacks a reset in Qt 4.x d->shared = QSharedPointer(new Private::Shared()); d->shared->data.reset(new Data()); } IccColorProfile::IccColorProfile(const QByteArray &rawData) : KoColorProfile(QString()), d(new Private) { d->shared = QSharedPointer(new Private::Shared()); d->shared->data.reset(new Data()); setRawData(rawData); init(); } IccColorProfile::IccColorProfile(const IccColorProfile &rhs) : KoColorProfile(rhs) , d(new Private(*rhs.d)) { Q_ASSERT(d->shared); } IccColorProfile::~IccColorProfile() { Q_ASSERT(d->shared); } KoColorProfile *IccColorProfile::clone() const { return new IccColorProfile(*this); } QByteArray IccColorProfile::rawData() const { return d->shared->data->rawData(); } void IccColorProfile::setRawData(const QByteArray &rawData) { d->shared->data->setRawData(rawData); } bool IccColorProfile::valid() const { if (d->shared->lcmsProfile) { return d->shared->lcmsProfile->valid(); } return false; } float IccColorProfile::version() const { if (d->shared->lcmsProfile) { return d->shared->lcmsProfile->version(); } return 0.0; } bool IccColorProfile::isSuitableForOutput() const { if (d->shared->lcmsProfile) { return d->shared->lcmsProfile->isSuitableForOutput(); } return false; } bool IccColorProfile::isSuitableForPrinting() const { if (d->shared->lcmsProfile) { return d->shared->lcmsProfile->isSuitableForPrinting(); } return false; } bool IccColorProfile::isSuitableForDisplay() const { if (d->shared->lcmsProfile) { return d->shared->lcmsProfile->isSuitableForDisplay(); } return false; } bool IccColorProfile::supportsPerceptual() const { if (d->shared->lcmsProfile) { return d->shared->lcmsProfile->supportsPerceptual(); } return false; } bool IccColorProfile::supportsSaturation() const { if (d->shared->lcmsProfile) { return d->shared->lcmsProfile->supportsSaturation(); } return false; } bool IccColorProfile::supportsAbsolute() const { if (d->shared->lcmsProfile) { return d->shared->lcmsProfile->supportsAbsolute(); } return false; } bool IccColorProfile::supportsRelative() const { if (d->shared->lcmsProfile) { return d->shared->lcmsProfile->supportsRelative(); } return false; } bool IccColorProfile::hasColorants() const { if (d->shared->lcmsProfile) { return d->shared->lcmsProfile->hasColorants(); } return false; } bool IccColorProfile::hasTRC() const { if (d->shared->lcmsProfile) return d->shared->lcmsProfile->hasTRC(); return false; } +bool IccColorProfile::isLinear() const +{ + if (d->shared->lcmsProfile) + return d->shared->lcmsProfile->isLinear(); + return false; +} QVector IccColorProfile::getColorantsXYZ() const { if (d->shared->lcmsProfile) { return d->shared->lcmsProfile->getColorantsXYZ(); } return QVector(9); } QVector IccColorProfile::getColorantsxyY() const { if (d->shared->lcmsProfile) { return d->shared->lcmsProfile->getColorantsxyY(); } return QVector(9); } QVector IccColorProfile::getWhitePointXYZ() const { QVector d50Dummy(3); d50Dummy << 0.9642 << 1.0000 << 0.8249; if (d->shared->lcmsProfile) { return d->shared->lcmsProfile->getWhitePointXYZ(); } return d50Dummy; } QVector IccColorProfile::getWhitePointxyY() const { QVector d50Dummy(3); d50Dummy << 0.34773 << 0.35952 << 1.0; if (d->shared->lcmsProfile) { return d->shared->lcmsProfile->getWhitePointxyY(); } return d50Dummy; } QVector IccColorProfile::getEstimatedTRC() const { QVector dummy(3); dummy.fill(2.2);//estimated sRGB trc. if (d->shared->lcmsProfile) { return d->shared->lcmsProfile->getEstimatedTRC(); } return dummy; } void IccColorProfile::linearizeFloatValue(QVector & Value) const { if (d->shared->lcmsProfile) d->shared->lcmsProfile->LinearizeFloatValue(Value); } void IccColorProfile::delinearizeFloatValue(QVector & Value) const { if (d->shared->lcmsProfile) d->shared->lcmsProfile->DelinearizeFloatValue(Value); } void IccColorProfile::linearizeFloatValueFast(QVector & Value) const { if (d->shared->lcmsProfile) d->shared->lcmsProfile->LinearizeFloatValueFast(Value); } void IccColorProfile::delinearizeFloatValueFast(QVector &Value) const { if (d->shared->lcmsProfile) d->shared->lcmsProfile->DelinearizeFloatValueFast(Value); } QByteArray IccColorProfile::uniqueId() const { QByteArray dummy; if (d->shared->lcmsProfile) { dummy = d->shared->lcmsProfile->getProfileUniqueId(); } return dummy; } bool IccColorProfile::load() { QFile file(fileName()); file.open(QIODevice::ReadOnly); QByteArray rawData = file.readAll(); setRawData(rawData); file.close(); if (init()) { return true; } qWarning() << "Failed to load profile from " << fileName(); return false; } bool IccColorProfile::save() { return false; } bool IccColorProfile::init() { if (!d->shared->lcmsProfile) { d->shared->lcmsProfile.reset(new LcmsColorProfileContainer(d->shared->data.data())); } if (d->shared->lcmsProfile->init()) { setName(d->shared->lcmsProfile->name()); setInfo(d->shared->lcmsProfile->info()); setManufacturer(d->shared->lcmsProfile->manufacturer()); setCopyright(d->shared->lcmsProfile->copyright()); if (d->shared->lcmsProfile->valid()) { calculateFloatUIMinMax(); } return true; } else { return false; } } LcmsColorProfileContainer *IccColorProfile::asLcms() const { Q_ASSERT(d->shared->lcmsProfile); return d->shared->lcmsProfile.data(); } bool IccColorProfile::operator==(const KoColorProfile &rhs) const { const IccColorProfile *rhsIcc = dynamic_cast(&rhs); if (rhsIcc) { return d->shared == rhsIcc->d->shared; } return false; } const QVector &IccColorProfile::getFloatUIMinMax(void) const { Q_ASSERT(!d->shared->uiMinMaxes.isEmpty()); return d->shared->uiMinMaxes; } void IccColorProfile::calculateFloatUIMinMax(void) { QVector &ret = d->shared->uiMinMaxes; cmsHPROFILE cprofile = d->shared->lcmsProfile->lcmsProfile(); Q_ASSERT(cprofile); cmsColorSpaceSignature color_space_sig = cmsGetColorSpace(cprofile); unsigned int num_channels = cmsChannelsOf(color_space_sig); unsigned int color_space_mask = _cmsLCMScolorSpace(color_space_sig); Q_ASSERT(num_channels >= 1 && num_channels <= 4); // num_channels==1 is for grayscale, we need to handle it Q_ASSERT(color_space_mask); // to try to find the max range of float/doubles for this profile, // pass in min/max int and make the profile convert that // this is far from perfect, we need a better way, if possible to get the "bounds" of a profile uint16_t in_min_pixel[4] = {0, 0, 0, 0}; uint16_t in_max_pixel[4] = {0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF}; qreal out_min_pixel[4] = {0, 0, 0, 0}; qreal out_max_pixel[4] = {0, 0, 0, 0}; cmsHTRANSFORM trans = cmsCreateTransform( cprofile, (COLORSPACE_SH(color_space_mask) | CHANNELS_SH(num_channels) | BYTES_SH(2)), cprofile, (COLORSPACE_SH(color_space_mask) | FLOAT_SH(1) | CHANNELS_SH(num_channels) | BYTES_SH(0)), //NOTE THAT 'BYTES' FIELD IS SET TO ZERO ON DLB because 8 bytes overflows the bitfield INTENT_PERCEPTUAL, 0); // does the intent matter in this case? if (trans) { cmsDoTransform(trans, in_min_pixel, out_min_pixel, 1); cmsDoTransform(trans, in_max_pixel, out_max_pixel, 1); cmsDeleteTransform(trans); }//else, we'll just default to [0..1] below ret.resize(num_channels); for (unsigned int i = 0; i < num_channels; ++i) { if (out_min_pixel[i] < out_max_pixel[i]) { ret[i].minVal = out_min_pixel[i]; ret[i].maxVal = out_max_pixel[i]; } else { // apparently we can't even guarantee that converted_to_double(0x0000) < converted_to_double(0xFFFF) // assume [0..1] in such cases // we need to find a really solid way of determining the bounds of a profile, if possible ret[i].minVal = 0; ret[i].maxVal = 1; } } } diff --git a/plugins/color/lcms2engine/colorprofiles/IccColorProfile.h b/plugins/color/lcms2engine/colorprofiles/IccColorProfile.h index 948876ba66..3eb3ace963 100644 --- a/plugins/color/lcms2engine/colorprofiles/IccColorProfile.h +++ b/plugins/color/lcms2engine/colorprofiles/IccColorProfile.h @@ -1,143 +1,144 @@ /* * Copyright (c) 2007 Cyrille Berger * * 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; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KO_ICC_COLOR_PROFILE_H_ #define _KO_ICC_COLOR_PROFILE_H_ #include "KoColorProfile.h" #include "KoChannelInfo.h" class LcmsColorProfileContainer; /** * This class contains an ICC color profile. */ class IccColorProfile : public KoColorProfile { public: using KoColorProfile::save; /** * Contains the data associated with a profile. This is * shared through internal representation. */ class Data { public: Data(); explicit Data(const QByteArray &rawData); ~Data(); QByteArray rawData(); void setRawData(const QByteArray &); private: struct Private; QScopedPointer const d; }; /** * This class should be used to wrap the ICC profile * representation coming from various CMS engine. */ class Container { public: Container(); virtual ~Container(); public: virtual QString name() const = 0; virtual QString info() const = 0; virtual QString manufacturer() const = 0; virtual QString copyright() const = 0; virtual bool valid() const = 0; virtual bool isSuitableForOutput() const = 0; virtual bool isSuitableForPrinting() const = 0; virtual bool isSuitableForDisplay() const = 0; virtual bool hasColorants() const = 0; virtual QVector getColorantsXYZ() const = 0; virtual QVector getColorantsxyY() const = 0; virtual QVector getWhitePointXYZ() const = 0; virtual QVector getWhitePointxyY() const = 0; virtual QVector getEstimatedTRC() const = 0; virtual QByteArray getProfileUniqueId() const = 0; }; public: explicit IccColorProfile(const QString &fileName = QString()); explicit IccColorProfile(const QByteArray &rawData); IccColorProfile(const IccColorProfile &rhs); ~IccColorProfile() override; KoColorProfile *clone() const override; bool load() override; virtual bool save(); /** * @return an array with the raw data of the profile */ QByteArray rawData() const override; bool valid() const override; float version() const override; bool isSuitableForOutput() const override; bool isSuitableForPrinting() const override; bool isSuitableForDisplay() const override; bool supportsPerceptual() const override; bool supportsSaturation() const override; bool supportsAbsolute() const override; bool supportsRelative() const override; bool hasColorants() const override; bool hasTRC() const override; + bool isLinear() const override; QVector getColorantsXYZ() const override; QVector getColorantsxyY() const override; QVector getWhitePointXYZ() const override; QVector getWhitePointxyY() const override; QVector getEstimatedTRC() const override; void linearizeFloatValue(QVector & Value) const override; void delinearizeFloatValue(QVector & Value) const override; void linearizeFloatValueFast(QVector & Value) const override; void delinearizeFloatValueFast(QVector & Value) const override; QByteArray uniqueId() const override; bool operator==(const KoColorProfile &) const override; QString type() const override { return "icc"; } /** * Returns the set of min/maxes for each channel in this profile. * These (sometimes approximate) min and maxes are suitable * for UI building. * Furthermore, then only apply to the floating point uses of this profile, * and not the integer variants. */ const QVector &getFloatUIMinMax(void) const; protected: void setRawData(const QByteArray &rawData); public: LcmsColorProfileContainer *asLcms() const; protected: bool init(); void calculateFloatUIMinMax(void); private: struct Private; QScopedPointer d; }; #endif diff --git a/plugins/color/lcms2engine/colorprofiles/LcmsColorProfileContainer.cpp b/plugins/color/lcms2engine/colorprofiles/LcmsColorProfileContainer.cpp index 025212cf2c..8172fa50f7 100644 --- a/plugins/color/lcms2engine/colorprofiles/LcmsColorProfileContainer.cpp +++ b/plugins/color/lcms2engine/colorprofiles/LcmsColorProfileContainer.cpp @@ -1,560 +1,569 @@ /* * This file is part of the KDE project * Copyright (c) 2000 Matthias Elter * 2001 John Califf * 2004 Boudewijn Rempt * Copyright (c) 2007 Thomas Zander * Copyright (c) 2007 Adrian Page * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #include "LcmsColorProfileContainer.h" #include #include #include #include #include #include "kis_debug.h" class LcmsColorProfileContainer::Private { public: cmsHPROFILE profile; cmsColorSpaceSignature colorSpaceSignature; cmsProfileClassSignature deviceClass; QString productDescription; QString manufacturer; QString copyright; QString name; float version; IccColorProfile::Data *data {0}; bool valid {false}; bool suitableForOutput {false}; bool hasColorants; bool hasTRC; + bool isLinear {false}; bool adaptedFromD50; cmsCIEXYZ mediaWhitePoint; cmsCIExyY whitePoint; cmsCIEXYZTRIPLE colorants; cmsToneCurve *redTRC {0}; cmsToneCurve *greenTRC {0}; cmsToneCurve *blueTRC {0}; cmsToneCurve *grayTRC {0}; cmsToneCurve *redTRCReverse {0}; cmsToneCurve *greenTRCReverse {0}; cmsToneCurve *blueTRCReverse {0}; cmsToneCurve *grayTRCReverse {0}; cmsUInt32Number defaultIntent; bool isPerceptualCLUT; bool isRelativeCLUT; bool isAbsoluteCLUT; bool isSaturationCLUT; bool isMatrixShaper; QByteArray uniqueId; }; LcmsColorProfileContainer::LcmsColorProfileContainer() : d(new Private()) { d->profile = 0; } LcmsColorProfileContainer::LcmsColorProfileContainer(IccColorProfile::Data *data) : d(new Private()) { d->data = data; d->profile = 0; init(); } QByteArray LcmsColorProfileContainer::lcmsProfileToByteArray(const cmsHPROFILE profile) { cmsUInt32Number bytesNeeded = 0; // Make a raw data image ready for saving cmsSaveProfileToMem(profile, 0, &bytesNeeded); // calc size QByteArray rawData; rawData.resize(bytesNeeded); if (rawData.size() >= (int)bytesNeeded) { cmsSaveProfileToMem(profile, rawData.data(), &bytesNeeded); // fill buffer } else { qWarning() << "Couldn't resize the profile buffer, system is probably running out of memory."; rawData.resize(0); } return rawData; } IccColorProfile *LcmsColorProfileContainer::createFromLcmsProfile(const cmsHPROFILE profile) { IccColorProfile *iccprofile = new IccColorProfile(lcmsProfileToByteArray(profile)); cmsCloseProfile(profile); return iccprofile; } LcmsColorProfileContainer::~LcmsColorProfileContainer() { cmsCloseProfile(d->profile); delete d; } #define _BUFFER_SIZE_ 1000 bool LcmsColorProfileContainer::init() { if (d->profile) { cmsCloseProfile(d->profile); } d->profile = cmsOpenProfileFromMem((void *)d->data->rawData().constData(), d->data->rawData().size()); #ifndef NDEBUG if (d->data->rawData().size() == 4096) { qWarning() << "Profile has a size of 4096, which is suspicious and indicates a possible misuse of QIODevice::read(int), check your code."; } #endif if (d->profile) { wchar_t buffer[_BUFFER_SIZE_]; d->colorSpaceSignature = cmsGetColorSpace(d->profile); d->deviceClass = cmsGetDeviceClass(d->profile); cmsGetProfileInfo(d->profile, cmsInfoDescription, cmsNoLanguage, cmsNoCountry, buffer, _BUFFER_SIZE_); d->name = QString::fromWCharArray(buffer); //apparently this should give us a localised string??? Not sure about this. cmsGetProfileInfo(d->profile, cmsInfoModel, cmsNoLanguage, cmsNoCountry, buffer, _BUFFER_SIZE_); d->productDescription = QString::fromWCharArray(buffer); cmsGetProfileInfo(d->profile, cmsInfoManufacturer, cmsNoLanguage, cmsNoCountry, buffer, _BUFFER_SIZE_); d->manufacturer = QString::fromWCharArray(buffer); cmsGetProfileInfo(d->profile, cmsInfoCopyright, cmsNoLanguage, cmsNoCountry, buffer, _BUFFER_SIZE_); d->copyright = QString::fromWCharArray(buffer); cmsProfileClassSignature profile_class; profile_class = cmsGetDeviceClass(d->profile); d->valid = (profile_class != cmsSigNamedColorClass); //This is where obtain the whitepoint, and convert it to the actual white point of the profile in the case a Chromatic adaption tag is //present. This is necessary for profiles following the v4 spec. cmsCIEXYZ baseMediaWhitePoint;//dummy to hold copy of mediawhitepoint if this is modified by chromatic adaption. if (cmsIsTag(d->profile, cmsSigMediaWhitePointTag)) { d->mediaWhitePoint = *((cmsCIEXYZ *)cmsReadTag(d->profile, cmsSigMediaWhitePointTag)); baseMediaWhitePoint = d->mediaWhitePoint; cmsXYZ2xyY(&d->whitePoint, &d->mediaWhitePoint); if (cmsIsTag(d->profile, cmsSigChromaticAdaptationTag)) { //the chromatic adaption tag represent a matrix from the actual white point of the profile to D50. cmsCIEXYZ *CAM1 = (cmsCIEXYZ *)cmsReadTag(d->profile, cmsSigChromaticAdaptationTag); //We first put all our data into structures we can manipulate. double d3dummy [3] = {d->mediaWhitePoint.X, d->mediaWhitePoint.Y, d->mediaWhitePoint.Z}; QGenericMatrix<1, 3, double> whitePointMatrix(d3dummy); QTransform invertDummy(CAM1[0].X, CAM1[0].Y, CAM1[0].Z, CAM1[1].X, CAM1[1].Y, CAM1[1].Z, CAM1[2].X, CAM1[2].Y, CAM1[2].Z); //we then abuse QTransform's invert function because it probably does matrix invertion 20 times better than I can program. //if the matrix is uninvertable, invertedDummy will be an identity matrix, which for us means that it won't give any noticeble //effect when we start multiplying. QTransform invertedDummy = invertDummy.inverted(); //we then put the QTransform into a generic 3x3 matrix. double d9dummy [9] = {invertedDummy.m11(), invertedDummy.m12(), invertedDummy.m13(), invertedDummy.m21(), invertedDummy.m22(), invertedDummy.m23(), invertedDummy.m31(), invertedDummy.m32(), invertedDummy.m33() }; QGenericMatrix<3, 3, double> chromaticAdaptionMatrix(d9dummy); //multiplying our inverted adaption matrix with the whitepoint gives us the right whitepoint. QGenericMatrix<1, 3, double> result = chromaticAdaptionMatrix * whitePointMatrix; //and then we pour the matrix into the whitepoint variable. Generic matrix does row/column for indices even though it //uses column/row for initialising. d->mediaWhitePoint.X = result(0, 0); d->mediaWhitePoint.Y = result(1, 0); d->mediaWhitePoint.Z = result(2, 0); cmsXYZ2xyY(&d->whitePoint, &d->mediaWhitePoint); } } //This is for RGB profiles, but it only works for matrix profiles. Need to design it to work with non-matrix profiles. if (cmsIsTag(d->profile, cmsSigRedColorantTag)) { cmsCIEXYZTRIPLE tempColorants; tempColorants.Red = *((cmsCIEXYZ *)cmsReadTag(d->profile, cmsSigRedColorantTag)); tempColorants.Green = *((cmsCIEXYZ *)cmsReadTag(d->profile, cmsSigGreenColorantTag)); tempColorants.Blue = *((cmsCIEXYZ *)cmsReadTag(d->profile, cmsSigBlueColorantTag)); //convert to d65, this is useless. cmsAdaptToIlluminant(&d->colorants.Red, &baseMediaWhitePoint, &d->mediaWhitePoint, &tempColorants.Red); cmsAdaptToIlluminant(&d->colorants.Green, &baseMediaWhitePoint, &d->mediaWhitePoint, &tempColorants.Green); cmsAdaptToIlluminant(&d->colorants.Blue, &baseMediaWhitePoint, &d->mediaWhitePoint, &tempColorants.Blue); //d->colorants = tempColorants; d->hasColorants = true; } else { //qDebug()<name<<": has no colorants"; d->hasColorants = false; } //retrieve TRC. if (cmsIsTag(d->profile, cmsSigRedTRCTag) && cmsIsTag(d->profile, cmsSigBlueTRCTag) && cmsIsTag(d->profile, cmsSigGreenTRCTag)) { d->redTRC = ((cmsToneCurve *)cmsReadTag (d->profile, cmsSigRedTRCTag)); d->greenTRC = ((cmsToneCurve *)cmsReadTag (d->profile, cmsSigGreenTRCTag)); d->blueTRC = ((cmsToneCurve *)cmsReadTag (d->profile, cmsSigBlueTRCTag)); if (d->redTRC) d->redTRCReverse = cmsReverseToneCurve(d->redTRC); if (d->greenTRC) d->greenTRCReverse = cmsReverseToneCurve(d->greenTRC); if (d->blueTRC) d->blueTRCReverse = cmsReverseToneCurve(d->blueTRC); d->hasTRC = (d->redTRC && d->greenTRC && d->blueTRC && d->redTRCReverse && d->greenTRCReverse && d->blueTRCReverse); + if (d->hasTRC) d->isLinear = cmsIsToneCurveLinear(d->redTRC) + && cmsIsToneCurveLinear(d->greenTRC) + && cmsIsToneCurveLinear(d->blueTRC); } else if (cmsIsTag(d->profile, cmsSigGrayTRCTag)) { d->grayTRC = ((cmsToneCurve *)cmsReadTag (d->profile, cmsSigGrayTRCTag)); if (d->grayTRC) d->grayTRCReverse = cmsReverseToneCurve(d->grayTRC); d->hasTRC = (d->grayTRC && d->grayTRCReverse); + if (d->hasTRC) d->isLinear = cmsIsToneCurveLinear(d->grayTRC); } else { d->hasTRC = false; } // Check if the profile can convert (something->this) d->suitableForOutput = cmsIsMatrixShaper(d->profile) || (cmsIsCLUT(d->profile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT) && cmsIsCLUT(d->profile, INTENT_PERCEPTUAL, LCMS_USED_AS_OUTPUT)); d->version = cmsGetProfileVersion(d->profile); d->defaultIntent = cmsGetHeaderRenderingIntent(d->profile); d->isMatrixShaper = cmsIsMatrixShaper(d->profile); d->isPerceptualCLUT = cmsIsCLUT(d->profile, INTENT_PERCEPTUAL, LCMS_USED_AS_INPUT); d->isSaturationCLUT = cmsIsCLUT(d->profile, INTENT_SATURATION, LCMS_USED_AS_INPUT); d->isAbsoluteCLUT = cmsIsCLUT(d->profile, INTENT_SATURATION, LCMS_USED_AS_INPUT); d->isRelativeCLUT = cmsIsCLUT(d->profile, INTENT_RELATIVE_COLORIMETRIC, LCMS_USED_AS_INPUT); return true; } return false; } cmsHPROFILE LcmsColorProfileContainer::lcmsProfile() const { return d->profile; } cmsColorSpaceSignature LcmsColorProfileContainer::colorSpaceSignature() const { return d->colorSpaceSignature; } cmsProfileClassSignature LcmsColorProfileContainer::deviceClass() const { return d->deviceClass; } QString LcmsColorProfileContainer::manufacturer() const { return d->manufacturer; } QString LcmsColorProfileContainer::copyright() const { return d->copyright; } bool LcmsColorProfileContainer::valid() const { return d->valid; } float LcmsColorProfileContainer::version() const { return d->version; } bool LcmsColorProfileContainer::isSuitableForOutput() const { return d->suitableForOutput; } bool LcmsColorProfileContainer::isSuitableForPrinting() const { return deviceClass() == cmsSigOutputClass; } bool LcmsColorProfileContainer::isSuitableForDisplay() const { return deviceClass() == cmsSigDisplayClass; } bool LcmsColorProfileContainer::supportsPerceptual() const { return d->isPerceptualCLUT; } bool LcmsColorProfileContainer::supportsSaturation() const { return d->isSaturationCLUT; } bool LcmsColorProfileContainer::supportsAbsolute() const { return d->isAbsoluteCLUT;//LCMS2 doesn't convert matrix shapers via absolute intent, because of V4 workflow. } bool LcmsColorProfileContainer::supportsRelative() const { if (d->isRelativeCLUT || d->isMatrixShaper){ return true; } return false; } bool LcmsColorProfileContainer::hasColorants() const { return d->hasColorants; } bool LcmsColorProfileContainer::hasTRC() const { return d->hasTRC; } +bool LcmsColorProfileContainer::isLinear() const +{ + return d->isLinear; +} QVector LcmsColorProfileContainer::getColorantsXYZ() const { QVector colorants(9); colorants[0] = d->colorants.Red.X; colorants[1] = d->colorants.Red.Y; colorants[2] = d->colorants.Red.Z; colorants[3] = d->colorants.Green.X; colorants[4] = d->colorants.Green.Y; colorants[5] = d->colorants.Green.Z; colorants[6] = d->colorants.Blue.X; colorants[7] = d->colorants.Blue.Y; colorants[8] = d->colorants.Blue.Z; return colorants; } QVector LcmsColorProfileContainer::getColorantsxyY() const { cmsCIEXYZ temp1; cmsCIExyY temp2; QVector colorants(9); temp1.X = d->colorants.Red.X; temp1.Y = d->colorants.Red.Y; temp1.Z = d->colorants.Red.Z; cmsXYZ2xyY(&temp2, &temp1); colorants[0] = temp2.x; colorants[1] = temp2.y; colorants[2] = temp2.Y; temp1.X = d->colorants.Green.X; temp1.Y = d->colorants.Green.Y; temp1.Z = d->colorants.Green.Z; cmsXYZ2xyY(&temp2, &temp1); colorants[3] = temp2.x; colorants[4] = temp2.y; colorants[5] = temp2.Y; temp1.X = d->colorants.Blue.X; temp1.Y = d->colorants.Blue.Y; temp1.Z = d->colorants.Blue.Z; cmsXYZ2xyY(&temp2, &temp1); colorants[6] = temp2.x; colorants[7] = temp2.y; colorants[8] = temp2.Y; return colorants; } QVector LcmsColorProfileContainer::getWhitePointXYZ() const { QVector tempWhitePoint(3); tempWhitePoint[0] = d->mediaWhitePoint.X; tempWhitePoint[1] = d->mediaWhitePoint.Y; tempWhitePoint[2] = d->mediaWhitePoint.Z; return tempWhitePoint; } QVector LcmsColorProfileContainer::getWhitePointxyY() const { QVector tempWhitePoint(3); tempWhitePoint[0] = d->whitePoint.x; tempWhitePoint[1] = d->whitePoint.y; tempWhitePoint[2] = d->whitePoint.Y; return tempWhitePoint; } QVector LcmsColorProfileContainer::getEstimatedTRC() const { QVector TRCtriplet(3); if (d->hasColorants) { if (cmsIsToneCurveLinear(d->redTRC)) { TRCtriplet[0] = 1.0; } else { TRCtriplet[0] = cmsEstimateGamma(d->redTRC, 0.01); } if (cmsIsToneCurveLinear(d->greenTRC)) { TRCtriplet[1] = 1.0; } else { TRCtriplet[1] = cmsEstimateGamma(d->greenTRC, 0.01); } if (cmsIsToneCurveLinear(d->blueTRC)) { TRCtriplet[2] = 1.0; } else { TRCtriplet[2] = cmsEstimateGamma(d->blueTRC, 0.01); } } else { if (cmsIsTag(d->profile, cmsSigGrayTRCTag)) { if (cmsIsToneCurveLinear(d->grayTRC)) { TRCtriplet.fill(1.0); } else { TRCtriplet.fill(cmsEstimateGamma(d->grayTRC, 0.01)); } } else { TRCtriplet.fill(1.0); } } return TRCtriplet; } void LcmsColorProfileContainer::LinearizeFloatValue(QVector & Value) const { if (d->hasColorants) { if (!cmsIsToneCurveLinear(d->redTRC)) { Value[0] = cmsEvalToneCurveFloat(d->redTRC, Value[0]); } if (!cmsIsToneCurveLinear(d->greenTRC)) { Value[1] = cmsEvalToneCurveFloat(d->greenTRC, Value[1]); } if (!cmsIsToneCurveLinear(d->blueTRC)) { Value[2] = cmsEvalToneCurveFloat(d->blueTRC, Value[2]); } } else { if (cmsIsTag(d->profile, cmsSigGrayTRCTag)) { Value[0] = cmsEvalToneCurveFloat(d->grayTRC, Value[0]); } } } void LcmsColorProfileContainer::DelinearizeFloatValue(QVector & Value) const { if (d->hasColorants) { if (!cmsIsToneCurveLinear(d->redTRC)) { Value[0] = cmsEvalToneCurveFloat(d->redTRCReverse, Value[0]); } if (!cmsIsToneCurveLinear(d->greenTRC)) { Value[1] = cmsEvalToneCurveFloat(d->greenTRCReverse, Value[1]); } if (!cmsIsToneCurveLinear(d->blueTRC)) { Value[2] = cmsEvalToneCurveFloat(d->blueTRCReverse, Value[2]); } } else { if (cmsIsTag(d->profile, cmsSigGrayTRCTag)) { Value[0] = cmsEvalToneCurveFloat(d->grayTRCReverse, Value[0]); } } } void LcmsColorProfileContainer::LinearizeFloatValueFast(QVector & Value) const { const qreal scale = 65535.0; const qreal invScale = 1.0 / scale; if (d->hasColorants) { //we can only reliably delinearise in the 0-1.0 range, outside of that leave the value alone. QVector TRCtriplet(3); TRCtriplet[0] = Value[0] * scale; TRCtriplet[1] = Value[1] * scale; TRCtriplet[2] = Value[2] * scale; if (!cmsIsToneCurveLinear(d->redTRC) && Value[0]<1.0) { TRCtriplet[0] = cmsEvalToneCurve16(d->redTRC, TRCtriplet[0]); Value[0] = TRCtriplet[0] * invScale; } if (!cmsIsToneCurveLinear(d->greenTRC) && Value[1]<1.0) { TRCtriplet[1] = cmsEvalToneCurve16(d->greenTRC, TRCtriplet[1]); Value[1] = TRCtriplet[1] * invScale; } if (!cmsIsToneCurveLinear(d->blueTRC) && Value[2]<1.0) { TRCtriplet[2] = cmsEvalToneCurve16(d->blueTRC, TRCtriplet[2]); Value[2] = TRCtriplet[2] * invScale; } } else { if (cmsIsTag(d->profile, cmsSigGrayTRCTag) && Value[0]<1.0) { quint16 newValue = cmsEvalToneCurve16(d->grayTRC, Value[0] * scale); Value[0] = newValue * invScale; } } } void LcmsColorProfileContainer::DelinearizeFloatValueFast(QVector & Value) const { const qreal scale = 65535.0; const qreal invScale = 1.0 / scale; if (d->hasColorants) { //we can only reliably delinearise in the 0-1.0 range, outside of that leave the value alone. QVector TRCtriplet(3); TRCtriplet[0] = Value[0] * scale; TRCtriplet[1] = Value[1] * scale; TRCtriplet[2] = Value[2] * scale; if (!cmsIsToneCurveLinear(d->redTRC) && Value[0]<1.0) { TRCtriplet[0] = cmsEvalToneCurve16(d->redTRCReverse, TRCtriplet[0]); Value[0] = TRCtriplet[0] * invScale; } if (!cmsIsToneCurveLinear(d->greenTRC) && Value[1]<1.0) { TRCtriplet[1] = cmsEvalToneCurve16(d->greenTRCReverse, TRCtriplet[1]); Value[1] = TRCtriplet[1] * invScale; } if (!cmsIsToneCurveLinear(d->blueTRC) && Value[2]<1.0) { TRCtriplet[2] = cmsEvalToneCurve16(d->blueTRCReverse, TRCtriplet[2]); Value[2] = TRCtriplet[2] * invScale; } } else { if (cmsIsTag(d->profile, cmsSigGrayTRCTag) && Value[0]<1.0) { quint16 newValue = cmsEvalToneCurve16(d->grayTRCReverse, Value[0] * scale); Value[0] = newValue * invScale; } } } QString LcmsColorProfileContainer::name() const { return d->name; } QString LcmsColorProfileContainer::info() const { return d->productDescription; } QByteArray LcmsColorProfileContainer::getProfileUniqueId() const { if (d->uniqueId.isEmpty() && d->profile) { QByteArray id(sizeof(cmsProfileID), 0); cmsGetHeaderProfileID(d->profile, (quint8*)id.data()); bool isNull = std::all_of(id.constBegin(), id.constEnd(), [](char c) {return c == 0;}); if (isNull) { if (cmsMD5computeID(d->profile)) { cmsGetHeaderProfileID(d->profile, (quint8*)id.data()); isNull = false; } } if (!isNull) { d->uniqueId = id; } } return d->uniqueId; } diff --git a/plugins/color/lcms2engine/colorprofiles/LcmsColorProfileContainer.h b/plugins/color/lcms2engine/colorprofiles/LcmsColorProfileContainer.h index f1dca0c9c9..ee1d4bcc7f 100644 --- a/plugins/color/lcms2engine/colorprofiles/LcmsColorProfileContainer.h +++ b/plugins/color/lcms2engine/colorprofiles/LcmsColorProfileContainer.h @@ -1,119 +1,120 @@ /* * This file is part of the KDE project * Copyright (c) 2000 Matthias Elter * 2004 Boudewijn Rempt * Copyright (c) 2007 Thomas Zander * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Library General Public * License as published by the Free Software Foundation; either * version 2 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 * Library General Public License for more details. * * You should have received a copy of the GNU Library General Public License * along with this library; see the file COPYING.LIB. If not, write to * the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, * Boston, MA 02110-1301, USA. */ #ifndef _KO_LCMS_COLORPROFILE_H #define _KO_LCMS_COLORPROFILE_H #include "IccColorProfile.h" #include #include #include /** * This class contains an LCMS color profile. Don't use it outside LcmsColorSpace. */ class LcmsColorProfileContainer : public IccColorProfile::Container { friend class IccColorProfile; protected: LcmsColorProfileContainer(IccColorProfile::Data *); private: /** * Create a byte array from a lcms profile. */ static QByteArray lcmsProfileToByteArray(const cmsHPROFILE profile); public: /** * @param profile lcms memory structure with the profile, it is freed after the call * to this function * @return an ICC profile created from an LCMS profile */ static IccColorProfile *createFromLcmsProfile(const cmsHPROFILE profile); public: ~LcmsColorProfileContainer() override; /** * @return the ICC color space signature */ cmsColorSpaceSignature colorSpaceSignature() const; /** * @return the class of the color space signature */ cmsProfileClassSignature deviceClass() const; /** * @return the name of the manufacturer */ QString manufacturer() const override; /** * @return the embedded copyright */ QString copyright() const override; /** * @return the structure to use with LCMS functions */ cmsHPROFILE lcmsProfile() const; bool valid() const override; virtual float version() const; bool isSuitableForOutput() const override; bool isSuitableForPrinting() const override; bool isSuitableForDisplay() const override; virtual bool supportsPerceptual() const; virtual bool supportsSaturation() const; virtual bool supportsAbsolute() const; virtual bool supportsRelative() const; bool hasColorants() const override; virtual bool hasTRC() const; + bool isLinear() const; QVector getColorantsXYZ() const override; QVector getColorantsxyY() const override; QVector getWhitePointXYZ() const override; QVector getWhitePointxyY() const override; QVector getEstimatedTRC() const override; virtual void LinearizeFloatValue(QVector & Value) const; virtual void DelinearizeFloatValue(QVector & Value) const; virtual void LinearizeFloatValueFast(QVector & Value) const; virtual void DelinearizeFloatValueFast(QVector & Value) const; QString name() const override; QString info() const override; QByteArray getProfileUniqueId() const override; protected: LcmsColorProfileContainer(); private: bool init(); class Private; Private *const d; }; #endif // KOCOLORPROFILE_H