diff --git a/rtdata/themes/RawTherapee-GTK3-20_.css b/rtdata/themes/RawTherapee-GTK3-20_.css index f4f9ddb7fc..5c84d4ea3e 100644 --- a/rtdata/themes/RawTherapee-GTK3-20_.css +++ b/rtdata/themes/RawTherapee-GTK3-20_.css @@ -706,7 +706,8 @@ flowboxchild:selected { #HistogramArea, #HistogramRGBArea { - border-width: 0.083333333333333333em; + border: none; + /*border-width: 0.083333333333333333em;*/ } #histButton { diff --git a/rtengine/imagesource.h b/rtengine/imagesource.h index a1c3731f16..ebf9bc7c11 100644 --- a/rtengine/imagesource.h +++ b/rtengine/imagesource.h @@ -124,6 +124,11 @@ class ImageSource : public InitialImage virtual void WBauto(double &tempref, double &greenref, array2D &redloc, array2D &greenloc, array2D &blueloc, int bfw, int bfh, double &avg_rm, double &avg_gm, double &avg_bm, double &tempitc, double &greenitc, float &studgood, bool &twotimes, const procparams::WBParams & wbpar, int begx, int begy, int yEn, int xEn, int cx, int cy, const procparams::ColorManagementParams &cmp, const procparams::RAWParams &raw) = 0; virtual void getrgbloc(int begx, int begy, int yEn, int xEn, int cx, int cy, int bf_h, int bf_w) = 0; + virtual unsigned int getBitDepth() const + { + return 0; + } + virtual double getDefGain () const { return 1.0; diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 1bee058c8d..397d29cc07 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -99,10 +99,13 @@ ImProcCoordinator::ImProcCoordinator() : lhist16CCAM(65536), lhist16RETI(), lhist16LClad(65536), - histRed(256), histRedRaw(256), - histGreen(256), histGreenRaw(256), - histBlue(256), histBlueRaw(256), - histLuma(256), + + // Main histogram + histRed(65536), histRedRaw(65536), + histGreen(65536), histGreenRaw(65536), + histBlue(65536), histBlueRaw(65536), + histLuma(65536), + histToneCurve(256), histToneCurveBW(256), histLCurve(256), @@ -113,7 +116,7 @@ ImProcCoordinator::ImProcCoordinator() : histCCAM(256), histClad(256), bcabhist(256), - histChroma(256), + histChroma(65536), histLRETI(256), @@ -310,6 +313,14 @@ void ImProcCoordinator::updatePreviewImage(int todo, bool panningRelatedChange) } } + // Forward bit depth of image to the histogram + if (hListener) { + hListener->setBitDepth(imgsrc->getBitDepth()); + if (settings->verbose) { + printf("Image has a bit depth of %u bits\n",imgsrc->getBitDepth()); + } + } + if (((todo & ALL) == ALL) || (todo & M_MONITOR) || panningRelatedChange || (highDetailNeeded && options.prevdemo != PD_Sidecar)) { bwAutoR = bwAutoG = bwAutoB = -9000.f; @@ -1827,7 +1838,7 @@ bool ImProcCoordinator::updateLRGBHistograms() for (int i = y1; i < y2; i++) for (int j = x1; j < x2; j++) { - histChroma[(int)(sqrtf(SQR(nprevl->a[i][j]) + SQR(nprevl->b[i][j])) / 188.f)]++; //188 = 48000/256 + histChroma[(int)(sqrtf(SQR(nprevl->a[i][j]) + SQR(nprevl->b[i][j])) * 255.0/48000.0)]++; } } #ifdef _OPENMP @@ -1836,10 +1847,11 @@ bool ImProcCoordinator::updateLRGBHistograms() { histLuma.clear(); + double s = 0.0078125; // map L = [0..32768] to [0..255] for (int i = y1; i < y2; i++) for (int j = x1; j < x2; j++) { - histLuma[(int)(nprevl->L[i][j] / 128.f)]++; + histLuma[(int)(nprevl->L[i][j] * s)]++; } } #ifdef _OPENMP diff --git a/rtengine/rawimage.h b/rtengine/rawimage.h index 871267dac5..3af2cebb2f 100644 --- a/rtengine/rawimage.h +++ b/rtengine/rawimage.h @@ -108,6 +108,11 @@ class RawImage: public DCraw { return fuji_width; } + + unsigned int get_bitdepth() const + { + return tiff_bps; + } float const * get_FloatRawImage() const { diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index 689dddf20d..baf5676f9d 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -517,6 +517,11 @@ int RawImageSource::getRotateDegree() const return ri->get_rotateDegree(); } +unsigned int RawImageSource::getBitDepth() const +{ + return ri->get_bitdepth(); +} + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% void RawImageSource::transformRect (const PreviewProps &pp, int tran, int &ssx1, int &ssy1, int &width, int &height, int &fw) @@ -3769,7 +3774,7 @@ void RawImageSource::getAutoExpHistogram (LUTu & histogram, int& histcompr) } } -// Histogram MUST be 256 in size; gamma is applied, blackpoint and gain also +// For histogram data gamma is applied, blackpoint and gain also void RawImageSource::getRAWHistogram (LUTu & histRedRaw, LUTu & histGreenRaw, LUTu & histBlueRaw) { // BENCHFUN @@ -3777,9 +3782,10 @@ void RawImageSource::getRAWHistogram (LUTu & histRedRaw, LUTu & histGreenRaw, LU histGreenRaw.clear(); histBlueRaw.clear(); + const float maxrawvalue = static_cast(1 << getBitDepth()) - 1.f; const float maxWhite = rtengine::max(c_white[0], c_white[1], c_white[2], c_white[3]); - const float scale = maxWhite <= 1.f ? 65535.f : 1.f; // special case for float raw images in [0.0;1.0] range - const float multScale = maxWhite <= 1.f ? 1.f / 255.f : 255.f; + const float scale = maxWhite <= 1.f ? maxrawvalue : 1.f; // special case for float raw images in [0.0;1.0] range + const float multScale = maxWhite <= 1.f ? 1.f / maxrawvalue : maxrawvalue; const float mult[4] = { multScale / (c_white[0] - cblacksom[0]), multScale / (c_white[1] - cblacksom[1]), multScale / (c_white[2] - cblacksom[2]), @@ -3893,7 +3899,7 @@ void RawImageSource::getRAWHistogram (LUTu & histRedRaw, LUTu & histGreenRaw, LU [&](int c, int i) -> int { float f = mult[c] * std::max(0.f, i - cblacksom[c]); - return f > 0.f ? (f < 1.f ? 1 : std::min(int(f), 255)) : 0; + return f > 0.f ? (f < 1.f ? 1 : std::min(int(f), int(maxrawvalue))) : 0; }; for (int i = 0; i < histoSize; i++) { @@ -3915,11 +3921,11 @@ void RawImageSource::getRAWHistogram (LUTu & histRedRaw, LUTu & histGreenRaw, LU } if (ri->getSensorType() == ST_BAYER) // since there are twice as many greens, correct for it - for (int i = 0; i < 256; i++) { + for (int i = 0; i < 65536; i++) { histGreenRaw[i] >>= 1; } else if (ri->getSensorType() == ST_FUJI_XTRANS) // since Xtrans has 2.5 as many greens, correct for it - for (int i = 0; i < 256; i++) { + for (int i = 0; i < 65536; i++) { histGreenRaw[i] = (histGreenRaw[i] * 2) / 5; } else if (ri->get_colors() == 1) { // monochrome sensor => set all histograms equal diff --git a/rtengine/rawimagesource.h b/rtengine/rawimagesource.h index b463ea7884..57f06587ce 100644 --- a/rtengine/rawimagesource.h +++ b/rtengine/rawimagesource.h @@ -168,6 +168,8 @@ class RawImageSource final : public ImageSource void getSize (const PreviewProps &pp, int& w, int& h) override; int getRotateDegree() const override; + unsigned int getBitDepth() const override; + ImageMatrices* getImageMatrices () override { return &imatrices; diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index c4ffb91f75..0a9614e583 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -354,6 +354,8 @@ class HistogramListener virtual bool updateVectorscopeHS(void) const = 0; /** Returns if the listener wants the waveform to be updated. */ virtual bool updateWaveform(void) const = 0; + /** Set the bit depth of the image for the histogram binning */ + virtual void setBitDepth(unsigned int bitdepth) = 0; }; class HistogramObservable diff --git a/rtengine/stdimagesource.cc b/rtengine/stdimagesource.cc index 8cb8fa7925..fc0c1042f2 100644 --- a/rtengine/stdimagesource.cc +++ b/rtengine/stdimagesource.cc @@ -297,6 +297,11 @@ void StdImageSource::getSize (const PreviewProps &pp, int& w, int& h) h = pp.getHeight() / pp.getSkip() + (pp.getHeight() % pp.getSkip() > 0); } +unsigned int StdImageSource::getBitDepth() const +{ + return img->getBPS(); +} + void StdImageSource::getAutoExpHistogram (LUTu & histogram, int& histcompr) { if (img->getType() == sImage8) { diff --git a/rtengine/stdimagesource.h b/rtengine/stdimagesource.h index 9b95fe34ea..de65002183 100644 --- a/rtengine/stdimagesource.h +++ b/rtengine/stdimagesource.h @@ -87,6 +87,8 @@ class StdImageSource : public ImageSource void getFullSize (int& w, int& h, int tr = TR_NONE) override; void getSize (const PreviewProps &pp, int& w, int& h) override; + unsigned int getBitDepth() const override; + ImageIO* getImageIO () { return img; diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc index 34d6772061..5b218a7702 100644 --- a/rtgui/editorpanel.cc +++ b/rtgui/editorpanel.cc @@ -2305,6 +2305,13 @@ bool EditorPanel::updateWaveform(void) const || histogram_scope_type == ScopeType::NONE; } +void EditorPanel::setBitDepth(unsigned int bitdepth) +{ + if (histogramPanel) { + histogramPanel->setBitDepth(bitdepth); + } +} + void EditorPanel::scopeTypeChanged(ScopeType new_type) { histogram_scope_type = new_type; diff --git a/rtgui/editorpanel.h b/rtgui/editorpanel.h index a277ffd3a3..acd8a8b769 100644 --- a/rtgui/editorpanel.h +++ b/rtgui/editorpanel.h @@ -149,6 +149,7 @@ class EditorPanel final : bool updateVectorscopeHC(void) const override; bool updateVectorscopeHS(void) const override; bool updateWaveform(void) const override; + void setBitDepth(unsigned int bitdepth) override; // HistogramPanelListener void scopeTypeChanged(Options::ScopeType new_type) override; diff --git a/rtgui/histogrampanel.cc b/rtgui/histogrampanel.cc index 83db36cb44..b3a5ea542b 100644 --- a/rtgui/histogrampanel.cc +++ b/rtgui/histogrampanel.cc @@ -26,6 +26,8 @@ #include "../rtengine/LUT.h" #include "rtimage.h" #include "../rtengine/color.h" +#define BENCHMARK +#include "../rtengine/StopWatch.h" using namespace rtengine; @@ -709,19 +711,6 @@ void HistogramPanel::updateHistRGBAreaOptions() ); } -// -// -// -// HistogramScaling -double HistogramScaling::log(double vsize, double val) -{ - //double factor = 10.0; // can be tuned if necessary - higher is flatter curve - return vsize * std::log(factor / (factor + val)) / std::log(factor / (factor + vsize)); -} - -// -// -// // HistogramRGBArea HistogramRGBArea::HistogramRGBArea () : val(0), r(0), g(0), b(0), valid(false), @@ -749,7 +738,6 @@ HistogramRGBArea::~HistogramRGBArea () } } - void HistogramRGBArea::getPreferredThickness(int& min_thickness, int& natural_thickness) const { int minimumLength = 0; @@ -973,9 +961,9 @@ void HistogramRGBAreaHori::drawBar(Cairo::RefPtr cc, double valu { double pos; if (options.histogramDrawMode < 2) { - pos = padding + value * (winw - padding * 2.0) / max_value + 0.5 * scale; + pos = value * winw / max_value + 0.5 * scale; } else { - pos = padding + HistogramScaling::log (max_value, value) * (winw - padding * 2.0) / max_value + 0.5 * scale; + pos = HistogramScaling::log (max_value, value) * winw + 0.5 * scale; } cc->move_to(pos, 0.0); cc->line_to(pos, winh - 0.0); @@ -1011,9 +999,9 @@ void HistogramRGBAreaVert::drawBar(Cairo::RefPtr cc, double valu { 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; + pos = value * (winh - 1) / max_value + 0.5 * scale; } else { - pos = padding + HistogramScaling::log (max_value, value) * (winh - padding * 2.0) / max_value + 0.5 * scale; + pos = HistogramScaling::log (max_value, value) * winh + 0.5 * scale; } cc->move_to(0.0, winh - pos); cc->line_to(winw, winh - pos); @@ -1070,9 +1058,9 @@ HistogramArea::HistogramArea (DrawModeListener *fml) : pointer_a(0), pointer_b(0) { - rhist(256); - ghist(256); - bhist(256); + rhist(65536); + ghist(65536); + bhist(65536); lhist(256); chist(256); @@ -1110,7 +1098,6 @@ 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 = RTScalable::getScale(); minimum_width = 200 * s; natural_width = 400 * s; @@ -1118,7 +1105,6 @@ void HistogramArea::get_preferred_width_vfunc (int &minimum_width, int &natural_ void HistogramArea::get_preferred_height_for_width_vfunc (int width, int &minimum_height, int &natural_height) const { - minimum_height = 0; natural_height = 0; } @@ -1128,6 +1114,11 @@ void HistogramArea::get_preferred_width_for_height_vfunc (int height, int &minim get_preferred_width_vfunc (minimum_width, natural_width); } +void HistogramArea::setBitDepth(unsigned int bitdepth) +{ + HistogramArea::bitdepth = bitdepth; +} + 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; @@ -1236,6 +1227,8 @@ void HistogramArea::update( void HistogramArea::updateBackBuffer () { + //BENCHFUNMICRO + if (!get_realized ()) { return; } @@ -1245,52 +1238,57 @@ void HistogramArea::updateBackBuffer () window->get_geometry(winx, winy, winw, winh); // This will create or update the size of the BackBuffer::surface - setDrawRectangle(Cairo::FORMAT_ARGB32, 0, 0, winw, winh, true); + setDrawRectangle(Cairo::FORMAT_ARGB32, 0, 0, winw, winh, true); // sets w and h in the BackBuffer Cairo::RefPtr cr = Cairo::Context::create(surface); const Glib::RefPtr style = get_style_context(); - double s = RTScalable::getScale(); - // Setup drawing - cr->set_source_rgba (0., 0., 0., 0.); - cr->set_operator (Cairo::OPERATOR_CLEAR); - cr->paint (); - cr->set_operator (Cairo::OPERATOR_SOURCE); + cr->set_source_rgba(0., 0., 0., 0.); + cr->set_operator(Cairo::OPERATOR_CLEAR); + cr->paint(); + cr->set_operator(Cairo::OPERATOR_SOURCE); // Prepare drawing gridlines first - cr->set_source_rgba (1., 1., 1., 0.25); - cr->set_line_width (1.0 * s); + cr->set_source_rgba(1., 1., 1., 0.25); + cr->set_line_width (RTScalable::getScale()); cr->set_antialias(Cairo::ANTIALIAS_NONE); cr->set_line_join(Cairo::LINE_JOIN_MITER); cr->set_line_cap(Cairo::LINE_CAP_BUTT); - std::valarray ch_ds (1); + std::valarray ch_ds(1); ch_ds[0] = 4; - cr->set_dash (ch_ds, 0); + cr->set_dash(ch_ds, 0); - // determine the number of h-gridlines based on current h + // determine the number of horizontal gridlines based on current height 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) + + // set up vertical grid based on image bitdepth + int nrOfVGridPartitions = bitdepth; + if (options.histogramScopeType == ScopeType::HISTOGRAM) { + nrOfVGridPartitions = 8; // Preview image is always 8-bit + } + int maxHValue = (1 << nrOfVGridPartitions) - 1; + float maxHValuef = static_cast(maxHValue); // draw vertical gridlines if (options.histogramScopeType == ScopeType::HISTOGRAM || options.histogramScopeType == ScopeType::HISTOGRAM_RAW) { - for (int i = 0; i <= nrOfVGridPartitions; i++) { - double xpos = padding + 0.5; + float xpos; + for (int i = 0; i < nrOfVGridPartitions; i++) { if (options.histogramDrawMode < 2) { - xpos += (pow(2.0,i) - 1) * (w - padding * 2.0) / 255.0; + xpos = (pow(2.0,i) - 1) / maxHValuef * (w - 1.0); } else { - xpos += HistogramScaling::log (255, pow(2.0,i) - 1) * (w - padding * 2.0) / 255.0; + xpos = HistogramScaling::log(maxHValuef, pow(2.0,i) - 1) * (w - 1.0); } - cr->move_to (xpos, 0.); - cr->line_to (xpos, h); - cr->stroke (); + cr->move_to(xpos + 1.0, 0); + cr->line_to(xpos + 1.0, h); + cr->stroke(); } } // draw horizontal gridlines 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; + const double ypos = h - (pow(2.0,i) - 1) * (h - 1) / 255.0; cr->move_to(0, ypos); cr->line_to(w, ypos); cr->stroke(); @@ -1299,14 +1297,14 @@ void HistogramArea::updateBackBuffer () // Vectorscope has no gridlines. } else if (options.histogramDrawMode == 0) { for (int i = 1; i < nrOfHGridPartitions; i++) { - cr->move_to (padding, i * static_cast(h) / nrOfHGridPartitions + 0.5); - cr->line_to (w - padding, i * static_cast(h) / nrOfHGridPartitions + 0.5); + cr->move_to (0, i * static_cast(h) / nrOfHGridPartitions + 0.5); + cr->line_to (w, 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 * static_cast(h) / nrOfHGridPartitions) + 0.5); - cr->line_to (w - padding, h - HistogramScaling::log (h, i * static_cast(h) / nrOfHGridPartitions) + 0.5); + cr->move_to (0, h - HistogramScaling::log (h, i * static_cast(h) / nrOfHGridPartitions) + 0.5); + cr->line_to (w, h - HistogramScaling::log (h, i * static_cast(h) / nrOfHGridPartitions) + 0.5); cr->stroke (); } } @@ -1321,105 +1319,126 @@ void HistogramArea::updateBackBuffer () LUTu& gh = rawMode ? ghistRaw : ghist; LUTu& bh = rawMode ? bhistRaw : bhist; - // make double copies of LUT, one for faster access, another one to scale down the raw histos - LUTu rhchanged(256), ghchanged(256), bhchanged(256); - unsigned int lhisttemp[256] ALIGNED16 {0}, chisttemp[256] ALIGNED16 {0}, rhtemp[256] ALIGNED16 {0}, ghtemp[256] ALIGNED16 {0}, bhtemp[256] ALIGNED16 {0}; - const int scale = (rawMode ? 8 : 1); + // Compute the highest point of the histogram for scaling + unsigned int rgbmax = 0; + unsigned int lumamax = 0; + unsigned int chromamax = 0; - for(int i = 0; i < 256; i++) { - if(needLuma) { - lhisttemp[i] = lhist[i]; + for (int i = 1; i < maxHValue; i++) { // Values at far left and right end are handled differently + if (needLuma && lhist[i] > lumamax) { + lumamax = lhist[i]; } - if(needChroma) { - chisttemp[i] = chist[i]; + if (needChroma && chist[i] > chromamax) { + chromamax = chist[i]; } - if(needRed) { - rhchanged[i] = rhtemp[i] = rh[i] / scale; + if (needRed && rh[i] > rgbmax) { + rgbmax = rh[i]; } - if(needGreen) { - ghchanged[i] = ghtemp[i] = gh[i] / scale; + if (needGreen && gh[i] > rgbmax) { + rgbmax = gh[i]; } - if(needBlue) { - bhchanged[i] = bhtemp[i] = bh[i] / scale; + if (needBlue && bh[i] > rgbmax) { + rgbmax = bh[i]; } } + + // Compute the first and last non-zero histogram bins + int rgbFirst = -1, rgbLast = -1; + for (int i = 0; i < maxHValue + 1; i++) { + if (needRed && rgbFirst == -1 && rh[i] != 0) rgbFirst = i; + if (needGreen && rgbFirst == -1 && gh[i] != 0) rgbFirst = i; + if (needBlue && rgbFirst == -1 && bh[i] != 0) rgbFirst = i; + if (rgbFirst != -1) break; + } + for (int i = maxHValue + 1; i >= 0; i--) { + if (needRed && rgbLast == -1 && rh[i] != 0) rgbLast = i; + if (needGreen && rgbLast == -1 && gh[i] != 0) rgbLast = i; + if (needBlue && rgbLast == -1 && bh[i] != 0) rgbLast = i; + if (rgbLast != -1) break; + } - // Compute the highest point of the histogram for scaling - // Values at far left and right end (0 and 255) are handled differently - - unsigned int histheight = 0; - - for (int i = 1; i < 255; i++) { - if (needLuma && lhisttemp[i] > histheight) { - histheight = lhisttemp[i]; - } - - if (needChroma && chisttemp[i] > histheight) { - histheight = chisttemp[i]; - } - - if (needRed && rhtemp[i] > histheight) { - histheight = rhtemp[i]; - } - - if (needGreen && ghtemp[i] > histheight) { - histheight = ghtemp[i]; + cr->set_operator (Cairo::OPERATOR_OVER); + + if (rgbFirst != -1) { // Draw min/max marker lines + + float posX = static_cast(rgbFirst); + if (drawMode == 2) { + const float mult = maxHValuef / xlogf(HistogramScaling::factor / (HistogramScaling::factor + maxHValuef)); + posX = HistogramScaling::logMult(posX, mult); } - - if (needBlue && bhtemp[i] > histheight) { - histheight = bhtemp[i]; + posX = posX * (w - 1.f) / maxHValuef; + cr->move_to (posX + 1.f, 0); + cr->line_to (posX + 1.f, h); + cr->set_source_rgba (1., 1., 1., 0.55); + cr->stroke(); + } + + if (rgbLast != -1) { // Draw min/max marker lines + + float posX = static_cast(rgbLast); + if (drawMode == 2) { + const float mult = maxHValuef / xlogf(HistogramScaling::factor / (HistogramScaling::factor + maxHValuef)); + posX = HistogramScaling::logMult(posX, mult); } + posX = posX * (w - 1.f) / maxHValuef; + cr->move_to (posX + 1.f, 0); + cr->line_to (posX + 1.f, h); + cr->set_source_rgba (1., 1., 1., 0.55); + cr->stroke(); } - - int realhistheight = histheight; - - if (realhistheight < winh - 2) { - realhistheight = winh - 2; + + // Draw EV0 line at -3 stops from maximum (cf. RawDigger) + float posX = static_cast(1 << (nrOfVGridPartitions - 3)) - 1.f; + if (drawMode == 2) { + const float mult = maxHValuef / xlogf(HistogramScaling::factor / (HistogramScaling::factor + maxHValuef)); + posX = HistogramScaling::logMult(posX, mult); } - - cr->set_antialias (Cairo::ANTIALIAS_SUBPIXEL); - cr->set_line_width (1.0 * s); - cr->set_operator (Cairo::OPERATOR_OVER); - + posX = posX * (w - 1.f) / maxHValuef; + cr->move_to (posX + 1.f, 0); + cr->line_to (posX + 1.f, h); + cr->set_source_rgba (1., 1., 1., 0.55); + cr->stroke(); + + // Draw all curves int ui = 0, oi = 0; if (needLuma && !rawMode) { - drawCurve(cr, lhist, realhistheight, w, h); - cr->set_source_rgba (0.65, 0.65, 0.65, 0.65); - cr->fill (); - drawMarks(cr, lhist, realhistheight, w, ui, oi); + drawCurve(cr, lhist, lumamax); + cr->set_source_rgb (0.7, 0.7, 0.7); + cr->stroke (); + drawMarks(cr, lhist, lumamax, w, ui, oi); } if (needChroma && !rawMode) { - drawCurve(cr, chist, realhistheight, w, h); + drawCurve(cr, chist, chromamax); cr->set_source_rgb (0.9, 0.9, 0.); cr->stroke (); - drawMarks(cr, chist, realhistheight, w, ui, oi); + drawMarks(cr, chist, chromamax, w, ui, oi); } if (needRed) { - drawCurve(cr, rhchanged, realhistheight, w, h); + drawCurve(cr, rh, rgbmax); cr->set_source_rgb (1.0, 0.0, 0.0); cr->stroke (); - drawMarks(cr, rhchanged, realhistheight, w, ui, oi); + drawMarks(cr, rh, rgbmax, w, ui, oi); } if (needGreen) { - drawCurve(cr, ghchanged, realhistheight, w, h); + drawCurve(cr, gh, rgbmax); cr->set_source_rgb (0.0, 1.0, 0.0); cr->stroke (); - drawMarks(cr, ghchanged, realhistheight, w, ui, oi); + drawMarks(cr, gh, rgbmax, w, ui, oi); } if (needBlue) { - drawCurve(cr, bhchanged, realhistheight, w, h); + drawCurve(cr, bh, rgbmax); cr->set_source_rgb (0.0, 0.4, 1.0); cr->stroke (); - drawMarks(cr, bhchanged, realhistheight, w, ui, oi); + drawMarks(cr, bh, rgbmax, w, ui, oi); } } else if (scopeType == ScopeType::PARADE && rwave.getWidth() > 0) { @@ -1430,9 +1449,6 @@ void HistogramArea::updateBackBuffer () drawVectorscope(cr, w, h); } - // Draw the frame's border - style->render_frame(cr, 0, 0, surface->get_width(), surface->get_height()); - oldwidth = w; oldheight = h; @@ -1459,39 +1475,85 @@ bool HistogramArea::updatePointer(int r, int g, int b, const Glib::ustring &prof void HistogramArea::on_realize () { - Gtk::DrawingArea::on_realize(); add_events(Gdk::POINTER_MOTION_MASK | Gdk::BUTTON_PRESS_MASK | Gdk::BUTTON_RELEASE_MASK); } -void HistogramArea::drawCurve(Cairo::RefPtr &cr, - const LUTu & data, double scale, int hsize, int vsize) +void HistogramArea::drawCurve(Cairo::RefPtr &cr, const LUTu & data, float scale) { - double s = RTScalable::getScale(); - - cr->set_line_width(s); - cr->move_to (padding, vsize - 1); - scale = scale <= 0.0 ? 0.001 : scale; // avoid division by zero and negative values + std::vector iscaled(65536); + std::vector vals(65536); - for (int i = 0; i < 256; i++) { - double val = data[i] * static_cast(vsize) / scale; - - if (drawMode > 0) { // scale y for single and double log-scale - val = HistogramScaling::log (static_cast(vsize), val); + const float maxvalue = rtengine::max(scale, 0.001f); // avoid division by zero and negative values + + // Limit the loop to the actual available data based on the image bit depth + int maxHValue = (1 << bitdepth) - 1; + if (options.histogramScopeType == ScopeType::HISTOGRAM) { + maxHValue = (1 << 8) - 1; // Preview image is always 8-bit + } + float maxHValuef = static_cast(maxHValue); + + if (drawMode > 0) { // scale y for single and double log-scale + const float mult = (h - 1.0f) / xlogf(HistogramScaling::factor / (HistogramScaling::factor + maxvalue)); +#ifdef __SSE2__ + const vfloat multv = F2V(mult); + for (int i = 0; i <= maxHValue; i += 4) { + const vfloat val = _mm_cvtepi32_ps(_mm_loadu_si128((__m128i_u*)&data[i])); + if (_mm_movemask_ps(ZEROV - val)) { + STVFU(vals[i], HistogramScaling::logMult(val, multv)); + } else { + STVFU(vals[i], ZEROV); + } } - - double iscaled = i; - if (drawMode == 2) { // scale x for double log-scale - iscaled = HistogramScaling::log (255.0, static_cast(i)); +#else + for (int i = 0; i <= maxHValue; ++i) { + if (data[i]) { + vals[i] = HistogramScaling::logMult(data[i], mult); + } else { + vals[i] = 0.f; + } } +#endif + } else { + const float mult = (h - 1.0f) / maxvalue; + for (int i = 0; i <= maxHValue; ++i) { + vals[i] = data[i] * mult; + } + } - double posX = padding + iscaled * (hsize - padding * 2.0) / 255.0; - double posY = vsize - 2 + val * (4 - vsize) / vsize; - - cr->line_to (posX, posY); + if (drawMode == 2) { // scale x for double log-scale + const float mult = (w - 1.f) / xlogf(HistogramScaling::factor / (HistogramScaling::factor + maxHValuef)); +#ifdef __SSE2__ + const vfloat multv = F2V(mult); + const vfloat fourv = F2V(4.f); + vfloat iv = _mm_setr_ps(0.f, 1.f, 2.f, 3.f); + for (int i = 0; i <= maxHValue; i += 4, iv += fourv) { + if (_mm_movemask_ps(ZEROV - LVFU(vals[i]))) { + STVFU(iscaled[i], HistogramScaling::logMult(iv, multv)); + } + } +#else + for (int i = 0; i <= maxHValue; ++i) { + if (vals[i] != 0.f) { + iscaled[i] = HistogramScaling::logMult(i, mult); + } + } +#endif + } else { + const float mult = (w - 1.f) / maxHValuef; + for (int i = 0; i <= maxHValue; ++i) { + iscaled[i] = i * mult; + } } - cr->line_to (hsize - padding, vsize - 1); + for (int i = 0; i <= maxHValue; i++) { + if (vals[i] == 0.f) + continue; + const float posY = h - 1.f - vals[i]; + const float posX = 1.f + iscaled[i]; + cr->move_to(posX, h); + cr->line_to(posX, posY); + } } void HistogramArea::drawMarks(Cairo::RefPtr &cr, @@ -1499,12 +1561,17 @@ void HistogramArea::drawMarks(Cairo::RefPtr &cr, { int s = 8 * RTScalable::getScale(); - if(data[0] > scale) { - cr->rectangle(padding, (ui++)*s, s, s); + if (data[0] > scale) { + cr->rectangle(0, (ui++)*s, s, s); + } + + int max = 1 << bitdepth; + if (options.histogramScopeType == ScopeType::HISTOGRAM) { + max = 1 << 8; // Preview image is always 8-bit } - if(data[255] > scale) { - cr->rectangle(hsize - s - padding, (oi++)*s, s, s); + if (data[max - 1] > scale) { + cr->rectangle(hsize - s, (oi++)*s, s, s); } cr->fill(); @@ -1611,8 +1678,8 @@ void HistogramArea::drawParade(Cairo::RefPtr &cr, int w, int h) 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); + cr->translate(i * display_wave_width, 0); + cr->scale(display_wave_width / wave_width, static_cast(h) / wave_height); surface = Cairo::ImageSurface::create( buffers[i], Cairo::FORMAT_ARGB32, wave_width, wave_height, cairo_stride); cr->set_source(surface, 0, 0); @@ -1662,12 +1729,10 @@ void HistogramArea::drawVectorscope(Cairo::RefPtr &cr, int w, in 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 bool fit_width = vect_width * h > vect_height * w; + const float scope_scale = fit_width ? static_cast(w) / vect_width : static_cast(h) / vect_height; const float scope_size = (vectorscope_scale > 0) ? - scope_scale * std::max(vect_width, vect_height) : std::min(w, h) - 2 * padding; + scope_scale * std::max(vect_width, vect_height) : std::min(w, h); 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(); @@ -1858,8 +1923,8 @@ void HistogramArea::drawWaveform(Cairo::RefPtr &cr, int w, int h 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); + cr->translate(0, 0); + cr->scale(static_cast(w) / wave_width, static_cast(h) / wave_height); if (needLuma) { surface = Cairo::ImageSurface::create( wave_buffer_luma.data(), Cairo::FORMAT_ARGB32, wave_width, wave_height, cairo_stride); diff --git a/rtgui/histogrampanel.h b/rtgui/histogrampanel.h index 393df51a50..dc554153cc 100644 --- a/rtgui/histogrampanel.h +++ b/rtgui/histogrampanel.h @@ -34,6 +34,8 @@ #include "../rtengine/array2D.h" #include "../rtengine/LUT.h" #include "../rtengine/noncopyable.h" +#include "../rtengine/opthelper.h" +#include "../rtengine/sleef.h" class HistogramArea; @@ -53,9 +55,25 @@ struct HistogramRGBAreaIdleHelper { class HistogramScaling { public: - double factor; - HistogramScaling() : factor(10.0) {} - double log (double vsize, double val); + float factor; + HistogramScaling() : factor(10.f) {} // factor(10.f) can be tuned if necessary - higher is flatter curve + + float log(float scale, float val) const + { + return xlogf(factor / (factor + val)) / xlogf(factor / (factor + scale)); + } + + float logMult(float val, float mult) const + { + return xlogf(factor / (factor + val)) * mult; + } + +#ifdef __SSE2__ + vfloat logMult(vfloat val, vfloat mult) const + { + return xlogf(F2V(factor) / (F2V(factor) + val)) * mult; + } +#endif }; class HistogramRGBArea : public Gtk::DrawingArea, public BackBuffer, protected HistogramScaling, public rtengine::NonCopyable @@ -82,8 +100,6 @@ class HistogramRGBArea : public Gtk::DrawingArea, public BackBuffer, protected H bool barDisplayed; Gtk::Grid* parent; - - double padding = 5.0; HistogramRGBAreaIdleHelper* harih; @@ -162,7 +178,7 @@ class HistogramArea final : public Gtk::DrawingArea, public BackBuffer, private protected: LUTu rhist, ghist, bhist, lhist, chist; - LUTu rhistRaw, ghistRaw, bhistRaw, lhistRaw; //lhistRaw is unused? + LUTu rhistRaw, ghistRaw, bhistRaw; int vectorscope_scale; array2D vect_hc, vect_hs; std::vector vect_hc_buffer, vect_hs_buffer; @@ -189,8 +205,8 @@ class HistogramArea final : public Gtk::DrawingArea, public BackBuffer, private bool isPressed; double movingPosition; bool needPointer; - - double padding = 5.0; + + unsigned int bitdepth; HistogramAreaIdleHelper* haih; @@ -226,6 +242,7 @@ class HistogramArea final : public Gtk::DrawingArea, public BackBuffer, private ); void updateOptions (bool r, bool g, bool b, bool l, bool c, int mode, Options::ScopeType type, bool pointer); bool updatePending(); + void setBitDepth(unsigned int bitdepth); void on_realize() override; bool on_draw(const ::Cairo::RefPtr< Cairo::Context> &cr) override; bool on_button_press_event (GdkEventButton* event) override; @@ -238,7 +255,7 @@ class HistogramArea final : public Gtk::DrawingArea, public BackBuffer, private type_signal_factor_changed signal_factor_changed(); private: - void drawCurve(Cairo::RefPtr &cr, const LUTu & data, double scale, int hsize, int vsize); + void drawCurve(Cairo::RefPtr &cr, const LUTu & data, float scale); 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); @@ -341,6 +358,10 @@ class HistogramPanel final : public Gtk::Grid, public PointerMotionListener, pub { histogramArea->update(histRed, histGreen, histBlue, histLuma, histChroma, histRedRaw, histGreenRaw, histBlueRaw, vectorscopeScale, vectorscopeHC, vectorscopeHS, waveformScale, waveformRed, waveformGreen, waveformBlue, waveformLuma); } + void setBitDepth(unsigned int bitdepth) + { + histogramArea->setBitDepth(bitdepth); + } // 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;