1 /* ============================================================
2 *
3 * This file is a part of digiKam project
4 * https://www.digikam.org
5 *
6 * Date : 2008-09-30
7 * Description : a widget to display an image histogram and its control widgets
8 *
9 * Copyright (C) 2008-2009 by Andi Clemens <andi dot clemens at gmail dot com>
10 * Copyright (C) 2011-2021 by Gilles Caulier <caulier dot gilles at gmail dot com>
11 *
12 * This program is free software; you can redistribute it
13 * and/or modify it under the terms of the GNU General
14 * Public License as published by the Free Software Foundation;
15 * either version 2, or (at your option)
16 * any later version.
17 *
18 * This program is distributed in the hope that it will be useful,
19 * but WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
21 * GNU General Public License for more details.
22 *
23 * ============================================================ */
24
25 #include "histogrambox.h"
26
27 // Qt includes
28
29 #include <QButtonGroup>
30 #include <QColor>
31 #include <QGridLayout>
32 #include <QHBoxLayout>
33 #include <QLabel>
34 #include <QMap>
35 #include <QPair>
36 #include <QString>
37 #include <QToolButton>
38 #include <QVBoxLayout>
39 #include <QWidget>
40 #include <QComboBox>
41 #include <QIcon>
42
43 // KDE includes
44
45 #include <klocalizedstring.h>
46
47 // Local includes
48
49 #include "digikam_debug.h"
50 #include "colorgradientwidget.h"
51 #include "histogramwidget.h"
52 #include "digikam_globals.h"
53
54 namespace Digikam
55 {
56
57 class Q_DECL_HIDDEN HistogramBox::Private
58 {
59 public:
60
Private()61 explicit Private()
62 : scaleBG (nullptr),
63 linHistoButton (nullptr),
64 logHistoButton (nullptr),
65 histoBox (nullptr),
66 channelCB (nullptr),
67 hGradient (nullptr),
68 histogramWidget (nullptr)
69 {
70 }
71
72 QButtonGroup* scaleBG;
73
74 QToolButton* linHistoButton;
75 QToolButton* logHistoButton;
76
77 QWidget* histoBox;
78 QComboBox* channelCB;
79
80 ColorGradientWidget* hGradient;
81 HistogramWidget* histogramWidget;
82 };
83
HistogramBox(QWidget * const parent,HistogramBoxType type,bool selectMode)84 HistogramBox::HistogramBox(QWidget* const parent, HistogramBoxType type, bool selectMode)
85 : QWidget(parent),
86 d (new Private)
87 {
88 d->channelCB = new QComboBox(this);
89 QLabel* const channelLabel = new QLabel(i18n("Channel:"), this);
90 channelLabel->setAlignment(Qt::AlignRight | Qt::AlignVCenter);
91
92 QWidget* const scaleBox = new QWidget(this);
93 QHBoxLayout* const hlay = new QHBoxLayout(scaleBox);
94 d->scaleBG = new QButtonGroup(scaleBox);
95 scaleBox->setWhatsThis(i18n("<p>Select the histogram scale.</p>"
96 "<p>If the image's maximal counts are small, you can use the <b>linear</b> scale.</p>"
97 "<p><b>Logarithmic</b> scale can be used when the maximal counts are big; "
98 "if it is used, all values (small and large) will be visible on the graph.</p>"));
99
100 d->linHistoButton = new QToolButton(scaleBox);
101 d->linHistoButton->setToolTip(i18nc("linear histogram scaling mode", "Linear"));
102 d->linHistoButton->setIcon(QIcon::fromTheme(QLatin1String("view-object-histogram-linear")));
103 d->linHistoButton->setCheckable(true);
104 d->scaleBG->addButton(d->linHistoButton, LinScaleHistogram);
105
106 d->logHistoButton = new QToolButton(scaleBox);
107 d->logHistoButton->setToolTip(i18nc("logarithmic histogram scaling mode", "Logarithmic"));
108 d->logHistoButton->setIcon(QIcon::fromTheme(QLatin1String("view-object-histogram-logarithmic")));
109 d->logHistoButton->setCheckable(true);
110 d->scaleBG->addButton(d->logHistoButton, LogScaleHistogram);
111
112 hlay->setSpacing(0);
113 hlay->setContentsMargins(QMargins());
114 hlay->addWidget(d->linHistoButton);
115 hlay->addWidget(d->logHistoButton);
116
117 d->scaleBG->setExclusive(true);
118 d->logHistoButton->setChecked(true);
119
120 d->histoBox = new QWidget;
121 QVBoxLayout* const histoBoxLayout = new QVBoxLayout;
122
123 d->histogramWidget = new HistogramWidget(256, 140, d->histoBox, selectMode, true, true);
124 d->histogramWidget->setWhatsThis(i18n("Here you can see the target preview image histogram drawing "
125 "of the selected image channel. This one is re-computed at any "
126 "settings changes."));
127
128 d->hGradient = new ColorGradientWidget(Qt::Horizontal, 10, d->histoBox);
129 d->hGradient->setColors(QColor("black"), QColor("white"));
130
131 histoBoxLayout->addWidget(d->histogramWidget);
132 histoBoxLayout->addWidget(d->hGradient);
133 histoBoxLayout->setContentsMargins(QMargins());
134 histoBoxLayout->setSpacing(1);
135 d->histoBox->setLayout(histoBoxLayout);
136
137 QGridLayout* const mainLayout = new QGridLayout;
138 mainLayout->addWidget(channelLabel, 0, 0, 1, 1);
139 mainLayout->addWidget(d->channelCB, 0, 1, 1, 1);
140 mainLayout->addWidget(scaleBox, 0, 3, 1, 2);
141 mainLayout->addWidget(d->histoBox, 2, 0, 1, 5);
142 mainLayout->setColumnStretch(2, 10);
143 mainLayout->setContentsMargins(QMargins());
144 mainLayout->setSpacing(5);
145 setLayout(mainLayout);
146
147 // ---------------------------------------------------------------
148
149 setHistogramType(type);
150
151 // ---------------------------------------------------------------
152
153 connect(d->channelCB, SIGNAL(activated(int)),
154 this, SLOT(slotChannelChanged()));
155
156 connect(d->scaleBG, SIGNAL(buttonReleased(int)),
157 this, SLOT(slotScaleChanged()));
158
159 connect(this, SIGNAL(signalChannelChanged(ChannelType)),
160 d->histogramWidget, SLOT(setChannelType(ChannelType)));
161
162 connect(this, SIGNAL(signalScaleChanged(HistogramScale)),
163 d->histogramWidget, SLOT(setScaleType(HistogramScale)));
164 }
165
~HistogramBox()166 HistogramBox::~HistogramBox()
167 {
168 histogram()->stopHistogramComputation();
169 delete d;
170 }
171
setGradientVisible(bool visible)172 void HistogramBox::setGradientVisible(bool visible)
173 {
174 d->hGradient->setVisible(visible);
175 }
176
setGradientColors(const QColor & from,const QColor & to)177 void HistogramBox::setGradientColors(const QColor& from, const QColor& to)
178 {
179 d->hGradient->setColors(from, to);
180 }
181
setStatisticsVisible(bool b)182 void HistogramBox::setStatisticsVisible(bool b)
183 {
184 d->histogramWidget->setStatisticsVisible(b);
185 }
186
channel() const187 ChannelType HistogramBox::channel() const
188 {
189 int index = d->channelCB->currentIndex();
190
191 return (ChannelType)(d->channelCB->itemData(index).toInt());
192 }
193
setChannel(ChannelType channel)194 void HistogramBox::setChannel(ChannelType channel)
195 {
196 int id = d->channelCB->findData(QVariant(channel));
197 d->channelCB->setCurrentIndex(id);
198 slotChannelChanged();
199 }
200
setChannelEnabled(bool enabled)201 void HistogramBox::setChannelEnabled(bool enabled)
202 {
203 d->channelCB->setEnabled(enabled);
204 }
205
scale() const206 HistogramScale HistogramBox::scale() const
207 {
208 return static_cast<HistogramScale>(d->scaleBG->checkedId());
209 }
210
setScale(HistogramScale scale)211 void HistogramBox::setScale(HistogramScale scale)
212 {
213 d->scaleBG->button((int)scale)->setChecked(true);
214 slotScaleChanged();
215 }
216
histogram() const217 HistogramWidget* HistogramBox::histogram() const
218 {
219 return d->histogramWidget;
220 }
221
setHistogramMargin(int margin)222 void HistogramBox::setHistogramMargin(int margin)
223 {
224 d->histoBox->layout()->setContentsMargins(margin, margin, margin, margin);
225 }
226
slotChannelChanged()227 void HistogramBox::slotChannelChanged()
228 {
229 switch (channel())
230 {
231 case LuminosityChannel:
232 setGradientColors(QColor("black"), QColor("white"));
233 break;
234
235 case RedChannel:
236 setGradientColors(QColor("black"), QColor("red"));
237 break;
238
239 case GreenChannel:
240 setGradientColors(QColor("black"), QColor("green"));
241 break;
242
243 case BlueChannel:
244 setGradientColors(QColor("black"), QColor("blue"));
245 break;
246
247 case AlphaChannel:
248 setGradientColors(QColor("black"), QColor("white"));
249 break;
250
251 case ColorChannels:
252 setGradientColors(QColor("black"), QColor("white"));
253 break;
254 }
255
256 emit signalChannelChanged(channel());
257 }
258
slotScaleChanged()259 void HistogramBox::slotScaleChanged()
260 {
261 emit signalScaleChanged(scale());
262 }
263
setHistogramType(HistogramBoxType type)264 void HistogramBox::setHistogramType(HistogramBoxType type)
265 {
266 // all possible channels for histogram widget are defined in this map
267
268 QMap<int, QPair<QString, QString> > channelDescMap;
269
270 // this string will contain the WhatsThis message for the channelCB
271
272 QString channelCBDescr(i18n("<p>Select the histogram channel to display:</p>"));
273
274 // those pairs hold the combobox text and WhatsThis description for each channel item
275
276 typedef QPair<QString, QString> ChannelPair;
277
278 ChannelPair luminosityPair(i18nc("The luminosity channel", "Luminosity"), i18n(
279 "<b>Luminosity</b>: display the image's luminosity values."));
280
281 ChannelPair redPair(i18nc("The red channel", "Red"), i18n(
282 "<b>Red</b>: display the red image-channel values."));
283
284 ChannelPair greenPair(i18nc("The green channel", "Green"), i18n(
285 "<b>Green</b>: display the green image-channel values."));
286
287 ChannelPair bluePair(i18nc("The blue channel", "Blue"), i18n(
288 "<b>Blue</b>: display the blue image-channel values."));
289
290 ChannelPair colorsPair(i18nc("The colors channel", "Colors"), i18n(
291 "<b>Colors</b>: Display all color channel values at the same time."));
292
293 ChannelPair alphaPair(i18nc("The alpha channel", "Alpha"), i18n(
294 "<b>Alpha</b>: display the alpha image-channel values. "
295 "This channel corresponds to the transparency value and "
296 "is supported by some image formats, such as PNG or TIF."));
297
298 channelDescMap.insert(LuminosityChannel, luminosityPair);
299 channelDescMap.insert(RedChannel, redPair);
300 channelDescMap.insert(GreenChannel, greenPair);
301 channelDescMap.insert(BlueChannel, bluePair);
302 channelDescMap.insert(ColorChannels, colorsPair);
303 channelDescMap.insert(AlphaChannel, alphaPair);
304
305 switch (type)
306 {
307 case RGB:
308 d->channelCB->clear();
309 d->channelCB->addItem(channelDescMap[RedChannel].first, QVariant(RedChannel));
310 d->channelCB->addItem(channelDescMap[GreenChannel].first, QVariant(GreenChannel));
311 d->channelCB->addItem(channelDescMap[BlueChannel].first, QVariant(BlueChannel));
312 channelCBDescr.append(QLatin1String("<p>"));
313 channelCBDescr.append(channelDescMap[RedChannel].second).append(QLatin1String("<br/>"));
314 channelCBDescr.append(channelDescMap[GreenChannel].second).append(QLatin1String("<br/>"));
315 channelCBDescr.append(channelDescMap[BlueChannel].second);
316 channelCBDescr.append(QLatin1String("</p>"));
317 break;
318
319 case RGBA:
320 d->channelCB->clear();
321 d->channelCB->addItem(channelDescMap[RedChannel].first, QVariant(RedChannel));
322 d->channelCB->addItem(channelDescMap[GreenChannel].first, QVariant(GreenChannel));
323 d->channelCB->addItem(channelDescMap[BlueChannel].first, QVariant(BlueChannel));
324 d->channelCB->addItem(channelDescMap[AlphaChannel].first, QVariant(AlphaChannel));
325 channelCBDescr.append(QLatin1String("<p>"));
326 channelCBDescr.append(channelDescMap[RedChannel].second).append(QLatin1String("<br/>"));
327 channelCBDescr.append(channelDescMap[GreenChannel].second).append(QLatin1String("<br/>"));
328 channelCBDescr.append(channelDescMap[BlueChannel].second).append(QLatin1String("<br/>"));
329 channelCBDescr.append(channelDescMap[AlphaChannel].second);
330 channelCBDescr.append(QLatin1String("</p>"));
331 break;
332
333 case LRGB:
334 d->channelCB->clear();
335 d->channelCB->addItem(channelDescMap[LuminosityChannel].first, QVariant(LuminosityChannel));
336 d->channelCB->addItem(channelDescMap[RedChannel].first, QVariant(RedChannel));
337 d->channelCB->addItem(channelDescMap[GreenChannel].first, QVariant(GreenChannel));
338 d->channelCB->addItem(channelDescMap[BlueChannel].first, QVariant(BlueChannel));
339 channelCBDescr.append(QLatin1String("<p>"));
340 channelCBDescr.append(channelDescMap[LuminosityChannel].second).append(QLatin1String("<br/>"));
341 channelCBDescr.append(channelDescMap[RedChannel].second).append(QLatin1String("<br/>"));
342 channelCBDescr.append(channelDescMap[GreenChannel].second).append(QLatin1String("<br/>"));
343 channelCBDescr.append(channelDescMap[BlueChannel].second);
344 channelCBDescr.append(QLatin1String("</p>"));
345 break;
346
347 case LRGBA:
348 d->channelCB->clear();
349 d->channelCB->addItem(channelDescMap[LuminosityChannel].first, QVariant(LuminosityChannel));
350 d->channelCB->addItem(channelDescMap[RedChannel].first, QVariant(RedChannel));
351 d->channelCB->addItem(channelDescMap[GreenChannel].first, QVariant(GreenChannel));
352 d->channelCB->addItem(channelDescMap[BlueChannel].first, QVariant(BlueChannel));
353 d->channelCB->addItem(channelDescMap[AlphaChannel].first, QVariant(AlphaChannel));
354 channelCBDescr.append(QLatin1String("<p>"));
355 channelCBDescr.append(channelDescMap[LuminosityChannel].second).append(QLatin1String("<br/>"));
356 channelCBDescr.append(channelDescMap[RedChannel].second).append(QLatin1String("<br/>"));
357 channelCBDescr.append(channelDescMap[GreenChannel].second).append(QLatin1String("<br/>"));
358 channelCBDescr.append(channelDescMap[BlueChannel].second).append(QLatin1String("<br/>"));
359 channelCBDescr.append(channelDescMap[AlphaChannel].second);
360 channelCBDescr.append(QLatin1String("</p>"));
361 break;
362
363 case LRGBC:
364 d->channelCB->clear();
365 d->channelCB->addItem(channelDescMap[LuminosityChannel].first, QVariant(LuminosityChannel));
366 d->channelCB->addItem(channelDescMap[RedChannel].first, QVariant(RedChannel));
367 d->channelCB->addItem(channelDescMap[GreenChannel].first, QVariant(GreenChannel));
368 d->channelCB->addItem(channelDescMap[BlueChannel].first, QVariant(BlueChannel));
369 d->channelCB->addItem(channelDescMap[ColorChannels].first, QVariant(ColorChannels));
370 channelCBDescr.append(QLatin1String("<p>"));
371 channelCBDescr.append(channelDescMap[LuminosityChannel].second).append(QLatin1String("<br/>"));
372 channelCBDescr.append(channelDescMap[RedChannel].second).append(QLatin1String("<br/>"));
373 channelCBDescr.append(channelDescMap[GreenChannel].second).append(QLatin1String("<br/>"));
374 channelCBDescr.append(channelDescMap[BlueChannel].second).append(QLatin1String("<br/>"));
375 channelCBDescr.append(channelDescMap[ColorChannels].second);
376 channelCBDescr.append(QLatin1String("</p>"));
377 break;
378
379 case LRGBAC:
380 d->channelCB->clear();
381 d->channelCB->addItem(channelDescMap[LuminosityChannel].first, QVariant(LuminosityChannel));
382 d->channelCB->addItem(channelDescMap[RedChannel].first, QVariant(RedChannel));
383 d->channelCB->addItem(channelDescMap[GreenChannel].first, QVariant(GreenChannel));
384 d->channelCB->addItem(channelDescMap[BlueChannel].first, QVariant(BlueChannel));
385 d->channelCB->addItem(channelDescMap[AlphaChannel].first, QVariant(AlphaChannel));
386 d->channelCB->addItem(channelDescMap[ColorChannels].first, QVariant(ColorChannels));
387 channelCBDescr.append(QLatin1String("<p>"));
388 channelCBDescr.append(channelDescMap[LuminosityChannel].second).append(QLatin1String("<br/>"));
389 channelCBDescr.append(channelDescMap[RedChannel].second).append(QLatin1String("<br/>"));
390 channelCBDescr.append(channelDescMap[GreenChannel].second).append(QLatin1String("<br/>"));
391 channelCBDescr.append(channelDescMap[BlueChannel].second).append(QLatin1String("<br/>"));
392 channelCBDescr.append(channelDescMap[AlphaChannel].second).append(QLatin1String("<br/>"));
393 channelCBDescr.append(channelDescMap[ColorChannels].second);
394 channelCBDescr.append(QLatin1String("</p>"));
395 break;
396
397 default:
398 break;
399 }
400
401 d->channelCB->setWhatsThis(channelCBDescr);
402 }
403
404 } // namespace Digikam
405