diff --git a/rtdata/images/svg/histogram-ellipsis-small.svg b/rtdata/images/svg/histogram-ellipsis-small.svg
new file mode 100644
index 000000000..4fdb17907
--- /dev/null
+++ b/rtdata/images/svg/histogram-ellipsis-small.svg
@@ -0,0 +1,135 @@
+
+
diff --git a/rtdata/images/svg/histogram-bayer-on-small.svg b/rtdata/images/svg/histogram-type-histogram-raw-small.svg
similarity index 100%
rename from rtdata/images/svg/histogram-bayer-on-small.svg
rename to rtdata/images/svg/histogram-type-histogram-raw-small.svg
diff --git a/rtdata/images/svg/histogram-bayer-off-small.svg b/rtdata/images/svg/histogram-type-histogram-small.svg
similarity index 59%
rename from rtdata/images/svg/histogram-bayer-off-small.svg
rename to rtdata/images/svg/histogram-type-histogram-small.svg
index 5d6c439fb..1d2f6547e 100644
--- a/rtdata/images/svg/histogram-bayer-off-small.svg
+++ b/rtdata/images/svg/histogram-type-histogram-small.svg
@@ -1,6 +1,4 @@
-
-
diff --git a/rtdata/images/svg/histogram-type-parade-small.svg b/rtdata/images/svg/histogram-type-parade-small.svg
new file mode 100644
index 000000000..f299f8ab2
--- /dev/null
+++ b/rtdata/images/svg/histogram-type-parade-small.svg
@@ -0,0 +1,132 @@
+
+
diff --git a/rtdata/images/svg/histogram-type-vectorscope-hc-small.svg b/rtdata/images/svg/histogram-type-vectorscope-hc-small.svg
new file mode 100644
index 000000000..ef2e8b51f
--- /dev/null
+++ b/rtdata/images/svg/histogram-type-vectorscope-hc-small.svg
@@ -0,0 +1,131 @@
+
+
diff --git a/rtdata/images/svg/histogram-type-vectorscope-hs-small.svg b/rtdata/images/svg/histogram-type-vectorscope-hs-small.svg
new file mode 100644
index 000000000..62bbf9586
--- /dev/null
+++ b/rtdata/images/svg/histogram-type-vectorscope-hs-small.svg
@@ -0,0 +1,135 @@
+
+
diff --git a/rtdata/images/svg/histogram-type-waveform-small.svg b/rtdata/images/svg/histogram-type-waveform-small.svg
new file mode 100644
index 000000000..5147ab2fc
--- /dev/null
+++ b/rtdata/images/svg/histogram-type-waveform-small.svg
@@ -0,0 +1,122 @@
+
+
diff --git a/rtdata/languages/default b/rtdata/languages/default
index 51d11b729..0263e99fd 100644
--- a/rtdata/languages/default
+++ b/rtdata/languages/default
@@ -246,11 +246,20 @@ GIMP_PLUGIN_INFO;Welcome to the RawTherapee GIMP plugin!\nOnce you are done edit
HISTOGRAM_TOOLTIP_B;Show/Hide blue histogram.
HISTOGRAM_TOOLTIP_BAR;Show/Hide RGB indicator bar.
HISTOGRAM_TOOLTIP_CHRO;Show/Hide chromaticity histogram.
+HISTOGRAM_TOOLTIP_CROSSHAIR;Show/Hide indicator crosshair.
HISTOGRAM_TOOLTIP_G;Show/Hide green histogram.
HISTOGRAM_TOOLTIP_L;Show/Hide CIELab luminance histogram.
HISTOGRAM_TOOLTIP_MODE;Toggle between linear, log-linear and log-log scaling of the histogram.
HISTOGRAM_TOOLTIP_R;Show/Hide red histogram.
HISTOGRAM_TOOLTIP_RAW;Show/Hide raw histogram.
+HISTOGRAM_TOOLTIP_SHOW_OPTIONS;Toggle visibility of the scope option buttons.
+HISTOGRAM_TOOLTIP_TRACE_BRIGHTNESS;Adjust scope brightness.
+HISTOGRAM_TOOLTIP_TYPE_HISTOGRAM;Histogram
+HISTOGRAM_TOOLTIP_TYPE_HISTOGRAM_RAW;Raw Histogram
+HISTOGRAM_TOOLTIP_TYPE_PARADE;RGB Parade
+HISTOGRAM_TOOLTIP_TYPE_WAVEFORM;Waveform
+HISTOGRAM_TOOLTIP_TYPE_VECTORSCOPE_HC;Hue-Chroma Vectorscope
+HISTOGRAM_TOOLTIP_TYPE_VECTORSCOPE_HS;Hue-Saturation Vectorscope
HISTORY_CHANGED;Changed
HISTORY_CUSTOMCURVE;Custom curve
HISTORY_FROMCLIPBOARD;From clipboard
diff --git a/rtdata/themes/RawTherapee-GTK3-20_.css b/rtdata/themes/RawTherapee-GTK3-20_.css
index 76f0004ee..f4f9ddb7f 100644
--- a/rtdata/themes/RawTherapee-GTK3-20_.css
+++ b/rtdata/themes/RawTherapee-GTK3-20_.css
@@ -720,6 +720,36 @@ flowboxchild:selected {
margin: 0;
}
+/* Vertical version of slider. */
+#histScale {
+ min-height: 4em;
+ min-width: 0.4166666666666666em;
+ margin: 0.5833333333333333em 0 0 0;
+}
+#histScale trough {
+ padding: 0.583333333333333333em 0;
+}
+#histScale trough highlight {
+ margin: -0.583333333333333333em 0;
+ padding: 0.1em 0 0 0.1em;
+}
+#histScale.fine-tune trough highlight {
+ padding: 0.5em 0 0 0.5em;
+}
+
+/* Copied from button.flat style. */
+button.radio#histButton {
+ background-image: none;
+}
+
+button.radio#histButton:checked {
+ background-image: linear-gradient(#343434, #2E2E2E, #292929);
+}
+
+button.radio#histButton:hover {
+ background-image: linear-gradient(shade(#343434,1.3), shade(#2E2E2E,1.3), shade(#292929,1.3));
+}
+
/*** end ***************************************************************************************/
#MyExpander {
diff --git a/rtdata/themes/TooWaBlue-GTK3-20_.css b/rtdata/themes/TooWaBlue-GTK3-20_.css
index ce4bb8d28..c4300413e 100644
--- a/rtdata/themes/TooWaBlue-GTK3-20_.css
+++ b/rtdata/themes/TooWaBlue-GTK3-20_.css
@@ -454,6 +454,47 @@ filechooser placessidebar list row:selected {
margin: 0;
}
+/* Vertical version of slider. */
+#histScale {
+ min-height: 4em;
+ min-width: 1.833333333333333333em;
+ margin: -0.333333333333333333em 0;
+}
+#histScale trough {
+ padding: 0.583333333333333333em 0;
+}
+#histScale highlight {
+ background-image: linear-gradient(to right, shade (@accent-color2,1.22), shade(@accent-color2,.88));
+ margin: -0.583333333333333333em 0;
+ padding: 0.333333333333333333em 0 0 0.333333333333333333em;
+}
+#histScale slider {
+}
+#histScale.fine-tune highlight {
+ padding: 0.5em 0 0 0.5em;
+}
+
+/* Copied from button.flat style. */
+button.radio#histButton {
+ border: 0.083333333333333333em solid transparent;
+ box-shadow: none;
+ background-image: none;
+ background-color: transparent;
+}
+button.radio#histButton:hover {
+ border-color: @bg-button-border;
+ box-shadow: inset 0 0.083333333333333333em rgba(242, 242, 242, 0.1);
+ background-image: linear-gradient(to bottom, rgba(100,100,100,.3), rgba(30,30,30,.3));
+ background-color: @bg-button-hover;
+}
+button.radio#histButton:active,
+button.radio#histButton:checked {
+ border-color: @bg-button-border;
+ box-shadow: inset 0 0.1em rgba(242, 242, 242, 0.08);
+ background-image: linear-gradient(to bottom, rgba(100,100,100,.3), rgba(30,30,30,.3));
+ background-color: @bg-button-active;
+}
+
/*** end ***************************************************************************************/
/*** Separator *********************************************************************************/
diff --git a/rtengine/array2D.h b/rtengine/array2D.h
index 512f7bcc1..ca4db3d06 100644
--- a/rtengine/array2D.h
+++ b/rtengine/array2D.h
@@ -64,8 +64,7 @@ constexpr unsigned int ARRAY2D_BYREFERENCE = 2;
template
-class array2D :
- public rtengine::NonCopyable
+class array2D
{
private:
@@ -125,6 +124,25 @@ public:
}
}
+ array2D(const array2D& other) :
+ width(other.width),
+ buffer(other.buffer)
+ {
+ initRows(other.rows.size());
+ }
+
+ array2D& operator =(const array2D& other)
+ {
+ if (this != &other) {
+ free();
+ width = other.width;
+ buffer = other.buffer;
+ initRows(other.rows.size());
+ }
+
+ return *this;
+ }
+
void fill(const T val, bool multiThread = false)
{
const ssize_t height = rows.size();
@@ -140,6 +158,7 @@ public:
{
buffer.clear();
rows.clear();
+ width = 0;
}
// use with indices
@@ -192,6 +211,24 @@ public:
}
}
+ array2D& operator+=(const array2D& rhs)
+ {
+ if (rhs.getWidth() == this->getWidth() && rhs.getHeight() == this->getHeight()) {
+ for (int i = 0; i < getHeight(); ++i) {
+#ifdef _OPENMP
+ #pragma omp simd
+#endif
+
+ for (int j = 0; j < getWidth(); ++j) {
+ rows[i][j] += rhs[i][j];
+ }
+ }
+ }
+
+ return *this;
+ }
+
+
int getWidth() const
{
return width;
diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc
index 405820b20..f9c4b786c 100644
--- a/rtengine/improccoordinator.cc
+++ b/rtengine/improccoordinator.cc
@@ -17,16 +17,14 @@
* along with RawTherapee. If not, see .
*/
#include
-#include
-#include
#include
#include "improccoordinator.h"
+#include "array2D.h"
#include "cieimage.h"
#include "color.h"
#include "colortemp.h"
-#include "jaggedarray.h"
#include "curves.h"
#include "dcp.h"
#include "iccstore.h"
@@ -47,6 +45,9 @@
namespace
{
+
+constexpr int VECTORSCOPE_SIZE = 128;
+
using rtengine::Coord2D;
Coord2D translateCoord(const rtengine::ImProcFunctions& ipf, int fw, int fh, int x, int y) {
@@ -129,6 +130,21 @@ ImProcCoordinator::ImProcCoordinator() :
histLRETI(256),
+ hist_lrgb_dirty(false),
+ hist_raw_dirty(false),
+
+ vectorscopeScale(0),
+ vectorscope_hc_dirty(false),
+ vectorscope_hs_dirty(false),
+ vectorscope_hc(VECTORSCOPE_SIZE, VECTORSCOPE_SIZE),
+ vectorscope_hs(VECTORSCOPE_SIZE, VECTORSCOPE_SIZE),
+ waveformScale(0),
+ waveform_dirty(false),
+ waveformRed(0, 0),
+ waveformGreen(0, 0),
+ waveformBlue(0, 0),
+ waveformLuma(0, 0),
+
CAMBrightCurveJ(), CAMBrightCurveQ(),
rCurve(),
@@ -348,6 +364,7 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
}
imgsrc->getRAWHistogram(histRedRaw, histGreenRaw, histBlueRaw);
+ hist_raw_dirty = !(hListener && hListener->updateHistogramRaw());
highDetailPreprocessComputed = highDetailNeeded;
@@ -1638,9 +1655,21 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange)
imageListener->imageReady(params->crop);
}
+ hist_lrgb_dirty = vectorscope_hc_dirty = vectorscope_hs_dirty = waveform_dirty = true;
if (hListener) {
- updateLRGBHistograms();
- hListener->histogramChanged(histRed, histGreen, histBlue, histLuma, histToneCurve, histLCurve, histCCurve, /*histCLurve, histLLCurve,*/ histLCAM, histCCAM, histRedRaw, histGreenRaw, histBlueRaw, histChroma, histLRETI);
+ if (hListener->updateHistogram()) {
+ updateLRGBHistograms();
+ }
+ if (hListener->updateVectorscopeHC()) {
+ updateVectorscopeHC();
+ }
+ if (hListener->updateVectorscopeHS()) {
+ updateVectorscopeHS();
+ }
+ if (hListener->updateWaveform()) {
+ updateWaveforms();
+ }
+ notifyHistogramChanged();
}
}
@@ -1741,8 +1770,42 @@ void ImProcCoordinator::setScale(int prevscale)
}
-void ImProcCoordinator::updateLRGBHistograms()
+void ImProcCoordinator::notifyHistogramChanged()
{
+ if (hListener) {
+ hListener->histogramChanged(
+ histRed,
+ histGreen,
+ histBlue,
+ histLuma,
+ histToneCurve,
+ histLCurve,
+ histCCurve,
+ histLCAM,
+ histCCAM,
+ histRedRaw,
+ histGreenRaw,
+ histBlueRaw,
+ histChroma,
+ histLRETI,
+ vectorscopeScale,
+ vectorscope_hc,
+ vectorscope_hs,
+ waveformScale,
+ waveformRed,
+ waveformGreen,
+ waveformBlue,
+ waveformLuma
+ );
+ }
+}
+
+bool ImProcCoordinator::updateLRGBHistograms()
+{
+
+ if (!hist_lrgb_dirty) {
+ return false;
+ }
int x1, y1, x2, y2;
params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2);
@@ -1800,6 +1863,159 @@ void ImProcCoordinator::updateLRGBHistograms()
}
}
+ hist_lrgb_dirty = false;
+ return true;
+
+}
+
+bool ImProcCoordinator::updateVectorscopeHC()
+{
+ if (!workimg || !vectorscope_hc_dirty) {
+ return false;
+ }
+
+ int x1, y1, x2, y2;
+ params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2);
+
+ constexpr int size = VECTORSCOPE_SIZE;
+ constexpr float norm_factor = size / (128.f * 655.36f);
+ vectorscope_hc.fill(0);
+
+ vectorscopeScale = (x2 - x1) * (y2 - y1);
+
+ const std::unique_ptr a(new float[vectorscopeScale]);
+ const std::unique_ptr b(new float[vectorscopeScale]);
+ const std::unique_ptr L(new float[vectorscopeScale]);
+ ipf.rgb2lab(*workimg, x1, y1, x2 - x1, y2 - y1, L.get(), a.get(), b.get(), params->icm);
+#ifdef _OPENMP
+ #pragma omp parallel
+#endif
+ {
+ array2D vectorscopeThr(size, size, ARRAY2D_CLEAR_DATA);
+#ifdef _OPENMP
+ #pragma omp for nowait
+#endif
+ for (int i = y1; i < y2; ++i) {
+ for (int j = x1, ofs_lab = (i - y1) * (x2 - x1); j < x2; ++j, ++ofs_lab) {
+ const int col = norm_factor * a[ofs_lab] + size / 2 + 0.5f;
+ const int row = norm_factor * b[ofs_lab] + size / 2 + 0.5f;
+ if (col >= 0 && col < size && row >= 0 && row < size) {
+ vectorscopeThr[row][col]++;
+ }
+ }
+ }
+#ifdef _OPENMP
+ #pragma omp critical
+#endif
+ {
+ vectorscope_hc += vectorscopeThr;
+ }
+ }
+
+ vectorscope_hc_dirty = false;
+ return true;
+}
+
+bool ImProcCoordinator::updateVectorscopeHS()
+{
+ if (!workimg || !vectorscope_hs_dirty) {
+ return false;
+ }
+
+ int x1, y1, x2, y2;
+ params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2);
+
+ constexpr int size = VECTORSCOPE_SIZE;
+ vectorscope_hs.fill(0);
+
+ vectorscopeScale = (x2 - x1) * (y2 - y1);
+
+#ifdef _OPENMP
+ #pragma omp parallel
+#endif
+ {
+ array2D vectorscopeThr(size, size, ARRAY2D_CLEAR_DATA);
+#ifdef _OPENMP
+ #pragma omp for nowait
+#endif
+ for (int i = y1; i < y2; ++i) {
+ int ofs = (i * pW + x1) * 3;
+ for (int j = x1; j < x2; ++j) {
+ const float red = 257.f * workimg->data[ofs++];
+ const float green = 257.f * workimg->data[ofs++];
+ const float blue = 257.f * workimg->data[ofs++];
+ float h, s, l;
+ Color::rgb2hslfloat(red, green, blue, h, s, l);
+ const auto sincosval = xsincosf(2.f * RT_PI_F * h);
+ const int col = s * sincosval.y * (size / 2) + size / 2;
+ const int row = s * sincosval.x * (size / 2) + size / 2;
+ if (col >= 0 && col < size && row >= 0 && row < size) {
+ vectorscopeThr[row][col]++;
+ }
+ }
+ }
+#ifdef _OPENMP
+ #pragma omp critical
+#endif
+ {
+ vectorscope_hs += vectorscopeThr;
+ }
+ }
+
+ vectorscope_hs_dirty = false;
+ return true;
+}
+
+bool ImProcCoordinator::updateWaveforms()
+{
+ if (!workimg) {
+ // free memory
+ waveformRed.free();
+ waveformGreen.free();
+ waveformBlue.free();
+ waveformLuma.free();
+ return true;
+ }
+
+ if (!waveform_dirty) {
+ return false;
+ }
+
+ int x1, y1, x2, y2;
+ params->crop.mapToResized(pW, pH, scale, x1, x2, y1, y2);
+ int waveform_width = waveformRed.getWidth();
+
+ if (waveform_width != x2 - x1) {
+ // Resize waveform arrays.
+ waveform_width = x2 - x1;
+ waveformRed(waveform_width, 256);
+ waveformGreen(waveform_width, 256);
+ waveformBlue(waveform_width, 256);
+ waveformLuma(waveform_width, 256);
+ }
+
+ // Start with zero.
+ waveformRed.fill(0);
+ waveformGreen.fill(0);
+ waveformBlue.fill(0);
+ waveformLuma.fill(0);
+
+ constexpr float luma_factor = 255.f / 32768.f;
+ for (int i = y1; i < y2; i++) {
+ int ofs = (i * pW + x1) * 3;
+ float* L_row = nprevl->L[i] + x1;
+
+ for (int j = 0; j < waveform_width; j++) {
+ waveformRed[workimg->data[ofs++]][j]++;
+ waveformGreen[workimg->data[ofs++]][j]++;
+ waveformBlue[workimg->data[ofs++]][j]++;
+ waveformLuma[LIM(L_row[j] * luma_factor, 0, 255)][j]++;
+ }
+ }
+
+ waveformScale = y2 - y1;
+ waveform_dirty = false;
+ return true;
}
bool ImProcCoordinator::getAutoWB(double& temp, double& green, double equal, double tempBias)
@@ -2244,4 +2460,61 @@ void ImProcCoordinator::setHighQualComputed()
highQualityComputed = true;
}
+void ImProcCoordinator::requestUpdateWaveform()
+{
+ if (!hListener) {
+ return;
+ }
+ bool updated = updateWaveforms();
+ if (updated) {
+ notifyHistogramChanged();
+ }
+}
+
+void ImProcCoordinator::requestUpdateHistogram()
+{
+ if (!hListener) {
+ return;
+ }
+ bool updated = updateLRGBHistograms();
+ if (updated) {
+ notifyHistogramChanged();
+ }
+}
+
+void ImProcCoordinator::requestUpdateHistogramRaw()
+{
+ if (!hListener) {
+ return;
+ }
+ // Don't need to actually update histogram because it is always
+ // up-to-date.
+ if (hist_raw_dirty) {
+ hist_raw_dirty = false;
+ notifyHistogramChanged();
+ }
+}
+
+void ImProcCoordinator::requestUpdateVectorscopeHC()
+{
+ if (!hListener) {
+ return;
+ }
+ bool updated = updateVectorscopeHC();
+ if (updated) {
+ notifyHistogramChanged();
+ }
+}
+
+void ImProcCoordinator::requestUpdateVectorscopeHS()
+{
+ if (!hListener) {
+ return;
+ }
+ bool updated = updateVectorscopeHS();
+ if (updated) {
+ notifyHistogramChanged();
+ }
+}
+
}
diff --git a/rtengine/improccoordinator.h b/rtengine/improccoordinator.h
index d90ee68ae..83bee6955 100644
--- a/rtengine/improccoordinator.h
+++ b/rtengine/improccoordinator.h
@@ -54,7 +54,7 @@ class Crop;
* but using this class' LUT and other precomputed parameters. The main preview area is displaying a non framed Crop object,
* while detail windows are framed Crop objects.
*/
-class ImProcCoordinator final : public StagedImageProcessor
+class ImProcCoordinator final : public StagedImageProcessor, public HistogramObservable
{
friend class Crop;
@@ -126,6 +126,16 @@ protected:
LUTu histBlue, histBlueRaw;
LUTu histLuma, histToneCurve, histToneCurveBW, histLCurve, histCCurve;
LUTu histLLCurve, histLCAM, histCCAM, histClad, bcabhist, histChroma, histLRETI;
+ bool hist_lrgb_dirty;
+ /// Used to simulate a lazy update of the raw histogram.
+ bool hist_raw_dirty;
+ int vectorscopeScale;
+ bool vectorscope_hc_dirty, vectorscope_hs_dirty;
+ array2D vectorscope_hc, vectorscope_hs;
+ /// Waveform's intensity. Same as height of reference image.
+ int waveformScale;
+ bool waveform_dirty;
+ array2D waveformRed, waveformGreen, waveformBlue, waveformLuma;
LUTf CAMBrightCurveJ, CAMBrightCurveQ;
@@ -195,8 +205,16 @@ protected:
MyMutex minit; // to gain mutually exclusive access to ... to what exactly?
+ void notifyHistogramChanged();
void reallocAll();
- void updateLRGBHistograms();
+ /// Updates L, R, G, and B histograms. Returns true unless not updated.
+ bool updateLRGBHistograms();
+ /// Updates the H-C vectorscope. Returns true unless not updated.
+ bool updateVectorscopeHC();
+ /// Updates the H-S vectorscope. Returns true unless not updated.
+ bool updateVectorscopeHS();
+ /// Updates all waveforms. Returns true unless not updated.
+ bool updateWaveforms();
void setScale(int prevscale);
void updatePreviewImage (int todo, bool panningRelatedChange);
@@ -449,7 +467,13 @@ public:
}
void setHistogramListener (HistogramListener *h) override
{
+ if (hListener) {
+ hListener->setObservable(nullptr);
+ }
hListener = h;
+ if (h) {
+ h->setObservable(this);
+ }
}
void setAutoCamListener (AutoCamListener* acl) override
{
@@ -550,6 +574,11 @@ public:
} denoiseInfoStore;
+ void requestUpdateHistogram() override;
+ void requestUpdateHistogramRaw() override;
+ void requestUpdateVectorscopeHC() override;
+ void requestUpdateVectorscopeHS() override;
+ void requestUpdateWaveform() override;
};
}
diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc
index 0fd3e954c..05cc115d9 100644
--- a/rtengine/improcfun.cc
+++ b/rtengine/improcfun.cc
@@ -5716,6 +5716,116 @@ void ImProcFunctions::rgb2lab(const Imagefloat &src, LabImage &dst, const Glib::
}
}
+void ImProcFunctions::rgb2lab(const Image8 &src, int x, int y, int w, int h, float L[], float a[], float b[], const procparams::ColorManagementParams &icm, bool consider_histogram_settings) const
+{ // Adapted from ImProcFunctions::lab2rgb
+ const int src_width = src.getWidth();
+ const int src_height = src.getHeight();
+
+ if (x < 0) {
+ x = 0;
+ }
+
+ if (y < 0) {
+ y = 0;
+ }
+
+ if (x + w > src_width) {
+ w = src_width - x;
+ }
+
+ if (y + h > src_height) {
+ h = src_height - y;
+ }
+
+ Glib::ustring profile;
+
+ cmsHPROFILE oprof = nullptr;
+
+ if (settings->HistogramWorking && consider_histogram_settings) {
+ profile = icm.workingProfile;
+ } else {
+ profile = icm.outputProfile;
+
+ if (icm.outputProfile.empty() || icm.outputProfile == ColorManagementParams::NoICMString) {
+ profile = "sRGB";
+ }
+ oprof = ICCStore::getInstance()->getProfile(profile);
+ }
+
+ if (oprof) {
+ cmsUInt32Number flags = cmsFLAGS_NOOPTIMIZE | cmsFLAGS_NOCACHE; // NOCACHE is important for thread safety
+
+ if (icm.outputBPC) {
+ flags |= cmsFLAGS_BLACKPOINTCOMPENSATION;
+ }
+
+ lcmsMutex->lock();
+ cmsHPROFILE LabIProf = cmsCreateLab4Profile(nullptr);
+ cmsHTRANSFORM hTransform = cmsCreateTransform (oprof, TYPE_RGB_8, LabIProf, TYPE_Lab_FLT, icm.outputIntent, flags);
+ cmsCloseProfile(LabIProf);
+ lcmsMutex->unlock();
+
+ // cmsDoTransform is relatively expensive
+#ifdef _OPENMP
+ #pragma omp parallel
+#endif
+ {
+ AlignedBuffer oBuf(3 * w);
+ float *outbuffer = oBuf.data;
+ int condition = y + h;
+
+#ifdef _OPENMP
+ #pragma omp for schedule(dynamic,16)
+#endif
+
+ for (int i = y; i < condition; i++) {
+ const int ix = 3 * (x + i * src_width);
+ int iy = 0;
+ float* rL = L + (i - y) * w;
+ float* ra = a + (i - y) * w;
+ float* rb = b + (i - y) * w;
+
+ cmsDoTransform (hTransform, src.data + ix, outbuffer, w);
+
+ for (int j = 0; j < w; j++) {
+ rL[j] = outbuffer[iy++] * 327.68f;
+ ra[j] = outbuffer[iy++] * 327.68f;
+ rb[j] = outbuffer[iy++] * 327.68f;
+ }
+ }
+ } // End of parallelization
+
+ cmsDeleteTransform(hTransform);
+ } else {
+ TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix(profile);
+ const float wp[3][3] = {
+ {static_cast(wprof[0][0]), static_cast(wprof[0][1]), static_cast(wprof[0][2])},
+ {static_cast(wprof[1][0]), static_cast(wprof[1][1]), static_cast(wprof[1][2])},
+ {static_cast(wprof[2][0]), static_cast(wprof[2][1]), static_cast(wprof[2][2])}
+ };
+
+ const int x2 = x + w;
+ const int y2 = y + h;
+ constexpr float rgb_factor = 65355.f / 255.f;
+
+#ifdef _OPENMP
+ #pragma omp parallel for schedule(dynamic,16) if (multiThread)
+#endif
+
+ for (int i = y; i < y2; i++) {
+ int offset = (i - y) * w;
+ for (int j = x; j < x2; j++) {
+ float X, Y, Z;
+ // lab2rgb uses gamma2curve, which is gammatab_srgb.
+ const auto& igamma = Color::igammatab_srgb;
+ Color::rgbxyz(igamma[rgb_factor * src.r(i, j)], igamma[rgb_factor * src.g(i, j)], igamma[rgb_factor * src.b(i, j)], X, Y, Z, wp);
+ Color::XYZ2Lab(X, Y, Z, L[offset], a[offset], b[offset]);
+ offset++;
+ }
+ }
+ }
+}
+
void ImProcFunctions::lab2rgb(const LabImage &src, Imagefloat &dst, const Glib::ustring &workingSpace)
{
TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix(workingSpace);
diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h
index d3ee81701..e44780a13 100644
--- a/rtengine/improcfun.h
+++ b/rtengine/improcfun.h
@@ -449,6 +449,7 @@ public:
void labColorCorrectionRegions(LabImage *lab);
Image8* lab2rgb(LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, bool consider_histogram_settings = true);
+ void rgb2lab(const Image8 &src, int x, int y, int w, int h, float L[], float a[], float b[], const procparams::ColorManagementParams &icm, bool consider_histogram_settings = true) const;
Imagefloat* lab2rgbOut(LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm);
// CieImage *ciec;
void workingtrc(const Imagefloat* src, Imagefloat* dst, int cw, int ch, int mul, const Glib::ustring &profile, double gampos, double slpos, cmsHTRANSFORM &transform, bool normalizeIn = true, bool normalizeOut = true, bool keepTransForm = false) const;
diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h
index 1671ae1f5..6f6baccd4 100644
--- a/rtengine/rtengine.h
+++ b/rtengine/rtengine.h
@@ -42,6 +42,9 @@
*
*/
+template
+class array2D;
+
template
class LUT;
@@ -302,6 +305,8 @@ public:
virtual void sizeChanged(int w, int h, int ow, int oh) = 0;
};
+class HistogramObservable;
+
/** This listener is used when the histogram of the final image has changed. */
class HistogramListener
{
@@ -327,8 +332,43 @@ public:
const LUTu& histGreenRaw,
const LUTu& histBlueRaw,
const LUTu& histChroma,
- const LUTu& histLRETI
+ const LUTu& histLRETI,
+ int vectorscopeScale,
+ const array2D& vectorscopeHC,
+ const array2D& vectorscopeHS,
+ int waveformScale,
+ const array2D& waveformRed,
+ const array2D& waveformGreen,
+ const array2D& waveformBlue,
+ const array2D& waveformLuma
) = 0;
+ /** Tells which observable is notifying the listener. */
+ virtual void setObservable(HistogramObservable* observable) = 0;
+ /** Returns if the listener wants the histogram to be updated. */
+ virtual bool updateHistogram(void) const = 0;
+ /** Returns if the listener wants the raw histogram to be updated. */
+ virtual bool updateHistogramRaw(void) const = 0;
+ /** Returns if the listener wants the H-C vectorscope to be updated. */
+ virtual bool updateVectorscopeHC(void) const = 0;
+ /** Returns if the listener wants the H-S vectorscope to be updated. */
+ virtual bool updateVectorscopeHS(void) const = 0;
+ /** Returns if the listener wants the waveform to be updated. */
+ virtual bool updateWaveform(void) const = 0;
+};
+
+class HistogramObservable
+{
+public:
+ /** Tells the observable to update the histogram data. */
+ virtual void requestUpdateHistogram() = 0;
+ /** Tells the observable to update the raw histogram data. */
+ virtual void requestUpdateHistogramRaw() = 0;
+ /** Tells the observable to update the H-C vectorscope data. */
+ virtual void requestUpdateVectorscopeHC() = 0;
+ /** Tells the observable to update the H-S vectorscope data. */
+ virtual void requestUpdateVectorscopeHS() = 0;
+ /** Tells the observable to update the waveform data. */
+ virtual void requestUpdateWaveform() = 0;
};
/** This listener is used when the auto exposure has been recomputed (e.g. when the clipping ratio changed). */
diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc
index 9d6860e0f..34d677206 100644
--- a/rtgui/editorpanel.cc
+++ b/rtgui/editorpanel.cc
@@ -21,6 +21,7 @@
#include
+#include "../rtengine/array2D.h"
#include "../rtengine/imagesource.h"
#include "../rtengine/iccstore.h"
#include "batchqueue.h"
@@ -47,6 +48,8 @@
using namespace rtengine::procparams;
+using ScopeType = Options::ScopeType;
+
namespace
{
@@ -470,7 +473,8 @@ EditorPanel::EditorPanel (FilePanel* filePanel)
iBeforeLockON (nullptr), iBeforeLockOFF (nullptr), previewHandler (nullptr), beforePreviewHandler (nullptr),
beforeIarea (nullptr), beforeBox (nullptr), afterBox (nullptr), beforeLabel (nullptr), afterLabel (nullptr),
beforeHeaderBox (nullptr), afterHeaderBox (nullptr), parent (nullptr), parentWindow (nullptr), openThm (nullptr),
- selectedFrame(0), isrc (nullptr), ipc (nullptr), beforeIpc (nullptr), err (0), isProcessing (false)
+ selectedFrame(0), isrc (nullptr), ipc (nullptr), beforeIpc (nullptr), err (0), isProcessing (false),
+ histogram_observable(nullptr), histogram_scope_type(ScopeType::NONE)
{
epih = new EditorPanelIdleHelper;
@@ -2245,16 +2249,94 @@ void EditorPanel::histogramChanged(
const LUTu& histGreenRaw,
const LUTu& histBlueRaw,
const LUTu& histChroma,
- const LUTu& histLRETI
+ const LUTu& histLRETI,
+ int vectorscopeScale,
+ const array2D& vectorscopeHC,
+ const array2D& vectorscopeHS,
+ int waveformScale,
+ const array2D& waveformRed,
+ const array2D& waveformGreen,
+ const array2D& waveformBlue,
+ const array2D& waveformLuma
)
{
if (histogramPanel) {
- histogramPanel->histogramChanged(histRed, histGreen, histBlue, histLuma, histChroma, histRedRaw, histGreenRaw, histBlueRaw);
+ histogramPanel->histogramChanged(histRed, histGreen, histBlue, histLuma, histChroma, histRedRaw, histGreenRaw, histBlueRaw, vectorscopeScale, vectorscopeHC, vectorscopeHS, waveformScale, waveformRed, waveformGreen, waveformBlue, waveformLuma);
}
tpc->updateCurveBackgroundHistogram(histToneCurve, histLCurve, histCCurve, histLCAM, histCCAM, histRed, histGreen, histBlue, histLuma, histLRETI);
}
+void EditorPanel::setObservable(rtengine::HistogramObservable* observable)
+{
+ histogram_observable = observable;
+}
+
+bool EditorPanel::updateHistogram(void) const
+{
+ return histogram_scope_type == ScopeType::HISTOGRAM
+ || histogram_scope_type == ScopeType::NONE;
+}
+
+bool EditorPanel::updateHistogramRaw(void) const
+{
+ return histogram_scope_type == ScopeType::HISTOGRAM_RAW
+ || histogram_scope_type == ScopeType::NONE;
+}
+
+bool EditorPanel::updateVectorscopeHC(void) const
+{
+ return
+ histogram_scope_type == ScopeType::VECTORSCOPE_HC
+ || histogram_scope_type == ScopeType::NONE;
+}
+
+bool EditorPanel::updateVectorscopeHS(void) const
+{
+ return
+ histogram_scope_type == ScopeType::VECTORSCOPE_HS
+ || histogram_scope_type == ScopeType::NONE;
+}
+
+bool EditorPanel::updateWaveform(void) const
+{
+ return histogram_scope_type == ScopeType::WAVEFORM
+ || histogram_scope_type == ScopeType::PARADE
+ || histogram_scope_type == ScopeType::NONE;
+}
+
+void EditorPanel::scopeTypeChanged(ScopeType new_type)
+{
+ histogram_scope_type = new_type;
+
+ if (!histogram_observable) {
+ return;
+ }
+
+ // Make sure the new scope is updated since we only actively update the
+ // current scope.
+ switch (new_type) {
+ case ScopeType::HISTOGRAM:
+ histogram_observable->requestUpdateHistogram();
+ break;
+ case ScopeType::HISTOGRAM_RAW:
+ histogram_observable->requestUpdateHistogramRaw();
+ break;
+ case ScopeType::VECTORSCOPE_HC:
+ histogram_observable->requestUpdateVectorscopeHC();
+ break;
+ case ScopeType::VECTORSCOPE_HS:
+ histogram_observable->requestUpdateVectorscopeHS();
+ break;
+ case ScopeType::PARADE:
+ case ScopeType::WAVEFORM:
+ histogram_observable->requestUpdateWaveform();
+ break;
+ case ScopeType::NONE:
+ break;
+ }
+}
+
bool EditorPanel::CheckSidePanelsVisibility()
{
if (tbTopPanel_1) {
@@ -2371,6 +2453,10 @@ void EditorPanel::updateHistogramPosition (int oldPosition, int newPosition)
break;
}
+ if (histogramPanel) {
+ histogramPanel->setPanelListener(this);
+ }
+
iareapanel->imageArea->setPointerMotionHListener (histogramPanel);
}
diff --git a/rtgui/editorpanel.h b/rtgui/editorpanel.h
index 826793507..a277ffd3a 100644
--- a/rtgui/editorpanel.h
+++ b/rtgui/editorpanel.h
@@ -32,6 +32,12 @@
#include "../rtengine/noncopyable.h"
#include "../rtengine/rtengine.h"
+namespace rtengine
+{
+template
+class array2D;
+}
+
class BatchQueueEntry;
class EditorPanel;
class FilePanel;
@@ -55,6 +61,7 @@ class EditorPanel final :
public ThumbnailListener,
public HistoryBeforeLineListener,
public rtengine::HistogramListener,
+ public HistogramPanelListener,
public rtengine::NonCopyable
{
public:
@@ -126,8 +133,25 @@ public:
const LUTu& histGreenRaw,
const LUTu& histBlueRaw,
const LUTu& histChroma,
- const LUTu& histLRETI
+ const LUTu& histLRETI,
+ int vectorscopeScale,
+ const array2D& vectorscopeHC,
+ const array2D& vectorscopeHS,
+ int waveformScale,
+ const array2D& waveformRed,
+ const array2D& waveformGreen,
+ const array2D& waveformBlue,
+ const array2D& waveformLuma
) override;
+ void setObservable(rtengine::HistogramObservable* observable) override;
+ bool updateHistogram(void) const override;
+ bool updateHistogramRaw(void) const override;
+ bool updateVectorscopeHC(void) const override;
+ bool updateVectorscopeHS(void) const override;
+ bool updateWaveform(void) const override;
+
+ // HistogramPanelListener
+ void scopeTypeChanged(Options::ScopeType new_type) override;
// event handlers
void info_toggled ();
@@ -260,4 +284,7 @@ private:
bool isProcessing;
IdleRegister idle_register;
+
+ rtengine::HistogramObservable* histogram_observable;
+ Options::ScopeType histogram_scope_type;
};
diff --git a/rtgui/histogrampanel.cc b/rtgui/histogrampanel.cc
index 62be4c1f4..d7e9bbc69 100644
--- a/rtgui/histogrampanel.cc
+++ b/rtgui/histogrampanel.cc
@@ -22,32 +22,51 @@
#include "options.h"
#include
#include
+#include "../rtengine/array2D.h"
#include "../rtengine/LUT.h"
#include "rtimage.h"
#include "../rtengine/color.h"
using namespace rtengine;
+constexpr float HistogramArea::MAX_BRIGHT;
+constexpr float HistogramArea::MIN_BRIGHT;
+
+using ScopeType = Options::ScopeType;
//
//
// HistogramPanel
-HistogramPanel::HistogramPanel() :
+HistogramPanel::HistogramPanel () :
pointer_moved_delayed_call(
[this](bool validPos, const Glib::ustring &profile, const Glib::ustring &profileW, int r, int g, int b)
{
+ bool update_hist_area;
+
if (!validPos) {
// do something to un-show vertical bars
- histogramRGBArea->updateBackBuffer(-1, -1, -1);
+ if (histogramRGBArea) {
+ histogramRGBArea->updateBackBuffer(-1, -1, -1);
+ }
+ update_hist_area = histogramArea->updatePointer(-1, -1, -1);
} else {
// do something to show vertical bars
- histogramRGBArea->updateBackBuffer(r, g, b, profile, profileW);
+ if (histogramRGBArea) {
+ histogramRGBArea->updateBackBuffer(r, g, b, profile, profileW);
+ }
+ update_hist_area = histogramArea->updatePointer(r, g, b, profile, profileW);
+ }
+ if (histogramRGBArea) {
+ histogramRGBArea->queue_draw();
+ }
+ if (update_hist_area) {
+ histogramArea->queue_draw();
}
- histogramRGBArea->queue_draw ();
},
50,
100
- )
+ ),
+ panel_listener(nullptr)
{
setExpandAlignProperties(this, true, true, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
set_name("HistogramPanel");
@@ -55,30 +74,49 @@ HistogramPanel::HistogramPanel() :
histogramArea = Gtk::manage (new HistogramArea (this));
setExpandAlignProperties(histogramArea, true, true, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
- histogramRGBArea = Gtk::manage (new HistogramRGBArea ());
- setExpandAlignProperties(histogramRGBArea, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_END);
- histogramRGBArea->show();
+ histogramRGBAreaHori.reset(new HistogramRGBAreaHori());
+ setExpandAlignProperties(histogramRGBAreaHori.get(), true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_END);
+
+ histogramRGBAreaVert.reset(new HistogramRGBAreaVert());
+ setExpandAlignProperties(histogramRGBAreaVert.get(), false, true, Gtk::ALIGN_END, Gtk::ALIGN_FILL);
+
+ switch (options.histogramScopeType) {
+ case ScopeType::NONE:
+ case ScopeType::HISTOGRAM_RAW:
+ case ScopeType::VECTORSCOPE_HC:
+ case ScopeType::VECTORSCOPE_HS:
+ histogramRGBArea = nullptr;
+ break;
+ case ScopeType::PARADE:
+ case ScopeType::WAVEFORM:
+ histogramRGBArea = histogramRGBAreaVert.get();
+ break;
+ case ScopeType::HISTOGRAM:
+ histogramRGBArea = histogramRGBAreaHori.get();
+ break;
+ }
// connecting the two childs
- histogramArea->signal_factor_changed().connect( sigc::mem_fun(*histogramRGBArea, &HistogramRGBArea::factorChanged) );
+ histogramArea->signal_factor_changed().connect( sigc::mem_fun(*histogramRGBAreaHori, &HistogramRGBArea::factorChanged) );
+ histogramArea->signal_factor_changed().connect( sigc::mem_fun(*histogramRGBAreaVert, &HistogramRGBArea::factorChanged) );
gfxGrid = Gtk::manage (new Gtk::Grid ());
- gfxGrid->set_orientation(Gtk::ORIENTATION_VERTICAL);
gfxGrid->set_row_spacing(1);
gfxGrid->set_column_spacing(1);
- histogramRGBArea->setParent(gfxGrid);
gfxGrid->add(*histogramArea);
-
- if (options.histogramBar) {
- gfxGrid->add (*histogramRGBArea);
- }
+ gfxGrid->attach_next_to(
+ *histogramRGBAreaVert, *histogramArea,
+ options.histogramPosition == 1 ? Gtk::POS_RIGHT : Gtk::POS_LEFT
+ );
+ gfxGrid->attach_next_to(*histogramRGBAreaHori, *histogramArea, Gtk::POS_BOTTOM);
+ histogramRGBAreaHori->set_no_show_all();
+ histogramRGBAreaVert->set_no_show_all();
redImage = new RTImage ("histogram-red-on-small.png");
greenImage = new RTImage ("histogram-green-on-small.png");
blueImage = new RTImage ("histogram-blue-on-small.png");
valueImage = new RTImage ("histogram-silver-on-small.png");
chroImage = new RTImage ("histogram-gold-on-small.png");
- rawImage = new RTImage ("histogram-bayer-on-small.png");
barImage = new RTImage ("histogram-bar-on-small.png");
redImage_g = new RTImage ("histogram-red-off-small.png");
@@ -86,21 +124,41 @@ HistogramPanel::HistogramPanel() :
blueImage_g = new RTImage ("histogram-blue-off-small.png");
valueImage_g = new RTImage ("histogram-silver-off-small.png");
chroImage_g = new RTImage ("histogram-gold-off-small.png");
- rawImage_g = new RTImage ("histogram-bayer-off-small.png");
barImage_g = new RTImage ("histogram-bar-off-small.png");
mode0Image = new RTImage ("histogram-mode-linear-small.png");
mode1Image = new RTImage ("histogram-mode-logx-small.png");
mode2Image = new RTImage ("histogram-mode-logxy-small.png");
+ Gtk::Image* histImage = Gtk::manage(new RTImage("histogram-type-histogram-small.png"));
+ Gtk::Image* histRawImage = Gtk::manage(new RTImage("histogram-type-histogram-raw-small.png"));
+ Gtk::Image* paradeImage = Gtk::manage(new RTImage("histogram-type-parade-small.png"));
+ Gtk::Image* waveImage = Gtk::manage(new RTImage("histogram-type-waveform-small.png"));
+ Gtk::Image* vectHcImage = Gtk::manage(new RTImage("histogram-type-vectorscope-hc-small.png"));
+ Gtk::Image* vectHsImage = Gtk::manage(new RTImage("histogram-type-vectorscope-hs-small.png"));
+
showRed = Gtk::manage (new Gtk::ToggleButton ());
showGreen = Gtk::manage (new Gtk::ToggleButton ());
showBlue = Gtk::manage (new Gtk::ToggleButton ());
showValue = Gtk::manage (new Gtk::ToggleButton ());
showChro = Gtk::manage (new Gtk::ToggleButton ());
- showRAW = Gtk::manage (new Gtk::ToggleButton ());
showMode = Gtk::manage (new Gtk::Button ());
showBAR = Gtk::manage (new Gtk::ToggleButton ());
+ scopeOptions = Gtk::manage (new Gtk::ToggleButton ());
+
+ Gtk::RadioButtonGroup scopeTypeGroup;
+ scopeHistBtn = Gtk::manage(new Gtk::RadioButton(scopeTypeGroup));
+ scopeHistRawBtn = Gtk::manage(new Gtk::RadioButton(scopeTypeGroup));
+ scopeParadeBtn = Gtk::manage(new Gtk::RadioButton(scopeTypeGroup));
+ scopeWaveBtn = Gtk::manage(new Gtk::RadioButton(scopeTypeGroup));
+ scopeVectHcBtn = Gtk::manage(new Gtk::RadioButton(scopeTypeGroup));
+ scopeVectHsBtn = Gtk::manage(new Gtk::RadioButton(scopeTypeGroup));
+ scopeHistBtn->set_mode(false);
+ scopeHistRawBtn->set_mode(false);
+ scopeParadeBtn->set_mode(false);
+ scopeWaveBtn->set_mode(false);
+ scopeVectHcBtn->set_mode(false);
+ scopeVectHsBtn->set_mode(false);
showRed->set_name("histButton");
showRed->set_can_focus(false);
@@ -112,40 +170,68 @@ HistogramPanel::HistogramPanel() :
showValue->set_can_focus(false);
showChro->set_name("histButton");
showChro->set_can_focus(false);
- showRAW->set_name("histButton");
- showRAW->set_can_focus(false);
showMode->set_name("histButton");
showMode->set_can_focus(false);
+ scopeOptions->set_name("histButton");
+ scopeOptions->set_can_focus(false);
showBAR->set_name("histButton");
showBAR->set_can_focus(false);
+ scopeHistBtn->set_name("histButton");
+ scopeHistBtn->set_can_focus(false);
+ scopeHistRawBtn->set_name("histButton");
+ scopeHistRawBtn->set_can_focus(false);
+ scopeParadeBtn->set_name("histButton");
+ scopeParadeBtn->set_can_focus(false);
+ scopeWaveBtn->set_name("histButton");
+ scopeWaveBtn->set_can_focus(false);
+ scopeVectHcBtn->set_name("histButton");
+ scopeVectHcBtn->set_can_focus(false);
+ scopeVectHsBtn->set_name("histButton");
+ scopeVectHsBtn->set_can_focus(false);
showRed->set_relief (Gtk::RELIEF_NONE);
showGreen->set_relief (Gtk::RELIEF_NONE);
showBlue->set_relief (Gtk::RELIEF_NONE);
showValue->set_relief (Gtk::RELIEF_NONE);
showChro->set_relief (Gtk::RELIEF_NONE);
- showRAW->set_relief (Gtk::RELIEF_NONE);
showMode->set_relief (Gtk::RELIEF_NONE);
+ scopeOptions->set_relief (Gtk::RELIEF_NONE);
showBAR->set_relief (Gtk::RELIEF_NONE);
+ scopeHistBtn->set_relief (Gtk::RELIEF_NONE);
+ scopeHistRawBtn->set_relief (Gtk::RELIEF_NONE);
+ scopeParadeBtn->set_relief (Gtk::RELIEF_NONE);
+ scopeWaveBtn->set_relief (Gtk::RELIEF_NONE);
+ scopeVectHcBtn->set_relief (Gtk::RELIEF_NONE);
+ scopeVectHsBtn->set_relief (Gtk::RELIEF_NONE);
showRed->set_tooltip_text (M("HISTOGRAM_TOOLTIP_R"));
showGreen->set_tooltip_text (M("HISTOGRAM_TOOLTIP_G"));
showBlue->set_tooltip_text (M("HISTOGRAM_TOOLTIP_B"));
showValue->set_tooltip_text (M("HISTOGRAM_TOOLTIP_L"));
showChro->set_tooltip_text (M("HISTOGRAM_TOOLTIP_CHRO"));
- showRAW->set_tooltip_text (M("HISTOGRAM_TOOLTIP_RAW"));
showMode->set_tooltip_text (M("HISTOGRAM_TOOLTIP_MODE"));
- showBAR->set_tooltip_text (M("HISTOGRAM_TOOLTIP_BAR"));
+ scopeOptions->set_tooltip_text(M("HISTOGRAM_TOOLTIP_SHOW_OPTIONS"));
+ scopeHistBtn->set_tooltip_text(M("HISTOGRAM_TOOLTIP_TYPE_HISTOGRAM"));
+ scopeHistRawBtn->set_tooltip_text(M("HISTOGRAM_TOOLTIP_TYPE_HISTOGRAM_RAW"));
+ scopeParadeBtn->set_tooltip_text(M("HISTOGRAM_TOOLTIP_TYPE_PARADE"));
+ scopeWaveBtn->set_tooltip_text(M("HISTOGRAM_TOOLTIP_TYPE_WAVEFORM"));
+ scopeVectHcBtn->set_tooltip_text(M("HISTOGRAM_TOOLTIP_TYPE_VECTORSCOPE_HC"));
+ scopeVectHsBtn->set_tooltip_text(M("HISTOGRAM_TOOLTIP_TYPE_VECTORSCOPE_HS"));
buttonGrid = Gtk::manage (new Gtk::Grid ());
- buttonGrid->set_orientation(Gtk::ORIENTATION_VERTICAL);
+ buttonGrid->set_orientation(Gtk::ORIENTATION_HORIZONTAL);
+ persistentButtons = Gtk::manage(new Gtk::Box());
+ persistentButtons->set_orientation(Gtk::ORIENTATION_VERTICAL);
+ optionButtons = Gtk::manage(new Gtk::Box());
+ optionButtons->set_orientation(Gtk::ORIENTATION_VERTICAL);
+
showRed->set_active (options.histogramRed);
showGreen->set_active (options.histogramGreen);
showBlue->set_active (options.histogramBlue);
showValue->set_active (options.histogramLuma);
showChro->set_active (options.histogramChroma);
- showRAW->set_active (options.histogramRAW);
// no showMode->set_active(), as it's not a ToggleButton
+ scopeOptions->set_active(options.histogramShowOptionButtons);
showBAR->set_active (options.histogramBar);
showRed->set_image (showRed->get_active() ? *redImage : *redImage_g);
@@ -153,56 +239,134 @@ HistogramPanel::HistogramPanel() :
showBlue->set_image (showBlue->get_active() ? *blueImage : *blueImage_g);
showValue->set_image (showValue->get_active() ? *valueImage : *valueImage_g);
showChro->set_image (showChro->get_active() ? *chroImage : *chroImage_g);
- showRAW->set_image (showRAW->get_active() ? *rawImage : *rawImage_g);
if (options.histogramDrawMode == 0)
showMode->set_image(*mode0Image);
else if (options.histogramDrawMode == 1)
showMode->set_image(*mode1Image);
else
showMode->set_image(*mode2Image);
+ scopeHistBtn->set_image(*histImage);
+ scopeHistRawBtn->set_image(*histRawImage);
+ scopeParadeBtn->set_image(*paradeImage);
+ scopeWaveBtn->set_image(*waveImage);
+ scopeVectHcBtn->set_image(*vectHcImage);
+ scopeVectHsBtn->set_image(*vectHsImage);
+ switch(options.histogramScopeType) {
+ case ScopeType::HISTOGRAM:
+ scopeHistBtn->set_active();
+ break;
+ case ScopeType::HISTOGRAM_RAW:
+ scopeHistRawBtn->set_active();
+ break;
+ case ScopeType::PARADE:
+ scopeParadeBtn->set_active();
+ break;
+ case ScopeType::WAVEFORM:
+ scopeWaveBtn->set_active();
+ break;
+ case ScopeType::VECTORSCOPE_HS:
+ scopeVectHsBtn->set_active();
+ break;
+ case ScopeType::VECTORSCOPE_HC:
+ scopeVectHcBtn->set_active();
+ break;
+ case ScopeType::NONE:
+ break;
+ }
+ scopeOptions->set_image(*Gtk::manage(new RTImage("histogram-ellipsis-small.png")));
showBAR->set_image (showBAR->get_active() ? *barImage : *barImage_g);
- raw_toggled(); // Make sure the luma/chroma toggles are enabled or disabled
-
- setExpandAlignProperties(showRed , false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
- setExpandAlignProperties(showGreen, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
- setExpandAlignProperties(showBlue , false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
- setExpandAlignProperties(showValue, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
- setExpandAlignProperties(showChro , false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
- setExpandAlignProperties(showRAW , false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
- setExpandAlignProperties(showMode , false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
- setExpandAlignProperties(showBAR , false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
+ setExpandAlignProperties(showRed , false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
+ setExpandAlignProperties(showGreen, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
+ setExpandAlignProperties(showBlue , false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
+ setExpandAlignProperties(showValue, false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
+ setExpandAlignProperties(showChro , false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
+ setExpandAlignProperties(showMode , false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
+ setExpandAlignProperties(scopeOptions, false, false, Gtk::ALIGN_START, Gtk::ALIGN_CENTER);
+ setExpandAlignProperties(showBAR , false, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_CENTER);
+ setExpandAlignProperties(scopeOptions, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
+ setExpandAlignProperties(scopeHistBtn, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
+ setExpandAlignProperties(scopeHistRawBtn, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
+ setExpandAlignProperties(scopeParadeBtn, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
+ setExpandAlignProperties(scopeWaveBtn, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
+ setExpandAlignProperties(scopeVectHcBtn, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
+ setExpandAlignProperties(scopeVectHsBtn, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_FILL);
+ setExpandAlignProperties(persistentButtons, false, true, Gtk::ALIGN_START, Gtk::ALIGN_FILL);
+ setExpandAlignProperties(optionButtons, false, true, Gtk::ALIGN_START, Gtk::ALIGN_FILL);
showRed->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::red_toggled), showRed );
showGreen->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::green_toggled), showGreen );
showBlue->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::blue_toggled), showBlue );
showValue->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::value_toggled), showValue );
showChro->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::chro_toggled), showChro );
- showRAW->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::raw_toggled), showRAW );
showMode->signal_released().connect( sigc::mem_fun(*this, &HistogramPanel::mode_released), showMode );
+ scopeOptions->signal_toggled().connect(sigc::mem_fun(*this, &HistogramPanel::scopeOptionsToggled));
showBAR->signal_toggled().connect( sigc::mem_fun(*this, &HistogramPanel::bar_toggled), showBAR );
+ scopeHistBtn->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &HistogramPanel::type_selected), scopeHistBtn));
+ scopeHistRawBtn->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &HistogramPanel::type_selected), scopeHistRawBtn));
+ scopeParadeBtn->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &HistogramPanel::type_selected), scopeParadeBtn));
+ scopeWaveBtn->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &HistogramPanel::type_selected), scopeWaveBtn));
+ scopeVectHcBtn->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &HistogramPanel::type_selected), scopeVectHcBtn));
+ scopeVectHsBtn->signal_toggled().connect(sigc::bind(sigc::mem_fun(*this, &HistogramPanel::type_selected), scopeVectHsBtn));
- buttonGrid->add (*showRed);
- buttonGrid->add (*showGreen);
- buttonGrid->add (*showBlue);
- buttonGrid->add (*showValue);
- buttonGrid->add (*showChro);
- buttonGrid->add (*showRAW);
- buttonGrid->add (*showMode);
- buttonGrid->add (*showBAR);
+ brightnessWidget = Gtk::manage(new Gtk::Scale(Gtk::ORIENTATION_VERTICAL));
+ brightnessWidget->set_inverted();
+ brightnessWidget->set_range(log(HistogramArea::MIN_BRIGHT), log(HistogramArea::MAX_BRIGHT));
+ brightnessWidget->set_draw_value(false);
+ brightnessWidget->signal_value_changed().connect(sigc::mem_fun(*this, &HistogramPanel::brightnessWidgetValueChanged));
+ brightnessWidget->set_name("histScale");
+ brightnessWidget->set_tooltip_text(M("HISTOGRAM_TOOLTIP_TRACE_BRIGHTNESS"));
+ setExpandAlignProperties(brightnessWidget, true, false, Gtk::ALIGN_CENTER, Gtk::ALIGN_START);
+
+ optionButtons->add(*showRed);
+ optionButtons->add(*showGreen);
+ optionButtons->add(*showBlue);
+ optionButtons->add(*showValue);
+ optionButtons->add(*showChro);
+ optionButtons->add(*showMode);
+ optionButtons->add(*showBAR);
+ optionButtons->add(*brightnessWidget);
+
+ Gtk::VSeparator* separator = Gtk::manage(new Gtk::VSeparator());
+ setExpandAlignProperties(separator, true, false, Gtk::ALIGN_FILL, Gtk::ALIGN_CENTER);
+ persistentButtons->add(*scopeHistBtn);
+ persistentButtons->add(*scopeHistRawBtn);
+ persistentButtons->add(*scopeParadeBtn);
+ persistentButtons->add(*scopeWaveBtn);
+ persistentButtons->add(*scopeVectHsBtn);
+ persistentButtons->add(*scopeVectHcBtn);
+ persistentButtons->add(*separator);
+ persistentButtons->add(*scopeOptions);
// Put the button vbox next to the window's border to be less disturbing
if (options.histogramPosition == 1) {
+ buttonGrid->add(*persistentButtons);
+ buttonGrid->add(*optionButtons);
+
add (*buttonGrid);
add (*gfxGrid);
} else {
+ buttonGrid->add(*optionButtons);
+ buttonGrid->add(*persistentButtons);
+
add (*gfxGrid);
add (*buttonGrid);
}
show_all ();
+ optionButtons->set_no_show_all();
+ optionButtons->set_visible(options.histogramShowOptionButtons);
+ type_changed();
+ updateHistAreaOptions();
+ if (histogramRGBArea) {
+ updateHistRGBAreaOptions();
+ }
+
+ brightness_changed_connection = histogramArea->getBrighnessChangedSignal().connect(sigc::mem_fun(*this, &HistogramPanel::brightnessUpdated));
rconn = signal_size_allocate().connect( sigc::mem_fun(*this, &HistogramPanel::resized) );
+
+ histogramArea->setBrightness(options.histogramTraceBrightness);
}
HistogramPanel::~HistogramPanel ()
@@ -214,7 +378,6 @@ HistogramPanel::~HistogramPanel ()
delete blueImage;
delete valueImage;
delete chroImage;
- delete rawImage;
delete mode0Image;
delete mode1Image;
delete mode2Image;
@@ -225,24 +388,51 @@ HistogramPanel::~HistogramPanel ()
delete blueImage_g;
delete valueImage_g;
delete chroImage_g;
- delete rawImage_g;
delete barImage_g;
}
+void HistogramPanel::showRGBBar()
+{
+ histogramRGBAreaHori->set_visible(
+ histogramRGBArea == histogramRGBAreaHori.get() && showBAR->get_active());
+ histogramRGBAreaVert->set_visible(
+ histogramRGBArea == histogramRGBAreaVert.get() && showBAR->get_active());
+ histogramRGBAreaHori->setShow(false);
+ histogramRGBAreaVert->setShow(false);
+
+ if (!histogramRGBArea) {
+ return;
+ }
+
+ setHistRGBInvalid();
+ histogramRGBArea->setShow(showBAR->get_active());
+}
+
void HistogramPanel::resized (Gtk::Allocation& req)
{
+ static int old_height = 0;
+ static int old_width = 0;
- histogramArea->updateBackBuffer ();
- histogramArea->queue_draw ();
+ bool size_changed =
+ old_height != req.get_height() || old_width != req.get_width();
+
+ if (!histogramArea->updatePending() && size_changed) {
+ histogramArea->updateBackBuffer ();
+ histogramArea->queue_draw ();
+ }
// set histogramRGBArea invalid;
- histogramRGBArea->updateBackBuffer(-1, -1, -1);
- histogramRGBArea->queue_draw ();
+ if (histogramRGBArea && size_changed) {
+ histogramRGBArea->updateBackBuffer(-1, -1, -1);
+ histogramRGBArea->queue_draw ();
+ }
// Store current height of the histogram
options.histogramHeight = get_height();
+ old_height = req.get_height();
+ old_width = req.get_width();
}
void HistogramPanel::red_toggled ()
@@ -275,21 +465,6 @@ void HistogramPanel::chro_toggled ()
rgbv_toggled();
}
-void HistogramPanel::raw_toggled ()
-{
- if (showRAW->get_active()) {
- showRAW->set_image(*rawImage);
- showValue->set_sensitive(false);
- showChro->set_sensitive(false);
- } else {
- showRAW->set_image(*rawImage_g);
- showValue->set_sensitive(true);
- showChro->set_sensitive(true);
- }
-
- rgbv_toggled();
-}
-
void HistogramPanel::mode_released ()
{
options.histogramDrawMode = (options.histogramDrawMode + 1) % 3;
@@ -302,21 +477,141 @@ void HistogramPanel::mode_released ()
rgbv_toggled();
}
+void HistogramPanel::brightnessWidgetValueChanged(void)
+{
+ ConnectionBlocker blocker(brightness_changed_connection);
+ histogramArea->setBrightness(exp(brightnessWidget->get_value()));
+ options.histogramTraceBrightness = histogramArea->getBrightness();
+}
+
+void HistogramPanel::brightnessUpdated(float brightness)
+{
+ brightnessWidget->set_value(log(brightness));
+ options.histogramTraceBrightness = histogramArea->getBrightness();
+}
+
+void HistogramPanel::scopeOptionsToggled()
+{
+ options.histogramShowOptionButtons = scopeOptions->get_active();
+ optionButtons->set_visible(scopeOptions->get_active());
+}
+
+void HistogramPanel::type_selected(Gtk::RadioButton* button)
+{
+ ScopeType new_type = ScopeType::NONE;
+
+ if (button == scopeHistBtn) {
+ new_type = ScopeType::HISTOGRAM;
+ } else if (button == scopeHistRawBtn) {
+ new_type = ScopeType::HISTOGRAM_RAW;
+ } else if (button == scopeParadeBtn) {
+ new_type = ScopeType::PARADE;
+ } else if (button == scopeWaveBtn) {
+ new_type = ScopeType::WAVEFORM;
+ } else if (button == scopeVectHcBtn) {
+ new_type = ScopeType::VECTORSCOPE_HC;
+ } else if (button == scopeVectHsBtn) {
+ new_type = ScopeType::VECTORSCOPE_HS;
+ }
+
+ if (new_type == options.histogramScopeType) {
+ return;
+ }
+
+ options.histogramScopeType = new_type;
+
+ type_changed();
+ updateHistAreaOptions();
+ if (histogramRGBArea) {
+ updateHistRGBAreaOptions();
+ }
+ histogramArea->setDirty(true);
+ histogramArea->queue_draw();
+}
+
+void HistogramPanel::type_changed()
+{
+ switch (options.histogramScopeType) {
+ case ScopeType::HISTOGRAM:
+ showRed->show();
+ showGreen->show();
+ showBlue->show();
+ showValue->show();
+ showChro->show();
+ showMode->show();
+ showBAR->show();
+ showBAR->set_tooltip_text(M("HISTOGRAM_TOOLTIP_BAR"));
+ brightnessWidget->hide();
+ histogramRGBArea = histogramRGBAreaHori.get();
+ break;
+ case ScopeType::HISTOGRAM_RAW:
+ showRed->show();
+ showGreen->show();
+ showBlue->show();
+ showValue->hide();
+ showChro->hide();
+ showMode->show();
+ showBAR->hide();
+ brightnessWidget->hide();
+ histogramRGBArea = nullptr;
+ break;
+ case ScopeType::PARADE:
+ case ScopeType::WAVEFORM:
+ showRed->show();
+ showGreen->show();
+ showBlue->show();
+ showValue->show();
+ showChro->hide();
+ showMode->hide();
+ showBAR->show();
+ showBAR->set_tooltip_text(M("HISTOGRAM_TOOLTIP_BAR"));
+ brightnessWidget->show();
+ histogramRGBArea = histogramRGBAreaVert.get();
+ break;
+ case ScopeType::VECTORSCOPE_HC:
+ case ScopeType::VECTORSCOPE_HS:
+ showRed->hide();
+ showGreen->hide();
+ showBlue->hide();
+ showValue->hide();
+ showChro->hide();
+ showMode->hide();
+ showBAR->show();
+ showBAR->set_tooltip_text(M("HISTOGRAM_TOOLTIP_CROSSHAIR"));
+ brightnessWidget->show();
+ histogramRGBArea = nullptr;
+ break;
+ case ScopeType::NONE:
+ break;
+ }
+
+ if (panel_listener) {
+ updateHistAreaOptions();
+ panel_listener->scopeTypeChanged(options.histogramScopeType);
+ }
+
+ showRGBBar();
+}
+
void HistogramPanel::bar_toggled ()
{
showBAR->set_image(showBAR->get_active() ? *barImage : *barImage_g);
rgbv_toggled();
+ showRGBBar();
}
void HistogramPanel::rgbv_toggled ()
{
// Update Display
- histogramArea->updateOptions (showRed->get_active(), showGreen->get_active(), showBlue->get_active(), showValue->get_active(), showChro->get_active(), showRAW->get_active(), options.histogramDrawMode);
+ updateHistAreaOptions();
+ histogramArea->updateBackBuffer ();
histogramArea->queue_draw ();
- histogramRGBArea->updateOptions (showRed->get_active(), showGreen->get_active(), showBlue->get_active(), showValue->get_active(), showChro->get_active(), showRAW->get_active(), showBAR->get_active());
- histogramRGBArea->updateBackBuffer (0, 0, 0);
- histogramRGBArea->queue_draw ();
+ if (histogramRGBArea) {
+ updateHistRGBAreaOptions();
+ histogramRGBArea->updateBackBuffer(-1, -1, -1);
+ histogramRGBArea->queue_draw ();
+ }
}
void HistogramPanel::setHistRGBInvalid ()
@@ -342,11 +637,27 @@ void HistogramPanel::reorder (Gtk::PositionType align)
removeIfThere(this, gfxGrid, false);
add (*gfxGrid);
gfxGrid->unreference();
+
+ gfxGrid->remove(*histogramRGBAreaVert);
+ gfxGrid->add(*histogramRGBAreaVert);
+
+ optionButtons->reference();
+ removeIfThere(buttonGrid, optionButtons, false);
+ buttonGrid->add(*optionButtons);
+ optionButtons->unreference();
} else {
buttonGrid->reference();
removeIfThere(this, buttonGrid, false);
add (*buttonGrid);
buttonGrid->unreference();
+
+ gfxGrid->remove(*histogramRGBAreaVert);
+ gfxGrid->attach_next_to(*histogramRGBAreaVert, *histogramArea, Gtk::POS_LEFT);
+
+ persistentButtons->reference();
+ removeIfThere(buttonGrid, persistentButtons, false);
+ buttonGrid->add(*persistentButtons);
+ persistentButtons->unreference();
}
}
@@ -361,6 +672,41 @@ void HistogramPanel::toggleButtonMode ()
showMode->set_image(*mode2Image);
}
+void HistogramPanel::setPanelListener(HistogramPanelListener* listener)
+{
+ panel_listener = listener;
+
+ if (listener) {
+ listener->scopeTypeChanged(options.histogramScopeType);
+ }
+}
+
+void HistogramPanel::updateHistAreaOptions()
+{
+ histogramArea->updateOptions(
+ showRed->get_active(),
+ showGreen->get_active(),
+ showBlue->get_active(),
+ showValue->get_active(),
+ showChro->get_active(),
+ options.histogramDrawMode,
+ options.histogramScopeType,
+ showBAR->get_active()
+ );
+}
+
+void HistogramPanel::updateHistRGBAreaOptions()
+{
+ histogramRGBArea->updateOptions(
+ showRed->get_active(),
+ showGreen->get_active(),
+ showBlue->get_active(),
+ showValue->get_active(),
+ showChro->get_active(),
+ showBAR->get_active()
+ );
+}
+
//
//
//
@@ -378,7 +724,7 @@ double HistogramScaling::log(double vsize, double val)
HistogramRGBArea::HistogramRGBArea () :
val(0), r(0), g(0), b(0), valid(false),
needRed(options.histogramRed), needGreen(options.histogramGreen), needBlue(options.histogramBlue),
- needLuma(options.histogramLuma), needChroma(options.histogramChroma), rawMode(options.histogramRAW),
+ needLuma(options.histogramLuma), needChroma(options.histogramChroma),
showMode(options.histogramBar), barDisplayed(options.histogramBar), parent(nullptr)
{
get_style_context()->add_class("drawingarea");
@@ -402,46 +748,41 @@ HistogramRGBArea::~HistogramRGBArea ()
}
-Gtk::SizeRequestMode HistogramRGBArea::get_request_mode_vfunc () const
+void HistogramRGBArea::getPreferredThickness(int& min_thickness, int& natural_thickness) const
{
- return Gtk::SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+ int minimumLength = 0;
+ int naturalLength = 0;
+ getPreferredLength(minimumLength, naturalLength);
+ getPreferredThicknessForLength(minimumLength, min_thickness, natural_thickness);
}
-void HistogramRGBArea::get_preferred_height_vfunc (int &minimum_height, int &natural_height) const
-{
- int minimumWidth = 0;
- int naturalWidth = 0;
- get_preferred_width_vfunc(minimumWidth, naturalWidth);
- get_preferred_height_for_width_vfunc (minimumWidth, minimum_height, natural_height);
-}
-
-void HistogramRGBArea::get_preferred_width_vfunc (int &minimum_width, int &natural_width) const
+void HistogramRGBArea::getPreferredLength(int& min_length, int& natural_length) const
{
int s = RTScalable::getScale();
- minimum_width = 60 * s;
- natural_width = 200 * s;
+ min_length = 60 * s;
+ natural_length = 200 * s;
}
-void HistogramRGBArea::get_preferred_height_for_width_vfunc (int width, int &minimum_height, int &natural_height) const
+void HistogramRGBArea::getPreferredThicknessForLength(int length, int& min_thickness, int& natural_thickness) const
{
- int bHeight = width / 30;
+ int bThickness = length / 30;
int s = RTScalable::getScale();
- if (bHeight > (10 * s)) {
- bHeight = 10 * s;
- } else if (bHeight < (5 * s)) {
- bHeight = 5 * s;
+ if (bThickness > (10 * s)) {
+ bThickness = 10 * s;
+ } else if (bThickness < (5 * s)) {
+ bThickness = 5 * s;
}
- minimum_height = bHeight;
- natural_height = bHeight;
+ min_thickness = bThickness;
+ natural_thickness = bThickness;
}
// unused?
-void HistogramRGBArea::get_preferred_width_for_height_vfunc (int height, int &minimum_width, int &natural_width) const
+void HistogramRGBArea::getPreferredLengthForThickness(int thickness, int& min_length, int& natural_length) const
{
- get_preferred_width_vfunc (minimum_width, natural_width);
+ getPreferredLength(min_length, natural_length);
}
bool HistogramRGBArea::getShow()
@@ -449,9 +790,18 @@ bool HistogramRGBArea::getShow()
return(showMode);
}
+void HistogramRGBArea::setShow(bool show)
+{
+ showMode = show;
+}
+
void HistogramRGBArea::updateBackBuffer (int r, int g, int b, const Glib::ustring &profile, const Glib::ustring &profileW)
{
- if (!get_realized () || !showMode || rawMode) {
+ if (!get_realized () || !showMode || !(
+ options.histogramScopeType == ScopeType::HISTOGRAM
+ || options.histogramScopeType == ScopeType::PARADE
+ || options.histogramScopeType == ScopeType::WAVEFORM
+ )) {
return;
}
@@ -479,75 +829,44 @@ void HistogramRGBArea::updateBackBuffer (int r, int g, int b, const Glib::ustrin
cc->set_line_width (1.0 * s);
if ( r != -1 && g != -1 && b != -1 ) {
- double xpos;
if (needRed) {
// Red
cc->set_source_rgb(1.0, 0.0, 0.0);
- if (options.histogramDrawMode < 2) {
- xpos = padding + r * (winw - padding * 2.0) / 255.0 + 0.5*s;
- } else {
- xpos = padding + HistogramScaling::log (255, r) * (winw - padding * 2.0) / 255.0 + 0.5*s;
- }
- cc->move_to(xpos, 0.0);
- cc->line_to(xpos, winh - 0.0);
- cc->stroke();
+ drawBar(cc, r, 255.0, winw, winh, s);
}
if (needGreen) {
// Green
cc->set_source_rgb(0.0, 1.0, 0.0);
- if (options.histogramDrawMode < 2) {
- xpos = padding + g * (winw - padding * 2.0) / 255.0 + 0.5*s;
- } else {
- xpos = padding + HistogramScaling::log (255, g) * (winw - padding * 2.0) / 255.0 + 0.5*s;
- }
- cc->move_to(xpos, 0.0);
- cc->line_to(xpos, winh - 0.0);
- cc->stroke();
+ drawBar(cc, g, 255.0, winw, winh, s);
}
if (needBlue) {
// Blue
cc->set_source_rgb(0.0, 0.4, 1.0);
- if (options.histogramDrawMode < 2) {
- xpos = padding + b * (winw - padding * 2.0) / 255.0 + 0.5*s;
- } else {
- xpos = padding + HistogramScaling::log (255, b) * (winw - padding * 2.0) / 255.0 + 0.5*s;
- }
- cc->move_to(xpos, 0.0);
- cc->line_to(xpos, winh - 0.0);
- cc->stroke();
+ drawBar(cc, b, 255.0, winw, winh, s);
}
- if(needLuma || needChroma) {
+ if(
+ (needLuma || needChroma)
+ && (options.histogramScopeType == ScopeType::HISTOGRAM
+ || options.histogramScopeType == ScopeType::PARADE
+ || options.histogramScopeType == ScopeType::WAVEFORM)
+ ) {
float Lab_L, Lab_a, Lab_b;
rtengine::Color::rgb2lab01(profile, profileW, r / 255.f, g / 255.f, b / 255.f, Lab_L, Lab_a, Lab_b, options.rtSettings.HistogramWorking);
if (needLuma) {
// Luma
cc->set_source_rgb(1.0, 1.0, 1.0);
- if (options.histogramDrawMode < 2) {
- xpos = padding + static_cast(Lab_L) * (winw - padding * 2.0) / 100.0 + 0.5*s;
- } else {
- xpos = padding + HistogramScaling::log(100, Lab_L) * (winw - padding * 2.0) / 100.0 + 0.5*s;
- }
- cc->move_to(xpos, 0.0);
- cc->line_to(xpos, winh - 0.0);
- cc->stroke();
+ drawBar(cc, Lab_L, 100.0, winw, winh, s);
}
- if (needChroma) {
+ if (needChroma && options.histogramScopeType == ScopeType::HISTOGRAM) {
// Chroma
double chromaval = sqrt(Lab_a * Lab_a + Lab_b * Lab_b) / 1.8;
cc->set_source_rgb(0.9, 0.9, 0.0);
- if (options.histogramDrawMode < 2) {
- xpos = padding + chromaval * (winw - padding * 2.0) / 100.0 + 0.5*s;
- } else {
- xpos = padding + HistogramScaling::log(100, chromaval) * (winw - padding * 2.0) / 100.0 + 0.5*s;
- }
- cc->move_to(xpos, 0.0);
- cc->line_to(xpos, winh - 0.0);
- cc->stroke();
+ drawBar(cc, chromaval, 100.0, winw, winh, s);
}
}
}
@@ -594,7 +913,7 @@ void HistogramRGBArea::update (int valh, int rh, int gh, int bh)
);
}
-void HistogramRGBArea::updateOptions (bool r, bool g, bool b, bool l, bool c, bool raw, bool bar)
+void HistogramRGBArea::updateOptions (bool r, bool g, bool b, bool l, bool c, bool bar)
{
options.histogramRed = needRed = r;
@@ -602,18 +921,8 @@ void HistogramRGBArea::updateOptions (bool r, bool g, bool b, bool l, bool c, bo
options.histogramBlue = needBlue = b;
options.histogramLuma = needLuma = l;
options.histogramChroma = needChroma = c;
- options.histogramRAW = rawMode = raw;
options.histogramBar = showMode = bar;
- // Show/hide the RGB bar widget
- if (bar && !barDisplayed) {
- parent->add(*this);
- barDisplayed = true;
- } else if (!bar && barDisplayed) {
- removeIfThere(parent, this, false);
- barDisplayed = false;
- }
-
}
void HistogramRGBArea::on_realize ()
@@ -658,16 +967,105 @@ void HistogramRGBArea::factorChanged (double newFactor)
factor = newFactor;
}
+void HistogramRGBAreaHori::drawBar(Cairo::RefPtr cc, double value, double max_value, int winw, int winh, double scale)
+{
+ double pos;
+ if (options.histogramDrawMode < 2) {
+ pos = padding + value * (winw - padding * 2.0) / max_value + 0.5 * scale;
+ } else {
+ pos = padding + HistogramScaling::log (max_value, value) * (winw - padding * 2.0) / max_value + 0.5 * scale;
+ }
+ cc->move_to(pos, 0.0);
+ cc->line_to(pos, winh - 0.0);
+ cc->stroke();
+}
+
+Gtk::SizeRequestMode HistogramRGBAreaHori::get_request_mode_vfunc () const
+{
+ return Gtk::SIZE_REQUEST_HEIGHT_FOR_WIDTH;
+}
+
+void HistogramRGBAreaHori::get_preferred_height_vfunc (int &minimum_height, int &natural_height) const
+{
+ getPreferredThickness(minimum_height, natural_height);
+}
+
+void HistogramRGBAreaHori::get_preferred_width_vfunc (int &minimum_width, int &natural_width) const
+{
+ getPreferredLength(minimum_width, natural_width);
+}
+
+void HistogramRGBAreaHori::get_preferred_height_for_width_vfunc (int width, int &minimum_height, int &natural_height) const
+{
+ getPreferredThicknessForLength(width, minimum_height, natural_height);
+}
+
+void HistogramRGBAreaHori::get_preferred_width_for_height_vfunc (int height, int &minimum_width, int &natural_width) const
+{
+ getPreferredLengthForThickness(height, minimum_width, natural_width);
+}
+
+void HistogramRGBAreaVert::drawBar(Cairo::RefPtr cc, double value, double max_value, int winw, int winh, double scale)
+{
+ double pos;
+ if (options.histogramDrawMode < 2 || options.histogramScopeType == ScopeType::PARADE || options.histogramScopeType == ScopeType::WAVEFORM) {
+ pos = padding + value * (winh - padding * 2.0 - 1) / max_value + 0.5 * scale;
+ } else {
+ pos = padding + HistogramScaling::log (max_value, value) * (winh - padding * 2.0) / max_value + 0.5 * scale;
+ }
+ cc->move_to(0.0, winh - pos);
+ cc->line_to(winw, winh - pos);
+ cc->stroke();
+}
+
+Gtk::SizeRequestMode HistogramRGBAreaVert::get_request_mode_vfunc () const
+{
+ return Gtk::SIZE_REQUEST_WIDTH_FOR_HEIGHT;
+}
+
+void HistogramRGBAreaVert::get_preferred_height_vfunc (int &minimum_height, int &natural_height) const
+{
+ getPreferredLength(minimum_height, natural_height);
+}
+
+void HistogramRGBAreaVert::get_preferred_width_vfunc (int &minimum_width, int &natural_width) const
+{
+ minimum_width = 10 * RTScalable::getScale();
+ natural_width = minimum_width;
+}
+
+void HistogramRGBAreaVert::get_preferred_height_for_width_vfunc (int width, int &minimum_height, int &natural_height) const
+{
+ getPreferredLengthForThickness(width, minimum_height, natural_height);
+}
+
+void HistogramRGBAreaVert::get_preferred_width_for_height_vfunc (int height, int &minimum_width, int &natural_width) const
+{
+ get_preferred_width_vfunc(minimum_width, natural_width);
+}
+
//
//
//
// HistogramArea
HistogramArea::HistogramArea (DrawModeListener *fml) :
+ vectorscope_scale(0),
+ vect_hc(0, 0), vect_hs(0, 0),
+ vect_hc_buffer_dirty(true), vect_hs_buffer_dirty(true),
+ waveform_scale(0),
+ rwave(0, 0), gwave(0, 0),bwave(0, 0), lwave(0, 0),
+ parade_buffer_r_dirty(true), parade_buffer_g_dirty(true), parade_buffer_b_dirty(true),
+ wave_buffer_dirty(true), wave_buffer_luma_dirty(true),
valid(false), drawMode(options.histogramDrawMode), myDrawModeListener(fml),
+ scopeType(options.histogramScopeType),
oldwidth(-1), oldheight(-1),
+ trace_brightness(1.0),
needRed(options.histogramRed), needGreen(options.histogramGreen), needBlue(options.histogramBlue),
- needLuma(options.histogramLuma), needChroma(options.histogramChroma), rawMode(options.histogramRAW),
- isPressed(false), movingPosition(0.0)
+ needLuma(options.histogramLuma), needChroma(options.histogramChroma),
+ isPressed(false), movingPosition(0.0),
+ needPointer(options.histogramBar),
+ pointer_red(-1), pointer_green(-1), pointer_blue(-1),
+ pointer_a(0), pointer_b(0)
{
rhist(256);
@@ -703,7 +1101,7 @@ Gtk::SizeRequestMode HistogramArea::get_request_mode_vfunc () const
void HistogramArea::get_preferred_height_vfunc (int &minimum_height, int &natural_height) const
{
- int s = (int)RTScalable::getScale();
+ int s = RTScalable::getScale();
minimum_height = 100 * s;
natural_height = 200 * s;
}
@@ -711,7 +1109,7 @@ void HistogramArea::get_preferred_height_vfunc (int &minimum_height, int &natura
void HistogramArea::get_preferred_width_vfunc (int &minimum_width, int &natural_width) const
{
- int s = (int)RTScalable::getScale();
+ int s = RTScalable::getScale();
minimum_width = 200 * s;
natural_width = 400 * s;
}
@@ -728,18 +1126,23 @@ void HistogramArea::get_preferred_width_for_height_vfunc (int height, int &minim
get_preferred_width_vfunc (minimum_width, natural_width);
}
-void HistogramArea::updateOptions (bool r, bool g, bool b, bool l, bool c, bool raw, int mode)
+void HistogramArea::updateOptions (bool r, bool g, bool b, bool l, bool c, int mode, ScopeType type, bool pointer)
{
+ wave_buffer_dirty = wave_buffer_dirty || needRed != r || needGreen != g || needBlue != b;
options.histogramRed = needRed = r;
options.histogramGreen = needGreen = g;
options.histogramBlue = needBlue = b;
options.histogramLuma = needLuma = l;
options.histogramChroma = needChroma = c;
- options.histogramRAW = rawMode = raw;
options.histogramDrawMode = drawMode = mode;
+ options.histogramScopeType = scopeType = type;
+ options.histogramBar = needPointer = pointer;
+}
- updateBackBuffer ();
+bool HistogramArea::updatePending(void)
+{
+ return haih->pending > 0 && !haih->destroyed;
}
void HistogramArea::update(
@@ -750,18 +1153,53 @@ void HistogramArea::update(
const LUTu& histChroma,
const LUTu& histRedRaw,
const LUTu& histGreenRaw,
- const LUTu& histBlueRaw
+ const LUTu& histBlueRaw,
+ int vectorscopeScale,
+ const array2D& vectorscopeHC,
+ const array2D& vectorscopeHS,
+ int waveformScale,
+ const array2D& waveformRed,
+ const array2D& waveformGreen,
+ const array2D& waveformBlue,
+ const array2D& waveformLuma
)
{
if (histRed) {
- rhist = histRed;
- ghist = histGreen;
- bhist = histBlue;
- lhist = histLuma;
- chist = histChroma;
- rhistRaw = histRedRaw;
- ghistRaw = histGreenRaw;
- bhistRaw = histBlueRaw;
+ switch (scopeType) {
+ case ScopeType::HISTOGRAM:
+ rhist = histRed;
+ ghist = histGreen;
+ bhist = histBlue;
+ lhist = histLuma;
+ chist = histChroma;
+ break;
+ case ScopeType::HISTOGRAM_RAW:
+ rhistRaw = histRedRaw;
+ ghistRaw = histGreenRaw;
+ bhistRaw = histBlueRaw;
+ break;
+ case ScopeType::PARADE:
+ case ScopeType::WAVEFORM:
+ waveform_scale = waveformScale;
+ rwave = waveformRed;
+ gwave = waveformGreen;
+ bwave = waveformBlue;
+ lwave = waveformLuma;
+ parade_buffer_r_dirty = parade_buffer_g_dirty = parade_buffer_b_dirty = wave_buffer_dirty = wave_buffer_luma_dirty = true;
+ break;
+ case ScopeType::VECTORSCOPE_HS:
+ vectorscope_scale = vectorscopeScale;
+ vect_hs = vectorscopeHS;
+ vect_hs_buffer_dirty = true;
+ break;
+ case ScopeType::VECTORSCOPE_HC:
+ vectorscope_scale = vectorscopeScale;
+ vect_hc = vectorscopeHC;
+ vect_hc_buffer_dirty = true;
+ break;
+ case ScopeType::NONE:
+ break;
+ }
valid = true;
} else {
valid = false;
@@ -796,7 +1234,6 @@ void HistogramArea::update(
void HistogramArea::updateBackBuffer ()
{
-
if (!get_realized ()) {
return;
}
@@ -830,40 +1267,53 @@ void HistogramArea::updateBackBuffer ()
cr->set_dash (ch_ds, 0);
// determine the number of h-gridlines based on current h
- int nrOfHGridPartitions = (int)rtengine::min (16.0, pow (2.0, floor ((h - 100) / 250) + 2));
+ int nrOfHGridPartitions = static_cast(rtengine::min (16.0, pow (2.0, floor ((h - 100) / 250) + 2)));
int nrOfVGridPartitions = 8; // always show 8 stops (lines at 1,3,7,15,31,63,127)
// draw vertical gridlines
- for (int i = 0; i <= nrOfVGridPartitions; i++) {
- double xpos = padding + 0.5;
- if (options.histogramDrawMode < 2) {
- xpos += (pow(2.0,i) - 1) * (w - padding * 2.0) / 255.0;
- } else {
- xpos += HistogramScaling::log (255, pow(2.0,i) - 1) * (w - padding * 2.0) / 255.0;
+ if (options.histogramScopeType == ScopeType::HISTOGRAM || options.histogramScopeType == ScopeType::HISTOGRAM_RAW) {
+ for (int i = 0; i <= nrOfVGridPartitions; i++) {
+ double xpos = padding + 0.5;
+ if (options.histogramDrawMode < 2) {
+ xpos += (pow(2.0,i) - 1) * (w - padding * 2.0) / 255.0;
+ } else {
+ xpos += HistogramScaling::log (255, pow(2.0,i) - 1) * (w - padding * 2.0) / 255.0;
+ }
+ cr->move_to (xpos, 0.);
+ cr->line_to (xpos, h);
+ cr->stroke ();
}
- cr->move_to (xpos, 0.);
- cr->line_to (xpos, h);
- cr->stroke ();
}
// draw horizontal gridlines
- if (options.histogramDrawMode == 0) {
+ if (options.histogramScopeType == ScopeType::PARADE || options.histogramScopeType == ScopeType::WAVEFORM) {
+ for (int i = 0; i <= nrOfVGridPartitions; i++) {
+ const double ypos = h - padding - (pow(2.0,i) - 1) * (h - 2 * padding - 1) / 255.0;
+ cr->move_to(0, ypos);
+ cr->line_to(w, ypos);
+ cr->stroke();
+ }
+ } else if (options.histogramScopeType == ScopeType::VECTORSCOPE_HC || options.histogramScopeType == ScopeType::VECTORSCOPE_HS) {
+ // Vectorscope has no gridlines.
+ } else if (options.histogramDrawMode == 0) {
for (int i = 1; i < nrOfHGridPartitions; i++) {
- cr->move_to (padding, i * (double)h / nrOfHGridPartitions + 0.5);
- cr->line_to (w - padding, i * (double)h / nrOfHGridPartitions + 0.5);
+ cr->move_to (padding, i * static_cast(h) / nrOfHGridPartitions + 0.5);
+ cr->line_to (w - padding, i * static_cast(h) / nrOfHGridPartitions + 0.5);
cr->stroke ();
}
} else {
for (int i = 1; i < nrOfHGridPartitions; i++) {
- cr->move_to (padding, h - HistogramScaling::log (h, i * (double)h / nrOfHGridPartitions) + 0.5);
- cr->line_to (w - padding, h - HistogramScaling::log (h, i * (double)h / nrOfHGridPartitions) + 0.5);
+ cr->move_to (padding, h - HistogramScaling::log (h, i * static_cast(h) / nrOfHGridPartitions) + 0.5);
+ cr->line_to (w - padding, h - HistogramScaling::log (h, i * static_cast(h) / nrOfHGridPartitions) + 0.5);
cr->stroke ();
}
}
cr->unset_dash();
- if (valid) {
+ if (valid && (scopeType == ScopeType::HISTOGRAM || scopeType == ScopeType::HISTOGRAM_RAW)) {
+ bool rawMode = scopeType == ScopeType::HISTOGRAM_RAW;
+
// For RAW mode use the other hists
LUTu& rh = rawMode ? rhistRaw : rhist;
LUTu& gh = rawMode ? ghistRaw : ghist;
@@ -970,6 +1420,12 @@ void HistogramArea::updateBackBuffer ()
drawMarks(cr, bhchanged, realhistheight, w, ui, oi);
}
+ } else if (scopeType == ScopeType::PARADE && rwave.getWidth() > 0) {
+ drawParade(cr, w, h);
+ } else if (scopeType == ScopeType::WAVEFORM && rwave.getWidth() > 0) {
+ drawWaveform(cr, w, h);
+ } else if (scopeType == ScopeType::VECTORSCOPE_HC || scopeType == ScopeType::VECTORSCOPE_HS) {
+ drawVectorscope(cr, w, h);
}
// Draw the frame's border
@@ -981,6 +1437,24 @@ void HistogramArea::updateBackBuffer ()
setDirty(false);
}
+bool HistogramArea::updatePointer(int r, int g, int b, const Glib::ustring &profile, const Glib::ustring &profileW)
+{
+ if (!needPointer || !(scopeType == ScopeType::VECTORSCOPE_HC || scopeType == ScopeType::VECTORSCOPE_HS)) {
+ return false;
+ }
+ if (pointer_red == r && pointer_green == g && pointer_blue == b) {
+ return false;
+ }
+
+ float L;
+ pointer_red = r;
+ pointer_green = g;
+ pointer_blue = b;
+ Color::rgb2lab01(profile, profileW, r / 255.f, g / 255.f, b / 255.f, L, pointer_a, pointer_b, options.rtSettings.HistogramWorking);
+ updateBackBuffer();
+ return true;
+}
+
void HistogramArea::on_realize ()
{
@@ -998,15 +1472,15 @@ void HistogramArea::drawCurve(Cairo::RefPtr &cr,
scale = scale <= 0.0 ? 0.001 : scale; // avoid division by zero and negative values
for (int i = 0; i < 256; i++) {
- double val = data[i] * (double)vsize / scale;
+ double val = data[i] * static_cast(vsize) / scale;
if (drawMode > 0) { // scale y for single and double log-scale
- val = HistogramScaling::log ((double)vsize, val);
+ val = HistogramScaling::log (static_cast(vsize), val);
}
double iscaled = i;
if (drawMode == 2) { // scale x for double log-scale
- iscaled = HistogramScaling::log (255.0, (double)i);
+ iscaled = HistogramScaling::log (255.0, static_cast(i));
}
double posX = padding + iscaled * (hsize - padding * 2.0) / 255.0;
@@ -1034,10 +1508,379 @@ void HistogramArea::drawMarks(Cairo::RefPtr &cr,
cr->fill();
}
+void HistogramArea::drawParade(Cairo::RefPtr &cr, int w, int h)
+{
+ // Arbitrary scale factor divided by current scale.
+ const float scale = trace_brightness * 32.f * 255.f / waveform_scale;
+ const int wave_width = rwave.getWidth();
+ const int wave_height = rwave.getHeight();
+
+ // See Cairo documentation on stride.
+ const int cairo_stride = Cairo::ImageSurface::format_stride_for_width(Cairo::FORMAT_ARGB32, rwave.getWidth());
+ const auto buffer_size = static_cast::size_type>(wave_height) * cairo_stride;
+
+ if (parade_buffer_r_dirty && needRed) {
+ parade_buffer_r.assign(buffer_size, 0);
+ assert(parade_buffer_r.size() % 4 == 0);
+
+ for (int val = 0; val < wave_height; val++) {
+ const int* const r_row = rwave[val];
+ std::uint32_t* const buffer_r_row = reinterpret_cast(parade_buffer_r.data() + (255 - val) * cairo_stride);
+ for (int col = 0; col < wave_width; col++) {
+ const unsigned char r = std::min(scale * r_row[col], 0xff);
+ if (r != 0) {
+ buffer_r_row[col] = (r << 16) | (r << 24);
+ }
+ }
+ }
+
+ parade_buffer_r_dirty = false;
+ }
+
+ if (parade_buffer_g_dirty && needGreen) {
+ parade_buffer_g.assign(buffer_size, 0);
+ assert(parade_buffer_g.size() % 4 == 0);
+
+ for (int val = 0; val < wave_height; val++) {
+ const int* const g_row = gwave[val];
+ std::uint32_t* const buffer_g_row = reinterpret_cast(parade_buffer_g.data() + (255 - val) * cairo_stride);
+ for (int col = 0; col < wave_width; col++) {
+ const unsigned char g = std::min(scale * g_row[col], 0xff);
+ if (g != 0) {
+ buffer_g_row[col] = (g << 8) | (g << 24);
+ }
+ }
+ }
+
+ parade_buffer_g_dirty = false;
+ }
+
+ if (parade_buffer_b_dirty && needBlue) {
+ parade_buffer_b.assign(buffer_size, 0);
+ assert(parade_buffer_b.size() % 4 == 0);
+
+ for (int val = 0; val < wave_height; val++) {
+ const int* const b_row = bwave[val];
+ std::uint32_t* const buffer_b_row = reinterpret_cast(parade_buffer_b.data() + (255 - val) * cairo_stride);
+ for (int col = 0; col < wave_width; col++) {
+ const unsigned char b = std::min(scale * b_row[col], 0xff);
+ if (b != 0) {
+ const unsigned char green = b / 2; // Make blue easier to see.
+ buffer_b_row[col] = b | (green << 8) | (b << 24);
+ }
+ }
+ }
+
+ parade_buffer_b_dirty = false;
+ }
+
+ if (wave_buffer_luma_dirty && needLuma) {
+ wave_buffer_luma.assign(buffer_size, 0);
+ assert(wave_buffer_luma.size() % 4 == 0);
+
+ for (int val = 0; val < wave_height; val++) {
+ const int* const l_row = lwave[val];
+ std::uint32_t* const buffer_row =
+ reinterpret_cast(wave_buffer_luma.data() + (255 - val) * cairo_stride);
+ for (int col = 0; col < wave_width; col++) {
+ const unsigned char l = std::min(scale * l_row[col], 0xff);
+ buffer_row[col] = l | (l << 8) | (l << 16) | (l << 24);
+ }
+ }
+
+ wave_buffer_luma_dirty = false;
+ }
+
+ std::vector buffers;
+ if (needLuma) {
+ buffers.push_back(wave_buffer_luma.data());
+ }
+ if (needRed) {
+ buffers.push_back(parade_buffer_r.data());
+ }
+ if (needGreen) {
+ buffers.push_back(parade_buffer_g.data());
+ }
+ if (needBlue) {
+ buffers.push_back(parade_buffer_b.data());
+ }
+
+ auto orig_matrix = cr->get_matrix();
+ const double display_wave_width = static_cast(w) / buffers.size();
+ for (unsigned i = 0; i < buffers.size(); i++) {
+ Cairo::RefPtr surface;
+ cr->translate(i * display_wave_width, padding);
+ cr->scale(display_wave_width / wave_width, (h - 2 * padding) / wave_height);
+ surface = Cairo::ImageSurface::create(
+ buffers[i], Cairo::FORMAT_ARGB32, wave_width, wave_height, cairo_stride);
+ cr->set_source(surface, 0, 0);
+ cr->set_operator(Cairo::OPERATOR_OVER);
+ cr->paint();
+ surface->finish();
+ cr->set_matrix(orig_matrix);
+ }
+}
+
+void HistogramArea::drawVectorscope(Cairo::RefPtr &cr, int w, int h)
+{
+ if (scopeType != ScopeType::VECTORSCOPE_HC && scopeType != ScopeType::VECTORSCOPE_HS) {
+ return;
+ }
+
+ const auto& vect = (scopeType == ScopeType::VECTORSCOPE_HC) ? vect_hc : vect_hs;
+ auto& vect_buffer = (scopeType == ScopeType::VECTORSCOPE_HC) ? vect_hc_buffer : vect_hs_buffer;
+ auto& vect_buffer_dirty = (scopeType == ScopeType::VECTORSCOPE_HC) ? vect_hc_buffer_dirty : vect_hs_buffer_dirty;
+
+ const int vect_width = vect.getWidth();
+ const int vect_height = vect.getHeight();
+ // Arbitrary scale factor multiplied by vectorscope area and divided by
+ // current scale.
+ const float scale = trace_brightness * 8.f * vect_width * vect_height / vectorscope_scale;
+
+ // See Cairo documentation on stride.
+ const int cairo_stride = Cairo::ImageSurface::format_stride_for_width(Cairo::FORMAT_ARGB32, vect_width);
+
+ if (vect_buffer_dirty && vectorscope_scale > 0) {
+ if (vect_buffer.size() != static_cast(cairo_stride) * vect_height) {
+ vect_buffer.resize(static_cast(cairo_stride) * vect_height);
+ }
+
+ assert(vect_buffer.size() % 4 == 0);
+
+ for (int y = 0; y < vect_height; y++) {
+ const int* const vect_row = vect[y];
+ std::uint32_t* const buffer_row =
+ reinterpret_cast(vect_buffer.data() + (vect_height - 1 - y) * cairo_stride);
+ for (int x = 0; x < vect_width; x++) {
+ const unsigned char value = std::min(scale * vect_row[x], 0xff);
+ buffer_row[x] = value | (value << 8) | (value << 16) | (value << 24);
+ }
+ }
+
+ vect_buffer_dirty = false;
+ }
+
+ const bool fit_width =
+ vect_width * (h - 2 * padding) > vect_height * (w - 2 * padding);
+ const float scope_scale = fit_width ?
+ (w - 2 * padding) / vect_width : (h - 2 * padding) / vect_height;
+ const float scope_size = (vectorscope_scale > 0) ?
+ scope_scale * std::max(vect_width, vect_height) : std::min(w, h) - 2 * padding;
+ const float o_x = (w - scope_scale * vect_width) / 2;
+ const float o_y = (h - scope_scale * vect_height) / 2;
+ const double s = RTScalable::getScale();
+ auto orig_matrix = cr->get_matrix();
+ const double line_length = scope_size / 2.0;
+ std::valarray ch_ds(1);
+
+ cr->translate(w / 2.0, h / 2.0);
+ cr->set_line_width (1.0 * s);
+ cr->set_antialias(Cairo::ANTIALIAS_SUBPIXEL);
+ ch_ds[0] = 4;
+
+ if (scopeType == ScopeType::VECTORSCOPE_HS) { // Hue-Saturation.
+ // RYGCBM lines.
+ cr->set_line_width (2.0 * s);
+ constexpr double color_labels[6][3] = {
+ {1, 0, 0}, // R
+ {0, 1, 0}, // G
+ {0, 0, 1}, // B
+ {0, 1, 1}, // C
+ {1, 0, 1}, // M
+ {1, 1, 0}, // Y
+ };
+ for (int i = 0; i < 3; i++) {
+ auto gradient = Cairo::LinearGradient::create(-line_length, 0, line_length, 0);
+ const double (&color_1)[3] = color_labels[i];
+ const double (&color_2)[3] = color_labels[i + 3];
+ cr->set_source(gradient);
+ gradient->add_color_stop_rgba(0, color_2[0], color_2[1], color_2[2], 0.5);
+ gradient->add_color_stop_rgba(0.5, 1, 1, 1, 0.25);
+ gradient->add_color_stop_rgba(1, color_1[0], color_1[1], color_1[2], 0.5);
+ cr->move_to(-line_length, 0);
+ cr->line_to(line_length, 0);
+ cr->rotate_degrees(-120);
+ cr->stroke();
+ }
+ cr->set_line_width (1.0 * s);
+ cr->set_source_rgba (1, 1, 1, 0.25);
+ // 100% saturation circle.
+ cr->arc(0, 0, scope_size / 2.0, 0, 2 * RT_PI);
+ cr->stroke();
+ // 25%, 50%, and 75% saturation.
+ cr->set_dash(ch_ds, 0);
+ for (int i = 1; i < 4; i++) {
+ cr->arc(0, 0, i * scope_size / 8.0, 0, 2 * RT_PI);
+ cr->stroke();
+ }
+ // HSV skin tone line derived from -I axis of YIQ.
+ cr->rotate(-0.134900 * RT_PI);
+ cr->move_to(0, 0);
+ cr->line_to(line_length, 0);
+ cr->stroke();
+ cr->unset_dash();
+ } else if (scopeType == ScopeType::VECTORSCOPE_HC) { // Hue-Chroma.
+ // a and b axes.
+ Cairo::RefPtr gradient;
+ cr->set_line_width (2.0 * s);
+ gradient = Cairo::LinearGradient::create(0, -line_length, 0, line_length);
+ cr->set_source(gradient);
+ gradient->add_color_stop_rgba(0, 1, 1, 0, 0.5); // "yellow"
+ gradient->add_color_stop_rgba(0.5, 1, 1, 1, 0.25); // neutral
+ gradient->add_color_stop_rgba(1, 0, 0, 1, 0.5); // "blue"
+ cr->move_to(0, 0);
+ cr->line_to(0, line_length);
+ cr->move_to(0, 0);
+ cr->line_to(0, -line_length);
+ cr->stroke();
+ gradient = Cairo::LinearGradient::create(-line_length, 0, line_length, 0);
+ cr->set_source(gradient);
+ gradient->add_color_stop_rgba(0, 0, 1, 0, 0.5); // "green"
+ gradient->add_color_stop_rgba(0.5, 1, 1, 1, 0.25); // neutral
+ gradient->add_color_stop_rgba(1, 1, 0, 1, 0.5); // "magenta"
+ cr->move_to(0, 0);
+ cr->line_to(line_length, 0);
+ cr->move_to(0, 0);
+ cr->line_to(-line_length, 0);
+ cr->stroke();
+ cr->set_source_rgba (1, 1, 1, 0.25);
+ cr->set_line_width (1.0 * s);
+ // 25%, 50%, 75%, and 100% of standard chroma range.
+ cr->set_dash(ch_ds, 0);
+ for (int i = 1; i <= 4; i++) {
+ cr->arc(0, 0, i * scope_size / 8.0, 0, 2 * RT_PI);
+ cr->stroke();
+ }
+ // CIELAB skin tone line, approximated by 50% saturation and
+ // value along the HSV skin tone line.
+ cr->rotate(-0.321713 * RT_PI);
+ cr->move_to(0, 0);
+ cr->line_to(line_length, 0);
+ cr->stroke();
+ cr->unset_dash();
+ }
+ cr->set_matrix(orig_matrix);
+
+ // Vectorscope trace.
+ if (vectorscope_scale > 0) {
+ Cairo::RefPtr surface = Cairo::ImageSurface::create(
+ vect_buffer.data(), Cairo::FORMAT_ARGB32, vect_width, vect_height, cairo_stride);
+ cr->translate(o_x, o_y);
+ cr->scale(scope_scale, scope_scale);
+ cr->set_source(surface, 0, 0);
+ cr->set_operator(Cairo::OPERATOR_OVER);
+ cr->paint();
+ surface->finish();
+ cr->set_matrix(orig_matrix);
+
+ if (needPointer && pointer_red >= 0 && pointer_green >= 0 && pointer_blue >= 0) {
+ float cx, cy;
+ if (scopeType == ScopeType::VECTORSCOPE_HS) {
+ float H, S, L;
+ Color::rgb2hslfloat(pointer_red * 257.f, pointer_green * 257.f, pointer_blue * 257.f, H, S, L);
+ cx = (w + scope_size * S * std::cos(H * 2 * RT_PI_F)) / 2;
+ cy = (h - scope_size * S * std::sin(H * 2 * RT_PI_F)) / 2;
+ } else {
+ constexpr float ab_factor = 1.f / 256.f;
+ cx = w / 2.f + scope_size * pointer_a * ab_factor;
+ cy = h / 2.f - scope_size * pointer_b * ab_factor;
+ }
+ const float crosshair_size = 20.f * s;
+ cr->set_source_rgba(1, 1, 1, 0.5);
+ cr->move_to(cx - crosshair_size, cy);
+ cr->line_to(cx + crosshair_size, cy);
+ cr->move_to(cx, cy - crosshair_size);
+ cr->line_to(cx, cy + crosshair_size);
+ cr->stroke();
+ cr->arc(cx, cy, 3 * s, 0, 2 * RT_PI);
+ cr->set_source_rgb(1, 1, 1);
+ cr->fill_preserve();
+ cr->set_source_rgb(0, 0, 0);
+ cr->set_line_width (1.0 * s);
+ cr->stroke();
+ }
+ }
+}
+
+void HistogramArea::drawWaveform(Cairo::RefPtr &cr, int w, int h)
+{
+ // Arbitrary scale factor divided by current scale.
+ const float scale = trace_brightness * 32.f * 255.f / waveform_scale;
+ const int wave_width = rwave.getWidth();
+ const int wave_height = rwave.getHeight();
+
+ // See Cairo documentation on stride.
+ const int cairo_stride = Cairo::ImageSurface::format_stride_for_width(Cairo::FORMAT_ARGB32, rwave.getWidth());
+ const auto buffer_size = static_cast::size_type>(wave_height) * cairo_stride;
+
+ if (wave_buffer_dirty && (needRed || needGreen || needBlue)) {
+ wave_buffer.assign(buffer_size, 0);
+ assert(wave_buffer.size() % 4 == 0);
+
+ for (int val = 0; val < wave_height; val++) {
+ const int* const r_row = rwave[val];
+ const int* const g_row = gwave[val];
+ const int* const b_row = bwave[val];
+ std::uint32_t* const buffer_row = reinterpret_cast(wave_buffer.data() + (255 - val) * cairo_stride);
+ for (int col = 0; col < wave_width; col++) {
+ const unsigned char r = needRed ? std::min(scale * r_row[col], 0xff) : 0;
+ const unsigned char g = needGreen ? std::min(scale * g_row[col], 0xff) : 0;
+ const unsigned char b = needBlue ? std::min(scale * b_row[col], 0xff) : 0;
+ const unsigned char value = rtengine::max(r, g, b);
+ if (value != 0) {
+ // Ensures correct order regardless of endianness.
+ buffer_row[col] = b | (g << 8) | (r << 16) | (value << 24);
+ }
+ }
+ }
+
+ wave_buffer_dirty = false;
+ }
+
+ if (wave_buffer_luma_dirty && needLuma) {
+ wave_buffer_luma.assign(buffer_size, 0);
+ assert(wave_buffer_luma.size() % 4 == 0);
+
+ for (int val = 0; val < wave_height; val++) {
+ const int* const l_row = lwave[val];
+ std::uint32_t* const buffer_row =
+ reinterpret_cast(wave_buffer_luma.data() + (255 - val) * cairo_stride);
+ for (int col = 0; col < wave_width; col++) {
+ const unsigned char l = std::min(scale * l_row[col], 0xff);
+ buffer_row[col] = l | (l << 8) | (l << 16) | (l << 24);
+ }
+ }
+
+ wave_buffer_luma_dirty = false;
+ }
+
+ Cairo::RefPtr surface;
+ auto orig_matrix = cr->get_matrix();
+ cr->translate(0, padding);
+ cr->scale(static_cast(w) / wave_width, (h - 2 * padding) / wave_height);
+ if (needLuma) {
+ surface = Cairo::ImageSurface::create(
+ wave_buffer_luma.data(), Cairo::FORMAT_ARGB32, wave_width, wave_height, cairo_stride);
+ cr->set_source(surface, 0, 0);
+ cr->set_operator(Cairo::OPERATOR_OVER);
+ cr->paint();
+ surface->finish();
+ }
+ if (needRed || needGreen || needBlue) {
+ surface = Cairo::ImageSurface::create(
+ wave_buffer.data(), Cairo::FORMAT_ARGB32, wave_width, wave_height, cairo_stride);
+ cr->set_source(surface, 0, 0);
+ cr->set_operator(Cairo::OPERATOR_OVER);
+ cr->paint();
+ surface->finish();
+ }
+ cr->set_matrix(orig_matrix);
+}
+
bool HistogramArea::on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr)
{
- if (get_width() != oldwidth || get_height() != oldheight || isDirty ()) {
+ if (!updatePending() && (get_width() != oldwidth || get_height() != oldheight || isDirty())) {
updateBackBuffer ();
}
@@ -1054,7 +1897,10 @@ bool HistogramArea::on_button_press_event (GdkEventButton* event)
isPressed = true;
movingPosition = event->x;
- if (event->type == GDK_2BUTTON_PRESS && event->button == 1) {
+ if (
+ event->type == GDK_2BUTTON_PRESS && event->button == 1
+ && (scopeType == ScopeType::HISTOGRAM || scopeType == ScopeType::HISTOGRAM_RAW)
+ ) {
drawMode = (drawMode + 1) % 3;
options.histogramDrawMode = (options.histogramDrawMode + 1) % 3;
@@ -1078,8 +1924,18 @@ bool HistogramArea::on_button_release_event (GdkEventButton* event)
bool HistogramArea::on_motion_notify_event (GdkEventMotion* event)
{
- if (isPressed)
- {
+ if (
+ drawMode == 0
+ && (scopeType == ScopeType::HISTOGRAM || scopeType == ScopeType::HISTOGRAM_RAW)
+ ) {
+ return false;
+ }
+
+ if (!isPressed) {
+ return true;
+ }
+
+ if (scopeType == ScopeType::HISTOGRAM || scopeType == ScopeType::HISTOGRAM_RAW) { // Adjust log scale.
double mod = 1 + (event->x - movingPosition) / get_width();
factor /= mod;
@@ -1092,11 +1948,45 @@ bool HistogramArea::on_motion_notify_event (GdkEventMotion* event)
setDirty(true);
queue_draw ();
+ } else if (
+ scopeType == ScopeType::PARADE
+ || scopeType == ScopeType::WAVEFORM
+ || scopeType == ScopeType::VECTORSCOPE_HC
+ || scopeType == ScopeType::VECTORSCOPE_HS
+ ) { // Adjust brightness.
+ constexpr float RANGE = MAX_BRIGHT / MIN_BRIGHT;
+ double dx = (event->x - movingPosition) / get_width();
+ float new_brightness = LIM(trace_brightness * pow(RANGE, dx), MIN_BRIGHT, MAX_BRIGHT);
+ setBrightness(new_brightness);
+ movingPosition = event->x;
}
return true;
}
+float HistogramArea::getBrightness(void)
+{
+ return trace_brightness;
+}
+
+void HistogramArea::setBrightness(float brightness)
+{
+ brightness = LIM(brightness, MIN_BRIGHT, MAX_BRIGHT);
+ if (brightness != trace_brightness) {
+ parade_buffer_r_dirty = parade_buffer_g_dirty = parade_buffer_b_dirty = wave_buffer_dirty = wave_buffer_luma_dirty = vect_hc_buffer_dirty = vect_hs_buffer_dirty = true;
+ trace_brightness = brightness;
+ setDirty(true);
+ queue_draw();
+
+ signal_brightness_changed.emit(trace_brightness);
+ }
+}
+
+HistogramArea::SignalBrightnessChanged HistogramArea::getBrighnessChangedSignal(void)
+{
+ return signal_brightness_changed;
+}
+
HistogramArea::type_signal_factor_changed HistogramArea::signal_factor_changed()
{
return sigFactorChanged;
diff --git a/rtgui/histogrampanel.h b/rtgui/histogrampanel.h
index 740b0a12c..393df51a5 100644
--- a/rtgui/histogrampanel.h
+++ b/rtgui/histogrampanel.h
@@ -18,6 +18,8 @@
*/
#pragma once
+#include
+
#include
#include
@@ -26,8 +28,10 @@
#include "delayed.h"
#include "guiutils.h"
+#include "options.h"
#include "pointermotionlistener.h"
+#include "../rtengine/array2D.h"
#include "../rtengine/LUT.h"
#include "../rtengine/noncopyable.h"
@@ -54,7 +58,7 @@ public:
double log (double vsize, double val);
};
-class HistogramRGBArea final : public Gtk::DrawingArea, public BackBuffer, private HistogramScaling, public rtengine::NonCopyable
+class HistogramRGBArea : public Gtk::DrawingArea, public BackBuffer, protected HistogramScaling, public rtengine::NonCopyable
{
private:
typedef const double (*TMatrix)[3];
@@ -74,7 +78,6 @@ protected:
bool needBlue;
bool needLuma;
bool needChroma;
- bool rawMode;
bool showMode;
bool barDisplayed;
@@ -84,32 +87,58 @@ protected:
HistogramRGBAreaIdleHelper* harih;
+ /** Draw an indicator bar for the value. */
+ virtual void drawBar(Cairo::RefPtr cc, double value, double max_value, int winw, int winh, double scale) = 0;
+
+ void getPreferredThickness(int& min_thickness, int& natural_length) const;
+ void getPreferredLength(int& min_length, int& natural_length) const;
+ void getPreferredThicknessForLength(int length, int& min_thickness, int& natural_length) const;
+ void getPreferredLengthForThickness(int thickness, int& min_length, int& natural_length) const;
+
public:
HistogramRGBArea();
~HistogramRGBArea() override;
void updateBackBuffer (int r, int g, int b, const Glib::ustring &profile = "", const Glib::ustring &profileW = "");
bool getShow ();
+ void setShow(bool show);
void setParent (Gtk::Grid* p)
{
parent = p;
};
void update (int val, int rh, int gh, int bh);
- void updateOptions (bool r, bool g, bool b, bool l, bool c, bool raw, bool show);
+ void updateOptions (bool r, bool g, bool b, bool l, bool c, bool show);
void on_realize() override;
bool on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) override;
bool on_button_press_event (GdkEventButton* event) override;
void factorChanged (double newFactor);
+};
+
+class HistogramRGBAreaHori final : public HistogramRGBArea
+{
private:
+ void drawBar(Cairo::RefPtr cc, double value, double max_value, int winw, int winh, double scale) override;
+
Gtk::SizeRequestMode get_request_mode_vfunc () const override;
void get_preferred_height_vfunc (int& minimum_height, int& natural_height) const override;
void get_preferred_width_vfunc (int &minimum_width, int &natural_width) const override;
void get_preferred_height_for_width_vfunc (int width, int &minimum_height, int &natural_height) const override;
void get_preferred_width_for_height_vfunc (int h, int &minimum_width, int &natural_width) const override;
+};
+class HistogramRGBAreaVert final : public HistogramRGBArea
+{
+private:
+ void drawBar(Cairo::RefPtr cc, double value, double max_value, int winw, int winh, double scale) override;
+
+ Gtk::SizeRequestMode get_request_mode_vfunc () const override;
+ void get_preferred_height_vfunc (int& minimum_height, int& natural_height) const override;
+ void get_preferred_width_vfunc (int &minimum_width, int &natural_width) const override;
+ void get_preferred_height_for_width_vfunc (int width, int &minimum_height, int &natural_height) const override;
+ void get_preferred_width_for_height_vfunc (int h, int &minimum_width, int &natural_width) const override;
};
class DrawModeListener
@@ -123,7 +152,10 @@ class HistogramArea final : public Gtk::DrawingArea, public BackBuffer, private
{
public:
typedef sigc::signal type_signal_factor_changed;
+ typedef sigc::signal SignalBrightnessChanged;
+ static constexpr float MIN_BRIGHT = 0.1;
+ static constexpr float MAX_BRIGHT = 3;
private:
IdleRegister idle_register;
type_signal_factor_changed sigFactorChanged;
@@ -131,26 +163,49 @@ private:
protected:
LUTu rhist, ghist, bhist, lhist, chist;
LUTu rhistRaw, ghistRaw, bhistRaw, lhistRaw; //lhistRaw is unused?
+ int vectorscope_scale;
+ array2D vect_hc, vect_hs;
+ std::vector vect_hc_buffer, vect_hs_buffer;
+ bool vect_hc_buffer_dirty, vect_hs_buffer_dirty;
+ int waveform_scale;
+ array2D rwave, gwave, bwave, lwave;
+ std::vector parade_buffer_r;
+ std::vector parade_buffer_g;
+ std::vector parade_buffer_b;
+ bool parade_buffer_r_dirty, parade_buffer_g_dirty, parade_buffer_b_dirty;
+ std::vector wave_buffer;
+ std::vector wave_buffer_luma;
+ bool wave_buffer_dirty, wave_buffer_luma_dirty;
bool valid;
int drawMode;
DrawModeListener *myDrawModeListener;
+ Options::ScopeType scopeType;
int oldwidth, oldheight;
+ /// Intensity of waveform and vectorscope trace.
+ float trace_brightness;
bool needRed, needGreen, needBlue, needLuma, needChroma;
- bool rawMode;
bool isPressed;
double movingPosition;
+ bool needPointer;
double padding = 5.0;
HistogramAreaIdleHelper* haih;
+ int pointer_red, pointer_green, pointer_blue;
+ float pointer_a, pointer_b;
+
+ SignalBrightnessChanged signal_brightness_changed;
+
public:
explicit HistogramArea(DrawModeListener *fml = nullptr);
~HistogramArea() override;
void updateBackBuffer ();
+ /// Update pointer values. Returns true if widget needs redrawing.
+ bool updatePointer(int r, int g, int b, const Glib::ustring &profile = "", const Glib::ustring &profileW = "");
void update(
const LUTu& histRed,
const LUTu& histGreen,
@@ -159,19 +214,35 @@ public:
const LUTu& histChroma,
const LUTu& histRedRaw,
const LUTu& histGreenRaw,
- const LUTu& histBlueRaw
+ const LUTu& histBlueRaw,
+ int vectorscopeScale,
+ const array2D& vectorscopeHC,
+ const array2D& vectorscopeHS,
+ int waveformScale,
+ const array2D& waveformRed,
+ const array2D& waveformGreen,
+ const array2D& waveformBlue,
+ const array2D& waveformLuma
);
- void updateOptions (bool r, bool g, bool b, bool l, bool c, bool raw, int mode);
+ void updateOptions (bool r, bool g, bool b, bool l, bool c, int mode, Options::ScopeType type, bool pointer);
+ bool updatePending();
void on_realize() override;
bool on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) override;
bool on_button_press_event (GdkEventButton* event) override;
bool on_button_release_event (GdkEventButton* event) override;
bool on_motion_notify_event (GdkEventMotion* event) override;
+ float getBrightness(void);
+ /** Set the trace brightness, with 1 being normal. */
+ void setBrightness(float brightness);
+ SignalBrightnessChanged getBrighnessChangedSignal(void);
type_signal_factor_changed signal_factor_changed();
private:
void drawCurve(Cairo::RefPtr &cr, const LUTu & data, double scale, int hsize, int vsize);
void drawMarks(Cairo::RefPtr &cr, const LUTu & data, double scale, int hsize, int & ui, int & oi);
+ void drawParade(Cairo::RefPtr &cr, int hsize, int vsize);
+ void drawVectorscope(Cairo::RefPtr &cr, int hsize, int vsize);
+ void drawWaveform(Cairo::RefPtr &cr, int hsize, int vsize);
Gtk::SizeRequestMode get_request_mode_vfunc () const override;
void get_preferred_height_vfunc (int& minimum_height, int& natural_height) const override;
void get_preferred_width_vfunc (int &minimum_width, int &natural_width) const override;
@@ -179,6 +250,12 @@ private:
void get_preferred_width_for_height_vfunc (int height, int &minimum_width, int &natural_width) const override;
};
+class HistogramPanelListener
+{
+public:
+ virtual void scopeTypeChanged(Options::ScopeType new_type) = 0;
+};
+
class HistogramPanel final : public Gtk::Grid, public PointerMotionListener, public DrawModeListener, public rtengine::NonCopyable
{
private:
@@ -188,22 +265,33 @@ protected:
Gtk::Grid* gfxGrid;
Gtk::Grid* buttonGrid;
+ Gtk::Box* persistentButtons;
+ Gtk::Box* optionButtons;
HistogramArea* histogramArea;
HistogramRGBArea* histogramRGBArea;
+ std::unique_ptr histogramRGBAreaHori;
+ std::unique_ptr histogramRGBAreaVert;
Gtk::ToggleButton* showRed;
Gtk::ToggleButton* showGreen;
Gtk::ToggleButton* showBlue;
Gtk::ToggleButton* showValue;
- Gtk::ToggleButton* showRAW;
Gtk::ToggleButton* showBAR;
Gtk::ToggleButton* showChro;
Gtk::Button* showMode;
+ Gtk::ToggleButton* scopeOptions;
+ Gtk::Scale* brightnessWidget;
+
+ Gtk::RadioButton* scopeHistBtn;
+ Gtk::RadioButton* scopeHistRawBtn;
+ Gtk::RadioButton* scopeParadeBtn;
+ Gtk::RadioButton* scopeWaveBtn;
+ Gtk::RadioButton* scopeVectHcBtn;
+ Gtk::RadioButton* scopeVectHsBtn;
Gtk::Image *redImage;
Gtk::Image *greenImage;
Gtk::Image *blueImage;
Gtk::Image *valueImage;
- Gtk::Image *rawImage;
Gtk::Image *barImage;
Gtk::Image *chroImage;
@@ -211,7 +299,6 @@ protected:
Gtk::Image *greenImage_g;
Gtk::Image *blueImage_g;
Gtk::Image *valueImage_g;
- Gtk::Image *rawImage_g;
Gtk::Image *barImage_g;
Gtk::Image *chroImage_g;
@@ -219,8 +306,14 @@ protected:
Gtk::Image *mode1Image;
Gtk::Image *mode2Image;
+ HistogramPanelListener* panel_listener;
+
+ sigc::connection brightness_changed_connection;
sigc::connection rconn;
void setHistInvalid ();
+ void showRGBBar();
+ void updateHistAreaOptions();
+ void updateHistRGBAreaOptions();
public:
@@ -235,9 +328,18 @@ public:
const LUTu& histChroma,
const LUTu& histRedRaw,
const LUTu& histGreenRaw,
- const LUTu& histBlueRaw)
+ const LUTu& histBlueRaw,
+ int vectorscopeScale,
+ const array2D& vectorscopeHC,
+ const array2D& vectorscopeHS,
+ int waveformScale,
+ const array2D& waveformRed,
+ const array2D& waveformGreen,
+ const array2D& waveformBlue,
+ const array2D& waveformLuma
+ )
{
- histogramArea->update(histRed, histGreen, histBlue, histLuma, histChroma, histRedRaw, histGreenRaw, histBlueRaw);
+ histogramArea->update(histRed, histGreen, histBlue, histLuma, histChroma, histRedRaw, histGreenRaw, histBlueRaw, vectorscopeScale, vectorscopeHC, vectorscopeHS, waveformScale, waveformRed, waveformGreen, waveformBlue, waveformLuma);
}
// pointermotionlistener interface
void pointerMoved (bool validPos, const Glib::ustring &profile, const Glib::ustring &profileW, int x, int y, int r, int g, int b, bool isRaw = false) override;
@@ -250,13 +352,19 @@ public:
void green_toggled ();
void blue_toggled ();
void value_toggled ();
- void raw_toggled ();
void chro_toggled ();
void bar_toggled ();
void mode_released ();
+ void brightnessWidgetValueChanged();
+ void brightnessUpdated(float brightness);
+ void scopeOptionsToggled();
+ void type_selected(Gtk::RadioButton* button);
+ void type_changed ();
void rgbv_toggled ();
void resized (Gtk::Allocation& req);
// drawModeListener interface
void toggleButtonMode () override;
+
+ void setPanelListener(HistogramPanelListener* listener);
};
diff --git a/rtgui/options.cc b/rtgui/options.cc
index 99e1b9f08..46d9b9ca5 100644
--- a/rtgui/options.cc
+++ b/rtgui/options.cc
@@ -446,10 +446,12 @@ void Options::setDefaults()
histogramBlue = true;
histogramLuma = false;
histogramChroma = false;
- histogramRAW = false;
histogramBar = true;
histogramHeight = 200;
histogramDrawMode = 0;
+ histogramScopeType = ScopeType::HISTOGRAM;
+ histogramShowOptionButtons = false;
+ histogramTraceBrightness = 1;
curvebboxpos = 1;
complexity = 2;
prevdemo = PD_Sidecar;
@@ -1417,7 +1419,10 @@ void Options::readFromFile(Glib::ustring fname)
}
if (keyFile.has_key("GUI", "HistogramRAW")) {
- histogramRAW = keyFile.get_boolean("GUI", "HistogramRAW");
+ // Legacy option, replaced by HistogramScopeType.
+ if (keyFile.get_boolean("GUI", "HistogramRAW")) {
+ histogramScopeType = ScopeType::HISTOGRAM_RAW;
+ }
}
if (keyFile.has_key("GUI", "HistogramBar")) {
@@ -1432,6 +1437,18 @@ void Options::readFromFile(Glib::ustring fname)
histogramDrawMode = keyFile.get_integer("GUI", "HistogramDrawMode");
}
+ if (keyFile.has_key("GUI", "HistogramScopeType")) {
+ histogramScopeType = static_cast(keyFile.get_integer("GUI", "HistogramScopeType"));
+ }
+
+ if (keyFile.has_key("GUI", "HistogramShowOptionButtons")) {
+ histogramShowOptionButtons = keyFile.get_boolean("GUI", "HistogramShowOptionButtons");
+ }
+
+ if (keyFile.has_key("GUI", "HistogramTraceBrightness")) {
+ histogramTraceBrightness = keyFile.get_double("GUI", "HistogramTraceBrightness");
+ }
+
if (keyFile.has_key("GUI", "NavigatorRGBUnit")) {
navRGBUnit = (NavigatorUnit)keyFile.get_integer("GUI", "NavigatorRGBUnit");
}
@@ -2251,10 +2268,12 @@ void Options::saveToFile(Glib::ustring fname)
keyFile.set_boolean("GUI", "HistogramBlue", histogramBlue);
keyFile.set_boolean("GUI", "HistogramLuma", histogramLuma);
keyFile.set_boolean("GUI", "HistogramChroma", histogramChroma);
- keyFile.set_boolean("GUI", "HistogramRAW", histogramRAW);
keyFile.set_boolean("GUI", "HistogramBar", histogramBar);
keyFile.set_integer("GUI", "HistogramHeight", histogramHeight);
keyFile.set_integer("GUI", "HistogramDrawMode", histogramDrawMode);
+ keyFile.set_integer("GUI", "HistogramScopeType", rtengine::toUnderlying(histogramScopeType));
+ keyFile.set_boolean("GUI", "HistogramShowOptionButtons", histogramShowOptionButtons);
+ keyFile.set_double("GUI", "HistogramTraceBrightness", histogramTraceBrightness);
keyFile.set_integer("GUI", "NavigatorRGBUnit", (int)navRGBUnit);
keyFile.set_integer("GUI", "NavigatorHSVUnit", (int)navHSVUnit);
keyFile.set_boolean("GUI", "ShowFilmStripToolBar", showFilmStripToolBar);
diff --git a/rtgui/options.h b/rtgui/options.h
index 02d62292c..d2e0c0543 100644
--- a/rtgui/options.h
+++ b/rtgui/options.h
@@ -168,13 +168,23 @@ private:
const Glib::ustring& entryName, Glib::ustring& destination);
public:
-
enum class NavigatorUnit {
PERCENT,
R0_255,
R0_1,
_COUNT
};
+
+ enum class ScopeType {
+ NONE = -1,
+ HISTOGRAM,
+ HISTOGRAM_RAW,
+ PARADE,
+ VECTORSCOPE_HC,
+ VECTORSCOPE_HS,
+ WAVEFORM
+ };
+
bool savesParamsAtExit;
SaveFormat saveFormat, saveFormatBatch;
Glib::ustring savePathTemplate;
@@ -308,10 +318,13 @@ public:
int histogramPosition; // 0=disabled, 1=left pane, 2=right pane
bool histogramRed, histogramGreen, histogramBlue;
- bool histogramLuma, histogramChroma, histogramRAW;
+ bool histogramLuma, histogramChroma;
bool histogramBar;
int histogramHeight;
int histogramDrawMode;
+ ScopeType histogramScopeType;
+ bool histogramShowOptionButtons;
+ float histogramTraceBrightness;
bool FileBrowserToolbarSingleRow;
bool hideTPVScrollbar;
int whiteBalanceSpotSize;