From 08846264c79d8b107b927c90629bc1c9a91d41e4 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Fri, 9 Feb 2018 21:25:39 +0100 Subject: [PATCH 01/12] 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/12] 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/12] 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/12] 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/12] 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/12] 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/12] 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/12] 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/12] 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/12] 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 { From 237e5b47c3122723a87aa3da8cacb94e05e536e5 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Tue, 27 Mar 2018 19:01:29 +0200 Subject: [PATCH 11/12] Bump cJSON to 1.7.5 Should fix CVE-2016-4303 and https://github.com/DaveGamble/cJSON/issues/30. --- rtengine/cJSON.c | 3223 +++++++++++++++++++++++++++++++++++++++------- rtengine/cJSON.h | 268 +++- 2 files changed, 2972 insertions(+), 519 deletions(-) diff --git a/rtengine/cJSON.c b/rtengine/cJSON.c index 263a45113..7e71ea9e8 100644 --- a/rtengine/cJSON.c +++ b/rtengine/cJSON.c @@ -1,5 +1,5 @@ /* - Copyright (c) 2009 Dave Gamble + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -23,6 +23,20 @@ /* cJSON */ /* JSON parser in C. */ +/* disable warnings about old C89 functions in MSVC */ +#if !defined(_CRT_SECURE_NO_DEPRECATE) && defined(_MSC_VER) +#define _CRT_SECURE_NO_DEPRECATE +#endif + +#ifdef __GNUC__ +#pragma GCC visibility push(default) +#endif +#if defined(_MSC_VER) +#pragma warning (push) +/* disable warning about single line comments in system headers */ +#pragma warning (disable : 4001) +#endif + #include #include #include @@ -30,585 +44,2890 @@ #include #include #include + +#ifdef ENABLE_LOCALES +#include +#endif + +#if defined(_MSC_VER) +#pragma warning (pop) +#endif +#ifdef __GNUC__ +#pragma GCC visibility pop +#endif + #include "cJSON.h" -static const char *ep; +/* define our own boolean type */ +#define true ((cJSON_bool)1) +#define false ((cJSON_bool)0) -const char *cJSON_GetErrorPtr(void) {return ep;} +typedef struct { + const unsigned char *json; + size_t position; +} error; +static error global_error = { NULL, 0 }; -static int cJSON_strcasecmp(const char *s1,const char *s2) +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void) { - if (!s1) return (s1==s2)?0:1; - if (!s2) return 1; - for(; tolower(*s1) == tolower(*s2); ++s1, ++s2) if(*s1 == 0) return 0; - return tolower(*(const unsigned char *)s1) - tolower(*(const unsigned char *)s2); + return (const char*) (global_error.json + global_error.position); } -static void *(*cJSON_malloc)(size_t sz) = malloc; -static void (*cJSON_free)(void *ptr) = free; +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item) { + if (!cJSON_IsString(item)) { + return NULL; + } -static char* cJSON_strdup(const char* str) -{ - size_t len; - char* copy; - - len = strlen(str) + 1; - if (!(copy = (char*)cJSON_malloc(len))) return 0; - memcpy(copy,str,len); - return copy; + return item->valuestring; } -void cJSON_InitHooks(cJSON_Hooks* hooks) +/* This is a safeguard to prevent copy-pasters from using incompatible C and header files */ +#if (CJSON_VERSION_MAJOR != 1) || (CJSON_VERSION_MINOR != 7) || (CJSON_VERSION_PATCH != 5) + #error cJSON.h and cJSON.c have different versions. Make sure that both have the same. +#endif + +CJSON_PUBLIC(const char*) cJSON_Version(void) { - if (!hooks) { /* Reset hooks */ - cJSON_malloc = malloc; - cJSON_free = free; + static char version[15]; + sprintf(version, "%i.%i.%i", CJSON_VERSION_MAJOR, CJSON_VERSION_MINOR, CJSON_VERSION_PATCH); + + return version; +} + +/* Case insensitive string comparison, doesn't consider two NULL pointers equal though */ +static int case_insensitive_strcmp(const unsigned char *string1, const unsigned char *string2) +{ + if ((string1 == NULL) || (string2 == NULL)) + { + return 1; + } + + if (string1 == string2) + { + return 0; + } + + for(; tolower(*string1) == tolower(*string2); (void)string1++, string2++) + { + if (*string1 == '\0') + { + return 0; + } + } + + return tolower(*string1) - tolower(*string2); +} + +typedef struct internal_hooks +{ + void *(*allocate)(size_t size); + void (*deallocate)(void *pointer); + void *(*reallocate)(void *pointer, size_t size); +} internal_hooks; + +#if defined(_MSC_VER) +/* work around MSVC error C2322: '...' address of dillimport '...' is not static */ +static void *internal_malloc(size_t size) +{ + return malloc(size); +} +static void internal_free(void *pointer) +{ + free(pointer); +} +static void *internal_realloc(void *pointer, size_t size) +{ + return realloc(pointer, size); +} +#else +#define internal_malloc malloc +#define internal_free free +#define internal_realloc realloc +#endif + +static internal_hooks global_hooks = { internal_malloc, internal_free, internal_realloc }; + +static unsigned char* cJSON_strdup(const unsigned char* string, const internal_hooks * const hooks) +{ + size_t length = 0; + unsigned char *copy = NULL; + + if (string == NULL) + { + return NULL; + } + + length = strlen((const char*)string) + sizeof(""); + copy = (unsigned char*)hooks->allocate(length); + if (copy == NULL) + { + return NULL; + } + memcpy(copy, string, length); + + return copy; +} + +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks) +{ + if (hooks == NULL) + { + /* Reset hooks */ + global_hooks.allocate = malloc; + global_hooks.deallocate = free; + global_hooks.reallocate = realloc; return; } - cJSON_malloc = (hooks->malloc_fn)?hooks->malloc_fn:malloc; - cJSON_free = (hooks->free_fn)?hooks->free_fn:free; + global_hooks.allocate = malloc; + if (hooks->malloc_fn != NULL) + { + global_hooks.allocate = hooks->malloc_fn; + } + + global_hooks.deallocate = free; + if (hooks->free_fn != NULL) + { + global_hooks.deallocate = hooks->free_fn; + } + + /* use realloc only if both free and malloc are used */ + global_hooks.reallocate = NULL; + if ((global_hooks.allocate == malloc) && (global_hooks.deallocate == free)) + { + global_hooks.reallocate = realloc; + } } /* Internal constructor. */ -static cJSON *cJSON_New_Item(void) +static cJSON *cJSON_New_Item(const internal_hooks * const hooks) { - cJSON* node = (cJSON*)cJSON_malloc(sizeof(cJSON)); - if (node) memset(node,0,sizeof(cJSON)); - return node; + cJSON* node = (cJSON*)hooks->allocate(sizeof(cJSON)); + if (node) + { + memset(node, '\0', sizeof(cJSON)); + } + + return node; } /* Delete a cJSON structure. */ -void cJSON_Delete(cJSON *c) +CJSON_PUBLIC(void) cJSON_Delete(cJSON *item) { - cJSON *next; - while (c) - { - next=c->next; - if (!(c->type&cJSON_IsReference) && c->child) cJSON_Delete(c->child); - if (!(c->type&cJSON_IsReference) && c->valuestring) cJSON_free(c->valuestring); - if (c->string) cJSON_free(c->string); - cJSON_free(c); - c=next; - } + cJSON *next = NULL; + while (item != NULL) + { + next = item->next; + if (!(item->type & cJSON_IsReference) && (item->child != NULL)) + { + cJSON_Delete(item->child); + } + if (!(item->type & cJSON_IsReference) && (item->valuestring != NULL)) + { + global_hooks.deallocate(item->valuestring); + } + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + global_hooks.deallocate(item->string); + } + global_hooks.deallocate(item); + item = next; + } } -/* Parse the input text to generate a number, and populate the result into item. */ -static const char *parse_number(cJSON *item,const char *num) +/* get the decimal point character of the current locale */ +static unsigned char get_decimal_point(void) { - double n=0,sign=1,scale=0;int subscale=0,signsubscale=1; +#ifdef ENABLE_LOCALES + struct lconv *lconv = localeconv(); + return (unsigned char) lconv->decimal_point[0]; +#else + return '.'; +#endif +} - if (*num=='-') sign=-1,num++; /* Has sign? */ - if (*num=='0') num++; /* is zero */ - if (*num>='1' && *num<='9') do n=(n*10.0)+(*num++ -'0'); while (*num>='0' && *num<='9'); /* Number? */ - if (*num=='.' && num[1]>='0' && num[1]<='9') {num++; do n=(n*10.0)+(*num++ -'0'),scale--; while (*num>='0' && *num<='9');} /* Fractional part? */ - if (*num=='e' || *num=='E') /* Exponent? */ - { num++;if (*num=='+') num++; else if (*num=='-') signsubscale=-1,num++; /* With sign? */ - while (*num>='0' && *num<='9') subscale=(subscale*10)+(*num++ - '0'); /* Number? */ - } +typedef struct +{ + const unsigned char *content; + size_t length; + size_t offset; + size_t depth; /* How deeply nested (in arrays/objects) is the input at the current offset. */ + internal_hooks hooks; +} parse_buffer; - n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ +/* check if the given size is left to read in a given parse buffer (starting with 1) */ +#define can_read(buffer, size) ((buffer != NULL) && (((buffer)->offset + size) <= (buffer)->length)) +/* check if the buffer can be accessed at the given index (starting with 0) */ +#define can_access_at_index(buffer, index) ((buffer != NULL) && (((buffer)->offset + index) < (buffer)->length)) +#define cannot_access_at_index(buffer, index) (!can_access_at_index(buffer, index)) +/* get a pointer to the buffer at the position */ +#define buffer_at_offset(buffer) ((buffer)->content + (buffer)->offset) - item->valuedouble=n; - item->valueint=(int)n; - item->type=cJSON_Number; - return num; +/* Parse the input text to generate a number, and populate the result into item. */ +static cJSON_bool parse_number(cJSON * const item, parse_buffer * const input_buffer) +{ + double number = 0; + unsigned char *after_end = NULL; + unsigned char number_c_string[64]; + unsigned char decimal_point = get_decimal_point(); + size_t i = 0; + + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; + } + + /* copy the number into a temporary buffer and replace '.' with the decimal point + * of the current locale (for strtod) + * This also takes care of '\0' not necessarily being available for marking the end of the input */ + for (i = 0; (i < (sizeof(number_c_string) - 1)) && can_access_at_index(input_buffer, i); i++) + { + switch (buffer_at_offset(input_buffer)[i]) + { + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + case '+': + case '-': + case 'e': + case 'E': + number_c_string[i] = buffer_at_offset(input_buffer)[i]; + break; + + case '.': + number_c_string[i] = decimal_point; + break; + + default: + goto loop_end; + } + } +loop_end: + number_c_string[i] = '\0'; + + number = strtod((const char*)number_c_string, (char**)&after_end); + if (number_c_string == after_end) + { + return false; /* parse_error */ + } + + item->valuedouble = number; + + /* use saturation in case of overflow */ + if (number >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)number; + } + + item->type = cJSON_Number; + + input_buffer->offset += (size_t)(after_end - number_c_string); + return true; +} + +/* don't ask me, but the original cJSON_SetNumberValue returns an integer or double */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number) +{ + if (number >= INT_MAX) + { + object->valueint = INT_MAX; + } + else if (number <= INT_MIN) + { + object->valueint = INT_MIN; + } + else + { + object->valueint = (int)number; + } + + return object->valuedouble = number; +} + +typedef struct +{ + unsigned char *buffer; + size_t length; + size_t offset; + size_t depth; /* current nesting depth (for formatted printing) */ + cJSON_bool noalloc; + cJSON_bool format; /* is this print a formatted print */ + internal_hooks hooks; +} printbuffer; + +/* realloc printbuffer if necessary to have at least "needed" bytes more */ +static unsigned char* ensure(printbuffer * const p, size_t needed) +{ + unsigned char *newbuffer = NULL; + size_t newsize = 0; + + if ((p == NULL) || (p->buffer == NULL)) + { + return NULL; + } + + if ((p->length > 0) && (p->offset >= p->length)) + { + /* make sure that offset is valid */ + return NULL; + } + + if (needed > INT_MAX) + { + /* sizes bigger than INT_MAX are currently not supported */ + return NULL; + } + + needed += p->offset + 1; + if (needed <= p->length) + { + return p->buffer + p->offset; + } + + if (p->noalloc) { + return NULL; + } + + /* calculate new buffer size */ + if (needed > (INT_MAX / 2)) + { + /* overflow of int, use INT_MAX if possible */ + if (needed <= INT_MAX) + { + newsize = INT_MAX; + } + else + { + return NULL; + } + } + else + { + newsize = needed * 2; + } + + if (p->hooks.reallocate != NULL) + { + /* reallocate with realloc if available */ + newbuffer = (unsigned char*)p->hooks.reallocate(p->buffer, newsize); + if (newbuffer == NULL) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + } + else + { + /* otherwise reallocate manually */ + newbuffer = (unsigned char*)p->hooks.allocate(newsize); + if (!newbuffer) + { + p->hooks.deallocate(p->buffer); + p->length = 0; + p->buffer = NULL; + + return NULL; + } + if (newbuffer) + { + memcpy(newbuffer, p->buffer, p->offset + 1); + } + p->hooks.deallocate(p->buffer); + } + p->length = newsize; + p->buffer = newbuffer; + + return newbuffer + p->offset; +} + +/* calculate the new length of the string in a printbuffer and update the offset */ +static void update_offset(printbuffer * const buffer) +{ + const unsigned char *buffer_pointer = NULL; + if ((buffer == NULL) || (buffer->buffer == NULL)) + { + return; + } + buffer_pointer = buffer->buffer + buffer->offset; + + buffer->offset += strlen((const char*)buffer_pointer); } /* Render the number nicely from the given item into a string. */ -static char *print_number(cJSON *item) +static cJSON_bool print_number(const cJSON * const item, printbuffer * const output_buffer) { - char *str; - double d=item->valuedouble; - if (fabs(((double)item->valueint)-d)<=DBL_EPSILON && d<=INT_MAX && d>=INT_MIN) - { - str=(char*)cJSON_malloc(21); /* 2^64+1 can be represented in 21 chars. */ - if (str) sprintf(str,"%d",item->valueint); - } - else - { - str=(char*)cJSON_malloc(64); /* This is a nice tradeoff. */ - if (str) - { - if (fabs(floor(d)-d)<=DBL_EPSILON && fabs(d)<1.0e60)sprintf(str,"%.0f",d); - else if (fabs(d)<1.0e-6 || fabs(d)>1.0e9) sprintf(str,"%e",d); - else sprintf(str,"%f",d); - } - } - return str; + unsigned char *output_pointer = NULL; + double d = item->valuedouble; + int length = 0; + size_t i = 0; + unsigned char number_buffer[26]; /* temporary buffer to print the number into */ + unsigned char decimal_point = get_decimal_point(); + double test; + + if (output_buffer == NULL) + { + return false; + } + + /* This checks for NaN and Infinity */ + if ((d * 0) != 0) + { + length = sprintf((char*)number_buffer, "null"); + } + else + { + /* Try 15 decimal places of precision to avoid nonsignificant nonzero digits */ + length = sprintf((char*)number_buffer, "%1.15g", d); + + /* Check whether the original double can be recovered */ + if ((sscanf((char*)number_buffer, "%lg", &test) != 1) || ((double)test != d)) + { + /* If not, print with 17 decimal places of precision */ + length = sprintf((char*)number_buffer, "%1.17g", d); + } + } + + /* sprintf failed or buffer overrun occured */ + if ((length < 0) || (length > (int)(sizeof(number_buffer) - 1))) + { + return false; + } + + /* reserve appropriate space in the output */ + output_pointer = ensure(output_buffer, (size_t)length + sizeof("")); + if (output_pointer == NULL) + { + return false; + } + + /* copy the printed number to the output and replace locale + * dependent decimal point with '.' */ + for (i = 0; i < ((size_t)length); i++) + { + if (number_buffer[i] == decimal_point) + { + output_pointer[i] = '.'; + continue; + } + + output_pointer[i] = number_buffer[i]; + } + output_pointer[i] = '\0'; + + output_buffer->offset += (size_t)length; + + return true; } -static unsigned parse_hex4(const char *str) +/* parse 4 digit hexadecimal number */ +static unsigned parse_hex4(const unsigned char * const input) { - unsigned h=0; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - h=h<<4;str++; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - h=h<<4;str++; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - h=h<<4;str++; - if (*str>='0' && *str<='9') h+=(*str)-'0'; else if (*str>='A' && *str<='F') h+=10+(*str)-'A'; else if (*str>='a' && *str<='f') h+=10+(*str)-'a'; else return 0; - return h; + unsigned int h = 0; + size_t i = 0; + + for (i = 0; i < 4; i++) + { + /* parse digit */ + if ((input[i] >= '0') && (input[i] <= '9')) + { + h += (unsigned int) input[i] - '0'; + } + else if ((input[i] >= 'A') && (input[i] <= 'F')) + { + h += (unsigned int) 10 + input[i] - 'A'; + } + else if ((input[i] >= 'a') && (input[i] <= 'f')) + { + h += (unsigned int) 10 + input[i] - 'a'; + } + else /* invalid */ + { + return 0; + } + + if (i < 3) + { + /* shift left to make place for the next nibble */ + h = h << 4; + } + } + + return h; } -/* Parse the input text into an unescaped cstring, and populate item. */ -static const unsigned char firstByteMark[7] = { 0x00, 0x00, 0xC0, 0xE0, 0xF0, 0xF8, 0xFC }; -static const char *parse_string(cJSON *item,const char *str) +/* converts a UTF-16 literal to UTF-8 + * A literal can be one or two sequences of the form \uXXXX */ +static unsigned char utf16_literal_to_utf8(const unsigned char * const input_pointer, const unsigned char * const input_end, unsigned char **output_pointer) { - const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; - if (*str!='\"') {ep=str;return 0;} /* not a string! */ + long unsigned int codepoint = 0; + unsigned int first_code = 0; + const unsigned char *first_sequence = input_pointer; + unsigned char utf8_length = 0; + unsigned char utf8_position = 0; + unsigned char sequence_length = 0; + unsigned char first_byte_mark = 0; - while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ + if ((input_end - first_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } - out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ - if (!out) return 0; + /* get the first utf16 sequence */ + first_code = parse_hex4(first_sequence + 2); - ptr=str+1;ptr2=out; - while (*ptr!='\"' && *ptr) - { - if (*ptr!='\\') *ptr2++=*ptr++; - else - { - ptr++; - switch (*ptr) - { - case 'b': *ptr2++='\b'; break; - case 'f': *ptr2++='\f'; break; - case 'n': *ptr2++='\n'; break; - case 'r': *ptr2++='\r'; break; - case 't': *ptr2++='\t'; break; - case 'u': /* transcode utf16 to utf8. */ - uc=parse_hex4(ptr+1);ptr+=4; /* get the unicode char. */ + /* check that the code is valid */ + if (((first_code >= 0xDC00) && (first_code <= 0xDFFF))) + { + goto fail; + } - if ((uc>=0xDC00 && uc<=0xDFFF) || uc==0) break; /* check for invalid. */ + /* UTF16 surrogate pair */ + if ((first_code >= 0xD800) && (first_code <= 0xDBFF)) + { + const unsigned char *second_sequence = first_sequence + 6; + unsigned int second_code = 0; + sequence_length = 12; /* \uXXXX\uXXXX */ - if (uc>=0xD800 && uc<=0xDBFF) /* UTF16 surrogate pairs. */ - { - if (ptr[1]!='\\' || ptr[2]!='u') break; /* missing second-half of surrogate. */ - uc2=parse_hex4(ptr+3);ptr+=6; - if (uc2<0xDC00 || uc2>0xDFFF) break; /* invalid second-half of surrogate. */ - uc=0x10000 + (((uc&0x3FF)<<10) | (uc2&0x3FF)); - } + if ((input_end - second_sequence) < 6) + { + /* input ends unexpectedly */ + goto fail; + } - len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; + if ((second_sequence[0] != '\\') || (second_sequence[1] != 'u')) + { + /* missing second half of the surrogate pair */ + goto fail; + } -#if defined( __GNUC__ ) && __GNUC__ >= 7// silence warning -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wimplicit-fallthrough" -#endif + /* get the second utf16 sequence */ + second_code = parse_hex4(second_sequence + 2); + /* check that the code is valid */ + if ((second_code < 0xDC00) || (second_code > 0xDFFF)) + { + /* invalid second half of the surrogate pair */ + goto fail; + } - switch (len) { - case 4: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 3: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 2: *--ptr2 =((uc | 0x80) & 0xBF); uc >>= 6; - case 1: *--ptr2 =(uc | firstByteMark[len]); - } -#if defined( __GNUC__ ) && __GNUC__ >= 7 -#pragma GCC diagnostic pop -#endif - ptr2+=len; - break; - default: *ptr2++=*ptr; break; - } - ptr++; - } - } - *ptr2=0; - if (*ptr=='\"') ptr++; - item->valuestring=out; - item->type=cJSON_String; - return ptr; + /* calculate the unicode codepoint from the surrogate pair */ + codepoint = 0x10000 + (((first_code & 0x3FF) << 10) | (second_code & 0x3FF)); + } + else + { + sequence_length = 6; /* \uXXXX */ + codepoint = first_code; + } + + /* encode as UTF-8 + * takes at maximum 4 bytes to encode: + * 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx */ + if (codepoint < 0x80) + { + /* normal ascii, encoding 0xxxxxxx */ + utf8_length = 1; + } + else if (codepoint < 0x800) + { + /* two bytes, encoding 110xxxxx 10xxxxxx */ + utf8_length = 2; + first_byte_mark = 0xC0; /* 11000000 */ + } + else if (codepoint < 0x10000) + { + /* three bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx */ + utf8_length = 3; + first_byte_mark = 0xE0; /* 11100000 */ + } + else if (codepoint <= 0x10FFFF) + { + /* four bytes, encoding 1110xxxx 10xxxxxx 10xxxxxx 10xxxxxx */ + utf8_length = 4; + first_byte_mark = 0xF0; /* 11110000 */ + } + else + { + /* invalid unicode codepoint */ + goto fail; + } + + /* encode as utf8 */ + for (utf8_position = (unsigned char)(utf8_length - 1); utf8_position > 0; utf8_position--) + { + /* 10xxxxxx */ + (*output_pointer)[utf8_position] = (unsigned char)((codepoint | 0x80) & 0xBF); + codepoint >>= 6; + } + /* encode first byte */ + if (utf8_length > 1) + { + (*output_pointer)[0] = (unsigned char)((codepoint | first_byte_mark) & 0xFF); + } + else + { + (*output_pointer)[0] = (unsigned char)(codepoint & 0x7F); + } + + *output_pointer += utf8_length; + + return sequence_length; + +fail: + return 0; +} + +/* Parse the input text into an unescaped cinput, and populate item. */ +static cJSON_bool parse_string(cJSON * const item, parse_buffer * const input_buffer) +{ + const unsigned char *input_pointer = buffer_at_offset(input_buffer) + 1; + const unsigned char *input_end = buffer_at_offset(input_buffer) + 1; + unsigned char *output_pointer = NULL; + unsigned char *output = NULL; + + /* not a string */ + if (buffer_at_offset(input_buffer)[0] != '\"') + { + goto fail; + } + + { + /* calculate approximate size of the output (overestimate) */ + size_t allocation_length = 0; + size_t skipped_bytes = 0; + while (((size_t)(input_end - input_buffer->content) < input_buffer->length) && (*input_end != '\"')) + { + /* is escape sequence */ + if (input_end[0] == '\\') + { + if ((size_t)(input_end + 1 - input_buffer->content) >= input_buffer->length) + { + /* prevent buffer overflow when last input character is a backslash */ + goto fail; + } + skipped_bytes++; + input_end++; + } + input_end++; + } + if (((size_t)(input_end - input_buffer->content) >= input_buffer->length) || (*input_end != '\"')) + { + goto fail; /* string ended unexpectedly */ + } + + /* This is at most how much we need for the output */ + allocation_length = (size_t) (input_end - buffer_at_offset(input_buffer)) - skipped_bytes; + output = (unsigned char*)input_buffer->hooks.allocate(allocation_length + sizeof("")); + if (output == NULL) + { + goto fail; /* allocation failure */ + } + } + + output_pointer = output; + /* loop through the string literal */ + while (input_pointer < input_end) + { + if (*input_pointer != '\\') + { + *output_pointer++ = *input_pointer++; + } + /* escape sequence */ + else + { + unsigned char sequence_length = 2; + if ((input_end - input_pointer) < 1) + { + goto fail; + } + + switch (input_pointer[1]) + { + case 'b': + *output_pointer++ = '\b'; + break; + case 'f': + *output_pointer++ = '\f'; + break; + case 'n': + *output_pointer++ = '\n'; + break; + case 'r': + *output_pointer++ = '\r'; + break; + case 't': + *output_pointer++ = '\t'; + break; + case '\"': + case '\\': + case '/': + *output_pointer++ = input_pointer[1]; + break; + + /* UTF-16 literal */ + case 'u': + sequence_length = utf16_literal_to_utf8(input_pointer, input_end, &output_pointer); + if (sequence_length == 0) + { + /* failed to convert UTF16-literal to UTF-8 */ + goto fail; + } + break; + + default: + goto fail; + } + input_pointer += sequence_length; + } + } + + /* zero terminate the output */ + *output_pointer = '\0'; + + item->type = cJSON_String; + item->valuestring = (char*)output; + + input_buffer->offset = (size_t) (input_end - input_buffer->content); + input_buffer->offset++; + + return true; + +fail: + if (output != NULL) + { + input_buffer->hooks.deallocate(output); + } + + if (input_pointer != NULL) + { + input_buffer->offset = (size_t)(input_pointer - input_buffer->content); + } + + return false; } /* Render the cstring provided to an escaped version that can be printed. */ -static char *print_string_ptr(const char *str) +static cJSON_bool print_string_ptr(const unsigned char * const input, printbuffer * const output_buffer) { - const char *ptr;char *ptr2,*out;int len=0;unsigned char token; + const unsigned char *input_pointer = NULL; + unsigned char *output = NULL; + unsigned char *output_pointer = NULL; + size_t output_length = 0; + /* numbers of additional characters needed for escaping */ + size_t escape_characters = 0; - if (!str) return cJSON_strdup(""); - ptr=str;while ((token=*ptr) && ++len) {if (strchr("\"\\\b\f\n\r\t",token)) len++; else if (token<32) len+=5;ptr++;} + if (output_buffer == NULL) + { + return false; + } - out=(char*)cJSON_malloc(len+3); - if (!out) return 0; + /* empty string */ + if (input == NULL) + { + output = ensure(output_buffer, sizeof("\"\"")); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "\"\""); - ptr2=out;ptr=str; - *ptr2++='\"'; - while (*ptr) - { - if ((unsigned char)*ptr>31 && *ptr!='\"' && *ptr!='\\') *ptr2++=*ptr++; - else - { - *ptr2++='\\'; - switch (token=*ptr++) - { - case '\\': *ptr2++='\\'; break; - case '\"': *ptr2++='\"'; break; - case '\b': *ptr2++='b'; break; - case '\f': *ptr2++='f'; break; - case '\n': *ptr2++='n'; break; - case '\r': *ptr2++='r'; break; - case '\t': *ptr2++='t'; break; - default: sprintf(ptr2,"u%04x",token);ptr2+=5; break; /* escape and print */ - } - } - } - *ptr2++='\"';*ptr2++=0; - return out; + return true; + } + + /* set "flag" to 1 if something needs to be escaped */ + for (input_pointer = input; *input_pointer; input_pointer++) + { + switch (*input_pointer) + { + case '\"': + case '\\': + case '\b': + case '\f': + case '\n': + case '\r': + case '\t': + /* one character escape sequence */ + escape_characters++; + break; + default: + if (*input_pointer < 32) + { + /* UTF-16 escape sequence uXXXX */ + escape_characters += 5; + } + break; + } + } + output_length = (size_t)(input_pointer - input) + escape_characters; + + output = ensure(output_buffer, output_length + sizeof("\"\"")); + if (output == NULL) + { + return false; + } + + /* no characters have to be escaped */ + if (escape_characters == 0) + { + output[0] = '\"'; + memcpy(output + 1, input, output_length); + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; + } + + output[0] = '\"'; + output_pointer = output + 1; + /* copy the string */ + for (input_pointer = input; *input_pointer != '\0'; (void)input_pointer++, output_pointer++) + { + if ((*input_pointer > 31) && (*input_pointer != '\"') && (*input_pointer != '\\')) + { + /* normal character, copy */ + *output_pointer = *input_pointer; + } + else + { + /* character needs to be escaped */ + *output_pointer++ = '\\'; + switch (*input_pointer) + { + case '\\': + *output_pointer = '\\'; + break; + case '\"': + *output_pointer = '\"'; + break; + case '\b': + *output_pointer = 'b'; + break; + case '\f': + *output_pointer = 'f'; + break; + case '\n': + *output_pointer = 'n'; + break; + case '\r': + *output_pointer = 'r'; + break; + case '\t': + *output_pointer = 't'; + break; + default: + /* escape and print as unicode codepoint */ + sprintf((char*)output_pointer, "u%04x", *input_pointer); + output_pointer += 4; + break; + } + } + } + output[output_length + 1] = '\"'; + output[output_length + 2] = '\0'; + + return true; +} + +/* Invoke print_string_ptr (which is useful) on an item. */ +static cJSON_bool print_string(const cJSON * const item, printbuffer * const p) +{ + return print_string_ptr((unsigned char*)item->valuestring, p); } -/* Invote print_string_ptr (which is useful) on an item. */ -static char *print_string(cJSON *item) {return print_string_ptr(item->valuestring);} /* Predeclare these prototypes. */ -static const char *parse_value(cJSON *item,const char *value); -static char *print_value(cJSON *item,int depth,int fmt); -static const char *parse_array(cJSON *item,const char *value); -static char *print_array(cJSON *item,int depth,int fmt); -static const char *parse_object(cJSON *item,const char *value); -static char *print_object(cJSON *item,int depth,int fmt); +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer); +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer); +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer); /* Utility to jump whitespace and cr/lf */ -static const char *skip(const char *in) {while (in && *in && (unsigned char)*in<=32) in++; return in;} +static parse_buffer *buffer_skip_whitespace(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL)) + { + return NULL; + } + + while (can_access_at_index(buffer, 0) && (buffer_at_offset(buffer)[0] <= 32)) + { + buffer->offset++; + } + + if (buffer->offset == buffer->length) + { + buffer->offset--; + } + + return buffer; +} + +/* skip the UTF-8 BOM (byte order mark) if it is at the beginning of a buffer */ +static parse_buffer *skip_utf8_bom(parse_buffer * const buffer) +{ + if ((buffer == NULL) || (buffer->content == NULL) || (buffer->offset != 0)) + { + return NULL; + } + + if (can_access_at_index(buffer, 4) && (strncmp((const char*)buffer_at_offset(buffer), "\xEF\xBB\xBF", 3) == 0)) + { + buffer->offset += 3; + } + + return buffer; +} /* Parse an object - create a new root, and populate. */ -cJSON *cJSON_ParseWithOpts(const char *value,const char **return_parse_end,int require_null_terminated) +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated) { - const char *end=0; - cJSON *c=cJSON_New_Item(); - ep=0; - if (!c) return 0; /* memory fail */ + parse_buffer buffer = { 0, 0, 0, 0, { 0, 0, 0 } }; + cJSON *item = NULL; - end=parse_value(c,skip(value)); - if (!end) {cJSON_Delete(c);return 0;} /* parse failure. ep is set. */ + /* reset error position */ + global_error.json = NULL; + global_error.position = 0; - /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ - if (require_null_terminated) {end=skip(end);if (*end) {cJSON_Delete(c);ep=end;return 0;}} - if (return_parse_end) *return_parse_end=end; - return c; + if (value == NULL) + { + goto fail; + } + + buffer.content = (const unsigned char*)value; + buffer.length = strlen((const char*)value) + sizeof(""); + buffer.offset = 0; + buffer.hooks = global_hooks; + + item = cJSON_New_Item(&global_hooks); + if (item == NULL) /* memory fail */ + { + goto fail; + } + + if (!parse_value(item, buffer_skip_whitespace(skip_utf8_bom(&buffer)))) + { + /* parse failure. ep is set. */ + goto fail; + } + + /* if we require null-terminated JSON without appended garbage, skip and then check for a null terminator */ + if (require_null_terminated) + { + buffer_skip_whitespace(&buffer); + if ((buffer.offset >= buffer.length) || buffer_at_offset(&buffer)[0] != '\0') + { + goto fail; + } + } + if (return_parse_end) + { + *return_parse_end = (const char*)buffer_at_offset(&buffer); + } + + return item; + +fail: + if (item != NULL) + { + cJSON_Delete(item); + } + + if (value != NULL) + { + error local_error; + local_error.json = (const unsigned char*)value; + local_error.position = 0; + + if (buffer.offset < buffer.length) + { + local_error.position = buffer.offset; + } + else if (buffer.length > 0) + { + local_error.position = buffer.length - 1; + } + + if (return_parse_end != NULL) + { + *return_parse_end = (const char*)local_error.json + local_error.position; + } + + global_error = local_error; + } + + return NULL; } + /* Default options for cJSON_Parse */ -cJSON *cJSON_Parse(const char *value) {return cJSON_ParseWithOpts(value,0,0);} +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value) +{ + return cJSON_ParseWithOpts(value, 0, 0); +} + +#define cjson_min(a, b) ((a < b) ? a : b) + +static unsigned char *print(const cJSON * const item, cJSON_bool format, const internal_hooks * const hooks) +{ + static const size_t default_buffer_size = 256; + printbuffer buffer[1]; + unsigned char *printed = NULL; + + memset(buffer, 0, sizeof(buffer)); + + /* create buffer */ + buffer->buffer = (unsigned char*) hooks->allocate(default_buffer_size); + buffer->length = default_buffer_size; + buffer->format = format; + buffer->hooks = *hooks; + if (buffer->buffer == NULL) + { + goto fail; + } + + /* print the value */ + if (!print_value(item, buffer)) + { + goto fail; + } + update_offset(buffer); + + /* check if reallocate is available */ + if (hooks->reallocate != NULL) + { + printed = (unsigned char*) hooks->reallocate(buffer->buffer, buffer->offset + 1); + buffer->buffer = NULL; + if (printed == NULL) { + goto fail; + } + } + else /* otherwise copy the JSON over to a new buffer */ + { + printed = (unsigned char*) hooks->allocate(buffer->offset + 1); + if (printed == NULL) + { + goto fail; + } + memcpy(printed, buffer->buffer, cjson_min(buffer->length, buffer->offset + 1)); + printed[buffer->offset] = '\0'; /* just to be sure */ + + /* free the buffer */ + hooks->deallocate(buffer->buffer); + } + + return printed; + +fail: + if (buffer->buffer != NULL) + { + hooks->deallocate(buffer->buffer); + } + + if (printed != NULL) + { + hooks->deallocate(printed); + } + + return NULL; +} /* Render a cJSON item/entity/structure to text. */ -char *cJSON_Print(cJSON *item) {return print_value(item,0,1);} -char *cJSON_PrintUnformatted(cJSON *item) {return print_value(item,0,0);} +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item) +{ + return (char*)print(item, true, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item) +{ + return (char*)print(item, false, &global_hooks); +} + +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if (prebuffer < 0) + { + return NULL; + } + + p.buffer = (unsigned char*)global_hooks.allocate((size_t)prebuffer); + if (!p.buffer) + { + return NULL; + } + + p.length = (size_t)prebuffer; + p.offset = 0; + p.noalloc = false; + p.format = fmt; + p.hooks = global_hooks; + + if (!print_value(item, &p)) + { + global_hooks.deallocate(p.buffer); + return NULL; + } + + return (char*)p.buffer; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buf, const int len, const cJSON_bool fmt) +{ + printbuffer p = { 0, 0, 0, 0, 0, 0, { 0, 0, 0 } }; + + if ((len < 0) || (buf == NULL)) + { + return false; + } + + p.buffer = (unsigned char*)buf; + p.length = (size_t)len; + p.offset = 0; + p.noalloc = true; + p.format = fmt; + p.hooks = global_hooks; + + return print_value(item, &p); +} /* Parser core - when encountering text, process appropriately. */ -static const char *parse_value(cJSON *item,const char *value) +static cJSON_bool parse_value(cJSON * const item, parse_buffer * const input_buffer) { - if (!value) return 0; /* Fail on null. */ - if (!strncmp(value,"null",4)) { item->type=cJSON_NULL; return value+4; } - if (!strncmp(value,"false",5)) { item->type=cJSON_False; return value+5; } - if (!strncmp(value,"true",4)) { item->type=cJSON_True; item->valueint=1; return value+4; } - if (*value=='\"') { return parse_string(item,value); } - if (*value=='-' || (*value>='0' && *value<='9')) { return parse_number(item,value); } - if (*value=='[') { return parse_array(item,value); } - if (*value=='{') { return parse_object(item,value); } + if ((input_buffer == NULL) || (input_buffer->content == NULL)) + { + return false; /* no input */ + } - ep=value;return 0; /* failure. */ + /* parse the different types of values */ + /* null */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "null", 4) == 0)) + { + item->type = cJSON_NULL; + input_buffer->offset += 4; + return true; + } + /* false */ + if (can_read(input_buffer, 5) && (strncmp((const char*)buffer_at_offset(input_buffer), "false", 5) == 0)) + { + item->type = cJSON_False; + input_buffer->offset += 5; + return true; + } + /* true */ + if (can_read(input_buffer, 4) && (strncmp((const char*)buffer_at_offset(input_buffer), "true", 4) == 0)) + { + item->type = cJSON_True; + item->valueint = 1; + input_buffer->offset += 4; + return true; + } + /* string */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '\"')) + { + return parse_string(item, input_buffer); + } + /* number */ + if (can_access_at_index(input_buffer, 0) && ((buffer_at_offset(input_buffer)[0] == '-') || ((buffer_at_offset(input_buffer)[0] >= '0') && (buffer_at_offset(input_buffer)[0] <= '9')))) + { + return parse_number(item, input_buffer); + } + /* array */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '[')) + { + return parse_array(item, input_buffer); + } + /* object */ + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '{')) + { + return parse_object(item, input_buffer); + } + + return false; } /* Render a value to text. */ -static char *print_value(cJSON *item,int depth,int fmt) +static cJSON_bool print_value(const cJSON * const item, printbuffer * const output_buffer) { - char *out=0; - if (!item) return 0; - switch ((item->type)&255) - { - case cJSON_NULL: out=cJSON_strdup("null"); break; - case cJSON_False: out=cJSON_strdup("false");break; - case cJSON_True: out=cJSON_strdup("true"); break; - case cJSON_Number: out=print_number(item);break; - case cJSON_String: out=print_string(item);break; - case cJSON_Array: out=print_array(item,depth,fmt);break; - case cJSON_Object: out=print_object(item,depth,fmt);break; - } - return out; + unsigned char *output = NULL; + + if ((item == NULL) || (output_buffer == NULL)) + { + return false; + } + + switch ((item->type) & 0xFF) + { + case cJSON_NULL: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "null"); + return true; + + case cJSON_False: + output = ensure(output_buffer, 6); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "false"); + return true; + + case cJSON_True: + output = ensure(output_buffer, 5); + if (output == NULL) + { + return false; + } + strcpy((char*)output, "true"); + return true; + + case cJSON_Number: + return print_number(item, output_buffer); + + case cJSON_Raw: + { + size_t raw_length = 0; + if (item->valuestring == NULL) + { + return false; + } + + raw_length = strlen(item->valuestring) + sizeof(""); + output = ensure(output_buffer, raw_length); + if (output == NULL) + { + return false; + } + memcpy(output, item->valuestring, raw_length); + return true; + } + + case cJSON_String: + return print_string(item, output_buffer); + + case cJSON_Array: + return print_array(item, output_buffer); + + case cJSON_Object: + return print_object(item, output_buffer); + + default: + return false; + } } /* Build an array from input text. */ -static const char *parse_array(cJSON *item,const char *value) +static cJSON_bool parse_array(cJSON * const item, parse_buffer * const input_buffer) { - cJSON *child; - if (*value!='[') {ep=value;return 0;} /* not an array! */ + cJSON *head = NULL; /* head of the linked list */ + cJSON *current_item = NULL; - item->type=cJSON_Array; - value=skip(value+1); - if (*value==']') return value+1; /* empty array. */ + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; - item->child=child=cJSON_New_Item(); - if (!item->child) return 0; /* memory fail */ - value=skip(parse_value(child,skip(value))); /* skip any spacing, get the value. */ - if (!value) return 0; + if (buffer_at_offset(input_buffer)[0] != '[') + { + /* not an array */ + goto fail; + } - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ - child->next=new_item;new_item->prev=child;child=new_item; - value=skip(parse_value(child,skip(value+1))); - if (!value) return 0; /* memory fail */ - } + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ']')) + { + /* empty array */ + goto success; + } - if (*value==']') return value+1; /* end of array */ - ep=value;return 0; /* malformed. */ + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse next value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || buffer_at_offset(input_buffer)[0] != ']') + { + goto fail; /* expected end of array */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Array; + item->child = head; + + input_buffer->offset++; + + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; } /* Render an array to text */ -static char *print_array(cJSON *item,int depth,int fmt) +static cJSON_bool print_array(const cJSON * const item, printbuffer * const output_buffer) { - char **entries; - char *out=0,*ptr,*ret;int len=5; - cJSON *child=item->child; - int numentries=0,i=0,fail=0; + unsigned char *output_pointer = NULL; + size_t length = 0; + cJSON *current_element = item->child; - /* How many entries in the array? */ - while (child) numentries++,child=child->next; - /* Explicitly handle numentries==0 */ - if (!numentries) - { - out=(char*)cJSON_malloc(3); - if (out) strcpy(out,"[]"); - return out; - } - /* Allocate an array to hold the values for each */ - entries=(char**)cJSON_malloc(numentries*sizeof(char*)); - if (!entries) return 0; - memset(entries,0,numentries*sizeof(char*)); - /* Retrieve all the results: */ - child=item->child; - while (child && !fail) - { - ret=print_value(child,depth+1,fmt); - entries[i++]=ret; - if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; - child=child->next; - } + if (output_buffer == NULL) + { + return false; + } - /* If we didn't fail, try to malloc the output string */ - if (!fail) out=(char*)cJSON_malloc(len); - /* If that fails, we fail. */ - if (!out) fail=1; + /* Compose the output array. */ + /* opening square bracket */ + output_pointer = ensure(output_buffer, 1); + if (output_pointer == NULL) + { + return false; + } - /* Handle failure. */ - if (fail) - { - for (i=0;ioffset++; + output_buffer->depth++; - /* Compose the output array. */ - *out='['; - ptr=out+1;*ptr=0; - for (i=0;inext) + { + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ','; + if(output_buffer->format) + { + *output_pointer++ = ' '; + } + *output_pointer = '\0'; + output_buffer->offset += length; + } + current_element = current_element->next; + } + + output_pointer = ensure(output_buffer, 2); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ']'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; } /* Build an object from the text. */ -static const char *parse_object(cJSON *item,const char *value) +static cJSON_bool parse_object(cJSON * const item, parse_buffer * const input_buffer) { - cJSON *child; - if (*value!='{') {ep=value;return 0;} /* not an object! */ + cJSON *head = NULL; /* linked list head */ + cJSON *current_item = NULL; - item->type=cJSON_Object; - value=skip(value+1); - if (*value=='}') return value+1; /* empty array. */ + if (input_buffer->depth >= CJSON_NESTING_LIMIT) + { + return false; /* to deeply nested */ + } + input_buffer->depth++; - item->child=child=cJSON_New_Item(); - if (!item->child) return 0; - value=skip(parse_string(child,skip(value))); - if (!value) return 0; - child->string=child->valuestring;child->valuestring=0; - if (*value!=':') {ep=value;return 0;} /* fail! */ - value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ - if (!value) return 0; + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '{')) + { + goto fail; /* not an object */ + } - while (*value==',') - { - cJSON *new_item; - if (!(new_item=cJSON_New_Item())) return 0; /* memory fail */ - child->next=new_item;new_item->prev=child;child=new_item; - value=skip(parse_string(child,skip(value+1))); - if (!value) return 0; - child->string=child->valuestring;child->valuestring=0; - if (*value!=':') {ep=value;return 0;} /* fail! */ - value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ - if (!value) return 0; - } + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == '}')) + { + goto success; /* empty object */ + } - if (*value=='}') return value+1; /* end of array */ - ep=value;return 0; /* malformed. */ + /* check if we skipped to the end of the buffer */ + if (cannot_access_at_index(input_buffer, 0)) + { + input_buffer->offset--; + goto fail; + } + + /* step back to character in front of the first element */ + input_buffer->offset--; + /* loop through the comma separated array elements */ + do + { + /* allocate next item */ + cJSON *new_item = cJSON_New_Item(&(input_buffer->hooks)); + if (new_item == NULL) + { + goto fail; /* allocation failure */ + } + + /* attach next item to list */ + if (head == NULL) + { + /* start the linked list */ + current_item = head = new_item; + } + else + { + /* add to the end and advance */ + current_item->next = new_item; + new_item->prev = current_item; + current_item = new_item; + } + + /* parse the name of the child */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_string(current_item, input_buffer)) + { + goto fail; /* faile to parse name */ + } + buffer_skip_whitespace(input_buffer); + + /* swap valuestring and string, because we parsed the name */ + current_item->string = current_item->valuestring; + current_item->valuestring = NULL; + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != ':')) + { + goto fail; /* invalid object */ + } + + /* parse the value */ + input_buffer->offset++; + buffer_skip_whitespace(input_buffer); + if (!parse_value(current_item, input_buffer)) + { + goto fail; /* failed to parse value */ + } + buffer_skip_whitespace(input_buffer); + } + while (can_access_at_index(input_buffer, 0) && (buffer_at_offset(input_buffer)[0] == ',')); + + if (cannot_access_at_index(input_buffer, 0) || (buffer_at_offset(input_buffer)[0] != '}')) + { + goto fail; /* expected end of object */ + } + +success: + input_buffer->depth--; + + item->type = cJSON_Object; + item->child = head; + + input_buffer->offset++; + return true; + +fail: + if (head != NULL) + { + cJSON_Delete(head); + } + + return false; } /* Render an object to text. */ -static char *print_object(cJSON *item,int depth,int fmt) +static cJSON_bool print_object(const cJSON * const item, printbuffer * const output_buffer) { - char **entries=0,**names=0; - char *out=0,*ptr,*ret,*str;int len=7,i=0,j; - cJSON *child=item->child; - int numentries=0,fail=0; - /* Count the number of entries. */ - while (child) numentries++,child=child->next; - /* Explicitly handle empty object case */ - if (!numentries) - { - out=(char*)cJSON_malloc(fmt?depth+4:3); - if (!out) return 0; - ptr=out;*ptr++='{'; - if (fmt) {*ptr++='\n';for (i=0;ichild; - /* Collect all the results into our arrays: */ - child=item->child;depth++;if (fmt) len+=depth; - while (child) - { - names[i]=str=print_string_ptr(child->string); - entries[i++]=ret=print_value(child,depth,fmt); - if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; - child=child->next; - } + if (output_buffer == NULL) + { + return false; + } - /* Try to allocate the output string */ - if (!fail) out=(char*)cJSON_malloc(len); - if (!out) fail=1; + /* Compose the output: */ + length = (size_t) (output_buffer->format ? 2 : 1); /* fmt: {\n */ + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } - /* Handle failure */ - if (fail) - { - for (i=0;idepth++; + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + output_buffer->offset += length; - /* Compose the output: */ - *out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0; - for (i=0;iformat) + { + size_t i; + output_pointer = ensure(output_buffer, output_buffer->depth); + if (output_pointer == NULL) + { + return false; + } + for (i = 0; i < output_buffer->depth; i++) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += output_buffer->depth; + } - cJSON_free(names);cJSON_free(entries); - if (fmt) for (i=0;istring, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + length = (size_t) (output_buffer->format ? 2 : 1); + output_pointer = ensure(output_buffer, length); + if (output_pointer == NULL) + { + return false; + } + *output_pointer++ = ':'; + if (output_buffer->format) + { + *output_pointer++ = '\t'; + } + output_buffer->offset += length; + + /* print value */ + if (!print_value(current_item, output_buffer)) + { + return false; + } + update_offset(output_buffer); + + /* print comma if not last */ + length = (size_t) ((output_buffer->format ? 1 : 0) + (current_item->next ? 1 : 0)); + output_pointer = ensure(output_buffer, length + 1); + if (output_pointer == NULL) + { + return false; + } + if (current_item->next) + { + *output_pointer++ = ','; + } + + if (output_buffer->format) + { + *output_pointer++ = '\n'; + } + *output_pointer = '\0'; + output_buffer->offset += length; + + current_item = current_item->next; + } + + output_pointer = ensure(output_buffer, output_buffer->format ? (output_buffer->depth + 1) : 2); + if (output_pointer == NULL) + { + return false; + } + if (output_buffer->format) + { + size_t i; + for (i = 0; i < (output_buffer->depth - 1); i++) + { + *output_pointer++ = '\t'; + } + } + *output_pointer++ = '}'; + *output_pointer = '\0'; + output_buffer->depth--; + + return true; } /* Get Array size/item / object item. */ -int cJSON_GetArraySize(cJSON *array) {cJSON *c=array->child;int i=0;while(c)i++,c=c->next;return i;} -cJSON *cJSON_GetArrayItem(cJSON *array,int item) {cJSON *c=array->child; while (c && item>0) item--,c=c->next; return c;} -cJSON *cJSON_GetObjectItem(cJSON *object,const char *string) {cJSON *c=object->child; while (c && cJSON_strcasecmp(c->string,string)) c=c->next; return c;} +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array) +{ + cJSON *child = NULL; + size_t size = 0; + + if (array == NULL) + { + return 0; + } + + child = array->child; + + while(child != NULL) + { + size++; + child = child->next; + } + + /* FIXME: Can overflow here. Cannot be fixed without breaking the API */ + + return (int)size; +} + +static cJSON* get_array_item(const cJSON *array, size_t index) +{ + cJSON *current_child = NULL; + + if (array == NULL) + { + return NULL; + } + + current_child = array->child; + while ((current_child != NULL) && (index > 0)) + { + index--; + current_child = current_child->next; + } + + return current_child; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index) +{ + if (index < 0) + { + return NULL; + } + + return get_array_item(array, (size_t)index); +} + +static cJSON *get_object_item(const cJSON * const object, const char * const name, const cJSON_bool case_sensitive) +{ + cJSON *current_element = NULL; + + if ((object == NULL) || (name == NULL)) + { + return NULL; + } + + current_element = object->child; + if (case_sensitive) + { + while ((current_element != NULL) && (strcmp(name, current_element->string) != 0)) + { + current_element = current_element->next; + } + } + else + { + while ((current_element != NULL) && (case_insensitive_strcmp((const unsigned char*)name, (const unsigned char*)(current_element->string)) != 0)) + { + current_element = current_element->next; + } + } + + return current_element; +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, false); +} + +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string) +{ + return get_object_item(object, string, true); +} + +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string) +{ + return cJSON_GetObjectItem(object, string) ? 1 : 0; +} /* Utility for array list handling. */ -static void suffix_object(cJSON *prev,cJSON *item) {prev->next=item;item->prev=prev;} +static void suffix_object(cJSON *prev, cJSON *item) +{ + prev->next = item; + item->prev = prev; +} + /* Utility for handling references. */ -static cJSON *create_reference(cJSON *item) {cJSON *ref=cJSON_New_Item();if (!ref) return 0;memcpy(ref,item,sizeof(cJSON));ref->string=0;ref->type|=cJSON_IsReference;ref->next=ref->prev=0;return ref;} +static cJSON *create_reference(const cJSON *item, const internal_hooks * const hooks) +{ + cJSON *reference = NULL; + if (item == NULL) + { + return NULL; + } + + reference = cJSON_New_Item(hooks); + if (reference == NULL) + { + return NULL; + } + + memcpy(reference, item, sizeof(cJSON)); + reference->string = NULL; + reference->type |= cJSON_IsReference; + reference->next = reference->prev = NULL; + return reference; +} + +static cJSON_bool add_item_to_array(cJSON *array, cJSON *item) +{ + cJSON *child = NULL; + + if ((item == NULL) || (array == NULL)) + { + return false; + } + + child = array->child; + + if (child == NULL) + { + /* list is empty, start new one */ + array->child = item; + } + else + { + /* append to the end */ + while (child->next) + { + child = child->next; + } + suffix_object(child, item); + } + + return true; +} /* Add item to array/object. */ -void cJSON_AddItemToArray(cJSON *array, cJSON *item) {cJSON *c=array->child;if (!item) return; if (!c) {array->child=item;} else {while (c && c->next) c=c->next; suffix_object(c,item);}} -void cJSON_AddItemToObject(cJSON *object,const char *string,cJSON *item) {if (!item) return; if (item->string) cJSON_free(item->string);item->string=cJSON_strdup(string);cJSON_AddItemToArray(object,item);} -void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) {cJSON_AddItemToArray(array,create_reference(item));} -void cJSON_AddItemReferenceToObject(cJSON *object,const char *string,cJSON *item) {cJSON_AddItemToObject(object,string,create_reference(item));} - -cJSON *cJSON_DetachItemFromArray(cJSON *array,int which) { - cJSON *c=array->child; - while (c && which>0) c=c->next,which--; - if (!c) return 0; - if (c->prev) c->prev->next=c->next; - if (c->next) c->next->prev=c->prev; - if (c==array->child) array->child=c->next;c->prev=c->next=0; - return c; +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item) +{ + add_item_to_array(array, item); +} + +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic push +#endif +#ifdef __GNUC__ +#pragma GCC diagnostic ignored "-Wcast-qual" +#endif +/* helper function to cast away const */ +static void* cast_away_const(const void* string) +{ + return (void*)string; +} +#if defined(__clang__) || (defined(__GNUC__) && ((__GNUC__ > 4) || ((__GNUC__ == 4) && (__GNUC_MINOR__ > 5)))) + #pragma GCC diagnostic pop +#endif + + +static cJSON_bool add_item_to_object(cJSON * const object, const char * const string, cJSON * const item, const internal_hooks * const hooks, const cJSON_bool constant_key) +{ + char *new_key = NULL; + int new_type = cJSON_Invalid; + + if ((object == NULL) || (string == NULL) || (item == NULL)) + { + return false; + } + + if (constant_key) + { + new_key = (char*)cast_away_const(string); + new_type = item->type | cJSON_StringIsConst; + } + else + { + new_key = (char*)cJSON_strdup((const unsigned char*)string, hooks); + if (new_key == NULL) + { + return false; + } + + new_type = item->type & ~cJSON_StringIsConst; + } + + if (!(item->type & cJSON_StringIsConst) && (item->string != NULL)) + { + hooks->deallocate(item->string); + } + + item->string = new_key; + item->type = new_type; + + return add_item_to_array(object, item); +} + +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, false); +} + +/* Add an item to an object with constant string as key */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item) +{ + add_item_to_object(object, string, item, &global_hooks, true); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item) +{ + if (array == NULL) + { + return; + } + + add_item_to_array(array, create_reference(item, &global_hooks)); +} + +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item) +{ + if ((object == NULL) || (string == NULL)) + { + return; + } + + add_item_to_object(object, string, create_reference(item, &global_hooks), &global_hooks, false); +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name) +{ + cJSON *null = cJSON_CreateNull(); + if (add_item_to_object(object, name, null, &global_hooks, false)) + { + return null; + } + + cJSON_Delete(null); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name) +{ + cJSON *true_item = cJSON_CreateTrue(); + if (add_item_to_object(object, name, true_item, &global_hooks, false)) + { + return true_item; + } + + cJSON_Delete(true_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name) +{ + cJSON *false_item = cJSON_CreateFalse(); + if (add_item_to_object(object, name, false_item, &global_hooks, false)) + { + return false_item; + } + + cJSON_Delete(false_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean) +{ + cJSON *bool_item = cJSON_CreateBool(boolean); + if (add_item_to_object(object, name, bool_item, &global_hooks, false)) + { + return bool_item; + } + + cJSON_Delete(bool_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number) +{ + cJSON *number_item = cJSON_CreateNumber(number); + if (add_item_to_object(object, name, number_item, &global_hooks, false)) + { + return number_item; + } + + cJSON_Delete(number_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string) +{ + cJSON *string_item = cJSON_CreateString(string); + if (add_item_to_object(object, name, string_item, &global_hooks, false)) + { + return string_item; + } + + cJSON_Delete(string_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw) +{ + cJSON *raw_item = cJSON_CreateRaw(raw); + if (add_item_to_object(object, name, raw_item, &global_hooks, false)) + { + return raw_item; + } + + cJSON_Delete(raw_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name) +{ + cJSON *object_item = cJSON_CreateObject(); + if (add_item_to_object(object, name, object_item, &global_hooks, false)) + { + return object_item; + } + + cJSON_Delete(object_item); + return NULL; +} + +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name) +{ + cJSON *array = cJSON_CreateArray(); + if (add_item_to_object(object, name, array, &global_hooks, false)) + { + return array; + } + + cJSON_Delete(array); + return NULL; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item) +{ + if ((parent == NULL) || (item == NULL)) + { + return NULL; + } + + if (item->prev != NULL) + { + /* not the first element */ + item->prev->next = item->next; + } + if (item->next != NULL) + { + /* not the last element */ + item->next->prev = item->prev; + } + + if (item == parent->child) + { + /* first element */ + parent->child = item->next; + } + /* make sure the detached item doesn't point anywhere anymore */ + item->prev = NULL; + item->next = NULL; + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which) +{ + if (which < 0) + { + return NULL; + } + + return cJSON_DetachItemViaPointer(array, get_array_item(array, (size_t)which)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which) +{ + cJSON_Delete(cJSON_DetachItemFromArray(array, which)); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItem(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON *to_detach = cJSON_GetObjectItemCaseSensitive(object, string); + + return cJSON_DetachItemViaPointer(object, to_detach); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObject(object, string)); +} + +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string) +{ + cJSON_Delete(cJSON_DetachItemFromObjectCaseSensitive(object, string)); } -void cJSON_DeleteItemFromArray(cJSON *array,int which) {cJSON_Delete(cJSON_DetachItemFromArray(array,which));} -cJSON *cJSON_DetachItemFromObject(cJSON *object,const char *string) {int i=0;cJSON *c=object->child;while (c && cJSON_strcasecmp(c->string,string)) i++,c=c->next;if (c) return cJSON_DetachItemFromArray(object,i);return 0;} -void cJSON_DeleteItemFromObject(cJSON *object,const char *string) {cJSON_Delete(cJSON_DetachItemFromObject(object,string));} /* Replace array/object items with new ones. */ -void cJSON_ReplaceItemInArray(cJSON *array,int which,cJSON *newitem) {cJSON *c=array->child;while (c && which>0) c=c->next,which--;if (!c) return; - newitem->next=c->next;newitem->prev=c->prev;if (newitem->next) newitem->next->prev=newitem; - if (c==array->child) array->child=newitem; else newitem->prev->next=newitem;c->next=c->prev=0;cJSON_Delete(c);} -void cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem){int i=0;cJSON *c=object->child;while(c && cJSON_strcasecmp(c->string,string))i++,c=c->next;if(c){newitem->string=cJSON_strdup(string);cJSON_ReplaceItemInArray(object,i,newitem);}} +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem) +{ + cJSON *after_inserted = NULL; + + if (which < 0) + { + return; + } + + after_inserted = get_array_item(array, (size_t)which); + if (after_inserted == NULL) + { + add_item_to_array(array, newitem); + return; + } + + newitem->next = after_inserted; + newitem->prev = after_inserted->prev; + after_inserted->prev = newitem; + if (after_inserted == array->child) + { + array->child = newitem; + } + else + { + newitem->prev->next = newitem; + } +} + +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement) +{ + if ((parent == NULL) || (replacement == NULL) || (item == NULL)) + { + return false; + } + + if (replacement == item) + { + return true; + } + + replacement->next = item->next; + replacement->prev = item->prev; + + if (replacement->next != NULL) + { + replacement->next->prev = replacement; + } + if (replacement->prev != NULL) + { + replacement->prev->next = replacement; + } + if (parent->child == item) + { + parent->child = replacement; + } + + item->next = NULL; + item->prev = NULL; + cJSON_Delete(item); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem) +{ + if (which < 0) + { + return; + } + + cJSON_ReplaceItemViaPointer(array, get_array_item(array, (size_t)which), newitem); +} + +static cJSON_bool replace_item_in_object(cJSON *object, const char *string, cJSON *replacement, cJSON_bool case_sensitive) +{ + if ((replacement == NULL) || (string == NULL)) + { + return false; + } + + /* replace the name in the replacement */ + if (!(replacement->type & cJSON_StringIsConst) && (replacement->string != NULL)) + { + cJSON_free(replacement->string); + } + replacement->string = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + replacement->type &= ~cJSON_StringIsConst; + + cJSON_ReplaceItemViaPointer(object, get_object_item(object, string, case_sensitive), replacement); + + return true; +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, false); +} + +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object, const char *string, cJSON *newitem) +{ + replace_item_in_object(object, string, newitem, true); +} /* Create basic types: */ -cJSON *cJSON_CreateNull(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_NULL;return item;} -cJSON *cJSON_CreateTrue(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_True;return item;} -cJSON *cJSON_CreateFalse(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_False;return item;} -cJSON *cJSON_CreateBool(int b) {cJSON *item=cJSON_New_Item();if(item)item->type=b?cJSON_True:cJSON_False;return item;} -cJSON *cJSON_CreateNumber(double num) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_Number;item->valuedouble=num;item->valueint=(int)num;}return item;} -cJSON *cJSON_CreateString(const char *string) {cJSON *item=cJSON_New_Item();if(item){item->type=cJSON_String;item->valuestring=cJSON_strdup(string);}return item;} -cJSON *cJSON_CreateArray(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Array;return item;} -cJSON *cJSON_CreateObject(void) {cJSON *item=cJSON_New_Item();if(item)item->type=cJSON_Object;return item;} +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_NULL; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_True; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool b) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = b ? cJSON_True : cJSON_False; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Number; + item->valuedouble = num; + + /* use saturation in case of overflow */ + if (num >= INT_MAX) + { + item->valueint = INT_MAX; + } + else if (num <= INT_MIN) + { + item->valueint = INT_MIN; + } + else + { + item->valueint = (int)num; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_String; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)string, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) + { + item->type = cJSON_String | cJSON_IsReference; + item->valuestring = (char*)cast_away_const(string); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Object | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child) { + cJSON *item = cJSON_New_Item(&global_hooks); + if (item != NULL) { + item->type = cJSON_Array | cJSON_IsReference; + item->child = (cJSON*)cast_away_const(child); + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type = cJSON_Raw; + item->valuestring = (char*)cJSON_strdup((const unsigned char*)raw, &global_hooks); + if(!item->valuestring) + { + cJSON_Delete(item); + return NULL; + } + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if(item) + { + item->type=cJSON_Array; + } + + return item; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void) +{ + cJSON *item = cJSON_New_Item(&global_hooks); + if (item) + { + item->type = cJSON_Object; + } + + return item; +} /* Create Arrays: */ -cJSON *cJSON_CreateIntArray(const int *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateFloatArray(const float *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateDoubleArray(const double *numbers,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} -cJSON *cJSON_CreateStringArray(const char **strings,int count) {int i;cJSON *n=0,*p=0,*a=cJSON_CreateArray();for(i=0;a && ichild=n;else suffix_object(p,n);p=n;}return a;} +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if (!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber((double)numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (numbers == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for(i = 0;a && (i < (size_t)count); i++) + { + n = cJSON_CreateNumber(numbers[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p, n); + } + p = n; + } + + return a; +} + +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count) +{ + size_t i = 0; + cJSON *n = NULL; + cJSON *p = NULL; + cJSON *a = NULL; + + if ((count < 0) || (strings == NULL)) + { + return NULL; + } + + a = cJSON_CreateArray(); + + for (i = 0; a && (i < (size_t)count); i++) + { + n = cJSON_CreateString(strings[i]); + if(!n) + { + cJSON_Delete(a); + return NULL; + } + if(!i) + { + a->child = n; + } + else + { + suffix_object(p,n); + } + p = n; + } + + return a; +} /* Duplication */ -cJSON *cJSON_Duplicate(cJSON *item,int recurse) +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse) { - cJSON *newitem,*cptr,*nptr=0,*newchild; - /* Bail on bad ptr */ - if (!item) return 0; - /* Create new item */ - newitem=cJSON_New_Item(); - if (!newitem) return 0; - /* Copy over all vars */ - newitem->type=item->type&(~cJSON_IsReference),newitem->valueint=item->valueint,newitem->valuedouble=item->valuedouble; - if (item->valuestring) {newitem->valuestring=cJSON_strdup(item->valuestring); if (!newitem->valuestring) {cJSON_Delete(newitem);return 0;}} - if (item->string) {newitem->string=cJSON_strdup(item->string); if (!newitem->string) {cJSON_Delete(newitem);return 0;}} - /* If non-recursive, then we're done! */ - if (!recurse) return newitem; - /* Walk the ->next chain for the child. */ - cptr=item->child; - while (cptr) - { - newchild=cJSON_Duplicate(cptr,1); /* Duplicate (with recurse) each item in the ->next chain */ - if (!newchild) {cJSON_Delete(newitem);return 0;} - if (nptr) {nptr->next=newchild,newchild->prev=nptr;nptr=newchild;} /* If newitem->child already set, then crosswire ->prev and ->next and move on */ - else {newitem->child=newchild;nptr=newchild;} /* Set newitem->child and move to it */ - cptr=cptr->next; - } - return newitem; + cJSON *newitem = NULL; + cJSON *child = NULL; + cJSON *next = NULL; + cJSON *newchild = NULL; + + /* Bail on bad ptr */ + if (!item) + { + goto fail; + } + /* Create new item */ + newitem = cJSON_New_Item(&global_hooks); + if (!newitem) + { + goto fail; + } + /* Copy over all vars */ + newitem->type = item->type & (~cJSON_IsReference); + newitem->valueint = item->valueint; + newitem->valuedouble = item->valuedouble; + if (item->valuestring) + { + newitem->valuestring = (char*)cJSON_strdup((unsigned char*)item->valuestring, &global_hooks); + if (!newitem->valuestring) + { + goto fail; + } + } + if (item->string) + { + newitem->string = (item->type&cJSON_StringIsConst) ? item->string : (char*)cJSON_strdup((unsigned char*)item->string, &global_hooks); + if (!newitem->string) + { + goto fail; + } + } + /* If non-recursive, then we're done! */ + if (!recurse) + { + return newitem; + } + /* Walk the ->next chain for the child. */ + child = item->child; + while (child != NULL) + { + newchild = cJSON_Duplicate(child, true); /* Duplicate (with recurse) each item in the ->next chain */ + if (!newchild) + { + goto fail; + } + if (next != NULL) + { + /* If newitem->child already set, then crosswire ->prev and ->next and move on */ + next->next = newchild; + newchild->prev = next; + next = newchild; + } + else + { + /* Set newitem->child and move to it */ + newitem->child = newchild; + next = newchild; + } + child = child->next; + } + + return newitem; + +fail: + if (newitem != NULL) + { + cJSON_Delete(newitem); + } + + return NULL; } -void cJSON_Minify(char *json) +CJSON_PUBLIC(void) cJSON_Minify(char *json) { - char *into=json; - while (*json) - { - if (*json==' ') json++; - else if (*json=='\t') json++; // Whitespace characters. - else if (*json=='\r') json++; - else if (*json=='\n') json++; - else if (*json=='/' && json[1]=='/') while (*json && *json!='\n') json++; // double-slash comments, to end of line. - else if (*json=='/' && json[1]=='*') {while (*json && !(*json=='*' && json[1]=='/')) json++;json+=2;} // multiline comments. - else if (*json=='\"'){*into++=*json++;while (*json && *json!='\"'){if (*json=='\\') *into++=*json++;*into++=*json++;}*into++=*json++;} // string literals, which are \" sensitive. - else *into++=*json++; // All other characters. - } - *into=0; // and null-terminate. + unsigned char *into = (unsigned char*)json; + + if (json == NULL) + { + return; + } + + while (*json) + { + if (*json == ' ') + { + json++; + } + else if (*json == '\t') + { + /* Whitespace characters. */ + json++; + } + else if (*json == '\r') + { + json++; + } + else if (*json=='\n') + { + json++; + } + else if ((*json == '/') && (json[1] == '/')) + { + /* double-slash comments, to end of line. */ + while (*json && (*json != '\n')) + { + json++; + } + } + else if ((*json == '/') && (json[1] == '*')) + { + /* multiline comments. */ + while (*json && !((*json == '*') && (json[1] == '/'))) + { + json++; + } + json += 2; + } + else if (*json == '\"') + { + /* string literals, which are \" sensitive. */ + *into++ = (unsigned char)*json++; + while (*json && (*json != '\"')) + { + if (*json == '\\') + { + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + *into++ = (unsigned char)*json++; + } + else + { + /* All other characters. */ + *into++ = (unsigned char)*json++; + } + } + + /* and null-terminate. */ + *into = '\0'; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Invalid; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_False; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xff) == cJSON_True; +} + + +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & (cJSON_True | cJSON_False)) != 0; +} +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_NULL; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Number; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_String; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Array; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Object; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item) +{ + if (item == NULL) + { + return false; + } + + return (item->type & 0xFF) == cJSON_Raw; +} + +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive) +{ + if ((a == NULL) || (b == NULL) || ((a->type & 0xFF) != (b->type & 0xFF)) || cJSON_IsInvalid(a)) + { + return false; + } + + /* check if type is valid */ + switch (a->type & 0xFF) + { + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + case cJSON_Number: + case cJSON_String: + case cJSON_Raw: + case cJSON_Array: + case cJSON_Object: + break; + + default: + return false; + } + + /* identical objects are equal */ + if (a == b) + { + return true; + } + + switch (a->type & 0xFF) + { + /* in these cases and equal type is enough */ + case cJSON_False: + case cJSON_True: + case cJSON_NULL: + return true; + + case cJSON_Number: + if (a->valuedouble == b->valuedouble) + { + return true; + } + return false; + + case cJSON_String: + case cJSON_Raw: + if ((a->valuestring == NULL) || (b->valuestring == NULL)) + { + return false; + } + if (strcmp(a->valuestring, b->valuestring) == 0) + { + return true; + } + + return false; + + case cJSON_Array: + { + cJSON *a_element = a->child; + cJSON *b_element = b->child; + + for (; (a_element != NULL) && (b_element != NULL);) + { + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + + a_element = a_element->next; + b_element = b_element->next; + } + + /* one of the arrays is longer than the other */ + if (a_element != b_element) { + return false; + } + + return true; + } + + case cJSON_Object: + { + cJSON *a_element = NULL; + cJSON *b_element = NULL; + cJSON_ArrayForEach(a_element, a) + { + /* TODO This has O(n^2) runtime, which is horrible! */ + b_element = get_object_item(b, a_element->string, case_sensitive); + if (b_element == NULL) + { + return false; + } + + if (!cJSON_Compare(a_element, b_element, case_sensitive)) + { + return false; + } + } + + /* doing this twice, once on a and b to prevent true comparison if a subset of b + * TODO: Do this the proper way, this is just a fix for now */ + cJSON_ArrayForEach(b_element, b) + { + a_element = get_object_item(a, b_element->string, case_sensitive); + if (a_element == NULL) + { + return false; + } + + if (!cJSON_Compare(b_element, a_element, case_sensitive)) + { + return false; + } + } + + return true; + } + + default: + return false; + } +} + +CJSON_PUBLIC(void *) cJSON_malloc(size_t size) +{ + return global_hooks.allocate(size); +} + +CJSON_PUBLIC(void) cJSON_free(void *object) +{ + global_hooks.deallocate(object); } diff --git a/rtengine/cJSON.h b/rtengine/cJSON.h index e701d8397..a9c68fa2c 100644 --- a/rtengine/cJSON.h +++ b/rtengine/cJSON.h @@ -1,5 +1,5 @@ /* - Copyright (c) 2009 Dave Gamble + Copyright (c) 2009-2017 Dave Gamble and cJSON contributors Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal @@ -28,113 +28,247 @@ extern "C" { #endif +/* project version */ +#define CJSON_VERSION_MAJOR 1 +#define CJSON_VERSION_MINOR 7 +#define CJSON_VERSION_PATCH 5 + +#include + /* cJSON Types: */ -#define cJSON_False 0 -#define cJSON_True 1 -#define cJSON_NULL 2 -#define cJSON_Number 3 -#define cJSON_String 4 -#define cJSON_Array 5 -#define cJSON_Object 6 +#define cJSON_Invalid (0) +#define cJSON_False (1 << 0) +#define cJSON_True (1 << 1) +#define cJSON_NULL (1 << 2) +#define cJSON_Number (1 << 3) +#define cJSON_String (1 << 4) +#define cJSON_Array (1 << 5) +#define cJSON_Object (1 << 6) +#define cJSON_Raw (1 << 7) /* raw json */ #define cJSON_IsReference 256 +#define cJSON_StringIsConst 512 /* The cJSON structure: */ -typedef struct cJSON { - struct cJSON *next, *prev; /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ - struct cJSON *child; /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ +typedef struct cJSON +{ + /* next/prev allow you to walk array/object chains. Alternatively, use GetArraySize/GetArrayItem/GetObjectItem */ + struct cJSON *next; + struct cJSON *prev; + /* An array or object item will have a child pointer pointing to a chain of the items in the array/object. */ + struct cJSON *child; - int type; /* The type of the item, as above. */ + /* The type of the item, as above. */ + int type; - char *valuestring; /* The item's string, if type==cJSON_String */ - int valueint; /* The item's number, if type==cJSON_Number */ - double valuedouble; /* The item's number, if type==cJSON_Number */ + /* The item's string, if type==cJSON_String and type == cJSON_Raw */ + char *valuestring; + /* writing to valueint is DEPRECATED, use cJSON_SetNumberValue instead */ + int valueint; + /* The item's number, if type==cJSON_Number */ + double valuedouble; - char *string; /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + /* The item's name string, if this item is the child of, or is in the list of subitems of an object. */ + char *string; } cJSON; -typedef struct cJSON_Hooks { - void *(*malloc_fn)(size_t sz); - void (*free_fn)(void *ptr); +typedef struct cJSON_Hooks +{ + void *(*malloc_fn)(size_t sz); + void (*free_fn)(void *ptr); } cJSON_Hooks; +typedef int cJSON_bool; + +#if !defined(__WINDOWS__) && (defined(WIN32) || defined(WIN64) || defined(_MSC_VER) || defined(_WIN32)) +#define __WINDOWS__ +#endif +#ifdef __WINDOWS__ + +/* When compiling for windows, we specify a specific calling convention to avoid issues where we are being called from a project with a different default calling convention. For windows you have 2 define options: + +CJSON_HIDE_SYMBOLS - Define this in the case where you don't want to ever dllexport symbols +CJSON_EXPORT_SYMBOLS - Define this on library build when you want to dllexport symbols (default) +CJSON_IMPORT_SYMBOLS - Define this if you want to dllimport symbol + +For *nix builds that support visibility attribute, you can define similar behavior by + +setting default visibility to hidden by adding +-fvisibility=hidden (for gcc) +or +-xldscope=hidden (for sun cc) +to CFLAGS + +then using the CJSON_API_VISIBILITY flag to "export" the same symbols the way CJSON_EXPORT_SYMBOLS does + +*/ + +/* export symbols by default, this is necessary for copy pasting the C and header file */ +#if !defined(CJSON_HIDE_SYMBOLS) && !defined(CJSON_IMPORT_SYMBOLS) && !defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_EXPORT_SYMBOLS +#endif + +#if defined(CJSON_HIDE_SYMBOLS) +#define CJSON_PUBLIC(type) type __stdcall +#elif defined(CJSON_EXPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllexport) type __stdcall +#elif defined(CJSON_IMPORT_SYMBOLS) +#define CJSON_PUBLIC(type) __declspec(dllimport) type __stdcall +#endif +#else /* !WIN32 */ +#if (defined(__GNUC__) || defined(__SUNPRO_CC) || defined (__SUNPRO_C)) && defined(CJSON_API_VISIBILITY) +#define CJSON_PUBLIC(type) __attribute__((visibility("default"))) type +#else +#define CJSON_PUBLIC(type) type +#endif +#endif + +/* Limits how deeply nested arrays/objects can be before cJSON rejects to parse them. + * This is to prevent stack overflows. */ +#ifndef CJSON_NESTING_LIMIT +#define CJSON_NESTING_LIMIT 1000 +#endif + +/* returns the version of cJSON as a string */ +CJSON_PUBLIC(const char*) cJSON_Version(void); + /* Supply malloc, realloc and free functions to cJSON */ -extern void cJSON_InitHooks(cJSON_Hooks* hooks); +CJSON_PUBLIC(void) cJSON_InitHooks(cJSON_Hooks* hooks); +/* Memory Management: the caller is always responsible to free the results from all variants of cJSON_Parse (with cJSON_Delete) and cJSON_Print (with stdlib free, cJSON_Hooks.free_fn, or cJSON_free as appropriate). The exception is cJSON_PrintPreallocated, where the caller has full responsibility of the buffer. */ +/* Supply a block of JSON, and this returns a cJSON object you can interrogate. */ +CJSON_PUBLIC(cJSON *) cJSON_Parse(const char *value); +/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ +/* If you supply a ptr in return_parse_end and parsing fails, then return_parse_end will contain a pointer to the error so will match cJSON_GetErrorPtr(). */ +CJSON_PUBLIC(cJSON *) cJSON_ParseWithOpts(const char *value, const char **return_parse_end, cJSON_bool require_null_terminated); -/* Supply a block of JSON, and this returns a cJSON object you can interrogate. Call cJSON_Delete when finished. */ -extern cJSON *cJSON_Parse(const char *value); -/* Render a cJSON entity to text for transfer/storage. Free the char* when finished. */ -extern char *cJSON_Print(cJSON *item); -/* Render a cJSON entity to text for transfer/storage without any formatting. Free the char* when finished. */ -extern char *cJSON_PrintUnformatted(cJSON *item); +/* Render a cJSON entity to text for transfer/storage. */ +CJSON_PUBLIC(char *) cJSON_Print(const cJSON *item); +/* Render a cJSON entity to text for transfer/storage without any formatting. */ +CJSON_PUBLIC(char *) cJSON_PrintUnformatted(const cJSON *item); +/* Render a cJSON entity to text using a buffered strategy. prebuffer is a guess at the final size. guessing well reduces reallocation. fmt=0 gives unformatted, =1 gives formatted */ +CJSON_PUBLIC(char *) cJSON_PrintBuffered(const cJSON *item, int prebuffer, cJSON_bool fmt); +/* Render a cJSON entity to text using a buffer already allocated in memory with given length. Returns 1 on success and 0 on failure. */ +/* NOTE: cJSON is not always 100% accurate in estimating how much memory it will use, so to be safe allocate 5 bytes more than you actually need */ +CJSON_PUBLIC(cJSON_bool) cJSON_PrintPreallocated(cJSON *item, char *buffer, const int length, const cJSON_bool format); /* Delete a cJSON entity and all subentities. */ -extern void cJSON_Delete(cJSON *c); +CJSON_PUBLIC(void) cJSON_Delete(cJSON *c); /* Returns the number of items in an array (or object). */ -extern int cJSON_GetArraySize(cJSON *array); +CJSON_PUBLIC(int) cJSON_GetArraySize(const cJSON *array); /* Retrieve item number "item" from array "array". Returns NULL if unsuccessful. */ -extern cJSON *cJSON_GetArrayItem(cJSON *array, int item); +CJSON_PUBLIC(cJSON *) cJSON_GetArrayItem(const cJSON *array, int index); /* Get item "string" from object. Case insensitive. */ -extern cJSON *cJSON_GetObjectItem(cJSON *object, const char *string); - +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItem(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON *) cJSON_GetObjectItemCaseSensitive(const cJSON * const object, const char * const string); +CJSON_PUBLIC(cJSON_bool) cJSON_HasObjectItem(const cJSON *object, const char *string); /* For analysing failed parses. This returns a pointer to the parse error. You'll probably need to look a few chars back to make sense of it. Defined when cJSON_Parse() returns 0. 0 when cJSON_Parse() succeeds. */ -extern const char *cJSON_GetErrorPtr(void); +CJSON_PUBLIC(const char *) cJSON_GetErrorPtr(void); + +/* Check if the item is a string and return its valuestring */ +CJSON_PUBLIC(char *) cJSON_GetStringValue(cJSON *item); + +/* These functions check the type of an item */ +CJSON_PUBLIC(cJSON_bool) cJSON_IsInvalid(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsFalse(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsTrue(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsBool(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNull(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsNumber(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsString(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsArray(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsObject(const cJSON * const item); +CJSON_PUBLIC(cJSON_bool) cJSON_IsRaw(const cJSON * const item); /* These calls create a cJSON item of the appropriate type. */ -extern cJSON *cJSON_CreateNull(void); -extern cJSON *cJSON_CreateTrue(void); -extern cJSON *cJSON_CreateFalse(void); -extern cJSON *cJSON_CreateBool(int b); -extern cJSON *cJSON_CreateNumber(double num); -extern cJSON *cJSON_CreateString(const char *string); -extern cJSON *cJSON_CreateArray(void); -extern cJSON *cJSON_CreateObject(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateNull(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateTrue(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateFalse(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateBool(cJSON_bool boolean); +CJSON_PUBLIC(cJSON *) cJSON_CreateNumber(double num); +CJSON_PUBLIC(cJSON *) cJSON_CreateString(const char *string); +/* raw json */ +CJSON_PUBLIC(cJSON *) cJSON_CreateRaw(const char *raw); +CJSON_PUBLIC(cJSON *) cJSON_CreateArray(void); +CJSON_PUBLIC(cJSON *) cJSON_CreateObject(void); + +/* Create a string where valuestring references a string so + * it will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateStringReference(const char *string); +/* Create an object/arrray that only references it's elements so + * they will not be freed by cJSON_Delete */ +CJSON_PUBLIC(cJSON *) cJSON_CreateObjectReference(const cJSON *child); +CJSON_PUBLIC(cJSON *) cJSON_CreateArrayReference(const cJSON *child); /* These utilities create an Array of count items. */ -extern cJSON *cJSON_CreateIntArray(const int *numbers, int count); -extern cJSON *cJSON_CreateFloatArray(const float *numbers, int count); -extern cJSON *cJSON_CreateDoubleArray(const double *numbers, int count); -extern cJSON *cJSON_CreateStringArray(const char **strings, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateIntArray(const int *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateFloatArray(const float *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateDoubleArray(const double *numbers, int count); +CJSON_PUBLIC(cJSON *) cJSON_CreateStringArray(const char **strings, int count); /* Append item to the specified array/object. */ -extern void cJSON_AddItemToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemToObject(cJSON *object, const char *string, cJSON *item); +/* Use this when string is definitely const (i.e. a literal, or as good as), and will definitely survive the cJSON object. + * WARNING: When this function was used, make sure to always check that (item->type & cJSON_StringIsConst) is zero before + * writing to `item->string` */ +CJSON_PUBLIC(void) cJSON_AddItemToObjectCS(cJSON *object, const char *string, cJSON *item); /* Append reference to item to the specified array/object. Use this when you want to add an existing cJSON to a new cJSON, but don't want to corrupt your existing cJSON. */ -extern void cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); -extern void cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToArray(cJSON *array, cJSON *item); +CJSON_PUBLIC(void) cJSON_AddItemReferenceToObject(cJSON *object, const char *string, cJSON *item); -/* Remove/Detach items from Arrays/Objects. */ -extern cJSON *cJSON_DetachItemFromArray(cJSON *array, int which); -extern void cJSON_DeleteItemFromArray(cJSON *array, int which); -extern cJSON *cJSON_DetachItemFromObject(cJSON *object, const char *string); -extern void cJSON_DeleteItemFromObject(cJSON *object, const char *string); +/* Remove/Detatch items from Arrays/Objects. */ +CJSON_PUBLIC(cJSON *) cJSON_DetachItemViaPointer(cJSON *parent, cJSON * const item); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(void) cJSON_DeleteItemFromArray(cJSON *array, int which); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(cJSON *) cJSON_DetachItemFromObjectCaseSensitive(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObject(cJSON *object, const char *string); +CJSON_PUBLIC(void) cJSON_DeleteItemFromObjectCaseSensitive(cJSON *object, const char *string); /* Update array items. */ -extern void cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); -extern void cJSON_ReplaceItemInObject(cJSON *object, const char *string, cJSON *newitem); +CJSON_PUBLIC(void) cJSON_InsertItemInArray(cJSON *array, int which, cJSON *newitem); /* Shifts pre-existing items to the right. */ +CJSON_PUBLIC(cJSON_bool) cJSON_ReplaceItemViaPointer(cJSON * const parent, cJSON * const item, cJSON * replacement); +CJSON_PUBLIC(void) cJSON_ReplaceItemInArray(cJSON *array, int which, cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObject(cJSON *object,const char *string,cJSON *newitem); +CJSON_PUBLIC(void) cJSON_ReplaceItemInObjectCaseSensitive(cJSON *object,const char *string,cJSON *newitem); /* Duplicate a cJSON item */ -extern cJSON *cJSON_Duplicate(cJSON *item, int recurse); +CJSON_PUBLIC(cJSON *) cJSON_Duplicate(const cJSON *item, cJSON_bool recurse); /* Duplicate will create a new, identical cJSON item to the one you pass, in new memory that will need to be released. With recurse!=0, it will duplicate any children connected to the item. The item->next and ->prev pointers are always zero on return from Duplicate. */ +/* Recursively compare two cJSON items for equality. If either a or b is NULL or invalid, they will be considered unequal. + * case_sensitive determines if object keys are treated case sensitive (1) or case insensitive (0) */ +CJSON_PUBLIC(cJSON_bool) cJSON_Compare(const cJSON * const a, const cJSON * const b, const cJSON_bool case_sensitive); -/* ParseWithOpts allows you to require (and check) that the JSON is null terminated, and to retrieve the pointer to the final byte parsed. */ -extern cJSON *cJSON_ParseWithOpts(const char *value, const char **return_parse_end, int require_null_terminated); -extern void cJSON_Minify(char *json); +CJSON_PUBLIC(void) cJSON_Minify(char *json); -/* Macros for creating things quickly. */ -#define cJSON_AddNullToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateNull()) -#define cJSON_AddTrueToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateTrue()) -#define cJSON_AddFalseToObject(object,name) cJSON_AddItemToObject(object, name, cJSON_CreateFalse()) -#define cJSON_AddBoolToObject(object,name,b) cJSON_AddItemToObject(object, name, cJSON_CreateBool(b)) -#define cJSON_AddNumberToObject(object,name,n) cJSON_AddItemToObject(object, name, cJSON_CreateNumber(n)) -#define cJSON_AddStringToObject(object,name,s) cJSON_AddItemToObject(object, name, cJSON_CreateString(s)) +/* Helper functions for creating and adding items to an object at the same time. + * They return the added item or NULL on failure. */ +CJSON_PUBLIC(cJSON*) cJSON_AddNullToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddTrueToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddFalseToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddBoolToObject(cJSON * const object, const char * const name, const cJSON_bool boolean); +CJSON_PUBLIC(cJSON*) cJSON_AddNumberToObject(cJSON * const object, const char * const name, const double number); +CJSON_PUBLIC(cJSON*) cJSON_AddStringToObject(cJSON * const object, const char * const name, const char * const string); +CJSON_PUBLIC(cJSON*) cJSON_AddRawToObject(cJSON * const object, const char * const name, const char * const raw); +CJSON_PUBLIC(cJSON*) cJSON_AddObjectToObject(cJSON * const object, const char * const name); +CJSON_PUBLIC(cJSON*) cJSON_AddArrayToObject(cJSON * const object, const char * const name); /* When assigning an integer value, it needs to be propagated to valuedouble too. */ -#define cJSON_SetIntValue(object,val) ((object)?(object)->valueint=(object)->valuedouble=(val):(val)) +#define cJSON_SetIntValue(object, number) ((object) ? (object)->valueint = (object)->valuedouble = (number) : (number)) +/* helper for the cJSON_SetNumberValue macro */ +CJSON_PUBLIC(double) cJSON_SetNumberHelper(cJSON *object, double number); +#define cJSON_SetNumberValue(object, number) ((object != NULL) ? cJSON_SetNumberHelper(object, (double)number) : (number)) + +/* Macro for iterating over an array or object */ +#define cJSON_ArrayForEach(element, array) for(element = (array != NULL) ? (array)->child : NULL; element != NULL; element = element->next) + +/* malloc/free objects using the malloc/free functions that have been set with cJSON_InitHooks */ +CJSON_PUBLIC(void *) cJSON_malloc(size_t size); +CJSON_PUBLIC(void) cJSON_free(void *object); #ifdef __cplusplus } From 69c38b4518640c15d28559a1d3d68aae199dd172 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Wed, 28 Mar 2018 16:08:38 +0200 Subject: [PATCH 12/12] Input profile selection does not display hidden folders on Windows, fixes #4475 --- rtgui/icmpanel.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/rtgui/icmpanel.cc b/rtgui/icmpanel.cc index bb9677d1c..f71ff354b 100644 --- a/rtgui/icmpanel.cc +++ b/rtgui/icmpanel.cc @@ -297,6 +297,10 @@ ICMPanel::ICMPanel () : FoldableToolPanel(this, "icm", M("TP_ICM_LABEL")), iunch ipDialog->add_filter (filter_icc); ipDialog->add_filter (filter_iccdng); ipDialog->add_filter (filter_any); +#ifdef WIN32 + ipDialog->set_show_hidden(true); // ProgramData is hidden on Windows +#endif + oldip = "";