File indexing completed on 2024-05-12 04:19:47
0001 // vim: set tabstop=4 shiftwidth=4 expandtab: 0002 /* 0003 Gwenview: an image viewer 0004 Copyright 2007 Aurélien Gâteau <agateau@kde.org> 0005 0006 This program is free software; you can redistribute it and/or 0007 modify it under the terms of the GNU General Public License 0008 as published by the Free Software Foundation; either version 2 0009 of the License, or (at your option) any later version. 0010 0011 This program is distributed in the hope that it will be useful, 0012 but WITHOUT ANY WARRANTY; without even the implied warranty of 0013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 0014 GNU General Public License for more details. 0015 0016 You should have received a copy of the GNU General Public License 0017 along with this program; if not, write to the Free Software 0018 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 0019 0020 */ 0021 // Self 0022 #include "redeyereductionimageoperation.h" 0023 0024 // STL 0025 #include <cmath> 0026 0027 // Qt 0028 #include <QImage> 0029 #include <QPainter> 0030 0031 // KF 0032 #include <KLocalizedString> 0033 0034 // Local 0035 #include "document/abstractdocumenteditor.h" 0036 #include "document/document.h" 0037 #include "document/documentjob.h" 0038 #include "gwenview_lib_debug.h" 0039 #include "ramp.h" 0040 0041 namespace Gwenview 0042 { 0043 class RedEyeReductionJob : public ThreadedDocumentJob 0044 { 0045 public: 0046 RedEyeReductionJob(const QRectF &rectF) 0047 : mRectF(rectF) 0048 { 0049 } 0050 0051 void threadedStart() override 0052 { 0053 if (!checkDocumentEditor()) { 0054 return; 0055 } 0056 QImage img = document()->image(); 0057 RedEyeReductionImageOperation::apply(&img, mRectF); 0058 document()->editor()->setImage(img); 0059 setError(NoError); 0060 } 0061 0062 private: 0063 QRectF mRectF; 0064 }; 0065 0066 struct RedEyeReductionImageOperationPrivate { 0067 QRectF mRectF; 0068 QImage mOriginalImage; 0069 }; 0070 0071 RedEyeReductionImageOperation::RedEyeReductionImageOperation(const QRectF &rectF) 0072 : d(new RedEyeReductionImageOperationPrivate) 0073 { 0074 d->mRectF = rectF; 0075 setText(i18n("Reduce Red Eye")); 0076 } 0077 0078 RedEyeReductionImageOperation::~RedEyeReductionImageOperation() 0079 { 0080 delete d; 0081 } 0082 0083 void RedEyeReductionImageOperation::redo() 0084 { 0085 const QImage img = document()->image(); 0086 const QRect rect = d->mRectF.toAlignedRect(); 0087 d->mOriginalImage = img.copy(rect); 0088 redoAsDocumentJob(new RedEyeReductionJob(d->mRectF)); 0089 } 0090 0091 void RedEyeReductionImageOperation::undo() 0092 { 0093 if (!document()->editor()) { 0094 qCWarning(GWENVIEW_LIB_LOG) << "!document->editor()"; 0095 return; 0096 } 0097 QImage img = document()->image(); 0098 { 0099 QPainter painter(&img); 0100 painter.setCompositionMode(QPainter::CompositionMode_Source); 0101 const QRect rect = d->mRectF.toAlignedRect(); 0102 painter.drawImage(rect.topLeft(), d->mOriginalImage); 0103 } 0104 document()->editor()->setImage(img); 0105 finish(true); 0106 } 0107 0108 /** 0109 * This code is inspired from code found in a Paint.net plugin: 0110 * http://paintdotnet.forumer.com/viewtopic.php?f=27&t=26193&p=205954&hilit=red+eye#p205954 0111 */ 0112 inline qreal computeRedEyeAlpha(const QColor &src) 0113 { 0114 int hue, sat, value; 0115 src.getHsv(&hue, &sat, &value); 0116 0117 qreal axs = 1.0; 0118 if (hue > 259) { 0119 static const Ramp ramp(30, 35, 0., 1.); 0120 axs = ramp(sat); 0121 } else { 0122 const Ramp ramp(hue * 2 + 29, hue * 2 + 40, 0., 1.); 0123 axs = ramp(sat); 0124 } 0125 0126 return qBound(qreal(0.), src.alphaF() * axs, qreal(1.)); 0127 } 0128 0129 void RedEyeReductionImageOperation::apply(QImage *img, const QRectF &rectF) 0130 { 0131 const QRect rect = rectF.toAlignedRect(); 0132 const qreal radius = rectF.width() / 2; 0133 const qreal centerX = rectF.x() + radius; 0134 const qreal centerY = rectF.y() + radius; 0135 const Ramp radiusRamp(qMin(qreal(radius * 0.7), qreal(radius - 1)), radius, qreal(1.), qreal(0.)); 0136 0137 uchar *line = img->scanLine(rect.top()) + rect.left() * 4; 0138 for (int y = rect.top(); y < rect.bottom(); ++y, line += img->bytesPerLine()) { 0139 QRgb *ptr = (QRgb *)line; 0140 0141 for (int x = rect.left(); x < rect.right(); ++x, ++ptr) { 0142 const qreal currentRadius = sqrt(pow(y - centerY, 2) + pow(x - centerX, 2)); 0143 qreal alpha = radiusRamp(currentRadius); 0144 if (qFuzzyCompare(alpha, 0)) { 0145 continue; 0146 } 0147 0148 const QColor src(*ptr); 0149 alpha *= computeRedEyeAlpha(src); 0150 int r = src.red(); 0151 int g = src.green(); 0152 int b = src.blue(); 0153 QColor dst; 0154 // Replace red with green, and blend according to alpha 0155 dst.setRed(int((1 - alpha) * r + alpha * g)); 0156 dst.setGreen(g); 0157 dst.setBlue(b); 0158 *ptr = dst.rgba(); 0159 } 0160 } 0161 } 0162 0163 } // namespace