From 08846264c79d8b107b927c90629bc1c9a91d41e4 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 9 Feb 2018 21:25:39 +0100 Subject: [PATCH 01/10] started work on making RT not clip the image at intermediate stages --- rtengine/curves.cc | 36 +++-- rtengine/curves.h | 124 ++++++++++++---- rtengine/dcp.cc | 18 ++- rtengine/dirpyr_equalizer.cc | 6 +- rtengine/iimage.h | 2 +- rtengine/imagefloat.cc | 61 ++++---- rtengine/improcfun.cc | 271 ++++++++++++++++++++--------------- rtengine/iplab2rgb.cc | 84 +++++++++-- rtengine/ipresize.cc | 6 +- rtengine/rt_math.h | 16 +++ rtengine/rtthumbnail.cc | 9 +- rtgui/editorpanel.cc | 9 +- rtgui/main.cc | 11 +- 13 files changed, 435 insertions(+), 218 deletions(-) diff --git a/rtengine/curves.cc b/rtengine/curves.cc index 508bd1640..dd25076fe 100644 --- a/rtengine/curves.cc +++ b/rtengine/curves.cc @@ -1827,6 +1827,14 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float const AdobeToneCurve& adobeTC = static_cast((const ToneCurve&) * this); for (size_t i = start; i < end; ++i) { + const bool oog_r = OOG(rc[i]); + const bool oog_g = OOG(gc[i]); + const bool oog_b = OOG(bc[i]); + + if (oog_r && oog_g && oog_b) { + continue; + } + float r = CLIP(rc[i]); float g = CLIP(gc[i]); float b = CLIP(bc[i]); @@ -1848,12 +1856,18 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float if (ar >= 65535.f && ag >= 65535.f && ab >= 65535.f) { // clip fast path, will also avoid strange colours of clipped highlights - rc[i] = gc[i] = bc[i] = 65535.f; + //rc[i] = gc[i] = bc[i] = 65535.f; + if (!oog_r) rc[i] = 65535.f; + if (!oog_g) gc[i] = 65535.f; + if (!oog_b) bc[i] = 65535.f; continue; } if (ar <= 0.f && ag <= 0.f && ab <= 0.f) { - rc[i] = gc[i] = bc[i] = 0; + //rc[i] = gc[i] = bc[i] = 0; + if (!oog_r) rc[i] = 0.f; + if (!oog_g) gc[i] = 0.f; + if (!oog_b) bc[i] = 0.f; continue; } @@ -1893,9 +1907,9 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float g = newg; b = newb; } - rc[i] = r; - gc[i] = g; - bc[i] = b; + if (!oog_r) rc[i] = r; + if (!oog_g) gc[i] = g; + if (!oog_b) bc[i] = b; continue; } @@ -2003,9 +2017,9 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float b = newb; } - rc[i] = r; - gc[i] = g; - bc[i] = b; + if (!oog_r) rc[i] = r; + if (!oog_g) gc[i] = g; + if (!oog_b) bc[i] = b; continue; } @@ -2066,9 +2080,9 @@ void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float g = newg; b = newb; } - rc[i] = r; - gc[i] = g; - bc[i] = b; + if (!oog_r) rc[i] = r; + if (!oog_g) gc[i] = g; + if (!oog_b) bc[i] = b; } } float PerceptualToneCurve::cf_range[2]; diff --git a/rtengine/curves.h b/rtengine/curves.h index d8e443fc9..e489ec38c 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -45,6 +45,29 @@ namespace rtengine class ToneCurve; class ColorAppearance; +namespace curves { + +inline void setLutVal(const LUTf &lut, float &val) +{ + if (!OOG(val)) { + val = lut[std::max(val, 0.f)]; + } else { + float m = lut[MAXVALF]; + val += (m - val); + } +} + +inline void setLutVal(float &val, float lutval, float maxval) +{ + if (!OOG(val)) { + val = lutval; + } else { + val += (maxval - val); + } +} + +} // namespace curves + class CurveFactory { @@ -733,7 +756,7 @@ inline void Lightcurve::Apply (float& Li) const assert (lutColCurve); - Li = lutColCurve[Li]; + curves::setLutVal(lutColCurve, Li); } class Brightcurve : public ColorAppearance @@ -748,7 +771,7 @@ inline void Brightcurve::Apply (float& Br) const assert (lutColCurve); - Br = lutColCurve[Br]; + curves::setLutVal(lutColCurve, Br); } class Chromacurve : public ColorAppearance @@ -763,7 +786,7 @@ inline void Chromacurve::Apply (float& Cr) const assert (lutColCurve); - Cr = lutColCurve[Cr]; + curves::setLutVal(lutColCurve, Cr); } class Saturcurve : public ColorAppearance { @@ -777,7 +800,7 @@ inline void Saturcurve::Apply (float& Sa) const assert (lutColCurve); - Sa = lutColCurve[Sa]; + curves::setLutVal(lutColCurve, Sa); } class Colorfcurve : public ColorAppearance @@ -792,7 +815,7 @@ inline void Colorfcurve::Apply (float& Cf) const assert (lutColCurve); - Cf = lutColCurve[Cf]; + curves::setLutVal(lutColCurve, Cf); } @@ -881,9 +904,9 @@ inline void StandardToneCurve::Apply (float& r, float& g, float& b) const assert (lutToneCurve); - r = lutToneCurve[r]; - g = lutToneCurve[g]; - b = lutToneCurve[b]; + curves::setLutVal(lutToneCurve, r); + curves::setLutVal(lutToneCurve, g); + curves::setLutVal(lutToneCurve, b); } inline void StandardToneCurve::BatchApply( @@ -910,27 +933,36 @@ inline void StandardToneCurve::BatchApply( break; #endif } - r[i] = lutToneCurve[r[i]]; - g[i] = lutToneCurve[g[i]]; - b[i] = lutToneCurve[b[i]]; + curves::setLutVal(lutToneCurve, r[i]); + curves::setLutVal(lutToneCurve, g[i]); + curves::setLutVal(lutToneCurve, b[i]); i++; } #ifdef __SSE2__ + float tmpr[4]; + float tmpg[4]; + float tmpb[4]; + float mv = lutToneCurve[MAXVALF]; for (; i + 3 < end; i += 4) { __m128 r_val = LVF(r[i]); __m128 g_val = LVF(g[i]); __m128 b_val = LVF(b[i]); - STVF(r[i], lutToneCurve[r_val]); - STVF(g[i], lutToneCurve[g_val]); - STVF(b[i], lutToneCurve[b_val]); + STVF(tmpr[0], lutToneCurve[r_val]); + STVF(tmpg[0], lutToneCurve[g_val]); + STVF(tmpb[0], lutToneCurve[b_val]); + for (int j = 0; j < 4; ++j) { + curves::setLutVal(r[i+j], tmpr[j], mv); + curves::setLutVal(g[i+j], tmpg[j], mv); + curves::setLutVal(b[i+j], tmpb[j], mv); + } } // Remainder in non-SSE. for (; i < end; ++i) { - r[i] = lutToneCurve[r[i]]; - g[i] = lutToneCurve[g[i]]; - b[i] = lutToneCurve[b[i]]; + curves::setLutVal(lutToneCurve, r[i]); + curves::setLutVal(lutToneCurve, g[i]); + curves::setLutVal(lutToneCurve, b[i]); } #endif } @@ -938,10 +970,13 @@ inline void StandardToneCurve::BatchApply( // Tone curve according to Adobe's reference implementation // values in 0xffff space // inlined to make sure there will be no cache flush when used -inline void AdobeToneCurve::Apply (float& r, float& g, float& b) const +inline void AdobeToneCurve::Apply (float& ir, float& ig, float& ib) const { assert (lutToneCurve); + float r = CLIP(ir); + float g = CLIP(ig); + float b = CLIP(ib); if (r >= g) { if (g > b) { @@ -964,6 +999,10 @@ inline void AdobeToneCurve::Apply (float& r, float& g, float& b) const RGBTone (g, b, r); // Case 7: g >= b > r } } + + setUnlessOOG(ir, r); + setUnlessOOG(ig, g); + setUnlessOOG(ib, b); } inline void AdobeToneCurve::RGBTone (float& r, float& g, float& b) const @@ -976,10 +1015,14 @@ inline void AdobeToneCurve::RGBTone (float& r, float& g, float& b) const } // Modifying the Luminance channel only -inline void LuminanceToneCurve::Apply(float &r, float &g, float &b) const +inline void LuminanceToneCurve::Apply(float &ir, float &ig, float &ib) const { assert (lutToneCurve); + float r = CLIP(ir); + float g = CLIP(ig); + float b = CLIP(ib); + float currLuminance = r * 0.2126729f + g * 0.7151521f + b * 0.0721750f; const float newLuminance = lutToneCurve[currLuminance]; currLuminance = currLuminance == 0.f ? 0.00001f : currLuminance; @@ -987,6 +1030,10 @@ inline void LuminanceToneCurve::Apply(float &r, float &g, float &b) const r = LIM(r * coef, 0.f, 65535.f); g = LIM(g * coef, 0.f, 65535.f); b = LIM(b * coef, 0.f, 65535.f); + + setUnlessOOG(ir, r); + setUnlessOOG(ig, g); + setUnlessOOG(ib, b); } inline float WeightedStdToneCurve::Triangle(float a, float a1, float b) const @@ -1020,14 +1067,14 @@ inline vfloat WeightedStdToneCurve::Triangle(vfloat a, vfloat a1, vfloat b) cons // Tone curve modifying the value channel only, preserving hue and saturation // values in 0xffff space -inline void WeightedStdToneCurve::Apply (float& r, float& g, float& b) const +inline void WeightedStdToneCurve::Apply (float& ir, float& ig, float& ib) const { assert (lutToneCurve); - r = CLIP(r); - g = CLIP(g); - b = CLIP(b); + float r = CLIP(ir); + float g = CLIP(ig); + float b = CLIP(ib); float r1 = lutToneCurve[r]; float g1 = Triangle(r, r1, g); float b1 = Triangle(r, r1, b); @@ -1043,6 +1090,10 @@ inline void WeightedStdToneCurve::Apply (float& r, float& g, float& b) const r = CLIP(r1 * 0.50f + r2 * 0.25f + r3 * 0.25f); g = CLIP(g1 * 0.25f + g2 * 0.50f + g3 * 0.25f); b = CLIP(b1 * 0.25f + b2 * 0.25f + b3 * 0.50f); + + setUnlessOOG(ir, r); + setUnlessOOG(ig, g); + setUnlessOOG(ib, b); } inline void WeightedStdToneCurve::BatchApply(const size_t start, const size_t end, float *r, float *g, float *b) const { @@ -1076,6 +1127,10 @@ inline void WeightedStdToneCurve::BatchApply(const size_t start, const size_t en const vfloat zd5v = F2V(0.5f); const vfloat zd25v = F2V(0.25f); + float tmpr[4]; + float tmpg[4]; + float tmpb[4]; + for (; i + 3 < end; i += 4) { vfloat r_val = LIMV(LVF(r[i]), ZEROV, c65535v); vfloat g_val = LIMV(LVF(g[i]), ZEROV, c65535v); @@ -1092,9 +1147,14 @@ inline void WeightedStdToneCurve::BatchApply(const size_t start, const size_t en vfloat r3 = Triangle(b_val, b3, r_val); vfloat g3 = Triangle(b_val, b3, g_val); - STVF(r[i], LIMV(r1 * zd5v + r2 * zd25v + r3 * zd25v, ZEROV, c65535v)); - STVF(g[i], LIMV(g1 * zd25v + g2 * zd5v + g3 * zd25v, ZEROV, c65535v)); - STVF(b[i], LIMV(b1 * zd25v + b2 * zd25v + b3 * zd5v, ZEROV, c65535v)); + STVF(tmpr[0], LIMV(r1 * zd5v + r2 * zd25v + r3 * zd25v, ZEROV, c65535v)); + STVF(tmpg[0], LIMV(g1 * zd25v + g2 * zd5v + g3 * zd25v, ZEROV, c65535v)); + STVF(tmpb[0], LIMV(b1 * zd25v + b2 * zd25v + b3 * zd5v, ZEROV, c65535v)); + for (int j = 0; j < 4; ++j) { + setUnlessOOG(r[i+j], tmpr[j]); + setUnlessOOG(g[i+j], tmpg[j]); + setUnlessOOG(b[i+j], tmpb[j]); + } } // Remainder in non-SSE. @@ -1106,14 +1166,14 @@ inline void WeightedStdToneCurve::BatchApply(const size_t start, const size_t en // Tone curve modifying the value channel only, preserving hue and saturation // values in 0xffff space -inline void SatAndValueBlendingToneCurve::Apply (float& r, float& g, float& b) const +inline void SatAndValueBlendingToneCurve::Apply (float& ir, float& ig, float& ib) const { assert (lutToneCurve); - r = CLIP(r); - g = CLIP(g); - b = CLIP(b); + float r = CLIP(ir); + float g = CLIP(ig); + float b = CLIP(ib); const float lum = (r + g + b) / 3.f; const float newLum = lutToneCurve[lum]; @@ -1137,6 +1197,10 @@ inline void SatAndValueBlendingToneCurve::Apply (float& r, float& g, float& b) c dV = v * coef; } Color::hsv2rgbdcp(h, s, v + dV, r, g, b); + + setUnlessOOG(ir, r); + setUnlessOOG(ig, g); + setUnlessOOG(ib, b); } } diff --git a/rtengine/dcp.cc b/rtengine/dcp.cc index 82ea35f0f..1af26cf74 100644 --- a/rtengine/dcp.cc +++ b/rtengine/dcp.cc @@ -1184,13 +1184,17 @@ void DCPProfile::step2ApplyTile(float* rc, float* gc, float* bc, int width, int } // with looktable and tonecurve we need to clip - newr = FCLIP(newr); - newg = FCLIP(newg); - newb = FCLIP(newb); + // newr = FCLIP(newr); + // newg = FCLIP(newg); + // newb = FCLIP(newb); if (as_in.data->apply_look_table) { + float cnewr = FCLIP(newr); + float cnewg = FCLIP(newg); + float cnewb = FCLIP(newb); + float h, s, v; - Color::rgb2hsvdcp(newr, newg, newb, h, s, v); + Color::rgb2hsvdcp(cnewr, cnewg, cnewb, h, s, v); hsdApply(look_info, look_table, h, s, v); s = CLIP01(s); @@ -1203,7 +1207,11 @@ void DCPProfile::step2ApplyTile(float* rc, float* gc, float* bc, int width, int h -= 6.0f; } - Color::hsv2rgbdcp( h, s, v, newr, newg, newb); + Color::hsv2rgbdcp( h, s, v, cnewr, cnewg, cnewb); + + setUnlessOOG(newr, cnewr); + setUnlessOOG(newg, cnewg); + setUnlessOOG(newb, cnewb); } if (as_in.data->use_tone_curve) { diff --git a/rtengine/dirpyr_equalizer.cc b/rtengine/dirpyr_equalizer.cc index 7d3ace3ff..69c01be8c 100644 --- a/rtengine/dirpyr_equalizer.cc +++ b/rtengine/dirpyr_equalizer.cc @@ -240,7 +240,7 @@ void ImProcFunctions :: dirpyr_equalizer(float ** src, float ** dst, int srcwidt for (int i = 0; i < srcheight; i++) for (int j = 0; j < srcwidth; j++) { - dst[i][j] = CLIP(buffer[i][j]); // TODO: Really a clip necessary? + dst[i][j] = /*CLIP*/(buffer[i][j]); // TODO: Really a clip necessary? } } @@ -367,7 +367,7 @@ void ImProcFunctions :: dirpyr_equalizercam (CieImage *ncie, float ** src, float for (int i = 0; i < srcheight; i++) for (int j = 0; j < srcwidth; j++) { if(ncie->J_p[i][j] > 8.f && ncie->J_p[i][j] < 92.f) { - dst[i][j] = CLIP( buffer[i][j] ); // TODO: Really a clip necessary? + dst[i][j] = /*CLIP*/( buffer[i][j] ); // TODO: Really a clip necessary? } else { dst[i][j] = src[i][j]; } @@ -375,7 +375,7 @@ void ImProcFunctions :: dirpyr_equalizercam (CieImage *ncie, float ** src, float } else { for (int i = 0; i < srcheight; i++) for (int j = 0; j < srcwidth; j++) { - dst[i][j] = CLIP( buffer[i][j] ); // TODO: Really a clip necessary? + dst[i][j] = /*CLIP*/( buffer[i][j] ); // TODO: Really a clip necessary? } } } diff --git a/rtengine/iimage.h b/rtengine/iimage.h index d0fe626c2..d09f46a3a 100644 --- a/rtengine/iimage.h +++ b/rtengine/iimage.h @@ -119,7 +119,7 @@ inline void ImageDatas::convertTo(unsigned char src, unsigned short& dst) const template<> inline void ImageDatas::convertTo(float src, unsigned char& dst) const { - dst = uint16ToUint8Rounded(src); + dst = uint16ToUint8Rounded(CLIP(src)); } template<> inline void ImageDatas::convertTo(unsigned char src, float& dst) const diff --git a/rtengine/imagefloat.cc b/rtengine/imagefloat.cc index 0352c0b10..d1b5c994b 100644 --- a/rtengine/imagefloat.cc +++ b/rtengine/imagefloat.cc @@ -146,6 +146,9 @@ void Imagefloat::setScanline (int row, unsigned char* buffer, int bps, float *mi } } + +namespace rtengine { extern void filmlike_clip(float *r, float *g, float *b); } + void Imagefloat::getScanline (int row, unsigned char* buffer, int bps) { @@ -163,18 +166,24 @@ void Imagefloat::getScanline (int row, unsigned char* buffer, int bps) sbuffer[ix++] = g(row, i) / 65535.f; sbuffer[ix++] = b(row, i) / 65535.f; } - } else if (bps == 16) { + } else { unsigned short *sbuffer = (unsigned short *)buffer; for (int i = 0, ix = 0; i < width; i++) { - sbuffer[ix++] = CLIP(r(row, i)); - sbuffer[ix++] = CLIP(g(row, i)); - sbuffer[ix++] = CLIP(b(row, i)); - } - } else if (bps == 8) { - for (int i = 0, ix = 0; i < width; i++) { - buffer[ix++] = rtengine::uint16ToUint8Rounded(CLIP(r(row, i))); - buffer[ix++] = rtengine::uint16ToUint8Rounded(CLIP(g(row, i))); - buffer[ix++] = rtengine::uint16ToUint8Rounded(CLIP(b(row, i))); + float ri = r(row, i); + float gi = g(row, i); + float bi = b(row, i); + if (ri > 65535.f || gi > 65535.f || bi > 65535.f) { + filmlike_clip(&ri, &gi, &bi); + } + if (bps == 16) { + sbuffer[ix++] = CLIP(ri); + sbuffer[ix++] = CLIP(gi); + sbuffer[ix++] = CLIP(bi); + } else if (bps == 8) { + buffer[ix++] = rtengine::uint16ToUint8Rounded(CLIP(ri)); + buffer[ix++] = rtengine::uint16ToUint8Rounded(CLIP(gi)); + buffer[ix++] = rtengine::uint16ToUint8Rounded(CLIP(bi)); + } } } } @@ -238,6 +247,8 @@ void Imagefloat::getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, Prev gm /= area; bm /= area; + const auto CLIP0 = [](float v) -> float { return std::max(v, 0.f); }; + #ifdef _OPENMP #pragma omp parallel { @@ -270,9 +281,9 @@ void Imagefloat::getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, Prev continue; } - lineR[dst_x] = CLIP(rm2 * r(src_y, src_x)); - lineG[dst_x] = CLIP(gm2 * g(src_y, src_x)); - lineB[dst_x] = CLIP(bm2 * b(src_y, src_x)); + lineR[dst_x] = CLIP0(rm2 * r(src_y, src_x)); + lineG[dst_x] = CLIP0(gm2 * g(src_y, src_x)); + lineB[dst_x] = CLIP0(bm2 * b(src_y, src_x)); } } else { // source image, first line of the current destination row @@ -303,15 +314,15 @@ void Imagefloat::getStdImage (ColorTemp ctemp, int tran, Imagefloat* image, Prev // convert back to gamma and clip if (src_sub_width == skip && src_sub_height == skip) { // Common case where the sub-region is complete - lineR[dst_x] = CLIP(rm * rtot); - lineG[dst_x] = CLIP(gm * gtot); - lineB[dst_x] = CLIP(bm * btot); + lineR[dst_x] = CLIP0(rm * rtot); + lineG[dst_x] = CLIP0(gm * gtot); + lineB[dst_x] = CLIP0(bm * btot); } else { // computing a special factor for this incomplete sub-region float area = src_sub_width * src_sub_height; - lineR[dst_x] = CLIP(rm2 * rtot / area); - lineG[dst_x] = CLIP(gm2 * gtot / area); - lineB[dst_x] = CLIP(bm2 * btot / area); + lineR[dst_x] = CLIP0(rm2 * rtot / area); + lineG[dst_x] = CLIP0(gm2 * gtot / area); + lineB[dst_x] = CLIP0(bm2 * btot / area); } } } @@ -357,9 +368,9 @@ Imagefloat::to8() for (int h = 0; h < height; ++h) { for (int w = 0; w < width; ++w) { - img8->r(h, w) = uint16ToUint8Rounded(r(h, w)); - img8->g(h, w) = uint16ToUint8Rounded(g(h, w)); - img8->b(h, w) = uint16ToUint8Rounded(b(h, w)); + img8->r(h, w) = uint16ToUint8Rounded(CLIP(r(h, w))); + img8->g(h, w) = uint16ToUint8Rounded(CLIP(g(h, w))); + img8->b(h, w) = uint16ToUint8Rounded(CLIP(b(h, w))); } } @@ -376,9 +387,9 @@ Imagefloat::to16() for (int h = 0; h < height; ++h) { for (int w = 0; w < width; ++w) { - img16->r(h, w) = r(h, w); - img16->g(h, w) = g(h, w); - img16->b(h, w) = b(h, w); + img16->r(h, w) = CLIP(r(h, w)); + img16->g(h, w) = CLIP(g(h, w)); + img16->b(h, w) = CLIP(b(h, w)); } } diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index e7ff01f46..ced6727c6 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -50,10 +50,13 @@ namespace { using namespace rtengine; -// begin of helper function for rgbProc() -void shadowToneCurve(const LUTf &shtonecurve, float *rtemp, float *gtemp, float *btemp, int istart, int tH, int jstart, int tW, int tileSize) { -#ifdef __SSE2__ + +// begin of helper function for rgbProc() +void shadowToneCurve(const LUTf &shtonecurve, float *rtemp, float *gtemp, float *btemp, int istart, int tH, int jstart, int tW, int tileSize) +{ + +#if defined( __SSE2__ ) && defined( __x86_64__ ) vfloat cr = F2V(0.299f); vfloat cg = F2V(0.587f); vfloat cb = F2V(0.114f); @@ -61,8 +64,9 @@ void shadowToneCurve(const LUTf &shtonecurve, float *rtemp, float *gtemp, float for (int i = istart, ti = 0; i < tH; i++, ti++) { int j = jstart, tj = 0; -#ifdef __SSE2__ - for (; j < tW - 3; j+=4, tj+=4) { +#if defined( __SSE2__ ) && defined( __x86_64__ ) + + for (; j < tW - 3; j += 4, tj += 4) { vfloat rv = LVF(rtemp[ti * tileSize + tj]); vfloat gv = LVF(gtemp[ti * tileSize + tj]); @@ -75,7 +79,9 @@ void shadowToneCurve(const LUTf &shtonecurve, float *rtemp, float *gtemp, float STVF(gtemp[ti * tileSize + tj], gv * tonefactorv); STVF(btemp[ti * tileSize + tj], bv * tonefactorv); } + #endif + for (; j < tW; j++, tj++) { float r = rtemp[ti * tileSize + tj]; @@ -92,17 +98,19 @@ void shadowToneCurve(const LUTf &shtonecurve, float *rtemp, float *gtemp, float } } -void highlightToneCurve(const LUTf &hltonecurve, float *rtemp, float *gtemp, float *btemp, int istart, int tH, int jstart, int tW, int tileSize, float exp_scale, float comp, float hlrange) { +void highlightToneCurve(const LUTf &hltonecurve, float *rtemp, float *gtemp, float *btemp, int istart, int tH, int jstart, int tW, int tileSize, float exp_scale, float comp, float hlrange) +{ -#ifdef __SSE2__ +#if defined( __SSE2__ ) && defined( __x86_64__ ) vfloat threev = F2V(3.f); vfloat maxvalfv = F2V(MAXVALF); #endif for (int i = istart, ti = 0; i < tH; i++, ti++) { int j = jstart, tj = 0; -#ifdef __SSE2__ - for (; j < tW - 3; j+=4, tj+=4) { +#if defined( __SSE2__ ) && defined( __x86_64__ ) + + for (; j < tW - 3; j += 4, tj += 4) { vfloat rv = LVF(rtemp[ti * tileSize + tj]); vfloat gv = LVF(gtemp[ti * tileSize + tj]); @@ -111,14 +119,15 @@ void highlightToneCurve(const LUTf &hltonecurve, float *rtemp, float *gtemp, flo //TODO: proper treatment of out-of-gamut colors //float tonefactor = hltonecurve[(0.299f*r+0.587f*g+0.114f*b)]; vmask maxMask = vmaskf_ge(vmaxf(rv, vmaxf(gv, bv)), maxvalfv); - if(_mm_movemask_ps((vfloat)maxMask)) { + + if (_mm_movemask_ps((vfloat)maxMask)) { for (int k = 0; k < 4; ++k) { float r = rtemp[ti * tileSize + tj + k]; float g = gtemp[ti * tileSize + tj + k]; float b = btemp[ti * tileSize + tj + k]; - float tonefactor = ((r < MAXVALF ? hltonecurve[r] : CurveFactory::hlcurve (exp_scale, comp, hlrange, r) ) + - (g < MAXVALF ? hltonecurve[g] : CurveFactory::hlcurve (exp_scale, comp, hlrange, g) ) + - (b < MAXVALF ? hltonecurve[b] : CurveFactory::hlcurve (exp_scale, comp, hlrange, b) ) ) / 3.0; + float tonefactor = ((r < MAXVALF ? hltonecurve[r] : CurveFactory::hlcurve(exp_scale, comp, hlrange, r)) + + (g < MAXVALF ? hltonecurve[g] : CurveFactory::hlcurve(exp_scale, comp, hlrange, g)) + + (b < MAXVALF ? hltonecurve[b] : CurveFactory::hlcurve(exp_scale, comp, hlrange, b))) / 3.0; // note: tonefactor includes exposure scaling, that is here exposure slider and highlight compression takes place rtemp[ti * tileSize + tj + k] = r * tonefactor; @@ -133,7 +142,9 @@ void highlightToneCurve(const LUTf &hltonecurve, float *rtemp, float *gtemp, flo STVF(btemp[ti * tileSize + tj], bv * tonefactorv); } } + #endif + for (; j < tW; j++, tj++) { float r = rtemp[ti * tileSize + tj]; @@ -142,9 +153,9 @@ void highlightToneCurve(const LUTf &hltonecurve, float *rtemp, float *gtemp, flo //TODO: proper treatment of out-of-gamut colors //float tonefactor = hltonecurve[(0.299f*r+0.587f*g+0.114f*b)]; - float tonefactor = ((r < MAXVALF ? hltonecurve[r] : CurveFactory::hlcurve (exp_scale, comp, hlrange, r) ) + - (g < MAXVALF ? hltonecurve[g] : CurveFactory::hlcurve (exp_scale, comp, hlrange, g) ) + - (b < MAXVALF ? hltonecurve[b] : CurveFactory::hlcurve (exp_scale, comp, hlrange, b) ) ) / 3.0; + float tonefactor = ((r < MAXVALF ? hltonecurve[r] : CurveFactory::hlcurve(exp_scale, comp, hlrange, r)) + + (g < MAXVALF ? hltonecurve[g] : CurveFactory::hlcurve(exp_scale, comp, hlrange, g)) + + (b < MAXVALF ? hltonecurve[b] : CurveFactory::hlcurve(exp_scale, comp, hlrange, b))) / 3.0; // note: tonefactor includes exposure scaling, that is here exposure slider and highlight compression takes place rtemp[ti * tileSize + tj] = r * tonefactor; @@ -224,9 +235,6 @@ void customToneCurve(const ToneCurve &customToneCurve, ToneCurveParams::TcMode c for (int i = istart, ti = 0; i < tH; i++, ti++) { for (int j = jstart, tj = 0; j < tW; j++, tj++) { - rtemp[ti * tileSize + tj] = CLIP (rtemp[ti * tileSize + tj]); - gtemp[ti * tileSize + tj] = CLIP (gtemp[ti * tileSize + tj]); - btemp[ti * tileSize + tj] = CLIP (btemp[ti * tileSize + tj]); userToneCurve.Apply(rtemp[ti * tileSize + tj], gtemp[ti * tileSize + tj], btemp[ti * tileSize + tj]); } } @@ -3260,7 +3268,7 @@ filmlike_clip_rgb_tone (float *r, float *g, float *b, const float L) *b = b_; } -static void +/*static*/ void filmlike_clip (float *r, float *g, float *b) { // This is Adobe's hue-stable film-like curve with a diagonal, ie only used for clipping. Can probably be further optimized. @@ -3670,6 +3678,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } float out_rgbx[4 * TS] ALIGNED16; // Line buffer for CLUT + float clutr[TS] ALIGNED16; + float clutg[TS] ALIGNED16; + float clutb[TS] ALIGNED16; LUTu histToneCurveThr; @@ -3760,9 +3771,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer filmlike_clip (&r, &g, &b); } - rtemp[ti * TS + tj] = r; - gtemp[ti * TS + tj] = g; - btemp[ti * TS + tj] = b; + setUnlessOOG(rtemp[ti * TS + tj], r); + setUnlessOOG(gtemp[ti * TS + tj], g); + setUnlessOOG(btemp[ti * TS + tj], b); } } @@ -3771,30 +3782,43 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int j = jstart, tj = 0; j < tW; j++, tj++) { //brightness/contrast - rtemp[ti * TS + tj] = tonecurve[ rtemp[ti * TS + tj] ]; - gtemp[ti * TS + tj] = tonecurve[ gtemp[ti * TS + tj] ]; - btemp[ti * TS + tj] = tonecurve[ btemp[ti * TS + tj] ]; + float r = tonecurve[ CLIP(rtemp[ti * TS + tj]) ]; + float g = tonecurve[ CLIP(gtemp[ti * TS + tj]) ]; + float b = tonecurve[ CLIP(btemp[ti * TS + tj]) ]; int y = CLIP (lumimulf[0] * Color::gamma2curve[rtemp[ti * TS + tj]] + lumimulf[1] * Color::gamma2curve[gtemp[ti * TS + tj]] + lumimulf[2] * Color::gamma2curve[btemp[ti * TS + tj]]); histToneCurveThr[y >> histToneCurveCompression]++; + + setUnlessOOG(rtemp[ti * TS + tj], r); + setUnlessOOG(gtemp[ti * TS + tj], g); + setUnlessOOG(btemp[ti * TS + tj], b); } } } else { + float tmpr[4]; + float tmpg[4]; + float tmpb[4]; + for (int i = istart, ti = 0; i < tH; i++, ti++) { int j = jstart, tj = 0; #ifdef __SSE2__ for (; j < tW - 3; j+=4, tj+=4) { //brightness/contrast - STVF(rtemp[ti * TS + tj], tonecurve(LVF(rtemp[ti * TS + tj]))); - STVF(gtemp[ti * TS + tj], tonecurve(LVF(gtemp[ti * TS + tj]))); - STVF(btemp[ti * TS + tj], tonecurve(LVF(btemp[ti * TS + tj]))); + STVF(tmpr[0], tonecurve(LVF(rtemp[ti * TS + tj]))); + STVF(tmpg[0], tonecurve(LVF(gtemp[ti * TS + tj]))); + STVF(tmpb[0], tonecurve(LVF(btemp[ti * TS + tj]))); + for (int k = 0; k < 4; ++k) { + setUnlessOOG(rtemp[ti * TS + tj + k], tmpr[k]); + setUnlessOOG(gtemp[ti * TS + tj + k], tmpg[k]); + setUnlessOOG(btemp[ti * TS + tj + k], tmpb[k]); + } } #endif for (; j < tW; j++, tj++) { //brightness/contrast - rtemp[ti * TS + tj] = tonecurve[rtemp[ti * TS + tj]]; - gtemp[ti * TS + tj] = tonecurve[gtemp[ti * TS + tj]]; - btemp[ti * TS + tj] = tonecurve[btemp[ti * TS + tj]]; + setUnlessOOG(rtemp[ti * TS + tj], tonecurve[rtemp[ti * TS + tj]]); + setUnlessOOG(gtemp[ti * TS + tj], tonecurve[gtemp[ti * TS + tj]]); + setUnlessOOG(btemp[ti * TS + tj], tonecurve[btemp[ti * TS + tj]]); } } } @@ -3842,17 +3866,17 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int j = jstart, tj = 0; j < tW; j++, tj++) { // individual R tone curve if (rCurve) { - rtemp[ti * TS + tj] = rCurve[ rtemp[ti * TS + tj] ]; + setUnlessOOG(rtemp[ti * TS + tj], rCurve[ rtemp[ti * TS + tj] ]); } // individual G tone curve if (gCurve) { - gtemp[ti * TS + tj] = gCurve[ gtemp[ti * TS + tj] ]; + setUnlessOOG(gtemp[ti * TS + tj], gCurve[ gtemp[ti * TS + tj] ]); } // individual B tone curve if (bCurve) { - btemp[ti * TS + tj] = bCurve[ btemp[ti * TS + tj] ]; + setUnlessOOG(btemp[ti * TS + tj], bCurve[ btemp[ti * TS + tj] ]); } } } @@ -3919,18 +3943,22 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer bool neg = false; bool more_rgb = false; //gamut control : Lab values are in gamut - Color::gamutLchonly (HH, sincosval, Lpro, Chpro, rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], wip, highlight, 0.15f, 0.96f, neg, more_rgb); + Color::gamutLchonly (HH, sincosval, Lpro, Chpro, r, g, b, wip, highlight, 0.15f, 0.96f, neg, more_rgb); #else //gamut control : Lab values are in gamut - Color::gamutLchonly (HH, sincosval, Lpro, Chpro, rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], wip, highlight, 0.15f, 0.96f); + Color::gamutLchonly (HH, sincosval, Lpro, Chpro, r, g, b, wip, highlight, 0.15f, 0.96f); #endif //end of gamut control } else { float x_, y_, z_; //calculate RGB with L_2 and old value of a and b Color::Lab2XYZ (L_2, a_1, b_1, x_, y_, z_) ; - Color::xyz2rgb (x_, y_, z_, rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj], wip); + Color::xyz2rgb (x_, y_, z_, r, g, b, wip); } + + setUnlessOOG(rtemp[ti * TS + tj], r); + setUnlessOOG(gtemp[ti * TS + tj], g); + setUnlessOOG(btemp[ti * TS + tj], b); } } } @@ -4081,9 +4109,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer bo *= preserv; } - rtemp[ti * TS + tj] = CLIP(ro); - gtemp[ti * TS + tj] = CLIP(go); - btemp[ti * TS + tj] = CLIP(bo); + rtemp[ti * TS + tj] = /*CLIP*/(ro); + gtemp[ti * TS + tj] = /*CLIP*/(go); + btemp[ti * TS + tj] = /*CLIP*/(bo); } } } @@ -4137,9 +4165,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer float b = btemp[ti * TS + tj]; float ro, go, bo; labtoning (r, g, b, ro, go, bo, algm, metchrom, twoc, satLimit, satLimitOpacity, ctColorCurve, ctOpacityCurve, clToningcurve, cl2Toningcurve, iplow, iphigh, wp, wip); - rtemp[ti * TS + tj] = CLIP (ro); //I used CLIP because there is a little bug in gamutLchonly that return 65536.ii intead of 65535 ==> crash - gtemp[ti * TS + tj] = CLIP (go); - btemp[ti * TS + tj] = CLIP (bo); + rtemp[ti * TS + tj] = /*CLIP*/ (ro); //I used CLIP because there is a little bug in gamutLchonly that return 65536.ii intead of 65535 ==> crash + gtemp[ti * TS + tj] = /*CLIP*/ (go); + btemp[ti * TS + tj] = /*CLIP*/ (bo); } } } @@ -4386,28 +4414,32 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer Color::rgbxyz (sourceR, sourceG, sourceB, x, y, z, v_work2xyz); Color::xyz2rgb (x, y, z, sourceR, sourceG, sourceB, v_xyz2clut); - STVF (rtemp[ti * TS + tj], sourceR); - STVF (gtemp[ti * TS + tj], sourceG); - STVF (btemp[ti * TS + tj], sourceB); + STVF (clutr[tj], sourceR); + STVF (clutg[tj], sourceG); + STVF (clutb[tj], sourceB); } #endif for (; j < tW; j++, tj++) { - float &sourceR = rtemp[ti * TS + tj]; - float &sourceG = gtemp[ti * TS + tj]; - float &sourceB = btemp[ti * TS + tj]; + float sourceR = rtemp[ti * TS + tj]; + float sourceG = gtemp[ti * TS + tj]; + float sourceB = btemp[ti * TS + tj]; float x, y, z; Color::rgbxyz ( sourceR, sourceG, sourceB, x, y, z, wprof ); - Color::xyz2rgb (x, y, z, sourceR, sourceG, sourceB, xyz2clut); + Color::xyz2rgb (x, y, z, clutr[tj], clutg[tj], clutb[tj], xyz2clut); } + } else { + memcpy(clutr, &rtemp[ti * TS], sizeof(float) * TS); + memcpy(clutg, >emp[ti * TS], sizeof(float) * TS); + memcpy(clutb, &btemp[ti * TS], sizeof(float) * TS); } for (int j = jstart, tj = 0; j < tW; j++, tj++) { - float &sourceR = rtemp[ti * TS + tj]; - float &sourceG = gtemp[ti * TS + tj]; - float &sourceB = btemp[ti * TS + tj]; + float &sourceR = clutr[tj]; + float &sourceG = clutg[tj]; + float &sourceB = clutb[tj]; // Apply gamma sRGB (default RT) sourceR = Color::gamma_srgbclipped (sourceR); @@ -4415,20 +4447,19 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer sourceB = Color::gamma_srgbclipped (sourceB); } - const std::size_t line_offset = ti * TS; hald_clut->getRGB ( film_simulation_strength, std::min (TS, tW - jstart), - rtemp + line_offset, - gtemp + line_offset, - btemp + line_offset, + clutr, + clutg, + clutb, out_rgbx ); for (int j = jstart, tj = 0; j < tW; j++, tj++) { - float &sourceR = rtemp[ti * TS + tj]; - float &sourceG = gtemp[ti * TS + tj]; - float &sourceB = btemp[ti * TS + tj]; + float &sourceR = clutr[tj]; + float &sourceG = clutg[tj]; + float &sourceB = clutb[tj]; // Apply inverse gamma sRGB sourceR = Color::igamma_srgb (out_rgbx[tj * 4 + 0]); @@ -4444,9 +4475,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer #ifdef __SSE2__ for (; j < tW - 3; j += 4, tj += 4) { - vfloat sourceR = LVF (rtemp[ti * TS + tj]); - vfloat sourceG = LVF (gtemp[ti * TS + tj]); - vfloat sourceB = LVF (btemp[ti * TS + tj]); + vfloat sourceR = LVF (clutr[tj]); + vfloat sourceG = LVF (clutg[tj]); + vfloat sourceB = LVF (clutb[tj]); vfloat x; vfloat y; @@ -4454,23 +4485,31 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer Color::rgbxyz (sourceR, sourceG, sourceB, x, y, z, v_clut2xyz); Color::xyz2rgb (x, y, z, sourceR, sourceG, sourceB, v_xyz2work); - STVF (rtemp[ti * TS + tj], sourceR); - STVF (gtemp[ti * TS + tj], sourceG); - STVF (btemp[ti * TS + tj], sourceB); + STVF (clutr[tj], sourceR); + STVF (clutg[tj], sourceG); + STVF (clutb[tj], sourceB); } #endif for (; j < tW; j++, tj++) { - float &sourceR = rtemp[ti * TS + tj]; - float &sourceG = gtemp[ti * TS + tj]; - float &sourceB = btemp[ti * TS + tj]; + float &sourceR = clutr[tj]; + float &sourceG = clutg[tj]; + float &sourceB = clutb[tj]; float x, y, z; Color::rgbxyz (sourceR, sourceG, sourceB, x, y, z, clut2xyz); Color::xyz2rgb ( x, y, z, sourceR, sourceG, sourceB, wiprof ); } } + + for (int j = jstart, tj = 0; j < tW; j++, tj++) { + if (!OOG(rtemp[ti * TS + tj]) || !OOG(gtemp[ti * TS + tj]) || !OOG(btemp[ti * TS + tj])) { + rtemp[ti * TS + tj] = clutr[tj]; + gtemp[ti * TS + tj] = clutg[tj]; + btemp[ti * TS + tj] = clutb[tj]; + } + } } } @@ -4598,7 +4637,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int j = 0; j < tW; j++) { //mix channel - tmpImage->r (i, j) = tmpImage->g (i, j) = tmpImage->b (i, j) = CLIP ((bwr * tmpImage->r (i, j) + bwg * tmpImage->g (i, j) + bwb * tmpImage->b (i, j)) * kcorec); + tmpImage->r (i, j) = tmpImage->g (i, j) = tmpImage->b (i, j) = /*CLIP*/ ((bwr * tmpImage->r (i, j) + bwg * tmpImage->g (i, j) + bwb * tmpImage->b (i, j)) * kcorec); #ifndef __SSE2__ @@ -4694,9 +4733,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer bo *= preserv; } - tmpImage->r(i, j) = CLIP(ro); - tmpImage->g(i, j) = CLIP(go); - tmpImage->b(i, j) = CLIP(bo); + tmpImage->r(i, j) = /*CLIP*/(ro); + tmpImage->g(i, j) = /*CLIP*/(go); + tmpImage->b(i, j) = /*CLIP*/(bo); } } } @@ -4803,9 +4842,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer float b = tmpImage->b (i, j); float ro, bo, go; labtoning (r, g, b, ro, go, bo, algm, metchrom, twoc, satLimit, satLimitOpacity, ctColorCurve, ctOpacityCurve, clToningcurve, cl2Toningcurve, iplow, iphigh, wp, wip); - tmpImage->r (i, j) = CLIP (ro); - tmpImage->g (i, j) = CLIP (go); - tmpImage->b (i, j) = CLIP (bo); + tmpImage->r (i, j) = /*CLIP*/ (ro); + tmpImage->g (i, j) = /*CLIP*/ (go); + tmpImage->b (i, j) = /*CLIP*/ (bo); } } @@ -5049,9 +5088,9 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, r += corr; } - r = CLIP(r); - g = CLIP(g); - b = CLIP(b); + // r = CLIP(r); + // g = CLIP(g); + // b = CLIP(b); } { @@ -5063,9 +5102,9 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, g += corr; } - r = CLIP(r); - b = CLIP(b); - g = CLIP(g); + // r = CLIP(r); + // b = CLIP(b); + // g = CLIP(g); } @@ -5079,9 +5118,9 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, b += corr; } - r = CLIP(r); - g = CLIP(g); - b = CLIP(b); + // r = CLIP(r); + // g = CLIP(g); + // b = CLIP(b); } // mid tones @@ -5112,9 +5151,9 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, g -= 20000.f * RedM; b -= 20000.f * RedM; } - r = CLIP(r); - g = CLIP(g); - b = CLIP(b); + // r = CLIP(r); + // g = CLIP(g); + // b = CLIP(b); } { @@ -5129,9 +5168,9 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, g += 10000.f * GreenM; b -= 20000.f * GreenM; } - r = CLIP(r); - g = CLIP(g); - b = CLIP(b); + // r = CLIP(r); + // g = CLIP(g); + // b = CLIP(b); } { @@ -5146,9 +5185,9 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, g -= 20000.f * BlueM; b += 10000.f * BlueM; } - r = CLIP(r); - g = CLIP(g); - b = CLIP(b); + // r = CLIP(r); + // g = CLIP(g); + // b = CLIP(b); } //high tones @@ -5173,9 +5212,9 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, b -= corr; } - r = CLIP(r); - g = CLIP(g); - b = CLIP(b); + // r = CLIP(r); + // g = CLIP(g); + // b = CLIP(b); } { @@ -5188,9 +5227,9 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, b -= corr; } - r = CLIP(r); - g = CLIP(g); - b = CLIP(b); + // r = CLIP(r); + // g = CLIP(g); + // b = CLIP(b); } { @@ -5203,9 +5242,9 @@ void ImProcFunctions::toningsmh(float r, float g, float b, float &ro, float &go, g -= corr; } - r = CLIP(r); - g = CLIP(g); - b = CLIP(b); + // r = CLIP(r); + // g = CLIP(g); + // b = CLIP(b); } ro = r; @@ -5262,24 +5301,24 @@ void ImProcFunctions::toning2col (float r, float g, float b, float &ro, float &g b -= factor * krl; } - g = CLIP(g); - b = CLIP(b); + // g = CLIP(g); + // b = CLIP(b); if (kgl > 0.f) { r -= factor * kgl; b -= factor * kgl; } - r = CLIP(r); - b = CLIP(b); + // r = CLIP(r); + // b = CLIP(b); if (kbl > 0.f) { r -= factor * kbl; g -= factor * kbl; } - r = CLIP(r); - g = CLIP(g); + // r = CLIP(r); + // g = CLIP(g); } //high tones @@ -5306,9 +5345,9 @@ void ImProcFunctions::toning2col (float r, float g, float b, float &ro, float &g g += factor * (kgh > 0.f ? kgh : 0.f); b += factor * (kbh > 0.f ? kbh : 0.f); - r = CLIP(r); - g = CLIP(g); - b = CLIP(b); + // r = CLIP(r); + // g = CLIP(g); + // b = CLIP(b); } float preserv = 1.f; @@ -5317,9 +5356,9 @@ void ImProcFunctions::toning2col (float r, float g, float b, float &ro, float &g preserv = lumbefore / lumafter; } - ro = CLIP(r * preserv); - go = CLIP(g * preserv); - bo = CLIP(b * preserv); + ro = /*CLIP*/(r * preserv); + go = /*CLIP*/(g * preserv); + bo = /*CLIP*/(b * preserv); } /** diff --git a/rtengine/iplab2rgb.cc b/rtengine/iplab2rgb.cc index 6979224a0..e56e63dea 100644 --- a/rtengine/iplab2rgb.cc +++ b/rtengine/iplab2rgb.cc @@ -30,8 +30,66 @@ namespace rtengine { +extern void filmlike_clip(float *r, float *g, float *b); + +namespace { + +inline void clipLAB(float iL, float ia, float ib, float &oL, float &oa, float &ob, const float scale, const float wp[3][3], const float wip[3][3]) +{ + if (iL < 0.f) { + oL = oa = ob = 0.f; + } else if (iL > 32768.f) { + + float X, Y, Z; + float r, g, b; + Color::Lab2XYZ(iL, ia, ib, X, Y, Z); + Color::xyz2rgb(X, Y, Z, r, g, b, wip); + filmlike_clip(&r, &g, &b); + Color::rgbxyz(r, g, b, X, Y, Z, wp); + Color::XYZ2Lab(X, Y, Z, oL, oa, ob); + oL /= scale; + oa /= scale; + ob /= scale; + + // oL = 32768.f / scale; + // oa = ob = 0.f; + } else { + oL = iL / scale; + oa = ia / scale; + ob = ib / scale; + } +} + + +inline void clipLAB(float iL, float ia, float ib, double &oL, double &oa, double &ob, const float scale, const float wp[3][3], const float wip[3][3]) +{ + float tL, ta, tb; + clipLAB(iL, ia, ib, tL, ta, tb, scale, wp, wip); + oL = tL; + oa = ta; + ob = tb; +} + +} // namespace + extern const Settings* settings; +#define DECLARE_WORKING_MATRICES_(space) \ + TMatrix wprof = ICCStore::getInstance()->workingSpaceMatrix ( space ); \ + 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])} \ + }; \ + \ + TMatrix wiprof = ICCStore::getInstance()->workingSpaceInverseMatrix ( space ); \ + const float wip[3][3] = { \ + {static_cast (wiprof[0][0]), static_cast (wiprof[0][1]), static_cast (wiprof[0][2])}, \ + {static_cast (wiprof[1][0]), static_cast (wiprof[1][1]), static_cast (wiprof[1][2])}, \ + {static_cast (wiprof[2][0]), static_cast (wiprof[2][1]), static_cast (wiprof[2][2])} \ + } + + // Used in ImProcCoordinator::updatePreviewImage (rtengine/improccoordinator.cc) // Crop::update (rtengine/dcrop.cc) // Thumbnail::processImage (rtengine/rtthumbnail.cc) @@ -40,6 +98,8 @@ extern const Settings* settings; // otherwise divide by 327.68, convert to xyz and apply the sRGB transform, before converting with gamma2curve void ImProcFunctions::lab2monitorRgb (LabImage* lab, Image8* image) { + DECLARE_WORKING_MATRICES_(params->icm.working); + if (monitorTransform) { int W = lab->W; @@ -68,9 +128,8 @@ void ImProcFunctions::lab2monitorRgb (LabImage* lab, Image8* image) float* rb = lab->b[i]; for (int j = 0; j < W; j++) { - buffer[iy++] = rL[j] / 327.68f; - buffer[iy++] = ra[j] / 327.68f; - buffer[iy++] = rb[j] / 327.68f; + clipLAB(rL[j], ra[j], rb[j], buffer[iy], buffer[iy+1], buffer[iy+2], 327.68f, wp, wip); + iy += 3; } cmsDoTransform (monitorTransform, buffer, data + ix, W); @@ -94,12 +153,14 @@ void ImProcFunctions::lab2monitorRgb (LabImage* lab, Image8* image) float R, G, B; float x_, y_, z_; + float L, a, b; for (int j = 0; j < W; ++j) { //float L1=rL[j],a1=ra[j],b1=rb[j];//for testing + clipLAB(rL[j], ra[j], rb[j], L, a, b, 1.f, wp, wip); - Color::Lab2XYZ(rL[j], ra[j], rb[j], x_, y_, z_ ); + Color::Lab2XYZ(L, a, b, x_, y_, z_ ); Color::xyz2srgb(x_, y_, z_, R, G, B); @@ -124,6 +185,8 @@ void ImProcFunctions::lab2monitorRgb (LabImage* lab, Image8* image) // otherwise divide by 327.68, convert to xyz and apply the RGB transform, before converting with gamma2curve Image8* ImProcFunctions::lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, bool consider_histogram_settings) { + DECLARE_WORKING_MATRICES_(icm.working); + //gamutmap(lab); if (cx < 0) { @@ -200,9 +263,8 @@ Image8* ImProcFunctions::lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, float* rb = lab->b[i]; for (int j = cx; j < cx + cw; j++) { - buffer[iy++] = rL[j] / 327.68f; - buffer[iy++] = ra[j] / 327.68f; - buffer[iy++] = rb[j] / 327.68f; + clipLAB(rL[j], ra[j], rb[j], buffer[iy], buffer[iy+1], buffer[iy+2], 327.68f, wp, wip); + iy += 3; } cmsDoTransform (hTransform, buffer, data + ix, cw); @@ -230,8 +292,10 @@ Image8* ImProcFunctions::lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, float R, G, B; float x_, y_, z_; + float L, a, b; for (int j = cx; j < cx + cw; ++j) { + clipLAB(rL[j], ra[j], rb[j], L, a, b, 1.f, wp, wip); Color::Lab2XYZ(rL[j], ra[j], rb[j], x_, y_, z_); Color::xyz2rgb(x_, y_, z_, R, G, B, xyz_rgb); @@ -330,9 +394,9 @@ Imagefloat* ImProcFunctions::lab2rgbOut (LabImage* lab, int cx, int cy, int cw, Color::xyz2srgb(x_, y_, z_, R, G, B); - image->r(i - cy, j - cx) = Color::gamma2curve[CLIP(R)]; - image->g(i - cy, j - cx) = Color::gamma2curve[CLIP(G)]; - image->b(i - cy, j - cx) = Color::gamma2curve[CLIP(B)]; + setUnlessOOG(image->r(i - cy, j - cx), Color::gamma2curve[CLIP(R)]); + setUnlessOOG(image->g(i - cy, j - cx), Color::gamma2curve[CLIP(G)]); + setUnlessOOG(image->b(i - cy, j - cx), Color::gamma2curve[CLIP(B)]); } } } diff --git a/rtengine/ipresize.cc b/rtengine/ipresize.cc index 03502e99b..f9ff94ef9 100644 --- a/rtengine/ipresize.cc +++ b/rtengine/ipresize.cc @@ -162,9 +162,9 @@ void ImProcFunctions::Lanczos (const Imagefloat* src, Imagefloat* dst, float sca b += wh[k] * lb[jj]; } - dst->r (i, j) = CLIP (r);//static_cast (r)); - dst->g (i, j) = CLIP (g);//static_cast (g)); - dst->b (i, j) = CLIP (b);//static_cast (b)); + dst->r (i, j) = /*CLIP*/ (r);//static_cast (r)); + dst->g (i, j) = /*CLIP*/ (g);//static_cast (g)); + dst->b (i, j) = /*CLIP*/ (b);//static_cast (b)); } } diff --git a/rtengine/rt_math.h b/rtengine/rt_math.h index ca93619ee..1918439ef 100644 --- a/rtengine/rt_math.h +++ b/rtengine/rt_math.h @@ -137,4 +137,20 @@ constexpr std::uint8_t uint16ToUint8Rounded(std::uint16_t i) return ((i + 128) - ((i + 128) >> 8)) >> 8; } + +template +constexpr bool OOG(const T &val, const T &high=T(MAXVAL)) +{ + return (val > high); +} + +template +void setUnlessOOG(T &out, const T &val) +{ + if (!OOG(out)) { + out = val; + } +} + + } diff --git a/rtengine/rtthumbnail.cc b/rtengine/rtthumbnail.cc index 05d4bead8..cd4d414ea 100644 --- a/rtengine/rtthumbnail.cc +++ b/rtengine/rtthumbnail.cc @@ -1038,11 +1038,11 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, eSensorT for (int j = 0; j < rwidth; j++) { float red = baseImg->r (i, j) * rmi; - baseImg->r (i, j) = CLIP (red); + baseImg->r (i, j) = /*CLIP*/ (red); float green = baseImg->g (i, j) * gmi; - baseImg->g (i, j) = CLIP (green); + baseImg->g (i, j) = /*CLIP*/ (green); float blue = baseImg->b (i, j) * bmi; - baseImg->b (i, j) = CLIP (blue); + baseImg->b (i, j) = /*CLIP*/ (blue); } } @@ -1234,6 +1234,7 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, eSensorT } } + // luminance processing // ipf.EPDToneMap(labView,0,6); @@ -1303,7 +1304,7 @@ IImage8* Thumbnail::processImage (const procparams::ProcParams& params, eSensorT ipf.ciecam_02float (cieView, adap, 1, 2, labView, ¶ms, customColCurve1, customColCurve2, customColCurve3, dummy, dummy, CAMBrightCurveJ, CAMBrightCurveQ, CAMMean, 5, sk, execsharp, d, dj, yb, rtt); delete cieView; } - + // color processing //ipf.colorCurve (labView, labView); diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc index 278b14fe0..2f88e0c11 100644 --- a/rtgui/editorpanel.cc +++ b/rtgui/editorpanel.cc @@ -1978,6 +1978,11 @@ bool EditorPanel::saveImmediately (const Glib::ustring &filename, const SaveForm { rtengine::procparams::ProcParams pparams; ipc->getParams (&pparams); + + if (gimpPlugin) { + pparams.icm.gamma = "linear_g1.0"; + } + rtengine::ProcessingJob *job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams); // save immediately @@ -1985,7 +1990,9 @@ bool EditorPanel::saveImmediately (const Glib::ustring &filename, const SaveForm int err = 0; - if (sf.format == "tif") { + if (gimpPlugin) { + err = img->saveAsTIFF (filename, 32, true); + } else if (sf.format == "tif") { err = img->saveAsTIFF (filename, sf.tiffBits, sf.tiffUncompressed); } else if (sf.format == "png") { err = img->saveAsPNG (filename, sf.pngBits); diff --git a/rtgui/main.cc b/rtgui/main.cc index 5c2296b58..9b2cf64ae 100644 --- a/rtgui/main.cc +++ b/rtgui/main.cc @@ -674,15 +674,8 @@ int main (int argc, char **argv) m.run (*rtWindow); gdk_threads_leave(); - if (gimpPlugin && - rtWindow->epanel && rtWindow->epanel->isRealized()) { - SaveFormat sf; - sf.format = "tif"; - sf.tiffBits = 16; - sf.tiffUncompressed = true; - sf.saveParams = true; - - if (!rtWindow->epanel->saveImmediately (argv2, sf)) { + if (gimpPlugin && rtWindow->epanel && rtWindow->epanel->isRealized()) { + if (!rtWindow->epanel->saveImmediately(argv2, SaveFormat())) { ret = -2; } } From 324ecae66914a429d75c2c5357f981724b4542ff Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Mon, 12 Feb 2018 18:06:40 +0100 Subject: [PATCH 02/10] fixed typo --- rtengine/curves.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/rtengine/curves.h b/rtengine/curves.h index e489ec38c..01b17a02c 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -53,7 +53,7 @@ inline void setLutVal(const LUTf &lut, float &val) val = lut[std::max(val, 0.f)]; } else { float m = lut[MAXVALF]; - val += (m - val); + val += (m - MAXVALF); } } @@ -62,7 +62,7 @@ inline void setLutVal(float &val, float lutval, float maxval) if (!OOG(val)) { val = lutval; } else { - val += (maxval - val); + val += (maxval - MAXVALF); } } From e550e634af70ba0988e47144f3251b7e5affd36c Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 13 Feb 2018 10:55:42 +0100 Subject: [PATCH 03/10] fixed crashes in color toning due to unbounded processing --- rtengine/improcfun.cc | 26 +++++++++++++++----------- 1 file changed, 15 insertions(+), 11 deletions(-) diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index ced6727c6..0f7ee85d5 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -4109,9 +4109,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer bo *= preserv; } - rtemp[ti * TS + tj] = /*CLIP*/(ro); - gtemp[ti * TS + tj] = /*CLIP*/(go); - btemp[ti * TS + tj] = /*CLIP*/(bo); + setUnlessOOG(rtemp[ti * TS + tj], CLIP(ro)); + setUnlessOOG(gtemp[ti * TS + tj], CLIP(go)); + setUnlessOOG(btemp[ti * TS + tj], CLIP(bo)); } } } @@ -4165,9 +4165,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer float b = btemp[ti * TS + tj]; float ro, go, bo; labtoning (r, g, b, ro, go, bo, algm, metchrom, twoc, satLimit, satLimitOpacity, ctColorCurve, ctOpacityCurve, clToningcurve, cl2Toningcurve, iplow, iphigh, wp, wip); - rtemp[ti * TS + tj] = /*CLIP*/ (ro); //I used CLIP because there is a little bug in gamutLchonly that return 65536.ii intead of 65535 ==> crash - gtemp[ti * TS + tj] = /*CLIP*/ (go); - btemp[ti * TS + tj] = /*CLIP*/ (bo); + setUnlessOOG(rtemp[ti * TS + tj], CLIP (ro)); // I used CLIP because there is a little bug in gamutLchonly that return 65536.ii intead of 65535 ==> crash + setUnlessOOG(gtemp[ti * TS + tj], CLIP (go)); + setUnlessOOG(btemp[ti * TS + tj], CLIP (bo)); } } } @@ -5356,9 +5356,9 @@ void ImProcFunctions::toning2col (float r, float g, float b, float &ro, float &g preserv = lumbefore / lumafter; } - ro = /*CLIP*/(r * preserv); - go = /*CLIP*/(g * preserv); - bo = /*CLIP*/(b * preserv); + setUnlessOOG(ro, CLIP(r * preserv)); + setUnlessOOG(go, CLIP(g * preserv)); + setUnlessOOG(bo, CLIP(b * preserv)); } /** @@ -5422,11 +5422,15 @@ void ImProcFunctions::labtoning (float r, float g, float b, float &ro, float &go luma = 1.f - SQR (SQR ((lm * 65535.f) / (cl2Toningcurve[ (lm) * 65535.f]))); //apply C2=f(L) acts only on 'b' } + float rr, gg, bb; if (algm == 1) { - Color::interpolateRGBColor (realL, iplow, iphigh, algm, opacity, twoc, metchrom, chromat, luma, r, g, b, xl, yl, zl, x2, y2, z2, wp, wip, ro, go, bo); + Color::interpolateRGBColor (realL, iplow, iphigh, algm, opacity, twoc, metchrom, chromat, luma, r, g, b, xl, yl, zl, x2, y2, z2, wp, wip, rr, gg, bb); } else { - Color::interpolateRGBColor (realL, iplow, iphigh, algm, opacity2, twoc, metchrom, chromat, luma, r, g, b, xl, yl, zl, x2, y2, z2, wp, wip, ro, go, bo); + Color::interpolateRGBColor (realL, iplow, iphigh, algm, opacity2, twoc, metchrom, chromat, luma, r, g, b, xl, yl, zl, x2, y2, z2, wp, wip, rr, gg, bb); } + setUnlessOOG(ro, rr); + setUnlessOOG(go, gg); + setUnlessOOG(bo, bb); } From b37c94298ccae4ae4236cf76f8ba05125d1dfef5 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 16 Feb 2018 11:19:49 +0100 Subject: [PATCH 04/10] fixed Lab color toning with unbounded processing --- rtengine/improcfun.cc | 30 +++++++++++++++++------------- 1 file changed, 17 insertions(+), 13 deletions(-) diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 0f7ee85d5..396bf60f2 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -4165,9 +4165,11 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer float b = btemp[ti * TS + tj]; float ro, go, bo; labtoning (r, g, b, ro, go, bo, algm, metchrom, twoc, satLimit, satLimitOpacity, ctColorCurve, ctOpacityCurve, clToningcurve, cl2Toningcurve, iplow, iphigh, wp, wip); - setUnlessOOG(rtemp[ti * TS + tj], CLIP (ro)); // I used CLIP because there is a little bug in gamutLchonly that return 65536.ii intead of 65535 ==> crash - setUnlessOOG(gtemp[ti * TS + tj], CLIP (go)); - setUnlessOOG(btemp[ti * TS + tj], CLIP (bo)); + if (!OOG(rtemp[ti * TS + tj]) || !OOG(gtemp[ti * TS + tj]) || !OOG(btemp[ti * TS + tj])) { + rtemp[ti * TS + tj] = ro; + gtemp[ti * TS + tj] = go; + btemp[ti * TS + tj] = bo; + } } } } @@ -4842,9 +4844,11 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer float b = tmpImage->b (i, j); float ro, bo, go; labtoning (r, g, b, ro, go, bo, algm, metchrom, twoc, satLimit, satLimitOpacity, ctColorCurve, ctOpacityCurve, clToningcurve, cl2Toningcurve, iplow, iphigh, wp, wip); - tmpImage->r (i, j) = /*CLIP*/ (ro); - tmpImage->g (i, j) = /*CLIP*/ (go); - tmpImage->b (i, j) = /*CLIP*/ (bo); + if (!OOG(tmpImage->r(i, j)) || !OOG(tmpImage->g(i, j)) || !OOG(tmpImage->b(i, j))) { + tmpImage->r (i, j) = ro; + tmpImage->g (i, j) = go; + tmpImage->b (i, j) = bo; + } } } @@ -5375,9 +5379,13 @@ void ImProcFunctions::toning2col (float r, float g, float b, float &ro, float &g **/ void ImProcFunctions::labtoning (float r, float g, float b, float &ro, float &go, float &bo, int algm, int metchrom, int twoc, float satLimit, float satLimitOpacity, const ColorGradientCurve & ctColorCurve, const OpacityCurve & ctOpacityCurve, LUTf & clToningcurve, LUTf & cl2Toningcurve, float iplow, float iphigh, double wp[3][3], double wip[3][3] ) { + ro = CLIP(r); + go = CLIP(g); + bo = CLIP(b); + float realL; float h, s, l; - Color::rgb2hsl (r, g, b, h, s, l); + Color::rgb2hsl (ro, go, bo, h, s, l); float x2, y2, z2; float xl, yl, zl; @@ -5422,15 +5430,11 @@ void ImProcFunctions::labtoning (float r, float g, float b, float &ro, float &go luma = 1.f - SQR (SQR ((lm * 65535.f) / (cl2Toningcurve[ (lm) * 65535.f]))); //apply C2=f(L) acts only on 'b' } - float rr, gg, bb; if (algm == 1) { - Color::interpolateRGBColor (realL, iplow, iphigh, algm, opacity, twoc, metchrom, chromat, luma, r, g, b, xl, yl, zl, x2, y2, z2, wp, wip, rr, gg, bb); + Color::interpolateRGBColor (realL, iplow, iphigh, algm, opacity, twoc, metchrom, chromat, luma, r, g, b, xl, yl, zl, x2, y2, z2, wp, wip, ro, go, bo); } else { - Color::interpolateRGBColor (realL, iplow, iphigh, algm, opacity2, twoc, metchrom, chromat, luma, r, g, b, xl, yl, zl, x2, y2, z2, wp, wip, rr, gg, bb); + Color::interpolateRGBColor (realL, iplow, iphigh, algm, opacity2, twoc, metchrom, chromat, luma, r, g, b, xl, yl, zl, x2, y2, z2, wp, wip, ro, go, bo); } - setUnlessOOG(ro, rr); - setUnlessOOG(go, gg); - setUnlessOOG(bo, bb); } From 33e0c2878922656df165674193a864ee5c862b85 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Mon, 19 Feb 2018 16:31:30 +0100 Subject: [PATCH 05/10] started working on no negative clipping --- rtengine/color.cc | 61 +++++++++++++++++++++++++++++++------------ rtengine/color.h | 4 +++ rtengine/curves.h | 7 +++-- rtengine/iplab2rgb.cc | 6 ++--- rtengine/rt_math.h | 2 +- rtgui/editorpanel.cc | 4 --- 6 files changed, 57 insertions(+), 27 deletions(-) diff --git a/rtengine/color.cc b/rtengine/color.cc index 3be412b6d..71fb097f6 100644 --- a/rtengine/color.cc +++ b/rtengine/color.cc @@ -1747,15 +1747,41 @@ void Color::Lab2XYZ(vfloat L, vfloat a, vfloat b, vfloat &x, vfloat &y, vfloat & } #endif // __SSE2__ +inline float Color::computeXYZ2Lab(float f) +{ + if (f < 0.f) { + return 327.68 * ((kappa * f / MAXVALF + 16.0) / 116.0); + } else if (f > 65535.f) { + return (327.68f * xcbrtf(f / MAXVALF)); + } else { + return cachef[f]; + } +} + + +inline float Color::computeXYZ2LabY(float f) +{ + if (f < 0.f) { + return 327.68 * (kappa * f / MAXVALF); + } else if (f > 65535.f) { + return 327.68f * (116.f * xcbrtf(f / MAXVALF) - 16.f); + } else { + return cachefy[f]; + } +} + + void Color::RGB2Lab(float *R, float *G, float *B, float *L, float *a, float *b, const float wp[3][3], int width) { #ifdef __SSE2__ + vfloat minvalfv = F2V(0.f); vfloat maxvalfv = F2V(MAXVALF); vfloat c500v = F2V(500.f); vfloat c200v = F2V(200.f); #endif int i = 0; + #ifdef __SSE2__ for(;i < width - 3; i+=4) { const vfloat rv = LVFU(R[i]); @@ -1766,17 +1792,18 @@ void Color::RGB2Lab(float *R, float *G, float *B, float *L, float *a, float *b, const vfloat zv = F2V(wp[2][0]) * rv + F2V(wp[2][1]) * gv + F2V(wp[2][2]) * bv; vmask maxMask = vmaskf_gt(vmaxf(xv, vmaxf(yv, zv)), maxvalfv); - if (_mm_movemask_ps((vfloat)maxMask)) { + vmask minMask = vmaskf_lt(vminf(xv, vminf(yv, zv)), minvalfv); + if (_mm_movemask_ps((vfloat)maxMask) || _mm_movemask_ps((vfloat)minMask)) { // take slower code path for all 4 pixels if one of the values is > MAXVALF. Still faster than non SSE2 version for(int k = 0; k < 4; ++k) { float x = xv[k]; float y = yv[k]; float z = zv[k]; - float fx = (x <= 65535.f ? cachef[x] : (327.68f * xcbrtf(x / MAXVALF))); - float fy = (y <= 65535.f ? cachef[y] : (327.68f * xcbrtf(y / MAXVALF))); - float fz = (z <= 65535.f ? cachef[z] : (327.68f * xcbrtf(z / MAXVALF))); + float fx = computeXYZ2Lab(x); + float fy = computeXYZ2Lab(y); + float fz = computeXYZ2Lab(z); - L[i + k] = (y <= 65535.0f ? cachefy[y] : 327.68f * (116.f * xcbrtf(y / MAXVALF) - 16.f)); + L[i + k] = computeXYZ2LabY(y); a[i + k] = (500.f * (fx - fy) ); b[i + k] = (200.f * (fy - fz) ); } @@ -1800,11 +1827,11 @@ void Color::RGB2Lab(float *R, float *G, float *B, float *L, float *a, float *b, float z = wp[2][0] * rv + wp[2][1] * gv + wp[2][2] * bv; float fx, fy, fz; - fx = (x <= 65535.0f ? cachef[x] : (327.68f * xcbrtf(x / MAXVALF))); - fy = (y <= 65535.0f ? cachef[y] : (327.68f * xcbrtf(y / MAXVALF))); - fz = (z <= 65535.0f ? cachef[z] : (327.68f * xcbrtf(z / MAXVALF))); + fx = computeXYZ2Lab(x); + fy = computeXYZ2Lab(y); + fz = computeXYZ2Lab(z); - L[i] = (y <= 65535.0f ? cachefy[y] : 327.68f * (116.f * xcbrtf(y / MAXVALF) - 16.f)); + L[i] = computeXYZ2LabY(y); a[i] = 500.0f * (fx - fy); b[i] = 200.0f * (fy - fz); } @@ -1818,11 +1845,11 @@ void Color::XYZ2Lab(float X, float Y, float Z, float &L, float &a, float &b) float y = Y; float fx, fy, fz; - fx = (x <= 65535.0f ? cachef[x] : (327.68f * xcbrtf(x / MAXVALF))); - fy = (y <= 65535.0f ? cachef[y] : (327.68f * xcbrtf(y / MAXVALF))); - fz = (z <= 65535.0f ? cachef[z] : (327.68f * xcbrtf(z / MAXVALF))); + fx = computeXYZ2Lab(x); + fy = computeXYZ2Lab(y); + fz = computeXYZ2Lab(z); - L = (y <= 65535.0f ? cachefy[y] : 327.68f * (116.f * xcbrtf(y / MAXVALF) - 16.f)); + L = computeXYZ2LabY(y); a = (500.0f * (fx - fy) ); b = (200.0f * (fy - fz) ); } @@ -1854,11 +1881,11 @@ void Color::Yuv2Lab(float Yin, float u, float v, float &L, float &a, float &b, c gamutmap(X, Y, Z, wp); - float fx = (X <= 65535.0 ? cachef[X] : (327.68 * std::cbrt(X / MAXVALF))); - float fy = (Y <= 65535.0 ? cachef[Y] : (327.68 * std::cbrt(Y / MAXVALF))); - float fz = (Z <= 65535.0 ? cachef[Z] : (327.68 * std::cbrt(Z / MAXVALF))); + float fx = computeXYZ2Lab(X); + float fy = computeXYZ2Lab(Y); + float fz = computeXYZ2Lab(Z); - L = (Y <= 65535.0f ? cachefy[Y] : 327.68f * (116.f * xcbrtf(Y / MAXVALF) - 16.f)); + L = computeXYZ2LabY(Y); a = (500.0 * (fx - fy) ); b = (200.0 * (fy - fz) ); } diff --git a/rtengine/color.h b/rtengine/color.h index 4b865f1d9..a0d8d93ee 100644 --- a/rtengine/color.h +++ b/rtengine/color.h @@ -97,6 +97,10 @@ private: #ifdef __SSE2__ static vfloat hue2rgb(vfloat p, vfloat q, vfloat t); #endif + + static float computeXYZ2Lab(float f); + static float computeXYZ2LabY(float f); + public: typedef enum Channel { diff --git a/rtengine/curves.h b/rtengine/curves.h index 01b17a02c..3762b3f1a 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -51,6 +51,9 @@ inline void setLutVal(const LUTf &lut, float &val) { if (!OOG(val)) { val = lut[std::max(val, 0.f)]; + } else if (val < 0.f) { + float m = lut[0.f]; + val += m; } else { float m = lut[MAXVALF]; val += (m - MAXVALF); @@ -61,8 +64,8 @@ inline void setLutVal(float &val, float lutval, float maxval) { if (!OOG(val)) { val = lutval; - } else { - val += (maxval - MAXVALF); + } else if (val > 0.f) { + val += maxval - MAXVALF; } } diff --git a/rtengine/iplab2rgb.cc b/rtengine/iplab2rgb.cc index e56e63dea..676213ef8 100644 --- a/rtengine/iplab2rgb.cc +++ b/rtengine/iplab2rgb.cc @@ -394,9 +394,9 @@ Imagefloat* ImProcFunctions::lab2rgbOut (LabImage* lab, int cx, int cy, int cw, Color::xyz2srgb(x_, y_, z_, R, G, B); - setUnlessOOG(image->r(i - cy, j - cx), Color::gamma2curve[CLIP(R)]); - setUnlessOOG(image->g(i - cy, j - cx), Color::gamma2curve[CLIP(G)]); - setUnlessOOG(image->b(i - cy, j - cx), Color::gamma2curve[CLIP(B)]); + image->r(i - cy, j - cx) = Color::gamma2curve[CLIP(R)]; + image->g(i - cy, j - cx) = Color::gamma2curve[CLIP(G)]; + image->b(i - cy, j - cx) = Color::gamma2curve[CLIP(B)]; } } } diff --git a/rtengine/rt_math.h b/rtengine/rt_math.h index 1918439ef..a8c835a98 100644 --- a/rtengine/rt_math.h +++ b/rtengine/rt_math.h @@ -141,7 +141,7 @@ constexpr std::uint8_t uint16ToUint8Rounded(std::uint16_t i) template constexpr bool OOG(const T &val, const T &high=T(MAXVAL)) { - return (val > high); + return (val < T(0)) || (val > high); } template diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc index 2f88e0c11..60dd25e7e 100644 --- a/rtgui/editorpanel.cc +++ b/rtgui/editorpanel.cc @@ -1979,10 +1979,6 @@ bool EditorPanel::saveImmediately (const Glib::ustring &filename, const SaveForm rtengine::procparams::ProcParams pparams; ipc->getParams (&pparams); - if (gimpPlugin) { - pparams.icm.gamma = "linear_g1.0"; - } - rtengine::ProcessingJob *job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams); // save immediately From aa673ce15d50635b03ec37b5a06dcb7006c9fd7a Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Tue, 20 Feb 2018 13:58:32 +0100 Subject: [PATCH 06/10] fattal: removed negative clipping --- rtengine/tmo_fattal02.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/rtengine/tmo_fattal02.cc b/rtengine/tmo_fattal02.cc index dfdeb56b5..459f1dec0 100644 --- a/rtengine/tmo_fattal02.cc +++ b/rtengine/tmo_fattal02.cc @@ -1104,7 +1104,7 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) } float oldMedian; - const float percentile = float(LIM(1, params->fattal.anchor, 100)) / 100.f; + const float percentile = float(LIM(params->fattal.anchor, 1, 100)) / 100.f; findMinMaxPercentile (Yr.data(), Yr.getRows() * Yr.getCols(), percentile, oldMedian, percentile, oldMedian, multiThread); // median filter on the deep shadows, to avoid boosting noise // because w2 >= w and h2 >= h, we can use the L buffer as temporary buffer for Median_Denoise() @@ -1159,15 +1159,15 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) for (int x = 0; x < w; x++) { int xx = x * wr + 1; - float Y = std::max(Yr (x, y), epsilon); - float l = std::max (L (xx, yy), epsilon) * (scale / Y); - rgb->r (y, x) = std::max (rgb->r (y, x), 0.f) * l; - rgb->g (y, x) = std::max (rgb->g (y, x), 0.f) * l; - rgb->b (y, x) = std::max (rgb->b (y, x), 0.f) * l; + float Y = std::max(Yr(x, y), epsilon); + float l = std::max(L(xx, yy), epsilon) * (scale / Y); + rgb->r(y, x) *= l; + rgb->g(y, x) *= l; + rgb->b(y, x) *= l; - assert (std::isfinite (rgb->r (y, x))); - assert (std::isfinite (rgb->g (y, x))); - assert (std::isfinite (rgb->b (y, x))); + assert(std::isfinite(rgb->r(y, x))); + assert(std::isfinite(rgb->g(y, x))); + assert(std::isfinite(rgb->b(y, x))); } } } From 4f4e3ef7e0fd95f97202493e02749c136f41ab64 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 22 Feb 2018 17:45:42 +0100 Subject: [PATCH 07/10] fixed NaN generation due to unbounded processing (no 0 clipping) --- rtengine/color.cc | 2 +- rtengine/curves.h | 12 ++++++------ rtengine/improcfun.cc | 15 ++++++++------- 3 files changed, 15 insertions(+), 14 deletions(-) diff --git a/rtengine/color.cc b/rtengine/color.cc index 71fb097f6..ab9223358 100644 --- a/rtengine/color.cc +++ b/rtengine/color.cc @@ -719,7 +719,7 @@ void Color::rgb2hsv(float r, float g, float b, float &h, float &s, float &v) if (del_Max < 0.00001 && del_Max > -0.00001) { // no fabs, slow! s = 0.f; } else { - s = del_Max / var_Max; + s = del_Max / (var_Max == 0.0 ? 1.0 : var_Max); if (var_R == var_Max) { h = (var_G - var_B) / del_Max; diff --git a/rtengine/curves.h b/rtengine/curves.h index 3762b3f1a..23a2aa0c3 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -943,9 +943,9 @@ inline void StandardToneCurve::BatchApply( } #ifdef __SSE2__ - float tmpr[4]; - float tmpg[4]; - float tmpb[4]; + vfloat tmpr; + vfloat tmpg; + vfloat tmpb; float mv = lutToneCurve[MAXVALF]; for (; i + 3 < end; i += 4) { __m128 r_val = LVF(r[i]); @@ -1130,9 +1130,9 @@ inline void WeightedStdToneCurve::BatchApply(const size_t start, const size_t en const vfloat zd5v = F2V(0.5f); const vfloat zd25v = F2V(0.25f); - float tmpr[4]; - float tmpg[4]; - float tmpb[4]; + vfloat tmpr; + vfloat tmpg; + vfloat tmpb; for (; i + 3 < end; i += 4) { vfloat r_val = LIMV(LVF(r[i]), ZEROV, c65535v); diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index f21d88c66..63ceccdaa 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -178,8 +178,9 @@ void proPhotoBlue(float *rtemp, float *gtemp, float *btemp, int istart, int tH, for (int k = 0; k < 4; ++k) { float r = rtemp[ti * tileSize + tj + k]; float g = gtemp[ti * tileSize + tj + k]; - if (r == 0.0f || g == 0.0f) { - float b = btemp[ti * tileSize + tj + k]; + float b = btemp[ti * tileSize + tj + k]; + + if ((r == 0.0f || g == 0.0f) && rtengine::min(r, g, b) >= 0.f) { float h, s, v; Color::rgb2hsv (r, g, b, h, s, v); s *= 0.99f; @@ -192,9 +193,9 @@ void proPhotoBlue(float *rtemp, float *gtemp, float *btemp, int istart, int tH, for (; j < tW; j++, tj++) { float r = rtemp[ti * tileSize + tj]; float g = gtemp[ti * tileSize + tj]; + float b = btemp[ti * tileSize + tj]; - if (r == 0.0f || g == 0.0f) { - float b = btemp[ti * tileSize + tj]; + if ((r == 0.0f || g == 0.0f) && rtengine::min(r, g, b) >= 0.f) { float h, s, v; Color::rgb2hsv (r, g, b, h, s, v); s *= 0.99f; @@ -3795,9 +3796,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } } } else { - float tmpr[4]; - float tmpg[4]; - float tmpb[4]; + vfloat tmpr; + vfloat tmpg; + vfloat tmpb; for (int i = istart, ti = 0; i < tH; i++, ti++) { int j = jstart, tj = 0; From 1dbd5d8a5ac1bd8583a2ae363ee1af32243995ab Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Thu, 22 Feb 2018 17:46:11 +0100 Subject: [PATCH 08/10] removed more clipping places --- rtengine/improcfun.cc | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 63ceccdaa..9b1963e37 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -4252,9 +4252,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int i = istart, ti = 0; i < tH; i++, ti++) { for (int j = jstart, tj = 0; j < tW; j++, tj++) { const SatAndValueBlendingToneCurve& userToneCurvebw = static_cast (customToneCurvebw1); - rtemp[ti * TS + tj] = CLIP (rtemp[ti * TS + tj]); - gtemp[ti * TS + tj] = CLIP (gtemp[ti * TS + tj]); - btemp[ti * TS + tj] = CLIP (btemp[ti * TS + tj]); + // rtemp[ti * TS + tj] = CLIP (rtemp[ti * TS + tj]); + // gtemp[ti * TS + tj] = CLIP (gtemp[ti * TS + tj]); + // btemp[ti * TS + tj] = CLIP (btemp[ti * TS + tj]); userToneCurvebw.Apply (rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj]); } } @@ -4262,9 +4262,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int i = istart, ti = 0; i < tH; i++, ti++) { for (int j = jstart, tj = 0; j < tW; j++, tj++) { const WeightedStdToneCurve& userToneCurvebw = static_cast (customToneCurvebw1); - rtemp[ti * TS + tj] = CLIP (rtemp[ti * TS + tj]); - gtemp[ti * TS + tj] = CLIP (gtemp[ti * TS + tj]); - btemp[ti * TS + tj] = CLIP (btemp[ti * TS + tj]); + // rtemp[ti * TS + tj] = CLIP (rtemp[ti * TS + tj]); + // gtemp[ti * TS + tj] = CLIP (gtemp[ti * TS + tj]); + // btemp[ti * TS + tj] = CLIP (btemp[ti * TS + tj]); userToneCurvebw.Apply (rtemp[ti * TS + tj], gtemp[ti * TS + tj], btemp[ti * TS + tj]); } @@ -4697,9 +4697,9 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer for (int j = 0; j < tW; j++) { const WeightedStdToneCurve& userToneCurve = static_cast (customToneCurvebw2); - tmpImage->r (i, j) = CLIP (tmpImage->r (i, j)); - tmpImage->g (i, j) = CLIP (tmpImage->g (i, j)); - tmpImage->b (i, j) = CLIP (tmpImage->b (i, j)); + // tmpImage->r (i, j) = CLIP (tmpImage->r (i, j)); + // tmpImage->g (i, j) = CLIP (tmpImage->g (i, j)); + // tmpImage->b (i, j) = CLIP (tmpImage->b (i, j)); userToneCurve.Apply (tmpImage->r (i, j), tmpImage->g (i, j), tmpImage->b (i, j)); } From 8155698ed656ff1f37f5dd9f19c8fd7a054c9d39 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Sun, 25 Feb 2018 21:21:49 +0100 Subject: [PATCH 09/10] removed one further clipping place --- rtengine/rawimagesource.cc | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index aef4447ce..fc7e086ed 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -3877,9 +3877,9 @@ lab2ProphotoRgbD50(float L, float A, float B, float& r, float& g, float& b) r = prophoto_xyz[0][0] * X + prophoto_xyz[0][1] * Y + prophoto_xyz[0][2] * Z; g = prophoto_xyz[1][0] * X + prophoto_xyz[1][1] * Y + prophoto_xyz[1][2] * Z; b = prophoto_xyz[2][0] * X + prophoto_xyz[2][1] * Y + prophoto_xyz[2][2] * Z; - r = CLIP01(r); - g = CLIP01(g); - b = CLIP01(b); + // r = CLIP01(r); + // g = CLIP01(g); + // b = CLIP01(b); } // Converts raw image including ICC input profile to working space - floating point version From 0b6f483e4b362106f9967545986bd8ae6915d6da Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Mon, 26 Feb 2018 11:46:37 +0100 Subject: [PATCH 10/10] never clip in RawImageSource::getImage (regardless of whether highlight reconstruction is enabled) --- rtengine/rawimagesource.cc | 59 ++------------------------------------ 1 file changed, 2 insertions(+), 57 deletions(-) diff --git a/rtengine/rawimagesource.cc b/rtengine/rawimagesource.cc index fc7e086ed..57c1c8295 100644 --- a/rtengine/rawimagesource.cc +++ b/rtengine/rawimagesource.cc @@ -153,7 +153,7 @@ void transLineFuji (const float* const red, const float* const green, const floa } } -void transLineD1x (const float* const red, const float* const green, const float* const blue, const int i, rtengine::Imagefloat* const image, const int tran, const int imwidth, const int imheight, const bool oddHeight, const bool clip) +void transLineD1x (const float* const red, const float* const green, const float* const blue, const int i, rtengine::Imagefloat* const image, const int tran, const int imwidth, const int imheight, const bool oddHeight) { // Nikon D1X has an uncommon sensor with 4028 x 1324 sensels. // Vertical sensel size is 2x horizontal sensel size @@ -222,12 +222,6 @@ void transLineD1x (const float* const red, const float* const green, const float image->r(row, col) = MAX(0.f, -0.0625f * (red[j] + image->r(row + 3, col)) + 0.5625f * (image->r(row - 1, col) + image->r(row + 1, col))); image->g(row, col) = MAX(0.f, -0.0625f * (green[j] + image->g(row + 3, col)) + 0.5625f * (image->g(row - 1, col) + image->g(row + 1, col))); image->b(row, col) = MAX(0.f, -0.0625f * (blue[j] + image->b(row + 3, col)) + 0.5625f * (image->b(row - 1, col) + image->b(row + 1, col))); - - if(clip) { - image->r(row, col) = MIN(image->r(row, col), rtengine::MAXVALF); - image->g(row, col) = MIN(image->g(row, col), rtengine::MAXVALF); - image->b(row, col) = MIN(image->b(row, col), rtengine::MAXVALF); - } } } @@ -285,12 +279,6 @@ void transLineD1x (const float* const red, const float* const green, const float image->r(j, col) = MAX(0.f, -0.0625f * (red[j] + image->r(j, col + 3)) + 0.5625f * (image->r(j, col - 1) + image->r(j, col + 1))); image->g(j, col) = MAX(0.f, -0.0625f * (green[j] + image->g(j, col + 3)) + 0.5625f * (image->g(j, col - 1) + image->g(j, col + 1))); image->b(j, col) = MAX(0.f, -0.0625f * (blue[j] + image->b(j, col + 3)) + 0.5625f * (image->b(j, col - 1) + image->b(j, col + 1))); - - if(clip) { - image->r(j, col) = MIN(image->r(j, col), rtengine::MAXVALF); - image->g(j, col) = MIN(image->g(j, col), rtengine::MAXVALF); - image->b(j, col) = MIN(image->b(j, col), rtengine::MAXVALF); - } } } @@ -318,12 +306,6 @@ void transLineD1x (const float* const red, const float* const green, const float image->g(row, 2 * i - 3) = MAX(0.f, -0.0625f * (green[j] + image->g(row, 2 * i - 6)) + 0.5625f * (image->g(row, 2 * i - 2) + image->g(row, 2 * i - 4))); image->b(row, 2 * i - 3) = MAX(0.f, -0.0625f * (blue[j] + image->b(row, 2 * i - 6)) + 0.5625f * (image->b(row, 2 * i - 2) + image->b(row, 2 * i - 4))); - if(clip) { - image->r(row, 2 * i - 3) = MIN(image->r(row, 2 * i - 3), rtengine::MAXVALF); - image->g(row, 2 * i - 3) = MIN(image->g(row, 2 * i - 3), rtengine::MAXVALF); - image->b(row, 2 * i - 3) = MIN(image->b(row, 2 * i - 3), rtengine::MAXVALF); - } - image->r(row, 2 * i) = red[j]; image->g(row, 2 * i) = green[j]; image->b(row, 2 * i) = blue[j]; @@ -336,12 +318,6 @@ void transLineD1x (const float* const red, const float* const green, const float image->g(row, 2 * i - 1) = MAX(0.f, -0.0625f * (green[j] + image->g(row, 2 * i - 4)) + 0.5625f * (image->g(row, 2 * i) + image->g(row, 2 * i - 2))); image->b(row, 2 * i - 1) = MAX(0.f, -0.0625f * (blue[j] + image->b(row, 2 * i - 4)) + 0.5625f * (image->b(row, 2 * i) + image->b(row, 2 * i - 2))); - if(clip) { - image->r(j, 2 * i - 1) = MIN(image->r(j, 2 * i - 1), rtengine::MAXVALF); - image->g(j, 2 * i - 1) = MIN(image->g(j, 2 * i - 1), rtengine::MAXVALF); - image->b(j, 2 * i - 1) = MIN(image->b(j, 2 * i - 1), rtengine::MAXVALF); - } - image->r(row, 2 * i + 1) = (red[j] + image->r(row, 2 * i - 1)) / 2; image->g(row, 2 * i + 1) = (green[j] + image->g(row, 2 * i - 1)) / 2; image->b(row, 2 * i + 1) = (blue[j] + image->b(row, 2 * i - 1)) / 2; @@ -373,12 +349,6 @@ void transLineD1x (const float* const red, const float* const green, const float image->r(2 * i - 3, j) = MAX(0.f, -0.0625f * (red[j] + image->r(2 * i - 6, j)) + 0.5625f * (image->r(2 * i - 2, j) + image->r(2 * i - 4, j))); image->g(2 * i - 3, j) = MAX(0.f, -0.0625f * (green[j] + image->g(2 * i - 6, j)) + 0.5625f * (image->g(2 * i - 2, j) + image->g(2 * i - 4, j))); image->b(2 * i - 3, j) = MAX(0.f, -0.0625f * (blue[j] + image->b(2 * i - 6, j)) + 0.5625f * (image->b(2 * i - 2, j) + image->b(2 * i - 4, j))); - - if(clip) { - image->r(2 * i - 3, j) = MIN(image->r(2 * i - 3, j), rtengine::MAXVALF); - image->g(2 * i - 3, j) = MIN(image->g(2 * i - 3, j), rtengine::MAXVALF); - image->b(2 * i - 3, j) = MIN(image->b(2 * i - 3, j), rtengine::MAXVALF); - } } } @@ -388,12 +358,6 @@ void transLineD1x (const float* const red, const float* const green, const float image->g(2 * i - 1, j) = MAX(0.f, -0.0625f * (green[j] + image->g(2 * i - 4, j)) + 0.5625f * (image->g(2 * i, j) + image->g(2 * i - 2, j))); image->b(2 * i - 1, j) = MAX(0.f, -0.0625f * (blue[j] + image->b(2 * i - 4, j)) + 0.5625f * (image->b(2 * i, j) + image->b(2 * i - 2, j))); - if(clip) { - image->r(2 * i - 1, j) = MIN(image->r(2 * i - 1, j), rtengine::MAXVALF); - image->g(2 * i - 1, j) = MIN(image->g(2 * i - 1, j), rtengine::MAXVALF); - image->b(2 * i - 1, j) = MIN(image->b(2 * i - 1, j), rtengine::MAXVALF); - } - image->r(2 * i + 1, j) = (red[j] + image->r(2 * i - 1, j)) / 2; image->g(2 * i + 1, j) = (green[j] + image->g(2 * i - 1, j)) / 2; image->b(2 * i + 1, j) = (blue[j] + image->b(2 * i - 1, j)) / 2; @@ -723,8 +687,6 @@ void RawImageSource::getImage (const ColorTemp &ctemp, int tran, Imagefloat* ima hlmax[1] = clmax[1] * gm; hlmax[2] = clmax[2] * bm; - const bool doClip = (chmax[0] >= clmax[0] || chmax[1] >= clmax[1] || chmax[2] >= clmax[2]) && !hrp.hrenabled; - float area = skip * skip; rm /= area; gm /= area; @@ -767,17 +729,6 @@ void RawImageSource::getImage (const ColorTemp &ctemp, int tran, Imagefloat* ima gtot *= gm; btot *= bm; - if (doClip) { - // note: as hlmax[] can be larger than CLIP and we can later apply negative - // exposure this means that we can clip away local highlights which actually - // are not clipped. We have to do that though as we only check pixel by pixel - // and don't know if this will transition into a clipped area, if so we need - // to clip also surrounding to make a good colour transition - rtot = CLIP(rtot); - gtot = CLIP(gtot); - btot = CLIP(btot); - } - line_red[j] = rtot; line_grn[j] = gtot; line_blue[j] = btot; @@ -802,12 +753,6 @@ void RawImageSource::getImage (const ColorTemp &ctemp, int tran, Imagefloat* ima gtot *= gm; btot *= bm; - if (doClip) { - rtot = CLIP(rtot); - gtot = CLIP(gtot); - btot = CLIP(btot); - } - line_red[j] = rtot; line_grn[j] = gtot; line_blue[j] = btot; @@ -821,7 +766,7 @@ void RawImageSource::getImage (const ColorTemp &ctemp, int tran, Imagefloat* ima } if(d1x) { - transLineD1x (line_red, line_grn, line_blue, ix, image, tran, imwidth, imheight, d1xHeightOdd, doClip); + transLineD1x (line_red, line_grn, line_blue, ix, image, tran, imwidth, imheight, d1xHeightOdd); } else if(fuji) { transLineFuji (line_red, line_grn, line_blue, ix, image, tran, imheight, fw); } else {