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