diff --git a/rtengine/ciecam02.cc b/rtengine/ciecam02.cc index 77c57048a..560d5721a 100644 --- a/rtengine/ciecam02.cc +++ b/rtengine/ciecam02.cc @@ -608,31 +608,31 @@ void Ciecam02::calculate_ab ( double &aa, double &bb, double h, double e, double } void Ciecam02::calculate_abfloat ( float &aa, float &bb, float h, float e, float t, float nbb, float a ) { - float2 sincosval = xsincosf ((h * rtengine::RT_PI) / 180.0f); + float2 sincosval = xsincosf(h * rtengine::RT_PI_F_180); float sinh = sincosval.x; float cosh = sincosval.y; float x = (a / nbb) + 0.305f; - float p3 = 1.05f; - bool swapValues = fabs ( sinh ) > fabs ( cosh ); + constexpr float p3 = 1.05f; + const bool swapValues = fabs(sinh) > fabs(cosh); if (swapValues) { - std::swap (sinh, cosh); + std::swap(sinh, cosh); } float c1 = 1.f; float c2 = sinh / cosh; if (swapValues) { - std::swap (c1, c2); + std::swap(c1, c2); } - float div = ((e / (t * cosh)) - (-0.31362f - (p3 * 0.15681f)) * c1 - ((0.01924f - (p3 * 4.49038f)) * (c2))); + float div = ((e / (t * cosh)) - (-0.31362f - (p3 * 0.15681f)) * c1 - ((0.01924f - (p3 * 4.49038f)) * c2)); // for large values of t the above calculation can change its sign which results in a hue shift of 180 degree // so we have to check the sign to avoid this shift. // Additionally it seems useful to limit the minimum value of div // I limited it, but I'm sure the actual limit is not the best one - if (signf (div) != signf (cosh) || fabsf (div) <= fabsf (cosh) * 2.f) { + if (signf(div) != signf(cosh) || fabsf(div) <= fabsf(cosh) * 2.f) { div = cosh * 2.f; } @@ -640,7 +640,7 @@ void Ciecam02::calculate_abfloat ( float &aa, float &bb, float h, float e, float bb = (aa * sinh) / cosh; if (swapValues) { - std::swap (aa, bb); + std::swap(aa, bb); } } #ifdef __SSE2__ @@ -1007,9 +1007,18 @@ void Ciecam02::xyz2jch_ciecam02float ( float &J, float &C, float &h, float aw, f bp = MAXR (bp, 0.0f); } - rpa = nonlinear_adaptationfloat ( rp, fl ); - gpa = nonlinear_adaptationfloat ( gp, fl ); - bpa = nonlinear_adaptationfloat ( bp, fl ); +#ifdef __SSE2__ + vfloat pv = _mm_setr_ps(rp, gp, bp, 1.f); + vfloat fv = F2V(fl); + vfloat outv = nonlinear_adaptationfloat(pv, fv); + rpa = outv[0]; + gpa = outv[1]; + bpa = outv[2]; +#else + rpa = nonlinear_adaptationfloat(rp, fl); + gpa = nonlinear_adaptationfloat(gp, fl); + bpa = nonlinear_adaptationfloat(bp, fl); +#endif ca = rpa - ((12.0f * gpa) - bpa) / 11.0f; cb = (0.11111111f) * (rpa + gpa - (2.0f * bpa)); @@ -1084,26 +1093,43 @@ void Ciecam02::jch2xyz_ciecam02float ( float &x, float &y, float &z, float J, fl float a, ca, cb; float e, t; gamu = 1; - xyz_to_cat02float ( rw, gw, bw, xw, yw, zw, gamu ); - e = ((961.53846f) * nc * ncb) * (xcosf ( ((h * rtengine::RT_PI) / 180.0f) + 2.0f ) + 3.8f); - a = pow_F ( J / 100.0f, 1.0f / (c * cz) ) * aw; - t = pow_F ( 10.f * C / (sqrtf ( J ) * pow1), 1.1111111f ); + xyz_to_cat02float(rw, gw, bw, xw, yw, zw, gamu); + e = ((961.53846f) * nc * ncb) * (xcosf(h * rtengine::RT_PI_F_180 + 2.0f) + 3.8f); - calculate_abfloat ( ca, cb, h, e, t, nbb, a ); - Aab_to_rgbfloat ( rpa, gpa, bpa, a, ca, cb, nbb ); +#ifdef __SSE2__ + vfloat powinv1 = _mm_setr_ps(J / 100.0f, 10.f * C / (sqrtf(J) * pow1), 1.f, 1.f); + vfloat powinv2 = _mm_setr_ps(1.0f / (c * cz), 1.1111111f, 1.f, 1.f); + vfloat powoutv = pow_F(powinv1, powinv2); + a = powoutv[0] * aw; + t = powoutv[1]; +#else + a = pow_F(J / 100.0f, 1.0f / (c * cz)) * aw; + t = pow_F(10.f * C / (sqrtf(J) * pow1), 1.1111111f); +#endif - rp = inverse_nonlinear_adaptationfloat ( rpa, fl ); - gp = inverse_nonlinear_adaptationfloat ( gpa, fl ); - bp = inverse_nonlinear_adaptationfloat ( bpa, fl ); + calculate_abfloat(ca, cb, h, e, t, nbb, a); + Aab_to_rgbfloat(rpa, gpa, bpa, a, ca, cb, nbb); - hpe_to_xyzfloat ( x, y, z, rp, gp, bp ); - xyz_to_cat02float ( rc, gc, bc, x, y, z, gamu ); +#ifdef __SSE2__ + vfloat pav = _mm_setr_ps(rpa, gpa, bpa, 1.f); + vfloat fv = F2V(fl); + vfloat outv = inverse_nonlinear_adaptationfloat(pav, fv); + rp = outv[0]; + gp = outv[1]; + bp = outv[2]; +#else + rp = inverse_nonlinear_adaptationfloat(rpa, fl); + gp = inverse_nonlinear_adaptationfloat(gpa, fl); + bp = inverse_nonlinear_adaptationfloat(bpa, fl); +#endif + hpe_to_xyzfloat(x, y, z, rp, gp, bp); + xyz_to_cat02float(rc, gc, bc, x, y, z, gamu); r = rc / (((yw * d) / rw) + (1.0f - d)); g = gc / (((yw * d) / gw) + (1.0f - d)); b = bc / (((yw * d) / bw) + (1.0f - d)); - cat02_to_xyzfloat ( x, y, z, r, g, b, gamu ); + cat02_to_xyzfloat(x, y, z, r, g, b, gamu); } #ifdef __SSE2__ diff --git a/rtengine/curves.cc b/rtengine/curves.cc index db6c4c5f5..8d52782a2 100644 --- a/rtengine/curves.cc +++ b/rtengine/curves.cc @@ -1822,64 +1822,242 @@ float PerceptualToneCurve::calculateToneCurveContrastValue() const return maxslope; } -void PerceptualToneCurve::Apply(float &r, float &g, float &b, PerceptualToneCurveState & state) const +void PerceptualToneCurve::BatchApply(const size_t start, const size_t end, float *rc, float *gc, float *bc, const PerceptualToneCurveState &state) const { - float x, y, z; - - if (!state.isProphoto) { - // convert to prophoto space to make sure the same result is had regardless of working color space - float newr = state.Working2Prophoto[0][0] * r + state.Working2Prophoto[0][1] * g + state.Working2Prophoto[0][2] * b; - float newg = state.Working2Prophoto[1][0] * r + state.Working2Prophoto[1][1] * g + state.Working2Prophoto[1][2] * b; - float newb = state.Working2Prophoto[2][0] * r + state.Working2Prophoto[2][1] * g + state.Working2Prophoto[2][2] * b; - r = newr; - g = newg; - b = newb; - } - const AdobeToneCurve& adobeTC = static_cast((const ToneCurve&) * this); - float ar = r; - float ag = g; - float ab = b; - adobeTC.Apply(ar, ag, ab); - if (ar >= 65535.f && ag >= 65535.f && ab >= 65535.f) { - // clip fast path, will also avoid strange colors of clipped highlights - r = g = b = 65535.f; - return; - } + for (size_t i = start; i < end; ++i) { + float r = CLIP(rc[i]); + float g = CLIP(gc[i]); + float b = CLIP(bc[i]); - if (ar <= 0.f && ag <= 0.f && ab <= 0.f) { - r = g = b = 0; - return; - } + if (!state.isProphoto) { + // convert to prophoto space to make sure the same result is had regardless of working color space + float newr = state.Working2Prophoto[0][0] * r + state.Working2Prophoto[0][1] * g + state.Working2Prophoto[0][2] * b; + float newg = state.Working2Prophoto[1][0] * r + state.Working2Prophoto[1][1] * g + state.Working2Prophoto[1][2] * b; + float newb = state.Working2Prophoto[2][0] * r + state.Working2Prophoto[2][1] * g + state.Working2Prophoto[2][2] * b; + r = newr; + g = newg; + b = newb; + } - // ProPhoto constants for luminance, that is xyz_prophoto[1][] - const float Yr = 0.2880402f; - const float Yg = 0.7118741f; - const float Yb = 0.0000857f; + float ar = r; + float ag = g; + float ab = b; + adobeTC.Apply(ar, ag, ab); - // we use the Adobe (RGB-HSV hue-stabilized) curve to decide luminance, which generally leads to a less contrasty result - // compared to a pure luminance curve. We do this to be more compatible with the most popular curves. - float oldLuminance = r * Yr + g * Yg + b * Yb; - float newLuminance = ar * Yr + ag * Yg + ab * Yb; - float Lcoef = newLuminance / oldLuminance; - r = LIM(r * Lcoef, 0.f, 65535.f); - g = LIM(g * Lcoef, 0.f, 65535.f); - b = LIM(b * Lcoef, 0.f, 65535.f); + 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; + continue; + } - // move to JCh so we can modulate chroma based on the global contrast-related chroma scaling factor - Color::Prophotoxyz(r, g, b, x, y, z); + if (ar <= 0.f && ag <= 0.f && ab <= 0.f) { + rc[i] = gc[i] = bc[i] = 0; + continue; + } - float J, C, h; - Ciecam02::xyz2jch_ciecam02float( J, C, h, - aw, fl, - x * 0.0015259022f, y * 0.0015259022f, z * 0.0015259022f, - xw, yw, zw, - c, nc, pow1, nbb, ncb, cz, d); + // ProPhoto constants for luminance, that is xyz_prophoto[1][] + constexpr float Yr = 0.2880402f; + constexpr float Yg = 0.7118741f; + constexpr float Yb = 0.0000857f; + + // we use the Adobe (RGB-HSV hue-stabilized) curve to decide luminance, which generally leads to a less contrasty result + // compared to a pure luminance curve. We do this to be more compatible with the most popular curves. + const float oldLuminance = r * Yr + g * Yg + b * Yb; + const float newLuminance = ar * Yr + ag * Yg + ab * Yb; + const float Lcoef = newLuminance / oldLuminance; + r = LIM(r * Lcoef, 0.f, 65535.f); + g = LIM(g * Lcoef, 0.f, 65535.f); + b = LIM(b * Lcoef, 0.f, 65535.f); + + // move to JCh so we can modulate chroma based on the global contrast-related chroma scaling factor + float x, y, z; + Color::Prophotoxyz(r, g, b, x, y, z); + + float J, C, h; + Ciecam02::xyz2jch_ciecam02float( J, C, h, + aw, fl, + x * 0.0015259022f, y * 0.0015259022f, z * 0.0015259022f, + xw, yw, zw, + c, nc, pow1, nbb, ncb, cz, d); - if (!isfinite(J) || !isfinite(C) || !isfinite(h)) { - // this can happen for dark noise colors or colors outside human gamut. Then we just return the curve's result. + if (!isfinite(J) || !isfinite(C) || !isfinite(h)) { + // this can happen for dark noise colours or colours outside human gamut. Then we just return the curve's result. + if (!state.isProphoto) { + float newr = state.Prophoto2Working[0][0] * r + state.Prophoto2Working[0][1] * g + state.Prophoto2Working[0][2] * b; + float newg = state.Prophoto2Working[1][0] * r + state.Prophoto2Working[1][1] * g + state.Prophoto2Working[1][2] * b; + float newb = state.Prophoto2Working[2][0] * r + state.Prophoto2Working[2][1] * g + state.Prophoto2Working[2][2] * b; + r = newr; + g = newg; + b = newb; + } + rc[i] = r; + gc[i] = g; + bc[i] = b; + + continue; + } + + float cmul = state.cmul_contrast; // chroma scaling factor + + // depending on color, the chroma scaling factor can be fine-tuned below + + { + // decrease chroma scaling sligthly of extremely saturated colors + float saturated_scale_factor = 0.95f; + constexpr float lolim = 35.f; // lower limit, below this chroma all colors will keep original chroma scaling factor + constexpr float hilim = 60.f; // high limit, above this chroma the chroma scaling factor is multiplied with the saturated scale factor value above + + if (C < lolim) { + // chroma is low enough, don't scale + saturated_scale_factor = 1.f; + } else if (C < hilim) { + // S-curve transition between low and high limit + float x = (C - lolim) / (hilim - lolim); // x = [0..1], 0 at lolim, 1 at hilim + + if (x < 0.5f) { + x = 2.f * SQR(x); + } else { + x = 1.f - 2.f * SQR(1 - x); + } + + saturated_scale_factor = (1.f - x) + saturated_scale_factor * x; + } else { + // do nothing, high saturation color, keep scale factor + } + + cmul *= saturated_scale_factor; + } + + { + // increase chroma scaling slightly of shadows + float nL = Color::gamma2curve[newLuminance]; // apply gamma so we make comparison and transition with a more perceptual lightness scale + float dark_scale_factor = 1.20f; + //float dark_scale_factor = 1.0 + state.debug.p2 / 100.0f; + constexpr float lolim = 0.15f; + constexpr float hilim = 0.50f; + + if (nL < lolim) { + // do nothing, keep scale factor + } else if (nL < hilim) { + // S-curve transition + float x = (nL - lolim) / (hilim - lolim); // x = [0..1], 0 at lolim, 1 at hilim + + if (x < 0.5f) { + x = 2.f * SQR(x); + } else { + x = 1.f - 2.f * SQR(1 - x); + } + + dark_scale_factor = dark_scale_factor * (1.0f - x) + x; + } else { + dark_scale_factor = 1.f; + } + + cmul *= dark_scale_factor; + } + + { + // to avoid strange CIECAM02 chroma errors on close-to-shadow-clipping colors we reduce chroma scaling towards 1.0 for black colors + float dark_scale_factor = 1.f / cmul; + constexpr float lolim = 4.f; + constexpr float hilim = 7.f; + + if (J < lolim) { + // do nothing, keep scale factor + } else if (J < hilim) { + // S-curve transition + float x = (J - lolim) / (hilim - lolim); + + if (x < 0.5f) { + x = 2.f * SQR(x); + } else { + x = 1.f - 2.f * SQR(1 - x); + } + + dark_scale_factor = dark_scale_factor * (1.f - x) + x; + } else { + dark_scale_factor = 1.f; + } + + cmul *= dark_scale_factor; + } + + C *= cmul; + + Ciecam02::jch2xyz_ciecam02float( x, y, z, + J, C, h, + xw, yw, zw, + c, nc, 1, pow1, nbb, ncb, fl, cz, d, aw ); + + if (!isfinite(x) || !isfinite(y) || !isfinite(z)) { + // can happen for colours on the rim of being outside gamut, that worked without chroma scaling but not with. Then we return only the curve's result. + if (!state.isProphoto) { + float newr = state.Prophoto2Working[0][0] * r + state.Prophoto2Working[0][1] * g + state.Prophoto2Working[0][2] * b; + float newg = state.Prophoto2Working[1][0] * r + state.Prophoto2Working[1][1] * g + state.Prophoto2Working[1][2] * b; + float newb = state.Prophoto2Working[2][0] * r + state.Prophoto2Working[2][1] * g + state.Prophoto2Working[2][2] * b; + r = newr; + g = newg; + b = newb; + } + + rc[i] = r; + gc[i] = g; + bc[i] = b; + + continue; + } + + Color::xyz2Prophoto(x, y, z, r, g, b); + r *= 655.35f; + g *= 655.35f; + b *= 655.35f; + r = LIM(r, 0.f, 65535.f); + g = LIM(g, 0.f, 65535.f); + b = LIM(b, 0.f, 65535.f); + + { + // limit saturation increase in rgb space to avoid severe clipping and flattening in extreme highlights + + // we use the RGB-HSV hue-stable "Adobe" curve as reference. For S-curve contrast it increases + // saturation greatly, but desaturates extreme highlights and thus provide a smooth transition to + // the white point. However the desaturation effect is quite strong so we make a weighting + const float as = Color::rgb2s(ar, ag, ab); + const float s = Color::rgb2s(r, g, b); + + const float sat_scale = as <= 0.f ? 1.f : s / as; // saturation scale compared to Adobe curve + float keep = 0.2f; + constexpr float lolim = 1.00f; // only mix in the Adobe curve if we have increased saturation compared to it + constexpr float hilim = 1.20f; + + if (sat_scale < lolim) { + // saturation is low enough, don't desaturate + keep = 1.f; + } else if (sat_scale < hilim) { + // S-curve transition + float x = (sat_scale - lolim) / (hilim - lolim); // x = [0..1], 0 at lolim, 1 at hilim + + if (x < 0.5f) { + x = 2.f * SQR(x); + } else { + x = 1.f - 2.f * SQR(1 - x); + } + + keep = (1.f - x) + keep * x; + } else { + // do nothing, very high increase, keep minimum amount + } + + if (keep < 1.f) { + // mix in some of the Adobe curve result + r = intp(keep, r, ar); + g = intp(keep, g, ag); + b = intp(keep, b, ab); + } + } + if (!state.isProphoto) { float newr = state.Prophoto2Working[0][0] * r + state.Prophoto2Working[0][1] * g + state.Prophoto2Working[0][2] * b; float newg = state.Prophoto2Working[1][0] * r + state.Prophoto2Working[1][1] * g + state.Prophoto2Working[1][2] * b; @@ -1888,175 +2066,11 @@ void PerceptualToneCurve::Apply(float &r, float &g, float &b, PerceptualToneCurv g = newg; b = newb; } - - return; - } - - float cmul = state.cmul_contrast; // chroma scaling factor - - // depending on color, the chroma scaling factor can be fine-tuned below - - { - // decrease chroma scaling sligthly of extremely saturated colors - float saturated_scale_factor = 0.95f; - const float lolim = 35.f; // lower limit, below this chroma all colors will keep original chroma scaling factor - const float hilim = 60.f; // high limit, above this chroma the chroma scaling factor is multiplied with the saturated scale factor value above - - if (C < lolim) { - // chroma is low enough, don't scale - saturated_scale_factor = 1.f; - } else if (C < hilim) { - // S-curve transition between low and high limit - float x = (C - lolim) / (hilim - lolim); // x = [0..1], 0 at lolim, 1 at hilim - - if (x < 0.5f) { - x = 2.f * SQR(x); - } else { - x = 1.f - 2.f * SQR(1 - x); - } - - saturated_scale_factor = (1.f - x) + saturated_scale_factor * x; - } else { - // do nothing, high saturation color, keep scale factor - } - - cmul *= saturated_scale_factor; - } - - { - // increase chroma scaling slightly of shadows - float nL = Color::gamma2curve[newLuminance]; // apply gamma so we make comparison and transition with a more perceptual lightness scale - float dark_scale_factor = 1.20f; - //float dark_scale_factor = 1.0 + state.debug.p2 / 100.0f; - const float lolim = 0.15f; - const float hilim = 0.50f; - - if (nL < lolim) { - // do nothing, keep scale factor - } else if (nL < hilim) { - // S-curve transition - float x = (nL - lolim) / (hilim - lolim); // x = [0..1], 0 at lolim, 1 at hilim - - if (x < 0.5f) { - x = 2.f * SQR(x); - } else { - x = 1.f - 2.f * SQR(1 - x); - } - - dark_scale_factor = dark_scale_factor * (1.0f - x) + x; - } else { - dark_scale_factor = 1.f; - } - - cmul *= dark_scale_factor; - } - - { - // to avoid strange CIECAM02 chroma errors on close-to-shadow-clipping colors we reduce chroma scaling towards 1.0 for black colors - float dark_scale_factor = 1.f / cmul; - const float lolim = 4.f; - const float hilim = 7.f; - - if (J < lolim) { - // do nothing, keep scale factor - } else if (J < hilim) { - // S-curve transition - float x = (J - lolim) / (hilim - lolim); - - if (x < 0.5f) { - x = 2.f * SQR(x); - } else { - x = 1.f - 2.f * SQR(1 - x); - } - - dark_scale_factor = dark_scale_factor * (1.f - x) + x; - } else { - dark_scale_factor = 1.f; - } - - cmul *= dark_scale_factor; - } - - C *= cmul; - - Ciecam02::jch2xyz_ciecam02float( x, y, z, - J, C, h, - xw, yw, zw, - c, nc, 1, pow1, nbb, ncb, fl, cz, d, aw ); - - if (!isfinite(x) || !isfinite(y) || !isfinite(z)) { - // can happen for colors on the rim of being outside gamut, that worked without chroma scaling but not with. Then we return only the curve's result. - if (!state.isProphoto) { - float newr = state.Prophoto2Working[0][0] * r + state.Prophoto2Working[0][1] * g + state.Prophoto2Working[0][2] * b; - float newg = state.Prophoto2Working[1][0] * r + state.Prophoto2Working[1][1] * g + state.Prophoto2Working[1][2] * b; - float newb = state.Prophoto2Working[2][0] * r + state.Prophoto2Working[2][1] * g + state.Prophoto2Working[2][2] * b; - r = newr; - g = newg; - b = newb; - } - - return; - } - - Color::xyz2Prophoto(x, y, z, r, g, b); - r *= 655.35f; - g *= 655.35f; - b *= 655.35f; - r = LIM(r, 0.f, 65535.f); - g = LIM(g, 0.f, 65535.f); - b = LIM(b, 0.f, 65535.f); - - { - // limit saturation increase in rgb space to avoid severe clipping and flattening in extreme highlights - - // we use the RGB-HSV hue-stable "Adobe" curve as reference. For S-curve contrast it increases - // saturation greatly, but desaturates extreme highlights and thus provide a smooth transition to - // the white point. However the desaturation effect is quite strong so we make a weighting - float ah, as, av, h, s, v; - Color::rgb2hsv(ar, ag, ab, ah, as, av); - Color::rgb2hsv(r, g, b, h, s, v); - - float sat_scale = as <= 0.f ? 1.f : s / as; // saturation scale compared to Adobe curve - float keep = 0.2f; - const float lolim = 1.00f; // only mix in the Adobe curve if we have increased saturation compared to it - const float hilim = 1.20f; - - if (sat_scale < lolim) { - // saturation is low enough, don't desaturate - keep = 1.f; - } else if (sat_scale < hilim) { - // S-curve transition - float x = (sat_scale - lolim) / (hilim - lolim); // x = [0..1], 0 at lolim, 1 at hilim - - if (x < 0.5f) { - x = 2.f * SQR(x); - } else { - x = 1.f - 2.f * SQR(1 - x); - } - - keep = (1.f - x) + keep * x; - } else { - // do nothing, very high increase, keep minimum amount - } - - if (keep < 1.f) { - // mix in some of the Adobe curve result - r = r * keep + (1.f - keep) * ar; - g = g * keep + (1.f - keep) * ag; - b = b * keep + (1.f - keep) * ab; - } - } - - if (!state.isProphoto) { - float newr = state.Prophoto2Working[0][0] * r + state.Prophoto2Working[0][1] * g + state.Prophoto2Working[0][2] * b; - float newg = state.Prophoto2Working[1][0] * r + state.Prophoto2Working[1][1] * g + state.Prophoto2Working[1][2] * b; - float newb = state.Prophoto2Working[2][0] * r + state.Prophoto2Working[2][1] * g + state.Prophoto2Working[2][2] * b; - r = newr; - g = newg; - b = newb; + rc[i] = r; + gc[i] = g; + bc[i] = b; } } - float PerceptualToneCurve::cf_range[2]; float PerceptualToneCurve::cf[1000]; float PerceptualToneCurve::f, PerceptualToneCurve::c, PerceptualToneCurve::nc, PerceptualToneCurve::yb, PerceptualToneCurve::la, PerceptualToneCurve::xw, PerceptualToneCurve::yw, PerceptualToneCurve::zw, PerceptualToneCurve::gamut; diff --git a/rtengine/curves.h b/rtengine/curves.h index f537bf6c5..f9c991e8e 100644 --- a/rtengine/curves.h +++ b/rtengine/curves.h @@ -872,7 +872,7 @@ private: public: static void init(); void initApplyState(PerceptualToneCurveState & state, Glib::ustring workingSpace) const; - void Apply(float& r, float& g, float& b, PerceptualToneCurveState & state) const; + void BatchApply(const size_t start, const size_t end, float *r, float *g, float *b, const PerceptualToneCurveState &state) const; }; // Standard tone curve diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 34c43be0e..7e8e153b1 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -233,14 +233,8 @@ void customToneCurve(const ToneCurve &customToneCurve, ToneCurveParams::TcMode c } } else if (curveMode == ToneCurveParams::TcMode::PERCEPTUAL) { // apply curve while keeping color appearance constant const PerceptualToneCurve& userToneCurve = static_cast (customToneCurve); - 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], ptcApplyState); - } + userToneCurve.BatchApply(0, tW - jstart, &rtemp[ti * tileSize], >emp[ti * tileSize], &btemp[ti * tileSize], ptcApplyState); } } } diff --git a/rtengine/rt_math.h b/rtengine/rt_math.h index 17a292618..4bdfe6f06 100644 --- a/rtengine/rt_math.h +++ b/rtengine/rt_math.h @@ -14,6 +14,7 @@ constexpr double MAXVALD = static_cast(MAXVAL); // double version of MAX constexpr double RT_PI = 3.14159265358979323846; // pi constexpr double RT_PI_2 = 1.57079632679489661923; // pi/2 +constexpr double RT_PI_180 = 0.017453292519943295769; // pi/180 constexpr double RT_1_PI = 0.31830988618379067154; // 1/pi constexpr double RT_2_PI = 0.63661977236758134308; // 2/pi constexpr double RT_SQRT1_2 = 0.70710678118654752440; // 1/sqrt(2) @@ -23,6 +24,7 @@ constexpr double RT_NAN = std::numeric_limits::quiet_NaN(); constexpr float RT_PI_F = RT_PI; constexpr float RT_PI_F_2 = RT_PI_2; +constexpr float RT_PI_F_180 = RT_PI_180; constexpr float RT_INFINITY_F = std::numeric_limits::infinity(); constexpr float RT_NAN_F = std::numeric_limits::quiet_NaN();