Commit cc1ae77a authored by Ilya Pominov's avatar Ilya Pominov Committed by Nate Graham
Browse files

Add possibility to change brightness & contrast & gamma

BUG: 82979
parent 92fd21e4
Pipeline #257524 passed with stage
in 3 minutes and 4 seconds
<?xml version="1.0"?>
<!DOCTYPE gui SYSTEM "kpartgui.dtd">
<gui name="gwenview" version="66">
<gui name="gwenview" version="67">
<MenuBar>
<Menu name="file" >
......@@ -67,6 +67,7 @@
<Separator/>
<Action name="resize" />
<Action name="crop" />
<Action name="brightness_contrast_gamma" />
<Action name="red_eye_reduction" />
<Action name="annotate" />
</Menu>
......
......@@ -42,6 +42,7 @@ Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#include <lib/annotate/annotatedialog.h>
#include <lib/annotate/annotateoperation.h>
#endif
#include <lib/bcg/bcgtool.h>
#include <lib/contextmanager.h>
#include <lib/crop/croptool.h>
#include <lib/document/documentfactory.h>
......@@ -76,6 +77,7 @@ struct ImageOpsContextManagerItem::Private {
QAction *mFlipAction = nullptr;
QAction *mResizeAction = nullptr;
QAction *mCropAction = nullptr;
QAction *mBCGAction = nullptr;
QAction *mRedEyeReductionAction = nullptr;
#ifdef KIMAGEANNOTATOR_FOUND
QAction *mAnnotateAction = nullptr;
......@@ -117,6 +119,11 @@ struct ImageOpsContextManagerItem::Private {
mCropAction->setIcon(QIcon::fromTheme(QStringLiteral("transform-crop-and-resize")));
actionCollection->setDefaultShortcut(mCropAction, Qt::SHIFT | Qt::Key_C);
mBCGAction = edit->addAction(QStringLiteral("brightness_contrast_gamma"), q, SLOT(startBCG()));
mBCGAction->setText(i18nc("@action:intoolbar", "Adjust Brightness/Contrast/Gamma"));
mBCGAction->setIcon(QIcon::fromTheme(QStringLiteral("contrast")));
actionCollection->setDefaultShortcut(mBCGAction, Qt::SHIFT | Qt::Key_B);
mRedEyeReductionAction = edit->addAction(QStringLiteral("red_eye_reduction"), q, SLOT(startRedEyeReduction()));
mRedEyeReductionAction->setText(i18n("Reduce Red Eye"));
mRedEyeReductionAction->setIcon(QIcon::fromTheme(QStringLiteral("redeyes")));
......@@ -138,7 +145,8 @@ struct ImageOpsContextManagerItem::Private {
}
});
#endif
mActionList << mRotateLeftAction << mRotateRightAction << mMirrorAction << mFlipAction << mResizeAction << mCropAction << mRedEyeReductionAction;
mActionList << mRotateLeftAction << mRotateRightAction << mMirrorAction << mFlipAction << mResizeAction << mCropAction << mBCGAction
<< mRedEyeReductionAction;
#ifdef KIMAGEANNOTATOR_FOUND
mActionList << mAnnotateAction;
#endif
......@@ -214,6 +222,7 @@ void ImageOpsContextManagerItem::updateActions()
d->mFlipAction->setEnabled(canModify);
d->mResizeAction->setEnabled(canModify);
d->mCropAction->setEnabled(canModify && viewMainPageIsVisible);
d->mBCGAction->setEnabled(canModify && viewMainPageIsVisible);
d->mRedEyeReductionAction->setEnabled(canModify && viewMainPageIsVisible);
#ifdef KIMAGEANNOTATOR_FOUND
d->mAnnotateAction->setEnabled(canModify);
......@@ -346,4 +355,22 @@ void ImageOpsContextManagerItem::restoreDefaultImageViewTool()
d->mMainWindow->setDistractionFreeMode(false);
}
void ImageOpsContextManagerItem::startBCG()
{
if (!d->ensureEditable()) {
return;
}
RasterImageView *view = d->mMainWindow->viewMainPage()->imageView();
if (!view) {
qCCritical(GWENVIEW_APP_LOG) << "No RasterImageView available!";
return;
}
auto tool = new BCGTool(view);
connect(tool, &BCGTool::imageOperationRequested, this, &ImageOpsContextManagerItem::applyImageOperation);
connect(tool, &BCGTool::done, this, &ImageOpsContextManagerItem::restoreDefaultImageViewTool);
d->mMainWindow->setDistractionFreeMode(true);
view->setCurrentTool(tool);
}
} // namespace
......@@ -52,6 +52,7 @@ private Q_SLOTS:
void startRedEyeReduction();
void applyImageOperation(AbstractImageOperation *);
void restoreDefaultImageViewTool();
void startBCG();
private:
struct Private;
......
......@@ -41,6 +41,10 @@ include_directories(
)
set(gwenviewlib_SRCS
bcg/bcgimageoperation.cpp
bcg/bcgtool.cpp
bcg/bcgwidget.cpp
bcg/imageutils.cpp
cms/iccjpeg.c
cms/cmsprofile.cpp
cms/cmsprofile_png.cpp
......
/*
Gwenview: an image viewer
Copyright 2022 Ilya Pominov <ipominov@astralinux.ru>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// Self
#include "bcgimageoperation.h"
// Qt
#include <QImage>
// KF
#include <KLocalizedString>
// Local
#include "document/abstractdocumenteditor.h"
#include "document/document.h"
#include "document/documentjob.h"
#include "gwenview_lib_debug.h"
#include "imageutils.h"
namespace Gwenview
{
class BCGJob : public ThreadedDocumentJob
{
public:
BCGJob(const BCGImageOperation::BrightnessContrastGamma &bcg)
: mBcg(bcg)
{
}
void threadedStart() override
{
if (!checkDocumentEditor()) {
return;
}
QImage img = document()->image();
BCGImageOperation::apply(img, mBcg);
document()->editor()->setImage(img);
setError(NoError);
}
private:
BCGImageOperation::BrightnessContrastGamma mBcg;
};
struct BCGImageOperationPrivate {
QImage mOriginalImage;
BCGImageOperation::BrightnessContrastGamma mBcg;
};
BCGImageOperation::BCGImageOperation(const BrightnessContrastGamma &bcg)
: d(new BCGImageOperationPrivate)
{
setText(i18n("Adjust Brightness/Contrast/Gamma"));
d->mBcg = bcg;
}
BCGImageOperation::~BCGImageOperation()
{
delete d;
}
void BCGImageOperation::redo()
{
d->mOriginalImage = document()->image();
redoAsDocumentJob(new BCGJob(d->mBcg));
}
void BCGImageOperation::undo()
{
if (!document()->editor()) {
qCWarning(GWENVIEW_LIB_LOG) << "!document->editor()";
return;
}
document()->editor()->setImage(d->mOriginalImage);
finish(true);
}
void BCGImageOperation::apply(QImage &img, const BrightnessContrastGamma &bcg)
{
if (bcg.brightness != 0) {
img = ImageUtils::changeBrightness(img, bcg.brightness);
}
if (bcg.contrast != 0) {
img = ImageUtils::changeContrast(img, bcg.contrast + 100);
}
if (bcg.gamma != 0) {
img = ImageUtils::changeGamma(img, bcg.gamma + 100);
}
}
} // namespace
/*
Gwenview: an image viewer
Copyright 2022 Ilya Pominov <ipominov@astralinux.ru>
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 BCGIMAGEOPERATION_H
#define BCGIMAGEOPERATION_H
#include <lib/gwenviewlib_export.h>
// Qt
// KF
// Local
#include <lib/abstractimageoperation.h>
namespace Gwenview
{
struct BCGImageOperationPrivate;
class GWENVIEWLIB_EXPORT BCGImageOperation : public AbstractImageOperation
{
Q_OBJECT
public:
struct BrightnessContrastGamma {
int brightness = 0;
int contrast = 0;
int gamma = 0;
};
BCGImageOperation(const BrightnessContrastGamma &bcg);
~BCGImageOperation() override;
void redo() override;
void undo() override;
static void apply(QImage &img, const BrightnessContrastGamma &bcg);
private:
BCGImageOperationPrivate *const d;
};
} // namespace
#endif /*BCGIMAGEOPERATION_H */
/*
Gwenview: an image viewer
Copyright 2022 Ilya Pominov <ipominov@astralinux.ru>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// Self
#include "bcgtool.h"
// Qt
#include <QAbstractButton>
#include <QDialogButtonBox>
#include <QPainter>
// KF
// Local
#include "bcgimageoperation.h"
#include "bcgwidget.h"
#include <lib/documentview/rasterimageview.h>
namespace Gwenview
{
struct BCGToolPrivate {
BCGImageOperation::BrightnessContrastGamma getBcg() const
{
BCGImageOperation::BrightnessContrastGamma bcg;
bcg.brightness = mBCGWidget->brightness();
bcg.contrast = mBCGWidget->contrast();
bcg.gamma = mBCGWidget->gamma();
return bcg;
}
BCGTool *q = nullptr;
BCGWidget *mBCGWidget = nullptr;
QImage mImage;
};
BCGTool::BCGTool(RasterImageView *view)
: AbstractRasterImageViewTool(view)
, d(new BCGToolPrivate)
{
d->q = this;
d->mBCGWidget = new BCGWidget;
connect(d->mBCGWidget, &BCGWidget::bcgChanged, this, &BCGTool::slotBCGRequested);
connect(d->mBCGWidget, &BCGWidget::done, this, [this](bool accept) {
if (accept) {
auto op = new BCGImageOperation(d->getBcg());
Q_EMIT imageOperationRequested(op);
}
Q_EMIT done();
});
d->mImage = view->document()->image();
}
BCGTool::~BCGTool()
{
// mBCGWidget is a child of its container not of us, so it is not deleted automatically
delete d->mBCGWidget;
delete d;
}
void BCGTool::paint(QPainter *painter)
{
const QRectF docRect = imageView()->mapToView(QRectF(QPointF(), imageView()->documentSize()));
painter->eraseRect(docRect);
painter->drawImage(docRect, d->mImage);
}
void BCGTool::keyPressEvent(QKeyEvent *event)
{
auto buttons = d->mBCGWidget->findChild<QDialogButtonBox *>();
switch (event->key()) {
case Qt::Key_Escape:
event->accept();
Q_EMIT buttons->rejected();
break;
case Qt::Key_Return:
case Qt::Key_Enter: {
event->accept();
auto focusButton = static_cast<QAbstractButton *>(buttons->focusWidget());
if (focusButton && buttons->buttonRole(focusButton) == QDialogButtonBox::RejectRole) {
Q_EMIT buttons->rejected();
} else {
Q_EMIT buttons->accepted();
}
break;
}
default:
break;
}
}
QWidget *BCGTool::widget() const
{
return d->mBCGWidget;
}
void BCGTool::slotBCGRequested()
{
d->mImage = imageView()->document()->image();
BCGImageOperation::apply(d->mImage, d->getBcg());
imageView()->update();
}
} // namespace
/*
Gwenview: an image viewer
Copyright 2022 Ilya Pominov <ipominov@astralinux.ru>
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 BCGTOOL_H
#define BCGTOOL_H
#include <lib/gwenviewlib_export.h>
// Qt
// KF
// Local
#include <lib/documentview/abstractrasterimageviewtool.h>
namespace Gwenview
{
class AbstractImageOperation;
struct BCGToolPrivate;
class GWENVIEWLIB_EXPORT BCGTool : public AbstractRasterImageViewTool
{
Q_OBJECT
public:
BCGTool(RasterImageView *parent);
~BCGTool() override;
void paint(QPainter *painter) override;
void keyPressEvent(QKeyEvent *event) override;
QWidget *widget() const override;
Q_SIGNALS:
void done();
void imageOperationRequested(AbstractImageOperation *);
private Q_SLOTS:
void slotBCGRequested();
private:
BCGToolPrivate *const d;
};
} // namespace
#endif /* BCGTOOL_H */
/*
Gwenview: an image viewer
Copyright 2022 Ilya Pominov <ipominov@astralinux.ru>
This program is free software; you can redistribute it and/or
modify it under the terms of the GNU General Public License
as published by the Free Software Foundation; either version 2
of the License, or (at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
// Self
#include "bcgwidget.h"
// Qt
#include <QDialogButtonBox>
#include <QFormLayout>
#include <QPushButton>
#include <QSlider>
#include <QSpinBox>
#include <QTimer>
#include <QVBoxLayout>
// KF
#include <KLocalizedString>
// Local
#include "gwenview_lib_debug.h"
namespace Gwenview
{
struct BCGWidgetPrivate {
void setupWidgets()
{
mCompressor = new QTimer(q);
mCompressor->setInterval(10);
mCompressor->setSingleShot(true);
QObject::connect(mCompressor, &QTimer::timeout, q, [this]() {
Q_EMIT q->bcgChanged();
});
auto sliderLayout = new QFormLayout;
sliderLayout->addRow(i18n("Brightness:"), createSlider(&mBrightness));
sliderLayout->addRow(i18n("Contrast:"), createSlider(&mContrast));
sliderLayout->addRow(i18n("Gamma:"), createSlider(&mGamma));
auto layout = new QVBoxLayout;
layout->addLayout(sliderLayout);
auto btnBox = new QDialogButtonBox(QDialogButtonBox::Ok | QDialogButtonBox::Cancel | QDialogButtonBox::Reset);
mOkBtn = btnBox->button(QDialogButtonBox::Ok);
mOkBtn->setEnabled(false);
mResetBtn = btnBox->button(QDialogButtonBox::Reset);
mResetBtn->setEnabled(false);
QObject::connect(btnBox, &QDialogButtonBox::rejected, q, [this]() {
Q_EMIT q->done(false);
});
QObject::connect(btnBox, &QDialogButtonBox::accepted, q, [this]() {
Q_EMIT q->done(true);
});
QObject::connect(btnBox->button(QDialogButtonBox::Reset), &QAbstractButton::clicked, q, &BCGWidget::reset);
layout->addWidget(btnBox);
q->setLayout(layout);
}
QLayout *createSlider(int *field)
{
auto slider = new QSlider(Qt::Horizontal);
slider->setMinimum(-100);
slider->setMaximum(100);
auto spinBox = new QSpinBox;
spinBox->setMinimum(-100);
spinBox->setMaximum(100);
QObject::connect(slider, &QSlider::valueChanged, spinBox, &QSpinBox::setValue);
QObject::connect(spinBox, qOverload<int>(&QSpinBox::valueChanged), q, [this, slider, field](int value) {
slider->blockSignals(true);
slider->setValue(value);
slider->blockSignals(false);
*field = value;
bool bcgChanged = mBrightness != 0 || mContrast != 0 || mGamma != 0;
mOkBtn->setEnabled(bcgChanged);
mResetBtn->setEnabled(bcgChanged);
mCompressor->start();
});
QObject::connect(q, &BCGWidget::reset, q, [slider]() {
slider->setValue(0);
});
auto layout = new QHBoxLayout;
layout->addWidget(slider);
layout->addWidget(spinBox);
return layout;
}
BCGWidget *q = nullptr;
QTimer *mCompressor = nullptr;
QAbstractButton *mOkBtn = nullptr;
QAbstractButton *mResetBtn = nullptr;
int mBrightness = 0;
int mContrast = 0;
int mGamma = 0;
};
BCGWidget::BCGWidget()
: QWidget(nullptr)
, d(new BCGWidgetPrivate)
{
setWindowFlags(Qt::Tool);
d->q = this;
d->setupWidgets();
}
BCGWidget::~BCGWidget()
{
delete d;
}
int BCGWidget::brightness() const
{
return d->mBrightness;
}
int BCGWidget::contrast() const
{
return d->mContrast;
}
int BCGWidget::gamma() const
{
return d->mGamma;
}
} // namespace
/*
Gwenview: an image viewer
Copyright 2022 Ilya Pominov <ipominov@astralinux.ru>
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 BCGWIDGET_H
#define BCGWIDGET_H