From bc8b8902e643fcf2f0771b42b1badd6da902ca9e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Mon, 20 Nov 2017 00:10:51 +0100 Subject: [PATCH 01/19] make rtengine::processImage return an Imagefloat instead of an Image16 This is for supporting saving to 32-bit float TIFFs --- rtengine/image16.cc | 80 +++++++++++++++++------------------ rtengine/image16.h | 2 +- rtengine/imagefloat.cc | 62 +++++++++++++++++++++++++++ rtengine/imagefloat.h | 1 + rtengine/improccoordinator.cc | 15 +++---- rtengine/improcfun.h | 6 +-- rtengine/iplab2rgb.cc | 13 +++--- rtengine/ipresize.cc | 10 ++--- rtengine/rtengine.h | 4 +- rtengine/simpleprocess.cc | 20 ++++----- rtgui/batchqueue.cc | 2 +- rtgui/batchqueue.h | 2 +- rtgui/editorpanel.cc | 26 ++++++------ rtgui/editorpanel.h | 8 ++-- rtgui/main-cli.cc | 2 +- 15 files changed, 157 insertions(+), 96 deletions(-) diff --git a/rtengine/image16.cc b/rtengine/image16.cc index e0f7470a6..1bd0680d0 100644 --- a/rtengine/image16.cc +++ b/rtengine/image16.cc @@ -325,50 +325,50 @@ Image16::tofloat() return imgfloat; } -// Parallized transformation; create transform with cmsFLAGS_NOCACHE! -void Image16::ExecCMSTransform(cmsHTRANSFORM hTransform, const LabImage &labImage, int cx, int cy) -{ - // LittleCMS cannot parallelize planar Lab float images - // so build temporary buffers to allow multi processor execution -#ifdef _OPENMP - #pragma omp parallel -#endif - { - AlignedBuffer bufferLab(width * 3); - AlignedBuffer bufferRGB(width * 3); +// // Parallized transformation; create transform with cmsFLAGS_NOCACHE! +// void Image16::ExecCMSTransform(cmsHTRANSFORM hTransform, const LabImage &labImage, int cx, int cy) +// { +// // LittleCMS cannot parallelize planar Lab float images +// // so build temporary buffers to allow multi processor execution +// #ifdef _OPENMP +// #pragma omp parallel +// #endif +// { +// AlignedBuffer bufferLab(width * 3); +// AlignedBuffer bufferRGB(width * 3); -#ifdef _OPENMP - #pragma omp for schedule(static) -#endif +// #ifdef _OPENMP +// #pragma omp for schedule(static) +// #endif - for (int y = cy; y < cy + height; y++) - { - unsigned short *pRGB, *pR, *pG, *pB; - float *pLab, *pL, *pa, *pb; +// for (int y = cy; y < cy + height; y++) +// { +// unsigned short *pRGB, *pR, *pG, *pB; +// float *pLab, *pL, *pa, *pb; - pLab= bufferLab.data; - pL = labImage.L[y] + cx; - pa = labImage.a[y] + cx; - pb = labImage.b[y] + cx; +// pLab= bufferLab.data; +// pL = labImage.L[y] + cx; +// pa = labImage.a[y] + cx; +// pb = labImage.b[y] + cx; - for (int x = 0; x < width; x++) { - *(pLab++) = *(pL++) / 327.68f; - *(pLab++) = *(pa++) / 327.68f; - *(pLab++) = *(pb++) / 327.68f; - } +// for (int x = 0; x < width; x++) { +// *(pLab++) = *(pL++) / 327.68f; +// *(pLab++) = *(pa++) / 327.68f; +// *(pLab++) = *(pb++) / 327.68f; +// } - cmsDoTransform (hTransform, bufferLab.data, bufferRGB.data, width); +// cmsDoTransform (hTransform, bufferLab.data, bufferRGB.data, width); - pRGB = bufferRGB.data; - pR = r(y - cy); - pG = g(y - cy); - pB = b(y - cy); +// pRGB = bufferRGB.data; +// pR = r(y - cy); +// pG = g(y - cy); +// pB = b(y - cy); - for (int x = 0; x < width; x++) { - *(pR++) = *(pRGB++); - *(pG++) = *(pRGB++); - *(pB++) = *(pRGB++); - } - } // End of parallelization - } -} +// for (int x = 0; x < width; x++) { +// *(pR++) = *(pRGB++); +// *(pG++) = *(pRGB++); +// *(pB++) = *(pRGB++); +// } +// } // End of parallelization +// } +// } diff --git a/rtengine/image16.h b/rtengine/image16.h index 4d74dfbc4..ee402d556 100644 --- a/rtengine/image16.h +++ b/rtengine/image16.h @@ -96,7 +96,7 @@ public: delete this; } - void ExecCMSTransform(cmsHTRANSFORM hTransform, const LabImage &labImage, int cx, int cy); + /* void ExecCMSTransform(cmsHTRANSFORM hTransform, const LabImage &labImage, int cx, int cy); */ }; } diff --git a/rtengine/imagefloat.cc b/rtengine/imagefloat.cc index 30871c9b1..41b10552a 100644 --- a/rtengine/imagefloat.cc +++ b/rtengine/imagefloat.cc @@ -154,11 +154,25 @@ void Imagefloat::getScanline (int row, unsigned char* buffer, int bps) int ix = 0; float* sbuffer = (float*) buffer; + // agriggio -- assume the image is normalized to [0, 65535] for (int i = 0; i < width; i++) { + sbuffer[ix++] = r(row, i) / 65535.f; + sbuffer[ix++] = g(row, i) / 65535.f; + sbuffer[ix++] = b(row, i) / 65535.f; + } + } else if (bps == 16) { + unsigned short *sbuffer = (unsigned short *)buffer; + for (int i = 0, ix = 0; i < width; i++) { sbuffer[ix++] = r(row, i); sbuffer[ix++] = g(row, i); sbuffer[ix++] = b(row, i); } + } else if (bps == 8) { + for (int i = 0, ix = 0; i < width; i++) { + buffer[ix++] = rtengine::uint16ToUint8Rounded(r(row, i)); + buffer[ix++] = rtengine::uint16ToUint8Rounded(g(row, i)); + buffer[ix++] = rtengine::uint16ToUint8Rounded(b(row, i)); + } } } @@ -511,3 +525,51 @@ void Imagefloat::ExecCMSTransform(cmsHTRANSFORM hTransform) } // End of parallelization } } + +// Parallized transformation; create transform with cmsFLAGS_NOCACHE! +void Imagefloat::ExecCMSTransform(cmsHTRANSFORM hTransform, const LabImage &labImage, int cx, int cy) +{ + // LittleCMS cannot parallelize planar Lab float images + // so build temporary buffers to allow multi processor execution +#ifdef _OPENMP + #pragma omp parallel +#endif + { + AlignedBuffer bufferLab(width * 3); + AlignedBuffer bufferRGB(width * 3); + +#ifdef _OPENMP + #pragma omp for schedule(static) +#endif + + for (int y = cy; y < cy + height; y++) + { + float *pRGB, *pR, *pG, *pB; + float *pLab, *pL, *pa, *pb; + + pLab= bufferLab.data; + pL = labImage.L[y] + cx; + pa = labImage.a[y] + cx; + pb = labImage.b[y] + cx; + + for (int x = 0; x < width; x++) { + *(pLab++) = *(pL++) / 327.68f; + *(pLab++) = *(pa++) / 327.68f; + *(pLab++) = *(pb++) / 327.68f; + } + + cmsDoTransform (hTransform, bufferLab.data, bufferRGB.data, width); + + pRGB = bufferRGB.data; + pR = r(y - cy); + pG = g(y - cy); + pB = b(y - cy); + + for (int x = 0; x < width; x++) { + *(pR++) = *(pRGB++); + *(pG++) = *(pRGB++); + *(pB++) = *(pRGB++); + } + } // End of parallelization + } +} diff --git a/rtengine/imagefloat.h b/rtengine/imagefloat.h index 7348588df..753406d25 100644 --- a/rtengine/imagefloat.h +++ b/rtengine/imagefloat.h @@ -106,6 +106,7 @@ public: void calcCroppedHistogram(const ProcParams ¶ms, float scale, LUTu & hist); void ExecCMSTransform(cmsHTRANSFORM hTransform); + void ExecCMSTransform(cmsHTRANSFORM hTransform, const LabImage &labImage, int cx, int cy); }; } diff --git a/rtengine/improccoordinator.cc b/rtengine/improccoordinator.cc index 96710ee63..976676fc3 100644 --- a/rtengine/improccoordinator.cc +++ b/rtengine/improccoordinator.cc @@ -1270,21 +1270,18 @@ void ImProcCoordinator::saveInputICCReference (const Glib::ustring& fname, bool } } - Image16* im16 = im->to16(); - delete im; - int imw, imh; double tmpScale = ipf.resizeScale (¶ms, fW, fH, imw, imh); if (tmpScale != 1.0) { - Image16* tempImage = new Image16 (imw, imh); - ipf.resize (im16, tempImage, tmpScale); - delete im16; - im16 = tempImage; + Imagefloat* tempImage = new Imagefloat (imw, imh); + ipf.resize (im, tempImage, tmpScale); + delete im; + im = tempImage; } - im16->saveTIFF (fname, 16, true); - delete im16; + im->saveTIFF (fname, 16, true); + delete im; if (plistener) { plistener->setProgressState (false); diff --git a/rtengine/improcfun.h b/rtengine/improcfun.h index 08c3fa4cc..74ae1e754 100644 --- a/rtengine/improcfun.h +++ b/rtengine/improcfun.h @@ -239,9 +239,9 @@ public: void transform (Imagefloat* original, Imagefloat* transformed, int cx, int cy, int sx, int sy, int oW, int oH, int fW, int fH, const FramesMetaData *metadata, int rawRotationDeg, bool fullImage); float resizeScale (const ProcParams* params, int fw, int fh, int &imw, int &imh); void lab2monitorRgb (LabImage* lab, Image8* image); - void resize (Image16* src, Image16* dst, float dScale); + void resize (Imagefloat* src, Imagefloat* dst, float dScale); void Lanczos (const LabImage* src, LabImage* dst, float scale); - void Lanczos (const Image16* src, Image16* dst, float scale); + void Lanczos (const Imagefloat* src, Imagefloat* dst, float scale); void deconvsharpening (float** luminance, float** buffer, int W, int H, const SharpeningParams &sharpenParam); void MLsharpen (LabImage* lab);// Manuel's clarity / sharpening @@ -347,7 +347,7 @@ public: void ToneMapFattal02(Imagefloat *rgb); Image8* lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm); - Image16* lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, GammaValues *ga = nullptr); + Imagefloat* lab2rgbOut (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, GammaValues *ga = nullptr); // CieImage *ciec; bool transCoord (int W, int H, int x, int y, int w, int h, int& xv, int& yv, int& wv, int& hv, double ascaleDef = -1, const LensCorrection *pLCPMap = nullptr); diff --git a/rtengine/iplab2rgb.cc b/rtengine/iplab2rgb.cc index abef0a878..ba2fe6ffb 100644 --- a/rtengine/iplab2rgb.cc +++ b/rtengine/iplab2rgb.cc @@ -262,7 +262,7 @@ Image8* ImProcFunctions::lab2rgb (LabImage* lab, int cx, int cy, int cw, int ch, * If a custom gamma profile can be created, divide by 327.68, convert to xyz and apply the custom gamma transform * otherwise divide by 327.68, convert to xyz and apply the sRGB transform, before converting with gamma2curve */ -Image16* ImProcFunctions::lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, GammaValues *ga) +Imagefloat* ImProcFunctions::lab2rgbOut (LabImage* lab, int cx, int cy, int cw, int ch, const procparams::ColorManagementParams &icm, GammaValues *ga) { if (cx < 0) { @@ -281,7 +281,7 @@ Image16* ImProcFunctions::lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int ch = lab->H - cy; } - Image16* image = new Image16 (cw, ch); + Imagefloat* image = new Imagefloat (cw, ch); cmsHPROFILE oprof = nullptr; if (ga) { @@ -300,11 +300,12 @@ Image16* ImProcFunctions::lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int } lcmsMutex->lock (); cmsHPROFILE iprof = cmsCreateLab4Profile(nullptr); - cmsHTRANSFORM hTransform = cmsCreateTransform (iprof, TYPE_Lab_FLT, oprof, TYPE_RGB_16, icm.outputIntent, flags); + cmsHTRANSFORM hTransform = cmsCreateTransform (iprof, TYPE_Lab_FLT, oprof, TYPE_RGB_FLT, icm.outputIntent, flags); lcmsMutex->unlock (); image->ExecCMSTransform(hTransform, *lab, cx, cy); cmsDeleteTransform(hTransform); + image->normalizeFloatTo65535(); } else { #ifdef _OPENMP #pragma omp parallel for schedule(dynamic,16) if (multiThread) @@ -329,9 +330,9 @@ Image16* ImProcFunctions::lab2rgb16 (LabImage* lab, int cx, int cy, int cw, int Color::xyz2srgb(x_, y_, z_, R, G, B); - image->r(i - cy, j - cx) = (int)Color::gamma2curve[CLIP(R)]; - image->g(i - cy, j - cx) = (int)Color::gamma2curve[CLIP(G)]; - image->b(i - cy, j - cx) = (int)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/ipresize.cc b/rtengine/ipresize.cc index 644e180c7..1a48e5c43 100644 --- a/rtengine/ipresize.cc +++ b/rtengine/ipresize.cc @@ -46,7 +46,7 @@ static inline float Lanc (float x, float a) } } -void ImProcFunctions::Lanczos (const Image16* src, Image16* dst, float scale) +void ImProcFunctions::Lanczos (const Imagefloat* src, Imagefloat* dst, float scale) { const float delta = 1.0f / scale; @@ -159,9 +159,9 @@ void ImProcFunctions::Lanczos (const Image16* src, Image16* dst, float scale) b += wh[k] * lb[jj]; } - dst->r (i, j) = CLIP (static_cast (r)); - dst->g (i, j) = CLIP (static_cast (g)); - dst->b (i, j) = CLIP (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)); } } @@ -396,7 +396,7 @@ float ImProcFunctions::resizeScale (const ProcParams* params, int fw, int fh, in return (float)dScale; } -void ImProcFunctions::resize (Image16* src, Image16* dst, float dScale) +void ImProcFunctions::resize (Imagefloat* src, Imagefloat* dst, float dScale) { #ifdef PROFILE time_t t1 = clock(); diff --git a/rtengine/rtengine.h b/rtengine/rtengine.h index edf903352..53ed06385 100644 --- a/rtengine/rtengine.h +++ b/rtengine/rtengine.h @@ -534,7 +534,7 @@ public: * @param pl is an optional ProgressListener if you want to keep track of the progress * @param tunnelMetaData tunnels IPTC and XMP to output without change * @return the resulting image, with the output profile applied, exif and iptc data set. You have to save it or you can access the pixel data directly. */ -IImage16* processImage (ProcessingJob* job, int& errorCode, ProgressListener* pl = nullptr, bool tunnelMetaData = false, bool flush = false); +IImagefloat* processImage (ProcessingJob* job, int& errorCode, ProgressListener* pl = nullptr, bool tunnelMetaData = false, bool flush = false); /** This class is used to control the batch processing. The class implementing this interface will be called when the full processing of an * image is ready and the next job to process is needed. */ @@ -545,7 +545,7 @@ public: * there is no jobs left. * @param img is the result of the last ProcessingJob * @return the next ProcessingJob to process */ - virtual ProcessingJob* imageReady (IImage16* img) = 0; + virtual ProcessingJob* imageReady (IImagefloat* img) = 0; virtual void error (Glib::ustring message) = 0; }; /** This function performs all the image processinf steps corresponding to the given ProcessingJob. It runs in the background, thus it returns immediately, diff --git a/rtengine/simpleprocess.cc b/rtengine/simpleprocess.cc index dc285ce63..45595a379 100644 --- a/rtengine/simpleprocess.cc +++ b/rtengine/simpleprocess.cc @@ -66,7 +66,7 @@ public: { } - Image16 *operator()() + Imagefloat *operator()() { if (!job->fast) { return normal_pipeline(); @@ -76,7 +76,7 @@ public: } private: - Image16 *normal_pipeline() + Imagefloat *normal_pipeline() { if (!stage_init()) { return nullptr; @@ -87,7 +87,7 @@ private: return stage_finish(); } - Image16 *fast_pipeline() + Imagefloat *fast_pipeline() { if (!job->pparams.resize.enabled) { return normal_pipeline(); @@ -831,7 +831,7 @@ private: } } - Image16 *stage_finish() + Imagefloat *stage_finish() { procparams::ProcParams& params = job->pparams; //ImProcFunctions ipf (¶ms, true); @@ -1227,7 +1227,7 @@ private: } } - Image16* readyImg = nullptr; + Imagefloat* readyImg = nullptr; cmsHPROFILE jprof = nullptr; bool customGamma = false; bool useLCMS = false; @@ -1237,7 +1237,7 @@ private: GammaValues ga; // if(params.blackwhite.enabled) params.toneCurve.hrenabled=false; - readyImg = ipf.lab2rgb16 (labView, cx, cy, cw, ch, params.icm, &ga); + readyImg = ipf.lab2rgbOut (labView, cx, cy, cw, ch, params.icm, &ga); customGamma = true; //or selected Free gamma @@ -1251,7 +1251,7 @@ private: // if Default gamma mode: we use the profile selected in the "Output profile" combobox; // gamma come from the selected profile, otherwise it comes from "Free gamma" tool - readyImg = ipf.lab2rgb16 (labView, cx, cy, cw, ch, params.icm); + readyImg = ipf.lab2rgbOut (labView, cx, cy, cw, ch, params.icm); if (settings->verbose) { printf ("Output profile_: \"%s\"\n", params.icm.output.c_str()); @@ -1281,7 +1281,7 @@ private: } if (tmpScale != 1.0 && params.resize.method == "Nearest") { // resize rgb data (gamma applied) - Image16* tempImage = new Image16 (imw, imh); + Imagefloat* tempImage = new Imagefloat (imw, imh); ipf.resize (readyImg, tempImage, tmpScale); delete readyImg; readyImg = tempImage; @@ -1566,7 +1566,7 @@ private: } // namespace -IImage16* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* pl, bool tunnelMetaData, bool flush) +IImagefloat* processImage (ProcessingJob* pjob, int& errorCode, ProgressListener* pl, bool tunnelMetaData, bool flush) { ImageProcessor proc (pjob, errorCode, pl, tunnelMetaData, flush); return proc(); @@ -1579,7 +1579,7 @@ void batchProcessingThread (ProcessingJob* job, BatchProcessingListener* bpl, bo while (currentJob) { int errorCode; - IImage16* img = processImage (currentJob, errorCode, bpl, tunnelMetaData, true); + IImagefloat* img = processImage (currentJob, errorCode, bpl, tunnelMetaData, true); if (errorCode) { bpl->error (M ("MAIN_MSG_CANNOTLOAD")); diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index 43ee5d79d..a2409a39f 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -578,7 +578,7 @@ void BatchQueue::startProcessing () } } -rtengine::ProcessingJob* BatchQueue::imageReady (rtengine::IImage16* img) +rtengine::ProcessingJob* BatchQueue::imageReady (rtengine::IImagefloat* img) { // save image img diff --git a/rtgui/batchqueue.h b/rtgui/batchqueue.h index 68373838e..0d91542b6 100644 --- a/rtgui/batchqueue.h +++ b/rtgui/batchqueue.h @@ -62,7 +62,7 @@ public: return (!fd.empty()); } - rtengine::ProcessingJob* imageReady (rtengine::IImage16* img); + rtengine::ProcessingJob* imageReady (rtengine::IImagefloat* img); void error (Glib::ustring msg); void setProgress (double p); void rightClicked (ThumbBrowserEntryBase* entry); diff --git a/rtgui/editorpanel.cc b/rtgui/editorpanel.cc index c2bec8692..7099007b6 100644 --- a/rtgui/editorpanel.cc +++ b/rtgui/editorpanel.cc @@ -1751,9 +1751,9 @@ void EditorPanel::procParamsChanged (Thumbnail* thm, int whoChangedIt) } } -bool EditorPanel::idle_saveImage (ProgressConnector *pc, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams) +bool EditorPanel::idle_saveImage (ProgressConnector *pc, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams) { - rtengine::IImage16* img = pc->returnValue(); + rtengine::IImagefloat* img = pc->returnValue(); delete pc; if ( img ) { @@ -1764,13 +1764,13 @@ bool EditorPanel::idle_saveImage (ProgressConnector *pc, Gl img->setSaveProgressListener (parent->getProgressListener()); if (sf.format == "tif") - ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImage16::saveAsTIFF), fname, sf.tiffBits, sf.tiffUncompressed), + ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsTIFF), fname, sf.tiffBits, sf.tiffUncompressed), sigc::bind (sigc::mem_fun (*this, &EditorPanel::idle_imageSaved), ld, img, fname, sf, pparams)); else if (sf.format == "png") - ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImage16::saveAsPNG), fname, sf.pngBits), + ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsPNG), fname, sf.pngBits), sigc::bind (sigc::mem_fun (*this, &EditorPanel::idle_imageSaved), ld, img, fname, sf, pparams)); else if (sf.format == "jpg") - ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImage16::saveAsJPEG), fname, sf.jpegQuality, sf.jpegSubSamp), + ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsJPEG), fname, sf.jpegQuality, sf.jpegSubSamp), sigc::bind (sigc::mem_fun (*this, &EditorPanel::idle_imageSaved), ld, img, fname, sf, pparams)); else { delete ld; @@ -1791,7 +1791,7 @@ bool EditorPanel::idle_saveImage (ProgressConnector *pc, Gl return false; } -bool EditorPanel::idle_imageSaved (ProgressConnector *pc, rtengine::IImage16* img, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams) +bool EditorPanel::idle_imageSaved (ProgressConnector *pc, rtengine::IImagefloat* img, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams) { img->free (); @@ -1916,7 +1916,7 @@ void EditorPanel::saveAsPressed () ipc->getParams (&pparams); rtengine::ProcessingJob* job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams); - ProgressConnector *ld = new ProgressConnector(); + ProgressConnector *ld = new ProgressConnector(); ld->startFunc (sigc::bind (sigc::ptr_fun (&rtengine::processImage), job, err, parent->getProgressListener(), options.tunnelMetaData, false ), sigc::bind (sigc::mem_fun ( *this, &EditorPanel::idle_saveImage ), ld, fnameOut, sf, pparams)); saveimgas->set_sensitive (false); @@ -1960,7 +1960,7 @@ void EditorPanel::sendToGimpPressed () rtengine::procparams::ProcParams pparams; ipc->getParams (&pparams); rtengine::ProcessingJob* job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams); - ProgressConnector *ld = new ProgressConnector(); + ProgressConnector *ld = new ProgressConnector(); ld->startFunc (sigc::bind (sigc::ptr_fun (&rtengine::processImage), job, err, parent->getProgressListener(), options.tunnelMetaData, false ), sigc::bind (sigc::mem_fun ( *this, &EditorPanel::idle_sendToGimp ), ld, openThm->getFileName() )); saveimgas->set_sensitive (false); @@ -1975,7 +1975,7 @@ bool EditorPanel::saveImmediately (const Glib::ustring &filename, const SaveForm rtengine::ProcessingJob *job = rtengine::ProcessingJob::create (ipc->getInitialImage(), pparams); // save immediately - rtengine::IImage16 *img = rtengine::processImage (job, err, nullptr, options.tunnelMetaData, false); + rtengine::IImagefloat *img = rtengine::processImage (job, err, nullptr, options.tunnelMetaData, false); int err = 0; @@ -2015,10 +2015,10 @@ void EditorPanel::syncFileBrowser() // synchronize filebrowser with image in E } } -bool EditorPanel::idle_sendToGimp ( ProgressConnector *pc, Glib::ustring fname) +bool EditorPanel::idle_sendToGimp ( ProgressConnector *pc, Glib::ustring fname) { - rtengine::IImage16* img = pc->returnValue(); + rtengine::IImagefloat* img = pc->returnValue(); delete pc; if (img) { @@ -2050,7 +2050,7 @@ bool EditorPanel::idle_sendToGimp ( ProgressConnector *pc, ProgressConnector *ld = new ProgressConnector(); img->setSaveProgressListener (parent->getProgressListener()); - ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImage16::saveAsTIFF), fileName, sf.tiffBits, sf.tiffUncompressed), + ld->startFunc (sigc::bind (sigc::mem_fun (img, &rtengine::IImagefloat::saveAsTIFF), fileName, sf.tiffBits, sf.tiffUncompressed), sigc::bind (sigc::mem_fun (*this, &EditorPanel::idle_sentToGimp), ld, img, fileName)); } else { Glib::ustring msg_ = Glib::ustring (" Error during image processing\n"); @@ -2063,7 +2063,7 @@ bool EditorPanel::idle_sendToGimp ( ProgressConnector *pc, return false; } -bool EditorPanel::idle_sentToGimp (ProgressConnector *pc, rtengine::IImage16* img, Glib::ustring filename) +bool EditorPanel::idle_sentToGimp (ProgressConnector *pc, rtengine::IImagefloat* img, Glib::ustring filename) { img->free (); int errore = pc->returnValue(); diff --git a/rtgui/editorpanel.h b/rtgui/editorpanel.h index 5beb9ee7d..50fc337ed 100644 --- a/rtgui/editorpanel.h +++ b/rtgui/editorpanel.h @@ -146,10 +146,10 @@ private: void close (); BatchQueueEntry* createBatchQueueEntry (); - bool idle_imageSaved (ProgressConnector *pc, rtengine::IImage16* img, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams); - bool idle_saveImage (ProgressConnector *pc, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams); - bool idle_sendToGimp ( ProgressConnector *pc, Glib::ustring fname); - bool idle_sentToGimp (ProgressConnector *pc, rtengine::IImage16* img, Glib::ustring filename); + bool idle_imageSaved (ProgressConnector *pc, rtengine::IImagefloat* img, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams); + bool idle_saveImage (ProgressConnector *pc, Glib::ustring fname, SaveFormat sf, rtengine::procparams::ProcParams &pparams); + bool idle_sendToGimp ( ProgressConnector *pc, Glib::ustring fname); + bool idle_sentToGimp (ProgressConnector *pc, rtengine::IImagefloat* img, Glib::ustring filename); Glib::ustring lastSaveAsFileName; bool realized; diff --git a/rtgui/main-cli.cc b/rtgui/main-cli.cc index 1d1917e8c..233e04956 100644 --- a/rtgui/main-cli.cc +++ b/rtgui/main-cli.cc @@ -822,7 +822,7 @@ int processLineParams ( int argc, char **argv ) } // Process image - rtengine::IImage16* resultImage = rtengine::processImage (job, errorCode, nullptr, options.tunnelMetaData); + rtengine::IImagefloat* resultImage = rtengine::processImage (job, errorCode, nullptr, options.tunnelMetaData); if ( !resultImage ) { errors++; From 912f9f436b1163c2235426eca4d8830eebcd4966 Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Mon, 20 Nov 2017 00:11:18 +0100 Subject: [PATCH 02/19] added support for 32-bit floating-point TIFF output --- rtengine/imageio.cc | 23 ++++++++++++++++------- rtexif/rtexif.cc | 2 ++ rtgui/saveformatpanel.cc | 12 +++++++++--- rtgui/saveformatpanel.h | 2 +- 4 files changed, 28 insertions(+), 11 deletions(-) diff --git a/rtengine/imageio.cc b/rtengine/imageio.cc index ff9c9b559..e7a45ecf6 100644 --- a/rtengine/imageio.cc +++ b/rtengine/imageio.cc @@ -1250,20 +1250,28 @@ int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed) } #if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ - bool needsReverse = bps == 16 && exifRoot->getOrder() == rtexif::MOTOROLA; + bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::MOTOROLA; #else - bool needsReverse = bps == 16 && exifRoot->getOrder() == rtexif::INTEL; + bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::INTEL; #endif for (int i = 0; i < height; i++) { getScanline (i, linebuffer, bps); - if (needsReverse) - for (int i = 0; i < lineWidth; i += 2) { - char c = linebuffer[i]; - linebuffer[i] = linebuffer[i + 1]; - linebuffer[i + 1] = c; + if (needsReverse) { + if (bps == 16) { + for (int i = 0; i < lineWidth; i += 2) { + char c = linebuffer[i]; + linebuffer[i] = linebuffer[i + 1]; + linebuffer[i + 1] = c; + } + } else { + for (int i = 0; i < lineWidth; i += 4) { + std::swap(linebuffer[i], linebuffer[i+3]); + std::swap(linebuffer[i+1], linebuffer[i+2]); + } } + } fwrite (linebuffer, lineWidth, 1, file); @@ -1362,6 +1370,7 @@ int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed) TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); TIFFSetField (out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); TIFFSetField (out, TIFFTAG_COMPRESSION, uncompressed ? COMPRESSION_NONE : COMPRESSION_DEFLATE); + TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, bps == 32 ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT); if (!uncompressed) { TIFFSetField (out, TIFFTAG_PREDICTOR, PREDICTOR_NONE); diff --git a/rtexif/rtexif.cc b/rtexif/rtexif.cc index bc0e2002f..1701b3058 100644 --- a/rtexif/rtexif.cc +++ b/rtexif/rtexif.cc @@ -3240,6 +3240,8 @@ int ExifManager::createTIFFHeader (const TagDirectory* root, const rtengine::pro Tag* stripOffs = new Tag (cl, lookupAttrib (ifdAttribs, "StripOffsets")); stripOffs->initInt (0, LONG, strips); cl->replaceTag (stripOffs); + Tag *sampleFormat = new Tag (cl, lookupAttrib (ifdAttribs, "SampleFormat"), bps == 32 ? 3 : 1, SHORT); + cl->replaceTag (sampleFormat); for (int i = 0; i < strips - 1; i++) { stripBC->setInt (rps * W * 3 * bps / 8, i * 4); diff --git a/rtgui/saveformatpanel.cc b/rtgui/saveformatpanel.cc index 13e687595..fef05de23 100644 --- a/rtgui/saveformatpanel.cc +++ b/rtgui/saveformatpanel.cc @@ -40,14 +40,16 @@ SaveFormatPanel::SaveFormatPanel () : listener (nullptr) format->append ("JPEG (8 bit)"); format->append ("TIFF (8 bit)"); format->append ("TIFF (16 bit)"); + format->append ("TIFF (32 bit float)"); format->append ("PNG (8 bit)"); format->append ("PNG (16 bit)"); fstr[0] = "jpg"; fstr[1] = "tif"; fstr[2] = "tif"; - fstr[3] = "png"; + fstr[3] = "tif"; fstr[4] = "png"; + fstr[5] = "png"; hb1->attach (*flab, 0, 0, 1, 1); hb1->attach (*format, 1, 0, 1, 1); @@ -121,8 +123,10 @@ void SaveFormatPanel::init (SaveFormat &sf) if (sf.format == "jpg") { format->set_active (0); } else if (sf.format == "png" && sf.pngBits == 16) { - format->set_active (4); + format->set_active (5); } else if (sf.format == "png" && sf.pngBits == 8) { + format->set_active (4); + } else if (sf.format == "tif" && sf.tiffBits == 32) { format->set_active (3); } else if (sf.format == "tif" && sf.tiffBits == 16) { format->set_active (2); @@ -146,7 +150,7 @@ SaveFormat SaveFormatPanel::getFormat () int sel = format->get_active_row_number(); sf.format = fstr[sel]; - if (sel == 4) { + if (sel == 5) { sf.pngBits = 16; } else { sf.pngBits = 8; @@ -154,6 +158,8 @@ SaveFormat SaveFormatPanel::getFormat () if (sel == 2) { sf.tiffBits = 16; + } else if (sel == 3) { + sf.tiffBits = 32; } else { sf.tiffBits = 8; } diff --git a/rtgui/saveformatpanel.h b/rtgui/saveformatpanel.h index 76ae7055d..8dc493051 100644 --- a/rtgui/saveformatpanel.h +++ b/rtgui/saveformatpanel.h @@ -44,7 +44,7 @@ protected: Gtk::Grid* jpegOpts; Gtk::Label* jpegSubSampLabel; FormatChangeListener* listener; - Glib::ustring fstr[5]; + Glib::ustring fstr[6]; Gtk::CheckButton* savesPP; From 5ea5cfd943d2f77f91f3b7f47e377074e57be629 Mon Sep 17 00:00:00 2001 From: Hombre Date: Tue, 19 Dec 2017 22:28:52 +0100 Subject: [PATCH 03/19] Fixing #4233 : "Segfault opening PREDICTOR_FLOATINGPOINT image" Now compressed file use Deflate compression for all bit depth, and PREDICTOR_HORIZONTAL for 8/16 bit integer images, and PREDICTOR_FLOATINGPOINT for 32 bits float images. --- rtengine/imageio.cc | 23 ++++++++++++++++++++++- 1 file changed, 22 insertions(+), 1 deletion(-) diff --git a/rtengine/imageio.cc b/rtengine/imageio.cc index e7a45ecf6..ba52c4672 100644 --- a/rtengine/imageio.cc +++ b/rtengine/imageio.cc @@ -1373,16 +1373,37 @@ int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed) TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, bps == 32 ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT); if (!uncompressed) { - TIFFSetField (out, TIFFTAG_PREDICTOR, PREDICTOR_NONE); + TIFFSetField (out, TIFFTAG_PREDICTOR, bps == 32 ? PREDICTOR_FLOATINGPOINT : PREDICTOR_HORIZONTAL); } if (profileData) { TIFFSetField (out, TIFFTAG_ICCPROFILE, profileLength, profileData); } +#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ + bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::MOTOROLA; +#else + bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::INTEL; +#endif + for (int row = 0; row < height; row++) { getScanline (row, linebuffer, bps); + if (needsReverse) { + if (bps == 16) { + for (int i = 0; i < lineWidth; i += 2) { + char c = linebuffer[i]; + linebuffer[i] = linebuffer[i + 1]; + linebuffer[i + 1] = c; + } + } else { + for (int i = 0; i < lineWidth; i += 4) { + std::swap(linebuffer[i], linebuffer[i+3]); + std::swap(linebuffer[i+1], linebuffer[i+2]); + } + } + } + if (TIFFWriteScanline (out, linebuffer, row, 0) < 0) { TIFFClose (out); delete [] linebuffer; From 96863bb95648575f8528dfa75bbf232c9c4deb40 Mon Sep 17 00:00:00 2001 From: Hombre Date: Mon, 1 Jan 2018 13:51:48 +0100 Subject: [PATCH 04/19] Sets UTF-8 as default charset for IPTC + suppress one of the 2 methods for saving TIFF images. --- rtengine/imageio.cc | 380 ++++++++++++++++++++----------------------- rtexif/rtexif.cc | 126 +------------- rtexif/stdattribs.cc | 2 +- 3 files changed, 182 insertions(+), 326 deletions(-) diff --git a/rtengine/imageio.cc b/rtengine/imageio.cc index ba52c4672..3790f00f6 100644 --- a/rtengine/imageio.cc +++ b/rtengine/imageio.cc @@ -75,15 +75,6 @@ FILE* g_fopen_withBinaryAndLock(const Glib::ustring& fname) return f; } -Glib::ustring to_utf8 (const std::string& str) -{ - try { - return Glib::locale_to_utf8 (str); - } catch (Glib::Error&) { - return Glib::convert_with_fallback (str, "UTF-8", "ISO-8859-1", "?"); - } -} - } Glib::ustring ImageIO::errorMsg[6] = {"Success", "Cannot read file.", "Invalid header.", "Error while reading header.", "File reading error", "Image format not supported."}; @@ -136,13 +127,19 @@ void ImageIO::setMetadata (const rtexif::TagDirectory* eroot, const rtengine::pr iptc = iptc_data_new (); + const unsigned char utf8Esc[] = {0x1B, '%', 'G'}; + IptcDataSet * ds = iptc_dataset_new (); + iptc_dataset_set_tag (ds, IPTC_RECORD_OBJECT_ENV, IPTC_TAG_CHARACTER_SET); + iptc_dataset_set_data (ds, utf8Esc, 3, IPTC_DONT_VALIDATE); + iptc_data_add_dataset (iptc, ds); + iptc_dataset_unref (ds); + for (rtengine::procparams::IPTCPairs::const_iterator i = iptcc.begin(); i != iptcc.end(); ++i) { if (i->first == "Keywords" && !(i->second.empty())) { for (unsigned int j = 0; j < i->second.size(); j++) { IptcDataSet * ds = iptc_dataset_new (); iptc_dataset_set_tag (ds, IPTC_RECORD_APP_2, IPTC_TAG_KEYWORDS); - std::string loc = to_utf8(i->second.at(j)); - iptc_dataset_set_data (ds, (unsigned char*)loc.c_str(), min(static_cast(64), loc.size()), IPTC_DONT_VALIDATE); + iptc_dataset_set_data (ds, (unsigned char*)i->second.at(j).c_str(), min(static_cast(64), i->second.at(j).bytes()), IPTC_DONT_VALIDATE); iptc_data_add_dataset (iptc, ds); iptc_dataset_unref (ds); } @@ -152,8 +149,7 @@ void ImageIO::setMetadata (const rtexif::TagDirectory* eroot, const rtengine::pr for (unsigned int j = 0; j < i->second.size(); j++) { IptcDataSet * ds = iptc_dataset_new (); iptc_dataset_set_tag (ds, IPTC_RECORD_APP_2, IPTC_TAG_SUPPL_CATEGORY); - std::string loc = to_utf8(i->second.at(j)); - iptc_dataset_set_data (ds, (unsigned char*)loc.c_str(), min(static_cast(32), loc.size()), IPTC_DONT_VALIDATE); + iptc_dataset_set_data (ds, (unsigned char*)i->second.at(j).c_str(), min(static_cast(32), i->second.at(j).bytes()), IPTC_DONT_VALIDATE); iptc_data_add_dataset (iptc, ds); iptc_dataset_unref (ds); } @@ -165,8 +161,7 @@ void ImageIO::setMetadata (const rtexif::TagDirectory* eroot, const rtengine::pr if (i->first == strTags[j].field && !(i->second.empty())) { IptcDataSet * ds = iptc_dataset_new (); iptc_dataset_set_tag (ds, IPTC_RECORD_APP_2, strTags[j].tag); - std::string loc = to_utf8(i->second.at(0)); - iptc_dataset_set_data (ds, (unsigned char*)loc.c_str(), min(strTags[j].size, loc.size()), IPTC_DONT_VALIDATE); + iptc_dataset_set_data (ds, (unsigned char*)i->second.at(0).c_str(), min(strTags[j].size, i->second.at(0).bytes()), IPTC_DONT_VALIDATE); iptc_data_add_dataset (iptc, ds); iptc_dataset_unref (ds); } @@ -1121,13 +1116,13 @@ int ImageIO::saveJPEG (Glib::ustring fname, int quality, int subSamp) int bytes = 0; if (!error && (bytes = iptc_jpeg_ps3_save_iptc (nullptr, 0, iptcdata, size, buffer, 65532)) < 0) { - if (iptcdata) { - iptc_data_free_buf (iptc, iptcdata); - } - error = true; } + if (iptcdata) { + iptc_data_free_buf (iptc, iptcdata); + } + if (!error) { jpeg_write_marker(&cinfo, JPEG_APP0 + 13, buffer, bytes); } @@ -1212,219 +1207,198 @@ int ImageIO::saveTIFF (Glib::ustring fname, int bps, bool uncompressed) int lineWidth = width * 3 * bps / 8; unsigned char* linebuffer = new unsigned char[lineWidth]; -// TODO the following needs to be looked into - do we really need two ways to write a Tiff file ? - if (exifRoot && uncompressed) { - FILE *file = g_fopen_withBinaryAndLock (fname); + // little hack to get libTiff to use proper byte order (see TIFFClienOpen()): + const char *mode = !exifRoot ? "w" : (exifRoot->getOrder() == rtexif::INTEL ? "wl" : "wb"); +#ifdef WIN32 + FILE *file = g_fopen_withBinaryAndLock (fname); + int fileno = _fileno(file); + int osfileno = _get_osfhandle(fileno); + TIFF* out = TIFFFdOpen (osfileno, fname.c_str(), mode); +#else + TIFF* out = TIFFOpen(fname.c_str(), mode); + int fileno = TIFFFileno (out); +#endif - if (!file) { - delete [] linebuffer; - return IMIO_CANNOTWRITEFILE; + if (!out) { + delete [] linebuffer; + return IMIO_CANNOTWRITEFILE; + } + + if (pl) { + pl->setProgressStr ("PROGRESSBAR_SAVETIFF"); + pl->setProgress (0.0); + } + + if (exifRoot) { + rtexif::TagDirectory* cl = (const_cast (exifRoot))->clone (nullptr); + + // ------------------ remove some unknown top level tags which produce warnings when opening a tiff (might be useless) ----------------- + + rtexif::Tag *removeTag = cl->getTag (0x9003); + + if (removeTag) { + removeTag->setKeep (false); } - if (pl) { - pl->setProgressStr ("PROGRESSBAR_SAVETIFF"); - pl->setProgress (0.0); + removeTag = cl->getTag (0x9211); + + if (removeTag) { + removeTag->setKeep (false); } - // buffer for the exif and iptc - unsigned int bufferSize; - unsigned char* buffer = nullptr; // buffer will be allocated in createTIFFHeader - unsigned char* iptcdata = nullptr; - unsigned int iptclen = 0; + // ------------------ Apply list of change ----------------- - if (iptc && iptc_data_save (iptc, &iptcdata, &iptclen) && iptcdata) { + for (auto currExifChange : exifChange) { + cl->applyChange (currExifChange.first, currExifChange.second); + } + + rtexif::Tag *tag = cl->getTag (TIFFTAG_EXIFIFD); + + if (tag && tag->isDirectory()) { + rtexif::TagDirectory *exif = tag->getDirectory(); + + if (exif) { + int exif_size = exif->calculateSize(); + unsigned char *buffer = new unsigned char[exif_size + 8]; + // TIFFOpen writes out the header and sets file pointer at position 8 + + exif->write (8, buffer); + + write (fileno, buffer + 8, exif_size); + + delete [] buffer; + // let libtiff know that scanlines or any other following stuff should go + // at a different offset: + TIFFSetWriteOffset (out, exif_size + 8); + TIFFSetField (out, TIFFTAG_EXIFIFD, 8); + } + } + + //TODO Even though we are saving EXIF IFD - MakerNote still comes out screwed. + + if ((tag = cl->getTag (TIFFTAG_MODEL)) != nullptr) { + TIFFSetField (out, TIFFTAG_MODEL, tag->getValue()); + } + + if ((tag = cl->getTag (TIFFTAG_MAKE)) != nullptr) { + TIFFSetField (out, TIFFTAG_MAKE, tag->getValue()); + } + + if ((tag = cl->getTag (TIFFTAG_DATETIME)) != nullptr) { + TIFFSetField (out, TIFFTAG_DATETIME, tag->getValue()); + } + + if ((tag = cl->getTag (TIFFTAG_ARTIST)) != nullptr) { + TIFFSetField (out, TIFFTAG_ARTIST, tag->getValue()); + } + + if ((tag = cl->getTag (TIFFTAG_COPYRIGHT)) != nullptr) { + TIFFSetField (out, TIFFTAG_COPYRIGHT, tag->getValue()); + } + + delete cl; + } + + unsigned char* iptcdata = nullptr; + unsigned int iptclen = 0; + + if (iptc && iptc_data_save (iptc, &iptcdata, &iptclen)) { + if (iptcdata) { iptc_data_free_buf (iptc, iptcdata); iptcdata = nullptr; } + } - int size = rtexif::ExifManager::createTIFFHeader (exifRoot, exifChange, width, height, bps, profileData, profileLength, (char*)iptcdata, iptclen, buffer, bufferSize); - - if (iptcdata) { - iptc_data_free_buf (iptc, iptcdata); + if (iptcdata) { + rtexif::Tag* iptcTag = new rtexif::Tag (nullptr, rtexif::lookupAttrib (rtexif::ifdAttribs, "IPTCData")); + iptcTag->initLongArray((char*)iptcdata, iptclen); +#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ + bool needsReverse = exifRoot && exifRoot->getOrder() == rtexif::MOTOROLA; +#else + bool needsReverse = exifRoot && exifRoot->getOrder() == rtexif::INTEL; +#endif + if (needsReverse) { + unsigned char *ptr = iptcTag->getValue(); + for (int a = 0; a < iptcTag->getCount(); ++a) { + unsigned char cc; + cc = ptr[3]; + ptr[3] = ptr[0]; + ptr[0] = cc; + cc = ptr[2]; + ptr[2] = ptr[1]; + ptr[1] = cc; + ptr += 4; + } } + TIFFSetField (out, TIFFTAG_RICHTIFFIPTC, iptcTag->getCount(), (long*)iptcTag->getValue()); + iptc_data_free_buf (iptc, iptcdata); + } - // The maximum lenght is strangely not the same than for the JPEG file... - // Which maximum length is the good one ? - if (size > 0 && size <= static_cast(bufferSize)) { - fwrite (buffer, size, 1, file); - } + TIFFSetField (out, TIFFTAG_SOFTWARE, "RawTherapee " RTVERSION); + TIFFSetField (out, TIFFTAG_IMAGEWIDTH, width); + TIFFSetField (out, TIFFTAG_IMAGELENGTH, height); + TIFFSetField (out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); + TIFFSetField (out, TIFFTAG_SAMPLESPERPIXEL, 3); + TIFFSetField (out, TIFFTAG_ROWSPERSTRIP, height); + TIFFSetField (out, TIFFTAG_BITSPERSAMPLE, bps); + TIFFSetField (out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); + TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); + TIFFSetField (out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); + TIFFSetField (out, TIFFTAG_COMPRESSION, uncompressed ? COMPRESSION_NONE : COMPRESSION_DEFLATE); + TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, bps == 32 ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT); + + if (!uncompressed) { + TIFFSetField (out, TIFFTAG_PREDICTOR, bps == 32 ? PREDICTOR_FLOATINGPOINT : PREDICTOR_HORIZONTAL); + } + + if (profileData) { + TIFFSetField (out, TIFFTAG_ICCPROFILE, profileLength, profileData); + } #if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ - bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::MOTOROLA; + bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::MOTOROLA; #else - bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::INTEL; + bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::INTEL; #endif - for (int i = 0; i < height; i++) { - getScanline (i, linebuffer, bps); + for (int row = 0; row < height; row++) { + getScanline (row, linebuffer, bps); - if (needsReverse) { - if (bps == 16) { - for (int i = 0; i < lineWidth; i += 2) { - char c = linebuffer[i]; - linebuffer[i] = linebuffer[i + 1]; - linebuffer[i + 1] = c; - } - } else { - for (int i = 0; i < lineWidth; i += 4) { - std::swap(linebuffer[i], linebuffer[i+3]); - std::swap(linebuffer[i+1], linebuffer[i+2]); - } + if (needsReverse) { + if (bps == 16) { + for (int i = 0; i < lineWidth; i += 2) { + char c = linebuffer[i]; + linebuffer[i] = linebuffer[i + 1]; + linebuffer[i + 1] = c; + } + } else { + for (int i = 0; i < lineWidth; i += 4) { + std::swap(linebuffer[i], linebuffer[i+3]); + std::swap(linebuffer[i+1], linebuffer[i+2]); } } - - fwrite (linebuffer, lineWidth, 1, file); - - if (pl && !(i % 100)) { - pl->setProgress ((double)(i + 1) / height); - } } - if(buffer) { - delete [] buffer; - } - - if (ferror(file)) { - writeOk = false; - } - - fclose (file); - } else { - // little hack to get libTiff to use proper byte order (see TIFFClienOpen()): - const char *mode = !exifRoot ? "w" : (exifRoot->getOrder() == rtexif::INTEL ? "wl" : "wb"); -#ifdef WIN32 - FILE *file = g_fopen_withBinaryAndLock (fname); - int fileno = _fileno(file); - int osfileno = _get_osfhandle(fileno); - TIFF* out = TIFFFdOpen (osfileno, fname.c_str(), mode); -#else - TIFF* out = TIFFOpen(fname.c_str(), mode); - int fileno = TIFFFileno (out); -#endif - - if (!out) { + if (TIFFWriteScanline (out, linebuffer, row, 0) < 0) { + TIFFClose (out); delete [] linebuffer; return IMIO_CANNOTWRITEFILE; } - if (pl) { - pl->setProgressStr ("PROGRESSBAR_SAVETIFF"); - pl->setProgress (0.0); + if (pl && !(row % 100)) { + pl->setProgress ((double)(row + 1) / height); } - - if (exifRoot) { - rtexif::Tag *tag = exifRoot->getTag (TIFFTAG_EXIFIFD); - - if (tag && tag->isDirectory()) { - rtexif::TagDirectory *exif = tag->getDirectory(); - - if (exif) { - int exif_size = exif->calculateSize(); - unsigned char *buffer = new unsigned char[exif_size + 8]; - // TIFFOpen writes out the header and sets file pointer at position 8 - - exif->write (8, buffer); - - write (fileno, buffer + 8, exif_size); - - delete [] buffer; - // let libtiff know that scanlines or any other following stuff should go - // at a different offset: - TIFFSetWriteOffset (out, exif_size + 8); - TIFFSetField (out, TIFFTAG_EXIFIFD, 8); - } - } - -//TODO Even though we are saving EXIF IFD - MakerNote still comes out screwed. - - if ((tag = exifRoot->getTag (TIFFTAG_MODEL)) != nullptr) { - TIFFSetField (out, TIFFTAG_MODEL, tag->getValue()); - } - - if ((tag = exifRoot->getTag (TIFFTAG_MAKE)) != nullptr) { - TIFFSetField (out, TIFFTAG_MAKE, tag->getValue()); - } - - if ((tag = exifRoot->getTag (TIFFTAG_DATETIME)) != nullptr) { - TIFFSetField (out, TIFFTAG_DATETIME, tag->getValue()); - } - - if ((tag = exifRoot->getTag (TIFFTAG_ARTIST)) != nullptr) { - TIFFSetField (out, TIFFTAG_ARTIST, tag->getValue()); - } - - if ((tag = exifRoot->getTag (TIFFTAG_COPYRIGHT)) != nullptr) { - TIFFSetField (out, TIFFTAG_COPYRIGHT, tag->getValue()); - } - - } - - TIFFSetField (out, TIFFTAG_SOFTWARE, "RawTherapee " RTVERSION); - TIFFSetField (out, TIFFTAG_IMAGEWIDTH, width); - TIFFSetField (out, TIFFTAG_IMAGELENGTH, height); - TIFFSetField (out, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); - TIFFSetField (out, TIFFTAG_SAMPLESPERPIXEL, 3); - TIFFSetField (out, TIFFTAG_ROWSPERSTRIP, height); - TIFFSetField (out, TIFFTAG_BITSPERSAMPLE, bps); - TIFFSetField (out, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); - TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, SAMPLEFORMAT_UINT); - TIFFSetField (out, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); - TIFFSetField (out, TIFFTAG_COMPRESSION, uncompressed ? COMPRESSION_NONE : COMPRESSION_DEFLATE); - TIFFSetField (out, TIFFTAG_SAMPLEFORMAT, bps == 32 ? SAMPLEFORMAT_IEEEFP : SAMPLEFORMAT_UINT); - - if (!uncompressed) { - TIFFSetField (out, TIFFTAG_PREDICTOR, bps == 32 ? PREDICTOR_FLOATINGPOINT : PREDICTOR_HORIZONTAL); - } - - if (profileData) { - TIFFSetField (out, TIFFTAG_ICCPROFILE, profileLength, profileData); - } - -#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ - bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::MOTOROLA; -#else - bool needsReverse = (bps == 16 || bps == 32) && exifRoot->getOrder() == rtexif::INTEL; -#endif - - for (int row = 0; row < height; row++) { - getScanline (row, linebuffer, bps); - - if (needsReverse) { - if (bps == 16) { - for (int i = 0; i < lineWidth; i += 2) { - char c = linebuffer[i]; - linebuffer[i] = linebuffer[i + 1]; - linebuffer[i + 1] = c; - } - } else { - for (int i = 0; i < lineWidth; i += 4) { - std::swap(linebuffer[i], linebuffer[i+3]); - std::swap(linebuffer[i+1], linebuffer[i+2]); - } - } - } - - if (TIFFWriteScanline (out, linebuffer, row, 0) < 0) { - TIFFClose (out); - delete [] linebuffer; - return IMIO_CANNOTWRITEFILE; - } - - if (pl && !(row % 100)) { - pl->setProgress ((double)(row + 1) / height); - } - } - - if (TIFFFlush(out) != 1) { - writeOk = false; - } - - TIFFClose (out); -#ifdef WIN32 - fclose (file); -#endif } + if (TIFFFlush(out) != 1) { + writeOk = false; + } + + TIFFClose (out); +#ifdef WIN32 + fclose (file); +#endif + delete [] linebuffer; if (pl) { diff --git a/rtexif/rtexif.cc b/rtexif/rtexif.cc index 60f8b6619..6179688ae 100644 --- a/rtexif/rtexif.cc +++ b/rtexif/rtexif.cc @@ -739,11 +739,12 @@ void TagDirectory::applyChange (std::string name, Glib::ustring value) } else { const TagAttrib* attrib = nullptr; - for (int i = 0; attribs[i].ignore != -1; i++) + for (int i = 0; attribs[i].ignore != -1; i++) { if (!strcmp (attribs[i].name, fseg.c_str())) { attrib = &attribs[i]; break; } + } if (attrib) { Tag* nt = new Tag (this, attrib); @@ -1663,15 +1664,11 @@ void Tag::toString (char* buffer, int ofs) return; } - size_t maxcount = 4; - - if (count < 4) { - maxcount = count; - } + size_t maxcount = rtengine::min(count, 10); strcpy (buffer, ""); - for (ssize_t i = 0; i < std::min(maxcount, valuesize - ofs); i++) { + for (ssize_t i = 0; i < rtengine::min(maxcount, valuesize - ofs); i++) { if (i > 0) { strcat (buffer, ", "); } @@ -3199,121 +3196,6 @@ int ExifManager::createJPEGMarker (const TagDirectory* root, const rtengine::pro return size + 6; } -int ExifManager::createTIFFHeader (const TagDirectory* root, const rtengine::procparams::ExifPairs& changeList, int W, int H, int bps, const char* profiledata, int profilelen, const char* iptcdata, int iptclen, unsigned char *&buffer, unsigned &bufferSize) -{ - -// write tiff header - int offs = 0; - ByteOrder order = HOSTORDER; - - if (root) { - order = root->getOrder (); - } - - TagDirectory* cl; - - if (root) { - cl = (const_cast (root))->clone (nullptr); - // remove some unknown top level tags which produce warnings when opening a tiff - Tag *removeTag = cl->getTag (0x9003); - - if (removeTag) { - removeTag->setKeep (false); - } - - removeTag = cl->getTag (0x9211); - - if (removeTag) { - removeTag->setKeep (false); - } - } else { - cl = new TagDirectory (nullptr, ifdAttribs, HOSTORDER); - } - -// add tiff strip data - int rps = 8; - int strips = ceil ((double)H / rps); - cl->replaceTag (new Tag (cl, lookupAttrib (ifdAttribs, "RowsPerStrip"), rps, LONG)); - Tag* stripBC = new Tag (cl, lookupAttrib (ifdAttribs, "StripByteCounts")); - stripBC->initInt (0, LONG, strips); - cl->replaceTag (stripBC); - Tag* stripOffs = new Tag (cl, lookupAttrib (ifdAttribs, "StripOffsets")); - stripOffs->initInt (0, LONG, strips); - cl->replaceTag (stripOffs); - Tag *sampleFormat = new Tag (cl, lookupAttrib (ifdAttribs, "SampleFormat"), bps == 32 ? 3 : 1, SHORT); - cl->replaceTag (sampleFormat); - - for (int i = 0; i < strips - 1; i++) { - stripBC->setInt (rps * W * 3 * bps / 8, i * 4); - } - - int remaining = (H - rps * floor ((double)H / rps)) * W * 3 * bps / 8; - - if (remaining) { - stripBC->setInt (remaining, (strips - 1) * 4); - } else { - stripBC->setInt (rps * W * 3 * bps / 8, (strips - 1) * 4); - } - - if (profiledata) { - Tag* icc = new Tag (cl, lookupAttrib (ifdAttribs, "ICCProfile")); - icc->initUndefArray (profiledata, profilelen); - cl->replaceTag (icc); - } - - if (iptcdata) { - Tag* iptc = new Tag (cl, lookupAttrib (ifdAttribs, "IPTCData")); - iptc->initLongArray (iptcdata, iptclen); - cl->replaceTag (iptc); - } - -// apply list of changes - for (rtengine::procparams::ExifPairs::const_iterator i = changeList.begin(); i != changeList.end(); ++i) { - cl->applyChange (i->first, i->second); - } - - // append default properties - const std::vector defTags = getDefaultTIFFTags (cl); - - defTags[0]->setInt (W, 0, LONG); - defTags[1]->setInt (H, 0, LONG); - defTags[8]->initInt (0, SHORT, 3); - - for (int i = 0; i < 3; i++) { - defTags[8]->setInt (bps, i * 2, SHORT); - } - - for (int i = defTags.size() - 1; i >= 0; i--) { - Tag* defTag = defTags[i]; - cl->replaceTag (defTag->clone (cl)); - delete defTag; - } - -// calculate strip offsets - int size = cl->calculateSize (); - int byps = bps / 8; - - for (int i = 0; i < strips; i++) { - stripOffs->setInt (size + 8 + i * rps * W * 3 * byps, i * 4); - } - - cl->sort (); - bufferSize = cl->calculateSize() + 8; - buffer = new unsigned char[bufferSize]; // this has to be deleted in caller - sset2 ((unsigned short)order, buffer + offs, order); - offs += 2; - sset2 (42, buffer + offs, order); - offs += 2; - sset4 (8, buffer + offs, order); - - int endOffs = cl->write (8, buffer); - -// cl->printAll(); - delete cl; - - return endOffs; -} - //----------------------------------------------------------------------------- // global functions to read byteorder dependent data //----------------------------------------------------------------------------- diff --git a/rtexif/stdattribs.cc b/rtexif/stdattribs.cc index a7e3fe00f..4982b0b81 100644 --- a/rtexif/stdattribs.cc +++ b/rtexif/stdattribs.cc @@ -809,10 +809,10 @@ const TagAttrib ifdAttribs[] = { {0, AC_WRITE, 0, nullptr, 0x828e, AUTO, "CFAPattern", &cfaInterpreter}, {0, AC_WRITE, 0, kodakIfdAttribs, 0x8290, AUTO, "KodakIFD", &stdInterpreter}, {0, AC_WRITE, 1, nullptr, 0x8298, AUTO, "Copyright", &stdInterpreter}, + {0, AC_SYSTEM, 0, nullptr, 0x83BB, AUTO, "IPTCData", &stdInterpreter}, {0, AC_DONTWRITE, 0, nullptr, 0x8606, AUTO, "LeafData", &stdInterpreter}, // is actually a subdir, but a proprietary format {0, AC_WRITE, 0, exifAttribs, 0x8769, AUTO, "Exif", &stdInterpreter}, {0, AC_SYSTEM, 0, nullptr, 0x8773, AUTO, "ICCProfile", &stdInterpreter}, - {0, AC_SYSTEM, 0, nullptr, 0x83BB, AUTO, "IPTCData", &stdInterpreter}, {0, AC_WRITE, 0, gpsAttribs, 0x8825, AUTO, "GPSInfo", &stdInterpreter}, {0, AC_WRITE, 0, nullptr, 0x9003, AUTO, "DateTimeOriginal", &stdInterpreter}, {0, AC_WRITE, 0, nullptr, 0x9004, AUTO, "DateTimeDigitized", &stdInterpreter}, From 9d2ccc19d3eecbbe6a7ee237b222ba6f8a583b8e Mon Sep 17 00:00:00 2001 From: Hombre Date: Mon, 1 Jan 2018 14:24:47 +0100 Subject: [PATCH 05/19] Better UNICODE support (UCS-2/UTF-8) for Exif.UserComment (#2017) - BOM is now checked and correctly handled - auto-detection of UTF-8 string if no BOM available, otherwise assume it's an UCS-2/UTF-16 string - try to autodetect endianess of UTF-16 string by counting zeros - possibility to enable writing BOM for this field (disabled for now) - for undefined charset (empty identifier), RT now assume that the string is what glib think is the local charset (not tested), and try to convert it to UTF-8 for display/editing JIS is still not handled though. --- rtexif/rtexif.cc | 48 ++++++++++++++----- rtexif/rtexif.h | 2 + rtexif/stdattribs.cc | 110 +++++++++++++++++++++++++++++++++++++++---- 3 files changed, 140 insertions(+), 20 deletions(-) diff --git a/rtexif/rtexif.cc b/rtexif/rtexif.cc index 6179688ae..3547f6ee3 100644 --- a/rtexif/rtexif.cc +++ b/rtexif/rtexif.cc @@ -1928,24 +1928,48 @@ void Tag::initInt (int data, TagType t, int cnt) setInt (data, 0, t); } +void Tag::swapByteOrder2(char *buffer, int count) +{ + char* ptr = buffer; + for (int i = 0; i < count; i+=2) { + unsigned char c = ptr[0]; + ptr[0] = ptr[1]; + ptr[1] = c; + ptr += 2; + } +} void Tag::initUserComment (const Glib::ustring &text) { + const bool useBOM = false; // set it to true if you want to output BOM in UCS-2/UTF-8 UserComments ; this could be turned to an options entry type = UNDEFINED; if (text.is_ascii()) { - count = 8 + strlen (text.c_str()); - valuesize = count; + valuesize = count = 8 + strlen (text.c_str()); value = new unsigned char[valuesize]; - strcpy ((char*)value, "ASCII"); - value[5] = value[6] = value[7] = 0; - strcpy ((char*)value + 8, text.c_str()); + memcpy(value, "ASCII\0\0\0", 8); + memcpy(value + 8, text.c_str(), valuesize - 8); } else { - wchar_t *commentStr = (wchar_t*)g_utf8_to_utf16 (text.c_str(), -1, NULL, NULL, NULL); - count = 8 + wcslen(commentStr)*2; - valuesize = count; - value = (unsigned char*)new char[valuesize]; - strcpy ((char*)value, "UNICODE"); - value[7] = 0; - wcscpy(((wchar_t*)value) + 4, commentStr); + wchar_t *commentStr = (wchar_t*)g_utf8_to_utf16 (text.c_str(), -1, nullptr, nullptr, nullptr); + size_t wcStrSize = wcslen(commentStr); + valuesize = count = wcStrSize * 2 + 8 + (useBOM ? 2 : 0); + value = new unsigned char[valuesize]; + memcpy(value, "UNICODE\0", 8); + + if (useBOM) { + if (getOrder() == INTEL) { //Little Endian + value[8] = 0xFF; + value[9] = 0xFE; + } else { + value[8] = 0xFE; + value[9] = 0xFF; + } + } + + // Swapping byte order to match the Exif's byte order + if (getOrder() != HOSTORDER) { + swapByteOrder2((char*)commentStr, wcStrSize * 2); + } + + memcpy(value + 8 + (useBOM ? 2 : 0), (char*)commentStr, wcStrSize * 2); g_free(commentStr); } } diff --git a/rtexif/rtexif.h b/rtexif/rtexif.h index 125d38c94..452cedba1 100644 --- a/rtexif/rtexif.h +++ b/rtexif/rtexif.h @@ -236,6 +236,8 @@ public: void initLongArray (const char* data, int len); void initRational (int num, int den); + static void swapByteOrder2 (char *buffer, int count); + // get basic tag properties int getID () const { diff --git a/rtexif/stdattribs.cc b/rtexif/stdattribs.cc index 4982b0b81..ca19b7f1c 100644 --- a/rtexif/stdattribs.cc +++ b/rtexif/stdattribs.cc @@ -452,12 +452,109 @@ public: } count = std::min (count, 65535); // limit to 65535 chars to avoid crashes in case of corrupted metadata - char *buffer = new char[count - 7]; + char *buffer = new char[count - 6]; // include 2 ending null chars for UCS-2 string (possibly) + char *value = (char*)t->getValue(); - if (!memcmp ((char*)t->getValue(), "ASCII\0\0\0", 8)) { - strncpy (buffer, (char*)t->getValue() + 8, count - 8); + if (!memcmp(value, "ASCII\0\0\0", 8)) { + memcpy(buffer, value + 8, count - 8); buffer[count - 8] = '\0'; + } else if (!memcmp(value, "UNICODE\0", 8)) { + memcpy(buffer, value + 8, count - 8); + buffer[count - 7] = buffer[count - 8] = '\0'; + Glib::ustring tmp1(buffer); + + + bool hasBOM = false; + enum ByteOrder bo = UNKNOWN; + if (count % 2 || (count >= 11 && (buffer[0] == 0xEF && buffer[1] == 0xBB && buffer[2] == 0xBF))) { + // odd string length can only be UTF-8, don't change anything + std::string retVal (buffer + 3); + delete [] buffer; + return retVal; + } else if (count >= 10) { + if (buffer[0] == 0xFF && buffer[1] == 0xFE) { + bo = INTEL; // little endian + hasBOM = true; + } else if (buffer[0] == 0xFE && buffer[1] == 0xFF) { + bo = MOTOROLA; // big endian + hasBOM = true; + } + } + if (bo == UNKNOWN) { + // auto-detecting byte order; we still don't know if it's UCS-2 or UTF-8 + int a = 0, b = 0, c = 0, d = 0; + for (int j = 8; j < count; j++) { + char cc = value[j]; + if (!(j%2)) { + // counting zeros for first byte + if (!cc) { + ++a; + } + } else { + // counting zeros for second byte + if (!cc) { + ++b; + } + } + if (!(cc & 0x80) || ((cc & 0xC0) == 0xC0) || ((cc & 0xC0) == 0x80)) { + ++c; + } + if ((cc & 0xC0) == 0x80) { + ++d; + } + } + if (c == (count - 8) && d) { + // this is an UTF-8 string + std::string retVal (buffer); + delete [] buffer; + return retVal; + } + if ((a || b) && a != b) { + bo = a > b ? MOTOROLA : INTEL; + } + } + if (bo == UNKNOWN) { + // assuming platform's byte order +#if __BYTE_ORDER__==__ORDER_LITTLE_ENDIAN__ + bo = INTEL; +#else + bo = MOTOROLA; +#endif + } + + // now swapping if necessary + if (!hasBOM && bo != HOSTORDER) { + if (t->getOrder() != HOSTORDER) { + Tag::swapByteOrder2(buffer, count - 8); + } + } + + glong written; + char* utf8Str = g_utf16_to_utf8((unsigned short int*)buffer, -1, nullptr, &written, nullptr); + delete [] buffer; + buffer = new char[written + 1]; + memcpy(buffer, utf8Str, written); + buffer[written] = 0; + } else if (!memcmp(value, "\0\0\0\0\0\0\0\0", 8)) { + // local charset string, whatever it is + memcpy(buffer, value + 8, count - 8); + buffer[count - 7] = buffer[count - 8] = '\0'; + + gsize written = 0; + char *utf8Str = g_locale_to_utf8(buffer, count - 8, nullptr, &written, nullptr); + if (utf8Str && written) { + delete [] buffer; + size_t length = strlen(utf8Str); + buffer = new char[length + 1]; + strcpy(buffer, utf8Str); + } else { + buffer[0] = 0; + } + if (utf8Str) { + g_free(utf8Str); + } } else { + // JIS: unsupported buffer[0] = 0; } @@ -467,11 +564,8 @@ public: } virtual void fromString (Tag* t, const std::string& value) { - char *buffer = new char[t->getCount()]; - memcpy (buffer, "ASCII\0\0\0", 8); - strcpy (buffer + 8, value.c_str()); - t->fromString (buffer, value.size() + 9); - delete [] buffer; + Glib::ustring tmpStr(value); + t->userCommentFromString (tmpStr); } }; UserCommentInterpreter userCommentInterpreter; From f3c0c2f0893cceaa5f85e2e8a7ddd9e810524022 Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Sun, 7 Jan 2018 11:59:56 +0100 Subject: [PATCH 06/19] Update TooWaBlue theme to version 2.61 Queue switch --- rtdata/themes/TooWaBlue-GTK3-20_.css | 67 ++++++++++++++++++++++------ 1 file changed, 54 insertions(+), 13 deletions(-) diff --git a/rtdata/themes/TooWaBlue-GTK3-20_.css b/rtdata/themes/TooWaBlue-GTK3-20_.css index 1e196bff8..af7fb00af 100644 --- a/rtdata/themes/TooWaBlue-GTK3-20_.css +++ b/rtdata/themes/TooWaBlue-GTK3-20_.css @@ -2,7 +2,7 @@ This file is part of RawTherapee. Copyright (c) 2016-2017 TooWaBoo - Version 2.60 + Version 2.61 RawTherapee is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by @@ -213,10 +213,6 @@ dialog frame > label { padding-left: 0.91667em; } -#BatchQueueButtons { - margin-top: 0.66667em; -} - frame > label { margin: 0; padding: 0.5em 0; @@ -637,6 +633,11 @@ scale:disabled trough { background-image: none; } +#BatchQueueButtonsMainContainer scale:disabled slider, +#BatchQueueButtonsMainContainer scale:disabled trough { + background-color: shade(@bg-light-grey,.85); +} + /*** end ***************************************************************************************/ /*** Progressbar *******************************************************************************/ @@ -1225,6 +1226,51 @@ popover.background modelbutton:hover { } /** end ****************************************************************************************/ +/*** Switch ***********************************************************************************/ +switch { + min-height: 2.16667em; + min-width: 11em; + margin: 0; + padding: 0; + border-radius: 0.2em; + background-image: none; + box-shadow: inset 0.08334em 0.08334em rgba(0, 0, 0, 0.08), 0 0.08334em rgba(242, 242, 242, 0.1); + border: 0.08334em solid @bg-entry-border; + background-color: @bg-scale-entry; + margin-bottom: 0.5em; +} + +switch slider { + border: 0.08334em solid @bg-entry-border; + background-image: linear-gradient(to bottom, shade (@accent-color2,1.15), shade (@accent-color2,.85)); + border: 0.08334em solid @bg-entry-border; + box-shadow: inset 0 0.08334em rgba(242, 242, 242, 0.1); + border-radius: 0.2em 0 0 0.2em; +} +switch:checked slider{ + border-radius: 0 0.2em 0.2em 0; +} + +switch:hover slider { + background-image: linear-gradient(to bottom, shade (@accent-color2,1.20), shade (@accent-color2,.90)); +} + +switch:checked { + background-color: rgb(140,0,20); + color: @headline-big; +} + +switch:disabled:not(:checked) { + box-shadow: none; + background-image: none; + background-color: shade (@bg-light-grey, .85); +} +switch:disabled slider { + background-image: linear-gradient(to bottom, rgba(125,125,125,.4), rgba(60,60,60,.4)); + background-color: shade (@bg-light-grey, .85); +} +/** end ****************************************************************************************/ + /*** Buttons ***********************************************************************************/ button { min-height: 2.16667em; @@ -1355,6 +1401,7 @@ button.MiddleH { /**/ /* Button base format for Toolbox and dialogs */ +#ToolPanelNotebook > stack > box > box > combobox .combo, dialog button, #MyExpander button, #BatchQueueButtonsMainContainer button { @@ -1371,7 +1418,7 @@ combobox .combo, dialog combobox .combo, #ToolPanelNotebook combobox .combo, #BatchQueueButtonsMainContainer combobox .combo { - padding: 0 0.208334em; + padding: 0 0.26667em; } /**/ @@ -1386,6 +1433,7 @@ dialog combobox .combo, #MyExpander combobox + label */ { margin-left: 0.16667em; } + #MyExpander label + * > button:not(.flat).Left, #MyExpander label + combobox:not(:first-child):not(:only-child), #MyExpander label + button:not(.flat):not(spinbutton) { @@ -1455,13 +1503,6 @@ messagedialog .dialog-action-area button:not(:only-child):nth-child(2) { } /**/ -/* Queue */ -#BatchQueueButtons button { - min-height: 2.16667em; - min-width: 10em; -} -/**/ - /* View & Filechooser Buttons */ dialog .view button, window .view button { From d1d89d7f7203c58af2038ce726eddbbfa27a45c5 Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Sun, 7 Jan 2018 12:00:58 +0100 Subject: [PATCH 07/19] Update Deutsch local --- rtdata/languages/Deutsch | 57 ++++++++++++++++++++-------------------- 1 file changed, 29 insertions(+), 28 deletions(-) diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch index 1daaa9119..e6cb9f96d 100644 --- a/rtdata/languages/Deutsch +++ b/rtdata/languages/Deutsch @@ -48,6 +48,7 @@ #47 19.11.2017 HDR-Dynamikkompression (TooWaBoo) RT 5.3 #48 13.12.2017 Erweiterung (TooWaBoo) RT 5.3 #49 21.12.2017 Lokaler Kontrast (TooWaBoo) RT 5.3 +#50 07.01.2018 Crop Settings (TooWaBoo) RT 5.3 ABOUT_TAB_BUILD;Version ABOUT_TAB_CREDITS;Danksagungen @@ -572,9 +573,9 @@ HISTORY_MSG_281;(Farbanpassungen)\nSättigung schützen\nIntensität HISTORY_MSG_282;(Farbanpassungen)\nSättigung schützen\nSchwelle HISTORY_MSG_283;(Farbanpassungen)\nIntensität HISTORY_MSG_284;(Farbanpassungen)\nSättigung schützen\nAutomatisch -HISTORY_MSG_285;(Rauschreduzierung)\nMedianmethode +HISTORY_MSG_285;(Rauschreduzierung)\nMedianfilter - Methode HISTORY_MSG_286;(Rauschreduzierung)\nMediantyp -HISTORY_MSG_287;(Rauschreduzierung)\nMedianiterationen +HISTORY_MSG_287;(Rauschreduzierung)\nMedianfilter - Iterationen HISTORY_MSG_288;(Weißbild)\nKontrolle zu heller Bereiche HISTORY_MSG_289;(Weißbild)\nAuto-Kontrolle zu\nheller Bereiche HISTORY_MSG_290;(Sensor-Matrix)\nSchwarzpunkt - Rot @@ -1151,7 +1152,7 @@ PREFERENCES_REMEMBERZOOMPAN;Zoom und Bildposition merken PREFERENCES_REMEMBERZOOMPAN_TOOLTIP;Öffnen eines neuen Bildes mit den Zoom- und Positionswerten\ndes vorangegangenen Bildes.\n\nFunktioniert nur unter folgenden Bedingungen:\nEin-Reitermodus aktiv\n“Demosaikmethode für 100%-Ansicht“ muss auf “Wie im Bild-\nverarbeitungsprofil vorgegeben“ eingestellt sein. PREFERENCES_RGBDTL_LABEL;Maximale Anzahl Threads für Rauschreduzierung PREFERENCES_RGBDTL_TOOLTIP;Die Rauschreduzierung benötigt mindestens 128MB RAM für ein 10 Megapixel-Bild oder 512MB für ein 40 Megapixel-Bild, und zusätzlich 128MB RAM pro Thread. Je mehr Threads parallel ablaufen, desto schneller ist die Berechnung. Bei Einstellung "0" werden so viele Threads wie möglich benutzt. -PREFERENCES_SAVE_TP_OPEN_NOW;Werkzeugstatus jetzt sichern +PREFERENCES_SAVE_TP_OPEN_NOW;Werkzeugstatus jetzt speichern PREFERENCES_SELECTFONT;Schriftart PREFERENCES_SELECTFONT_COLPICKER;Schriftart für die Farbwähler PREFERENCES_SELECTLANG;Sprache @@ -1513,7 +1514,7 @@ TP_DIRPYRDENOISE_LUMINANCE_CURVE;Luminanzkurve TP_DIRPYRDENOISE_LUMINANCE_DETAIL;Luminanzdetails TP_DIRPYRDENOISE_LUMINANCE_FRAME;Luminanz TP_DIRPYRDENOISE_LUMINANCE_SMOOTHING;Luminanz -TP_DIRPYRDENOISE_MAIN_COLORSPACE;Methode +TP_DIRPYRDENOISE_MAIN_COLORSPACE;Farbraum TP_DIRPYRDENOISE_MAIN_COLORSPACE_LAB;L*a*b* TP_DIRPYRDENOISE_MAIN_COLORSPACE_LABEL;Rauschreduzierung TP_DIRPYRDENOISE_MAIN_COLORSPACE_RGB;RGB @@ -1524,7 +1525,7 @@ TP_DIRPYRDENOISE_MAIN_MODE;Qualität TP_DIRPYRDENOISE_MAIN_MODE_AGGRESSIVE;Hoch TP_DIRPYRDENOISE_MAIN_MODE_CONSERVATIVE;Standard TP_DIRPYRDENOISE_MAIN_MODE_TOOLTIP;Einstellung der Qualität der Rauschreduzierung.\nDie Einstellung “Hoch“ verbessert die Rausch-\nreduzierung auf Kosten der Verarbeitungszeit. -TP_DIRPYRDENOISE_MEDIAN_METHOD;Medianmethode +TP_DIRPYRDENOISE_MEDIAN_METHOD;Methode TP_DIRPYRDENOISE_MEDIAN_METHOD_CHROMINANCE;Nur Farbe TP_DIRPYRDENOISE_MEDIAN_METHOD_LAB;L*a*b* TP_DIRPYRDENOISE_MEDIAN_METHOD_LABEL;Medianfilter @@ -1532,7 +1533,7 @@ TP_DIRPYRDENOISE_MEDIAN_METHOD_LUMINANCE;Nur Luminanz TP_DIRPYRDENOISE_MEDIAN_METHOD_RGB;RGB TP_DIRPYRDENOISE_MEDIAN_METHOD_TOOLTIP;Bei der Methode “Nur Luminanz“ und “L*a*b*“,\nwird der Medianfilter nach den Waveletschritten\nverarbeitet.\nBei RGB wird der Medianfilter am Ende der\nRauschreduzierung verarbeitet. TP_DIRPYRDENOISE_MEDIAN_METHOD_WEIGHTED;Gewichtet L* (wenig) + a*b* (normal) -TP_DIRPYRDENOISE_MEDIAN_PASSES;Medianiterationen +TP_DIRPYRDENOISE_MEDIAN_PASSES;Iterationen TP_DIRPYRDENOISE_MEDIAN_PASSES_TOOLTIP;Manchmal führt ein kleines 3×3-Fenster mit\nmehreren Iterationen zu besseren Ergebnissen\nals ein 7×7-Fenster mit nur einer Iteration. TP_DIRPYRDENOISE_MEDIAN_TYPE;Mediantyp TP_DIRPYRDENOISE_MEDIAN_TYPE_TOOLTIP;Einen Medianfilter mit der gewünschten Fenstergröße auswählen.\nJe größer das Fenster, umso länger dauert die Verarbeitungszeit.\n\n3×3 weich: Nutzt 5 Pixel in einem 3×3-Pixelfenster.\n3×3: Nutzt 9 Pixel in einem 3×3-Pixelfenster.\n5×5 weich: Nutzt 13 Pixel in einem 5×5-Pixelfenster.\n5×5: Nutzt 25 Pixel in einem 5×5-Pixelfenster.\n7×7: Nutzt 49 Pixel in einem 7×7-Pixelfenster.\n9×9: Nutzt 81 Pixel in einem 9×9-Pixelfenster.\n\nManchmal ist das Ergebnis mit einem kleineren Fenster und mehreren Iterationen besser, als mit einem größeren und nur einer Iteration. @@ -2226,25 +2227,25 @@ ZOOMPANEL_ZOOMOUT;Herauszoomen\nTaste: - ! Untranslated keys follow; remove the ! prefix after an entry is translated. !!!!!!!!!!!!!!!!!!!!!!!!! -!BATCHQUEUE_STARTSTOPHINT;Start or stop processing the images in the queue.\n\nShortcut: Ctrl+s -!GENERAL_SLIDER;Slider -!HISTORY_MSG_173;NR - Detail recovery -!HISTORY_MSG_203;NR - Color space -!HISTORY_MSG_256;NR - Median - Type -!HISTORY_MSG_297;NR - Mode -!HISTORY_MSG_METADATA_MODE;Metadata copy mode -!PREFERENCES_CROP;Crop editing -!PREFERENCES_CROP_AUTO_FIT;Automatically zoom to fit the crop area -!PREFERENCES_CROP_GUIDES;Guides shown when not editing the crop -!PREFERENCES_CROP_GUIDES_FRAME;Frame -!PREFERENCES_CROP_GUIDES_FULL;Original -!PREFERENCES_CROP_GUIDES_NONE;None -!PREFERENCES_EDITORCMDLINE;Custom command line -!TP_DIRPYRDENOISE_CHROMINANCE_CURVE_TOOLTIP;Increase (multiply) the value of all chrominance sliders.\nThis curve lets you adjust the strength of chromatic noise reduction as a function of chromaticity, for instance to increase the action in areas of low saturation and to decrease it in those of high saturation. -!TP_DIRPYRDENOISE_LABEL;Noise Reduction -!TP_METADATA_EDIT;Apply modifications -!TP_METADATA_MODE;Metadata copy mode -!TP_METADATA_STRIP;Strip all metadata -!TP_METADATA_TUNNEL;Copy unchanged -!TP_RAW_PIXELSHIFTONEGREEN;Use one green instead of average -!TP_RAW_PIXELSHIFTONEGREEN_TOOLTIP;Use one green instead of averaging two greens for regions without motion. +BATCHQUEUE_STARTSTOPHINT;Startet / Stoppt die Verarbeitung\nder Warteschlange.\n\nTaste: Strg + s +GENERAL_SLIDER;Regler +HISTORY_MSG_173;(Rauschreduzierung)\nLuminanzdetails +HISTORY_MSG_203;(Rauschreduzierung)\nFarbraum +HISTORY_MSG_256;(Rauschreduzierung)\nMedianfilter - Mediantyp +HISTORY_MSG_297;(Rauschreduzierung)\nQualität +HISTORY_MSG_METADATA_MODE;(Metadaten)\nKopiermodus +PREFERENCES_CROP;Einstelleung des Ausschnittswerkzeuges +PREFERENCES_CROP_AUTO_FIT;Automatischer Zoom des Ausschnitts +PREFERENCES_CROP_GUIDES;Hilfslinien anzeigen wenn Ausschnitt nicht verändert wird +PREFERENCES_CROP_GUIDES_FRAME;Rahmen +PREFERENCES_CROP_GUIDES_FULL;Vorgabe des Ausschnittswerkzeuges +PREFERENCES_CROP_GUIDES_NONE;Keine +PREFERENCES_EDITORCMDLINE;Benutzerdefinierte Befehlszeile +TP_DIRPYRDENOISE_CHROMINANCE_CURVE_TOOLTIP;Erhöht / Reduziert die Intensität der\nChrominanz-Rauschreduzierung in\nAbhängigkeit der Farbsättigung.\n +TP_DIRPYRDENOISE_LABEL;Rauschreduzierung +TP_METADATA_EDIT;Veränderte Daten +TP_METADATA_MODE;Kopiermodus +TP_METADATA_STRIP;Keine +TP_METADATA_TUNNEL;Unveränderte Daten +TP_RAW_PIXELSHIFTONEGREEN;Benutze ein Grün +TP_RAW_PIXELSHIFTONEGREEN_TOOLTIP;Benutze ein Grün anstelle des\nDurchschnitts beider Grüns für\nBereiche ohne Bewegung. From 4305cb74a75a681856f7dbc2a04c8492696ffab4 Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Sun, 7 Jan 2018 19:31:27 +0100 Subject: [PATCH 08/19] Update Deutsch --- rtdata/languages/Deutsch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch index e6cb9f96d..9e6012d53 100644 --- a/rtdata/languages/Deutsch +++ b/rtdata/languages/Deutsch @@ -2234,7 +2234,7 @@ HISTORY_MSG_203;(Rauschreduzierung)\nFarbraum HISTORY_MSG_256;(Rauschreduzierung)\nMedianfilter - Mediantyp HISTORY_MSG_297;(Rauschreduzierung)\nQualität HISTORY_MSG_METADATA_MODE;(Metadaten)\nKopiermodus -PREFERENCES_CROP;Einstelleung des Ausschnittswerkzeuges +PREFERENCES_CROP;Einstellung des Ausschnittswerkzeuges PREFERENCES_CROP_AUTO_FIT;Automatischer Zoom des Ausschnitts PREFERENCES_CROP_GUIDES;Hilfslinien anzeigen wenn Ausschnitt nicht verändert wird PREFERENCES_CROP_GUIDES_FRAME;Rahmen From 12f1aa853ae4fb3b202f9449552032690877f646 Mon Sep 17 00:00:00 2001 From: TooWaBoo Date: Sun, 7 Jan 2018 19:35:47 +0100 Subject: [PATCH 09/19] Update Deutsch --- rtdata/languages/Deutsch | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtdata/languages/Deutsch b/rtdata/languages/Deutsch index 9e6012d53..f502e61cc 100644 --- a/rtdata/languages/Deutsch +++ b/rtdata/languages/Deutsch @@ -2241,7 +2241,7 @@ PREFERENCES_CROP_GUIDES_FRAME;Rahmen PREFERENCES_CROP_GUIDES_FULL;Vorgabe des Ausschnittswerkzeuges PREFERENCES_CROP_GUIDES_NONE;Keine PREFERENCES_EDITORCMDLINE;Benutzerdefinierte Befehlszeile -TP_DIRPYRDENOISE_CHROMINANCE_CURVE_TOOLTIP;Erhöht / Reduziert die Intensität der\nChrominanz-Rauschreduzierung in\nAbhängigkeit der Farbsättigung.\n +TP_DIRPYRDENOISE_CHROMINANCE_CURVE_TOOLTIP;Erhöht / Reduziert die Intensität der\nChrominanz-Rauschreduzierung in\nAbhängigkeit der Farbsättigung. TP_DIRPYRDENOISE_LABEL;Rauschreduzierung TP_METADATA_EDIT;Veränderte Daten TP_METADATA_MODE;Kopiermodus From 393d62bdb69afc16dc9bc9256f018fd60e4acbff Mon Sep 17 00:00:00 2001 From: heckflosse Date: Sun, 7 Jan 2018 22:59:24 +0100 Subject: [PATCH 10/19] =?UTF-8?q?=C3=82Some=20speedups=20for=20scalar=20sl?= =?UTF-8?q?eef=20functions?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- rtengine/curves.h.save-failed | 1142 +++++++++++++++++++++++++++++++++ rtengine/rt_math.h | 2 + rtengine/sleef.c | 13 +- 3 files changed, 1150 insertions(+), 7 deletions(-) create mode 100644 rtengine/curves.h.save-failed diff --git a/rtengine/curves.h.save-failed b/rtengine/curves.h.save-failed new file mode 100644 index 000000000..47ab0fc6f --- /dev/null +++ b/rtengine/curves.h.save-failed @@ -0,0 +1,1142 @@ +/* + * This file is part of RawTherapee. + * + * Copyright (c) 2004-2010 Gabor Horvath + * + * RawTherapee is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * RawTherapee is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with RawTherapee. If not, see . + */ +#ifndef __CURVES_H__ +#define __CURVES_H__ + +#include +#include +#include +#include "rt_math.h" +#include "../rtgui/mycurve.h" +#include "../rtgui/myflatcurve.h" +#include "../rtgui/mydiagonalcurve.h" +#include "color.h" +#include "procparams.h" +#include "pipettebuffer.h" + +#include "LUT.h" + +#define CURVES_MIN_POLY_POINTS 1000 + +#include "rt_math.h" + +#define CLIPI(a) ((a)>0?((a)<65534?(a):65534):0) + +using namespace std; + +namespace rtengine +{ +class ToneCurve; +class ColorAppearance; + +class CurveFactory +{ + + friend class Curve; + +protected: + + // functions calculating the parameters of the contrast curve based on the desired slope at the center + static double solve_upper (double m, double c, double deriv); + static double solve_lower (double m, double c, double deriv); + static double dupper (const double b, const double m, const double c); + static double dlower (const double b, const double m, const double c); + + // basic convex function between (0,0) and (1,1). m1 and m2 controls the slope at the start and end point + static inline double basel (double x, double m1, double m2) + { + if (x == 0.0) { + return 0.0; + } + + double k = sqrt ((m1 - 1.0) * (m1 - m2) * 0.5) / (1.0 - m2); + double l = (m1 - m2) / (1.0 - m2) + k; + double lx = xlog(x); + return m2 * x + (1.0 - m2) * (2.0 - xexp(k * lx)) * xexp(l * lx); + } + // basic concave function between (0,0) and (1,1). m1 and m2 controls the slope at the start and end point + static inline double baseu (double x, double m1, double m2) + { + return 1.0 - basel(1.0 - x, m1, m2); + } + // convex curve between (0,0) and (1,1) with slope m at (0,0). hr controls the highlight recovery + static inline double cupper (double x, double m, double hr) + { + if (hr > 1.0) { + return baseu (x, m, 2.0 * (hr - 1.0) / m); + } + + double x1 = (1.0 - hr) / m; + double x2 = x1 + hr; + + if (x >= x2) { + return 1.0; + } + + if (x < x1) { + return x * m; + } + + return 1.0 - hr + hr * baseu((x - x1) / hr, m, 0); + } + // concave curve between (0,0) and (1,1) with slope m at (1,1). sr controls the shadow recovery + static inline double clower (double x, double m, double sr) + { + return 1.0 - cupper(1.0 - x, m, sr); + } + // convex curve between (0,0) and (1,1) with slope m at (0,0). hr controls the highlight recovery + static inline double cupper2 (double x, double m, double hr) + { + double x1 = (1.0 - hr) / m; + double x2 = x1 + hr; + + if (x >= x2) { + return 1.0; + } + + if (x < x1) { + return x * m; + } + + return 1.0 - hr + hr * baseu((x - x1) / hr, m, 0.3 * hr); + } + static inline double clower2 (double x, double m, double sr) + { + //curve for b<0; starts with positive slope and then rolls over toward straight line to x=y=1 + double x1 = sr / 1.5 + 0.00001; + + if (x > x1 || sr < 0.001) { + return 1 - (1 - x) * m; + } else { + double y1 = 1 - (1 - x1) * m; + return y1 + m * (x - x1) - (1 - m) * SQR(SQR(1 - x / x1)); + } + } + // tone curve base. a: slope (from exp.comp.), b: black point normalized by 65535, + // D: max. x value (can be>1), hr,sr: highlight,shadow recovery + static inline double basecurve (double x, double a, double b, double D, double hr, double sr) + { + if (b < 0) { + double m = 0.5;//midpoint + double slope = 1.0 + b; //slope of straight line between (0,-b) and (1,1) + double y = -b + m * slope; //value at midpoint + + if (x > m) { + return y + (x - m) * slope; //value on straight line between (m,y) and (1,1) + } else { + return y * clower2(x / m, slope * m / y, 2.0 - sr); + } + } else { + double slope = a / (1.0 - b); + double m = a * D > 1.0 ? b / a + (0.25) / slope : b + (1 - b) / 4; + double y = a * D > 1.0 ? 0.25 : (m - b / a) * slope; + + if (x <= m) { + return b == 0 ? x * slope : clower (x / m, slope * m / y, sr) * y; + } else if (a * D > 1.0) { + return y + (1.0 - y) * cupper2((x - m) / (D - m), slope * (D - m) / (1.0 - y), hr); + } else { + return y + (x - m) * slope; + } + } + } + static inline double simplebasecurve (double x, double b, double sr) + { + // a = 1, D = 1, hr = 0 (unused for a = D = 1) + if (b == 0.0) { + return x; + } else if (b < 0) { + double m = 0.5;//midpoint + double slope = 1.0 + b; //slope of straight line between (0,-b) and (1,1) + double y = -b + m * slope; //value at midpoint + + if (x > m) { + return y + (x - m) * slope; //value on straight line between (m,y) and (1,1) + } else { + return y * clower2(x / m, slope * m / y, 2.0 - sr); + } + } else { + double slope = 1.0 / (1.0 - b); + double m = b + (1 - b) * 0.25; + double y = (m - b) * slope; + + if (x <= m) { + return clower (x / m, slope * m / y, sr) * y; + } else { + return y + (x - m) * slope; + } + } + } + + +public: + const static double sRGBGamma; // standard average gamma + const static double sRGBGammaCurve; // 2.4 in the curve + + + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // accurately determine value from integer array with float as index + //linearly interpolate from ends of range if arg is out of bounds + static inline float interp(int *array, float f) + { + int index = CLIPI(floor(f)); + float part = (float)((f) - index) * (float)(array[index + 1] - array[index]); + return (float)array[index] + part; + } + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + // accurately determine value from float array with float as index + //linearly interpolate from ends of range if arg is out of bounds + static inline float flinterp(float *array, float f) + { + int index = CLIPI(floor(f)); + float part = ((f) - (float)index) * (array[index + 1] - array[index]); + return array[index] + part; + } + //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + static inline double centercontrast (double x, double b, double m); + + // standard srgb gamma and its inverse + static inline double gamma2 (double x) + { + return x <= 0.00304 ? x * 12.92 : 1.055 * exp(log(x) / sRGBGammaCurve) - 0.055; + } + static inline double igamma2 (double x) + { + return x <= 0.03928 ? x / 12.92 : exp(log((x + 0.055) / 1.055) * sRGBGammaCurve); + } + static inline float gamma2 (float x) + { + return x <= 0.00304 ? x * 12.92 : 1.055 * expf(logf(x) / sRGBGammaCurve) - 0.055; + } + static inline float igamma2 (float x) + { + return x <= 0.03928 ? x / 12.92 : expf(logf((x + 0.055) / 1.055) * sRGBGammaCurve); + } + // gamma function with adjustable parameters + static inline double gamma (double x, double gamma, double start, double slope, double mul, double add) + { + return (x <= start ? x*slope : exp(log(x) / gamma) * mul - add); + } + static inline double igamma (double x, double gamma, double start, double slope, double mul, double add) + { + return (x <= start * slope ? x / slope : exp(log((x + add) / mul) * gamma) ); + } + static inline float gamma (float x, float gamma, float start, float slope, float mul, float add) + { + return (x <= start ? x*slope : xexpf(xlogf(x) / gamma) * mul - add); + } + static inline float igamma (float x, float gamma, float start, float slope, float mul, float add) + { + return (x <= start * slope ? x / slope : xexpf(xlogf((x + add) / mul) * gamma) ); + } +#ifdef __SSE2__ + static inline vfloat igamma (vfloat x, vfloat gamma, vfloat start, vfloat slope, vfloat mul, vfloat add) + { +#if !defined(__clang__) + return (x <= start * slope ? x / slope : xexpf(xlogf((x + add) / mul) * gamma) ); +#else + return vself(vmaskf_le(x, start * slope), x / slope, xexpf(xlogf((x + add) / mul) * gamma)); +#endif + } +#endif + static inline float hlcurve (const float exp_scale, const float comp, const float hlrange, float level) + { + if (comp > 0.0) { + float val = level + (hlrange - 65536.0); + + if(val == 0.0f) { // to avoid division by zero + val = 0.000001f; + } + + float Y = val * exp_scale / hlrange; + Y *= comp; + + if(Y <= -1.0) { // to avoid log(<=0) + Y = -.999999f; + } + + float R = hlrange / (val * comp); + return log1p(Y) * R; + } else { + return exp_scale; + } + } + +public: + static void complexCurve (double ecomp, double black, double hlcompr, double hlcomprthresh, double shcompr, double br, double contr, + const std::vector& curvePoints, const std::vector& curvePoints2, + LUTu & histogram, LUTf & hlCurve, LUTf & shCurve, LUTf & outCurve, LUTu & outBeforeCCurveHistogram, ToneCurve & outToneCurve, ToneCurve & outToneCurve2, + + int skip = 1); + static void curveBW (const std::vector& curvePointsbw, const std::vector& curvePointsbw2, const LUTu & histogrambw, LUTu & outBeforeCCurveHistogrambw, + ToneCurve & customToneCurvebw1, ToneCurve & customToneCurvebw2, int skip); + + static void curveCL ( bool & clcutili, const std::vector& clcurvePoints, LUTf & clCurve, int skip); + + static void curveWavContL ( bool & wavcontlutili, const std::vector& wavclcurvePoints, LUTf & wavclCurve,/* LUTu & histogramwavcl, LUTu & outBeforeWavCLurveHistogram,*/int skip); + static void curveDehaContL ( bool & dehacontlutili, const std::vector& dehaclcurvePoints, LUTf & dehaclCurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram); + static void mapcurve ( bool & mapcontlutili, const std::vector& mapcurvePoints, LUTf & mapcurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram); + + static void curveToning ( const std::vector& curvePoints, LUTf & ToningCurve, int skip); + + static void complexsgnCurve ( bool & autili, bool & butili, bool & ccutili, bool & clcutili, const std::vector& acurvePoints, + const std::vector& bcurvePoints, const std::vector& cccurvePoints, const std::vector& lccurvePoints, LUTf & aoutCurve, LUTf & boutCurve, LUTf & satCurve, LUTf & lhskCurve, + int skip = 1); + static void complexLCurve (double br, double contr, const std::vector& curvePoints, const LUTu & histogram, LUTf & outCurve, LUTu & outBeforeCCurveHistogram, int skip, bool & utili); + + static void curveLightBrightColor ( + const std::vector& curvePoints, + const std::vector& curvePoints2, + const std::vector& curvePoints3, + const LUTu & histogram, LUTu & outBeforeCCurveHistogram, + const LUTu & histogramC, LUTu & outBeforeCCurveHistogramC, + ColorAppearance & outColCurve1, + ColorAppearance & outColCurve2, + ColorAppearance & outColCurve3, + int skip = 1); + static void RGBCurve (const std::vector& curvePoints, LUTf & outCurve, int skip); + +}; + +class Curve +{ + + class HashEntry + { + public: + unsigned short smallerValue; + unsigned short higherValue; + }; +protected: + int N; + int ppn; // targeted polyline point number + double* x; + double* y; + // begin of variables used in Parametric curves only + double mc; + double mfc; + double msc; + double mhc; + // end of variables used in Parametric curves only + std::vector poly_x; // X points of the faceted curve + std::vector poly_y; // Y points of the faceted curve + std::vector dyByDx; + std::vector hash; + unsigned short hashSize; // hash table's size, between [10, 100, 1000] + + double* ypp; + + // Fields for the elementary curve polygonisation + double x1, y1, x2, y2, x3, y3; + bool firstPointIncluded; + double increment; + int nbr_points; + + static inline double p00 (double x, double prot) + { + return CurveFactory::clower (x, 2.0, prot); + } + static inline double p11 (double x, double prot) + { + return CurveFactory::cupper (x, 2.0, prot); + } + static inline double p01 (double x, double prot) + { + return x <= 0.5 ? CurveFactory::clower (x * 2, 2.0, prot) * 0.5 : 0.5 + CurveFactory::cupper ((x - 0.5) * 2, 2.0, prot) * 0.5; + } + static inline double p10 (double x, double prot) + { + return x <= 0.5 ? CurveFactory::cupper (x * 2, 2.0, prot) * 0.5 : 0.5 + CurveFactory::clower ((x - 0.5) * 2, 2.0, prot) * 0.5; + } + static inline double pfull (double x, double prot, double sh, double hl) + { + return (1 - sh) * (1 - hl) * p00(x, prot) + sh * hl * p11(x, prot) + (1 - sh) * hl * p01(x, prot) + sh * (1 - hl) * p10(x, prot); + } + + void fillHash(); + void fillDyByDx(); + +public: + Curve (); + virtual ~Curve () {}; + void AddPolygons (); + int getSize () const; // return the number of control points + void getControlPoint(int cpNum, double &x, double &y) const; + virtual double getVal (double t) const = 0; + virtual void getVal (const std::vector& t, std::vector& res) const = 0; + + virtual bool isIdentity () const = 0; +}; + +class DiagonalCurve : public Curve +{ + +protected: + DiagonalCurveType kind; + + void spline_cubic_set (); + void NURBS_set (); + +public: + DiagonalCurve (const std::vector& points, int ppn = CURVES_MIN_POLY_POINTS); + virtual ~DiagonalCurve (); + + double getVal (double t) const; + void getVal (const std::vector& t, std::vector& res) const; + bool isIdentity () const + { + return kind == DCT_Empty; + }; +}; + +class FlatCurve : public Curve +{ + +private: + FlatCurveType kind; + double* leftTangent; + double* rightTangent; + double identityValue; + bool periodic; + + void CtrlPoints_set (); + +public: + + FlatCurve (const std::vector& points, bool isPeriodic = true, int ppn = CURVES_MIN_POLY_POINTS); + virtual ~FlatCurve (); + + double getVal (double t) const; + void getVal (const std::vector& t, std::vector& res) const; + bool setIdentityValue (double iVal); + bool isIdentity () const + { + return kind == FCT_Empty; + }; +}; + +class RetinextransmissionCurve +{ +private: + LUTf luttransmission; // 0xffff range + void Set(const Curve &pCurve); + +public: + virtual ~RetinextransmissionCurve() {}; + RetinextransmissionCurve(); + + void Reset(); + void Set(const Curve *pCurve); + void Set(const std::vector &curvePoints); + float operator[](float index) const + { + return luttransmission[index]; + } + + operator bool (void) const + { + return luttransmission; + } +}; + +class RetinexgaintransmissionCurve +{ +private: + LUTf lutgaintransmission; // 0xffff range + void Set(const Curve &pCurve); + +public: + virtual ~RetinexgaintransmissionCurve() {}; + RetinexgaintransmissionCurve(); + + void Reset(); + void Set(const Curve *pCurve); + void Set(const std::vector &curvePoints); + float operator[](float index) const + { + return lutgaintransmission[index]; + } + + operator bool (void) const + { + return lutgaintransmission; + } +}; + + + +class ToneCurve +{ +public: + LUTf lutToneCurve; // 0xffff range + + virtual ~ToneCurve() {}; + + void Reset(); + void Set(const Curve &pCurve, float gamma = 0); + operator bool (void) const + { + return lutToneCurve; + } +}; + +class OpacityCurve +{ +public: + LUTf lutOpacityCurve; // 0xffff range + + virtual ~OpacityCurve() {}; + + void Reset(); + void Set(const Curve *pCurve); + void Set(const std::vector &curvePoints, bool &opautili); + + // TODO: transfer this method to the Color class... + float blend (float x, float lower, float upper) const + { + return (upper - lower) * lutOpacityCurve[x * 500.f] + lower; + } + void blend3f (float x, float lower1, float upper1, float &result1, float lower2, float upper2, float &result2, float lower3, float upper3, float &result3) const + { + float opacity = lutOpacityCurve[x * 500.f]; + result1 = (upper1 - lower1) * opacity + lower1; + result2 = (upper2 - lower2) * opacity + lower2; + result3 = (upper3 - lower3) * opacity + lower3; + } + + operator bool (void) const + { + return lutOpacityCurve; + } +}; + +class WavCurve +{ +private: + LUTf lutWavCurve; // 0xffff range + void Set(const Curve &pCurve); + +public: + float sum; + + virtual ~WavCurve() {}; + WavCurve(); + void Reset(); + void Set(const std::vector &curvePoints); + float getSum() const + { + return sum; + } + + float operator[](float index) const + { + return lutWavCurve[index]; + } + operator bool (void) const + { + return lutWavCurve; + } +}; + +class WavOpacityCurveRG +{ +private: + LUTf lutOpacityCurveRG; // 0xffff range + void Set(const Curve &pCurve); +public: + virtual ~WavOpacityCurveRG() {}; + WavOpacityCurveRG(); + + void Reset(); + // void Set(const std::vector &curvePoints, bool &opautili); + void Set(const std::vector &curvePoints); + float operator[](float index) const + { + return lutOpacityCurveRG[index]; + } + + operator bool (void) const + { + return lutOpacityCurveRG; + } +}; +class WavOpacityCurveBY +{ +private: + LUTf lutOpacityCurveBY; // 0xffff range + void Set(const Curve &pCurve); + +public: + virtual ~WavOpacityCurveBY() {}; + WavOpacityCurveBY(); + + void Reset(); + void Set(const Curve *pCurve); + void Set(const std::vector &curvePoints); + float operator[](float index) const + { + return lutOpacityCurveBY[index]; + } + + operator bool (void) const + { + return lutOpacityCurveBY; + } +}; +class WavOpacityCurveW +{ +private: + LUTf lutOpacityCurveW; // 0xffff range + void Set(const Curve &pCurve); + +public: + virtual ~WavOpacityCurveW() {}; + WavOpacityCurveW(); + + void Reset(); + void Set(const Curve *pCurve); + void Set(const std::vector &curvePoints); + float operator[](float index) const + { + return lutOpacityCurveW[index]; + } + + operator bool (void) const + { + return lutOpacityCurveW; + } +}; + +class WavOpacityCurveWL +{ +private: + LUTf lutOpacityCurveWL; // 0xffff range + void Set(const Curve &pCurve); + +public: + virtual ~WavOpacityCurveWL() {}; + WavOpacityCurveWL(); + + void Reset(); + void Set(const Curve *pCurve); + void Set(const std::vector &curvePoints); + float operator[](float index) const + { + return lutOpacityCurveWL[index]; + } + + operator bool (void) const + { + return lutOpacityCurveWL; + } +}; + +class NoiseCurve +{ +private: + LUTf lutNoiseCurve; // 0xffff range + float sum; + void Set(const Curve &pCurve); + +public: + virtual ~NoiseCurve() {}; + NoiseCurve(); + void Reset(); + void Set(const std::vector &curvePoints); + + float getSum() const + { + return sum; + } + float operator[](float index) const + { + return lutNoiseCurve[index]; + } + operator bool (void) const + { + return lutNoiseCurve; + } +}; + +class ColorGradientCurve +{ +public: + LUTf lut1; // [0.;1.] range (float values) + LUTf lut2; // [0.;1.] range (float values) + LUTf lut3; // [0.;1.] range (float values) + double low; + double high; + + virtual ~ColorGradientCurve() {}; + + void Reset(); + void SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3], float satur, float lumin); + void SetXYZ(const std::vector &curvePoints, const double xyz_rgb[3][3], float satur, float lumin); + void SetRGB(const Curve *pCurve); + void SetRGB(const std::vector &curvePoints); + + /** + * @brief Get the value of Red, Green and Blue corresponding to the requested index + * @param index value in the [0 ; 1] range + * @param r corresponding red value [0 ; 65535] (return value) + * @param g corresponding green value [0 ; 65535] (return value) + * @param b corresponding blue value [0 ; 65535] (return value) + */ + void getVal(float index, float &r, float &g, float &b) const; + operator bool (void) const + { + return lut1 && lut2 && lut3; + } +}; + +class ColorAppearance +{ +public: + LUTf lutColCurve; // 0xffff range + + virtual ~ColorAppearance() {}; + + void Reset(); + void Set(const Curve &pCurve); + operator bool (void) const + { + return lutColCurve; + } +}; + +class Lightcurve : public ColorAppearance +{ +public: + void Apply(float& Li) const; +}; + +//lightness curve +inline void Lightcurve::Apply (float& Li) const +{ + + assert (lutColCurve); + + Li = lutColCurve[Li]; +} + +class Brightcurve : public ColorAppearance +{ +public: + void Apply(float& Br) const; +}; + +//brightness curve +inline void Brightcurve::Apply (float& Br) const +{ + + assert (lutColCurve); + + Br = lutColCurve[Br]; +} + +class Chromacurve : public ColorAppearance +{ +public: + void Apply(float& Cr) const; +}; + +//Chroma curve +inline void Chromacurve::Apply (float& Cr) const +{ + + assert (lutColCurve); + + Cr = lutColCurve[Cr]; +} +class Saturcurve : public ColorAppearance +{ +public: + void Apply(float& Sa) const; +}; + +//Saturation curve +inline void Saturcurve::Apply (float& Sa) const +{ + + assert (lutColCurve); + + Sa = lutColCurve[Sa]; +} + +class Colorfcurve : public ColorAppearance +{ +public: + void Apply(float& Cf) const; +}; + +//Colorfullness curve +inline void Colorfcurve::Apply (float& Cf) const +{ + + assert (lutColCurve); + + Cf = lutColCurve[Cf]; +} + + +class StandardToneCurve : public ToneCurve +{ +public: + void Apply(float& r, float& g, float& b) const; + + // Applies the tone curve to `r`, `g`, `b` arrays, starting at `r[start]` + // and ending at `r[end]` (and respectively for `b` and `g`). Uses SSE + // and requires that `r`, `g`, and `b` pointers have the same alignment. + void BatchApply( + const size_t start, const size_t end, + float *r, float *g, float *b) const; +}; + +class AdobeToneCurve : public ToneCurve +{ +private: + void RGBTone(float& r, float& g, float& b) const; // helper for tone curve + +public: + void Apply(float& r, float& g, float& b) const; +}; + +class SatAndValueBlendingToneCurve : public ToneCurve +{ +public: + void Apply(float& r, float& g, float& b) const; +}; + +class WeightedStdToneCurve : public ToneCurve +{ +private: + float Triangle(float refX, float refY, float X2) const; +#if defined( __SSE2__ ) && defined( __x86_64__ ) + vfloat Triangle(vfloat refX, vfloat refY, vfloat X2) const; +#endif +public: + void Apply(float& r, float& g, float& b) const; + void BatchApply(const size_t start, const size_t end, float *r, float *g, float *b) const; +}; + +class LuminanceToneCurve : public ToneCurve +{ +public: + void Apply(float& r, float& g, float& b) const; +}; + +class PerceptualToneCurveState +{ +public: + float Working2Prophoto[3][3]; + float Prophoto2Working[3][3]; + float cmul_contrast; + bool isProphoto; +}; + +// Tone curve whose purpose is to keep the color appearance constant, that is the curve changes contrast +// but colors appears to have the same hue and saturation as before. As contrast and saturation is tightly +// coupled in human vision saturation is modulated based on the curve's contrast, and that way the appearance +// can be kept perceptually constant (within limits). +class PerceptualToneCurve : public ToneCurve +{ +private: + static float cf_range[2]; + static float cf[1000]; + // for ciecam02 + static float f, c, nc, yb, la, xw, yw, zw, gamut; + static float n, d, nbb, ncb, cz, aw, wh, pfl, fl, pow1; + + static void cubic_spline(const float x[], const float y[], const int len, const float out_x[], float out_y[], const int out_len); + static float find_minimum_interval_halving(float (*func)(float x, void *arg), void *arg, float a, float b, float tol, int nmax); + static float find_tc_slope_fun(float k, void *arg); + static float get_curve_val(float x, float range[2], float lut[], size_t lut_size); + float calculateToneCurveContrastValue() const; +public: + static void init(); + void initApplyState(PerceptualToneCurveState & state, Glib::ustring workingSpace) const; + void BatchApply(const size_t start, const size_t end, float *r, float *g, float *b, const PerceptualToneCurveState &state) const; +}; + +// Standard tone curve +inline void StandardToneCurve::Apply (float& r, float& g, float& b) const +{ + + assert (lutToneCurve); + + r = lutToneCurve[r]; + g = lutToneCurve[g]; + b = lutToneCurve[b]; +} + +inline void StandardToneCurve::BatchApply( + const size_t start, const size_t end, + float *r, float *g, float *b) const { + assert (lutToneCurve); + assert (lutToneCurve.getClip() & LUT_CLIP_BELOW); + assert (lutToneCurve.getClip() & LUT_CLIP_ABOVE); + + // All pointers must have the same alignment for SSE usage. In the loop body below, + // we will only check `r`, assuming that the same result would hold for `g` and `b`. + assert (reinterpret_cast(r) % 16 == reinterpret_cast(g) % 16); + assert (reinterpret_cast(g) % 16 == reinterpret_cast(b) % 16); + + size_t i = start; + while (true) { + if (i >= end) { + // If we get to the end before getting to an aligned address, just return. + // (Or, for non-SSE mode, if we get to the end.) + return; +#if defined( __SSE2__ ) && defined( __x86_64__ ) + } else if (reinterpret_cast(&r[i]) % 16 == 0) { + // Otherwise, we get to the first aligned address; go to the SSE part. + break; +#endif + } + r[i] = lutToneCurve[r[i]]; + g[i] = lutToneCurve[g[i]]; + b[i] = lutToneCurve[b[i]]; + i++; + } + +#if defined( __SSE2__ ) && defined( __x86_64__ ) + 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]); + } + + // Remainder in non-SSE. + for (; i < end; ++i) { + r[i] = lutToneCurve[r[i]]; + g[i] = lutToneCurve[g[i]]; + b[i] = lutToneCurve[b[i]]; + } +#endif +} + +// 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 +{ + + assert (lutToneCurve); + + if (r >= g) { + if (g > b) { + RGBTone (r, g, b); // Case 1: r >= g > b + } else if (b > r) { + RGBTone (b, r, g); // Case 2: b > r >= g + } else if (b > g) { + RGBTone (r, b, g); // Case 3: r >= b > g + } else { // Case 4: r >= g == b + r = lutToneCurve[r]; + g = lutToneCurve[g]; + b = g; + } + } else { + if (r >= b) { + RGBTone (g, r, b); // Case 5: g > r >= b + } else if (b > g) { + RGBTone (b, g, r); // Case 6: b > g > r + } else { + RGBTone (g, b, r); // Case 7: g >= b > r + } + } +} + +inline void AdobeToneCurve::RGBTone (float& r, float& g, float& b) const +{ + float rold = r, gold = g, bold = b; + + r = lutToneCurve[rold]; + b = lutToneCurve[bold]; + g = b + ((r - b) * (gold - bold) / (rold - bold)); +} + +// Modifying the Luminance channel only +inline void LuminanceToneCurve::Apply(float &r, float &g, float &b) const +{ + assert (lutToneCurve); + + float currLuminance = r * 0.2126729f + g * 0.7151521f + b * 0.0721750f; + const float newLuminance = lutToneCurve[currLuminance]; + currLuminance = currLuminance == 0.f ? 0.00001f : currLuminance; + const float coef = newLuminance / currLuminance; + r = LIM(r * coef, 0.f, 65535.f); + g = LIM(g * coef, 0.f, 65535.f); + b = LIM(b * coef, 0.f, 65535.f); +} + +inline float WeightedStdToneCurve::Triangle(float a, float a1, float b) const +{ + if (a != b) { + float b1; + float a2 = a1 - a; + + if (b < a) { + b1 = b + a2 * b / a ; + } else { + b1 = b + a2 * (65535.f - b) / (65535.f - a); + } + + return b1; + } + + return a1; +} + +#if defined( __SSE2__ ) && defined( __x86_64__ ) +inline vfloat WeightedStdToneCurve::Triangle(vfloat a, vfloat a1, vfloat b) const +{ + vfloat a2 = a1 - a; + vmask cmask = vmaskf_lt(b, a); + vfloat b3 = vself(cmask, b, F2V(65535.f) - b); + vfloat a3 = vself(cmask, a, F2V(65535.f) - a); + return b + a2 * b3 / a3; +} +#endif + +// 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 +{ + + assert (lutToneCurve); + + r = CLIP(r); + g = CLIP(g); + b = CLIP(b); + float r1 = lutToneCurve[r]; + float g1 = Triangle(r, r1, g); + float b1 = Triangle(r, r1, b); + + float g2 = lutToneCurve[g]; + float r2 = Triangle(g, g2, r); + float b2 = Triangle(g, g2, b); + + float b3 = lutToneCurve[b]; + float r3 = Triangle(b, b3, r); + float g3 = Triangle(b, b3, g); + + 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); +} + +inline void WeightedStdToneCurve::BatchApply(const size_t start, const size_t end, float *r, float *g, float *b) const { + assert (lutToneCurve); + assert (lutToneCurve.getClip() & LUT_CLIP_BELOW); + assert (lutToneCurve.getClip() & LUT_CLIP_ABOVE); + + // All pointers must have the same alignment for SSE usage. In the loop body below, + // we will only check `r`, assuming that the same result would hold for `g` and `b`. + assert (reinterpret_cast(r) % 16 == reinterpret_cast(g) % 16); + assert (reinterpret_cast(g) % 16 == reinterpret_cast(b) % 16); + + size_t i = start; + while (true) { + if (i >= end) { + // If we get to the end before getting to an aligned address, just return. + // (Or, for non-SSE mode, if we get to the end.) + return; +#if defined( __SSE2__ ) && defined( __x86_64__ ) + } else if (reinterpret_cast(&r[i]) % 16 == 0) { + // Otherwise, we get to the first aligned address; go to the SSE part. + break; +#endif + } + Apply(r[i], g[i], b[i]); + i++; + } + +#if defined( __SSE2__ ) && defined( __x86_64__ ) + const vfloat c65535v = F2V(65535.f); + const vfloat zd5v = F2V(0.5f); + const vfloat zd25v = F2V(0.25f); + + for (; i + 3 < end; i += 4) { + vfloat r_val = LIMV(LVF(r[i]), ZEROV, c65535v); + vfloat g_val = LIMV(LVF(g[i]), ZEROV, c65535v); + vfloat b_val = LIMV(LVF(b[i]), ZEROV, c65535v); + vfloat r1 = lutToneCurve[r_val]; + vfloat g1 = Triangle(r_val, r1, g_val); + vfloat b1 = Triangle(r_val, r1, b_val); + + vfloat g2 = lutToneCurve[g_val]; + vfloat r2 = Triangle(g_val, g2, r_val); + vfloat b2 = Triangle(g_val, g2, b_val); + + vfloat b3 = lutToneCurve[b_val]; + 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)); + } + + // Remainder in non-SSE. + for (; i < end; ++i) { + Apply(r[i], g[i], b[i]); + } +#endif +} + +// 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 +{ + + assert (lutToneCurve); + + r = CLIP(r); + g = CLIP(g); + b = CLIP(b); + + const float lum = (r + g + b) / 3.f; + const float newLum = lutToneCurve[lum]; + + float h, s, v; + Color::rgb2hsvtc(r, g, b, h, s, v); + + float dV; + if (newLum >= lum) { + // Linearly targeting Value = 1 and Saturation = 0 + const float coef = (newLum - lum) / (65535.f - lum); + dV = (1.f - v) * coef; + s *= 1.f - coef; + } else { + // Linearly targeting Value = 0 + const float coef = (newLum - lum) / lum ; + dV = v * coef; + } + Color::hsv2rgbdcp(h, s, v + dV, r, g, b); +} + +} + +#undef CLIPI + +#endif diff --git a/rtengine/rt_math.h b/rtengine/rt_math.h index 4bdfe6f06..787e2e63f 100644 --- a/rtengine/rt_math.h +++ b/rtengine/rt_math.h @@ -25,6 +25,8 @@ constexpr double RT_NAN = std::numeric_limits::quiet_NaN(); constexpr float RT_PI_F = RT_PI; constexpr float RT_PI_F_2 = RT_PI_2; constexpr float RT_PI_F_180 = RT_PI_180; +constexpr float RT_1_PI_F = RT_1_PI; +constexpr float RT_2_PI_F = RT_2_PI; constexpr float RT_INFINITY_F = std::numeric_limits::infinity(); constexpr float RT_NAN_F = std::numeric_limits::quiet_NaN(); diff --git a/rtengine/sleef.c b/rtengine/sleef.c index 17dfccc0f..f03c9f1b3 100644 --- a/rtengine/sleef.c +++ b/rtengine/sleef.c @@ -923,9 +923,8 @@ __inline float mulsignf(float x, float y) { return intBitsToFloat(floatToRawIntBits(x) ^ (floatToRawIntBits(y) & (1 << 31))); } -__inline float signf(float d) { return mulsignf(1, d); } +__inline float signf(float d) { return copysign(1, d); } __inline float mlaf(float x, float y, float z) { return x * y + z; } -__inline float xrintf(float x) { return x < 0 ? (int)(x - 0.5f) : (int)(x + 0.5f); } __inline int xisnanf(float x) { return x != x; } __inline int xisinff(float x) { return x == rtengine::RT_INFINITY_F || x == -rtengine::RT_INFINITY_F; } @@ -984,7 +983,7 @@ __inline float xsinf(float d) { int q; float u, s; - q = (int)xrintf(d * (float)rtengine::RT_1_PI); + q = rint(d * rtengine::RT_1_PI_F); d = mlaf(q, -PI4_Af*4, d); d = mlaf(q, -PI4_Bf*4, d); @@ -1009,7 +1008,7 @@ __inline float xcosf(float d) { int q; float u, s; - q = 1 + 2*(int)xrintf(d * (float)rtengine::RT_1_PI - 0.5f); + q = 1 + 2*rint(d * rtengine::RT_1_PI_F - 0.5f); d = mlaf(q, -PI4_Af*2, d); d = mlaf(q, -PI4_Bf*2, d); @@ -1035,7 +1034,7 @@ __inline float2 xsincosf(float d) { float u, s, t; float2 r; - q = (int)rint(d * ((float)(2 * rtengine::RT_1_PI))); + q = rint(d * rtengine::RT_2_PI_F); s = d; @@ -1076,7 +1075,7 @@ __inline float xtanf(float d) { int q; float u, s, x; - q = (int)xrintf(d * (float)(2 * rtengine::RT_1_PI)); + q = rint(d * (float)(2 * rtengine::RT_1_PI)); x = d; @@ -1202,7 +1201,7 @@ __inline float xlogf(float d) { __inline float xexpf(float d) { if(d<=-104.0f) return 0.0f; - int q = (int)xrintf(d * R_LN2f); + int q = rint(d * R_LN2f); float s, u; s = mlaf(q, -L2Uf, d); From 1006dd1ebf98a3baaa89f35441da8005398838d1 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 8 Jan 2018 13:19:44 +0100 Subject: [PATCH 11/19] removed accidently committed file --- rtengine/curves.h.save-failed | 1142 --------------------------------- 1 file changed, 1142 deletions(-) delete mode 100644 rtengine/curves.h.save-failed diff --git a/rtengine/curves.h.save-failed b/rtengine/curves.h.save-failed deleted file mode 100644 index 47ab0fc6f..000000000 --- a/rtengine/curves.h.save-failed +++ /dev/null @@ -1,1142 +0,0 @@ -/* - * This file is part of RawTherapee. - * - * Copyright (c) 2004-2010 Gabor Horvath - * - * RawTherapee is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * RawTherapee is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with RawTherapee. If not, see . - */ -#ifndef __CURVES_H__ -#define __CURVES_H__ - -#include -#include -#include -#include "rt_math.h" -#include "../rtgui/mycurve.h" -#include "../rtgui/myflatcurve.h" -#include "../rtgui/mydiagonalcurve.h" -#include "color.h" -#include "procparams.h" -#include "pipettebuffer.h" - -#include "LUT.h" - -#define CURVES_MIN_POLY_POINTS 1000 - -#include "rt_math.h" - -#define CLIPI(a) ((a)>0?((a)<65534?(a):65534):0) - -using namespace std; - -namespace rtengine -{ -class ToneCurve; -class ColorAppearance; - -class CurveFactory -{ - - friend class Curve; - -protected: - - // functions calculating the parameters of the contrast curve based on the desired slope at the center - static double solve_upper (double m, double c, double deriv); - static double solve_lower (double m, double c, double deriv); - static double dupper (const double b, const double m, const double c); - static double dlower (const double b, const double m, const double c); - - // basic convex function between (0,0) and (1,1). m1 and m2 controls the slope at the start and end point - static inline double basel (double x, double m1, double m2) - { - if (x == 0.0) { - return 0.0; - } - - double k = sqrt ((m1 - 1.0) * (m1 - m2) * 0.5) / (1.0 - m2); - double l = (m1 - m2) / (1.0 - m2) + k; - double lx = xlog(x); - return m2 * x + (1.0 - m2) * (2.0 - xexp(k * lx)) * xexp(l * lx); - } - // basic concave function between (0,0) and (1,1). m1 and m2 controls the slope at the start and end point - static inline double baseu (double x, double m1, double m2) - { - return 1.0 - basel(1.0 - x, m1, m2); - } - // convex curve between (0,0) and (1,1) with slope m at (0,0). hr controls the highlight recovery - static inline double cupper (double x, double m, double hr) - { - if (hr > 1.0) { - return baseu (x, m, 2.0 * (hr - 1.0) / m); - } - - double x1 = (1.0 - hr) / m; - double x2 = x1 + hr; - - if (x >= x2) { - return 1.0; - } - - if (x < x1) { - return x * m; - } - - return 1.0 - hr + hr * baseu((x - x1) / hr, m, 0); - } - // concave curve between (0,0) and (1,1) with slope m at (1,1). sr controls the shadow recovery - static inline double clower (double x, double m, double sr) - { - return 1.0 - cupper(1.0 - x, m, sr); - } - // convex curve between (0,0) and (1,1) with slope m at (0,0). hr controls the highlight recovery - static inline double cupper2 (double x, double m, double hr) - { - double x1 = (1.0 - hr) / m; - double x2 = x1 + hr; - - if (x >= x2) { - return 1.0; - } - - if (x < x1) { - return x * m; - } - - return 1.0 - hr + hr * baseu((x - x1) / hr, m, 0.3 * hr); - } - static inline double clower2 (double x, double m, double sr) - { - //curve for b<0; starts with positive slope and then rolls over toward straight line to x=y=1 - double x1 = sr / 1.5 + 0.00001; - - if (x > x1 || sr < 0.001) { - return 1 - (1 - x) * m; - } else { - double y1 = 1 - (1 - x1) * m; - return y1 + m * (x - x1) - (1 - m) * SQR(SQR(1 - x / x1)); - } - } - // tone curve base. a: slope (from exp.comp.), b: black point normalized by 65535, - // D: max. x value (can be>1), hr,sr: highlight,shadow recovery - static inline double basecurve (double x, double a, double b, double D, double hr, double sr) - { - if (b < 0) { - double m = 0.5;//midpoint - double slope = 1.0 + b; //slope of straight line between (0,-b) and (1,1) - double y = -b + m * slope; //value at midpoint - - if (x > m) { - return y + (x - m) * slope; //value on straight line between (m,y) and (1,1) - } else { - return y * clower2(x / m, slope * m / y, 2.0 - sr); - } - } else { - double slope = a / (1.0 - b); - double m = a * D > 1.0 ? b / a + (0.25) / slope : b + (1 - b) / 4; - double y = a * D > 1.0 ? 0.25 : (m - b / a) * slope; - - if (x <= m) { - return b == 0 ? x * slope : clower (x / m, slope * m / y, sr) * y; - } else if (a * D > 1.0) { - return y + (1.0 - y) * cupper2((x - m) / (D - m), slope * (D - m) / (1.0 - y), hr); - } else { - return y + (x - m) * slope; - } - } - } - static inline double simplebasecurve (double x, double b, double sr) - { - // a = 1, D = 1, hr = 0 (unused for a = D = 1) - if (b == 0.0) { - return x; - } else if (b < 0) { - double m = 0.5;//midpoint - double slope = 1.0 + b; //slope of straight line between (0,-b) and (1,1) - double y = -b + m * slope; //value at midpoint - - if (x > m) { - return y + (x - m) * slope; //value on straight line between (m,y) and (1,1) - } else { - return y * clower2(x / m, slope * m / y, 2.0 - sr); - } - } else { - double slope = 1.0 / (1.0 - b); - double m = b + (1 - b) * 0.25; - double y = (m - b) * slope; - - if (x <= m) { - return clower (x / m, slope * m / y, sr) * y; - } else { - return y + (x - m) * slope; - } - } - } - - -public: - const static double sRGBGamma; // standard average gamma - const static double sRGBGammaCurve; // 2.4 in the curve - - - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // accurately determine value from integer array with float as index - //linearly interpolate from ends of range if arg is out of bounds - static inline float interp(int *array, float f) - { - int index = CLIPI(floor(f)); - float part = (float)((f) - index) * (float)(array[index + 1] - array[index]); - return (float)array[index] + part; - } - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - // accurately determine value from float array with float as index - //linearly interpolate from ends of range if arg is out of bounds - static inline float flinterp(float *array, float f) - { - int index = CLIPI(floor(f)); - float part = ((f) - (float)index) * (array[index + 1] - array[index]); - return array[index] + part; - } - //%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% - - static inline double centercontrast (double x, double b, double m); - - // standard srgb gamma and its inverse - static inline double gamma2 (double x) - { - return x <= 0.00304 ? x * 12.92 : 1.055 * exp(log(x) / sRGBGammaCurve) - 0.055; - } - static inline double igamma2 (double x) - { - return x <= 0.03928 ? x / 12.92 : exp(log((x + 0.055) / 1.055) * sRGBGammaCurve); - } - static inline float gamma2 (float x) - { - return x <= 0.00304 ? x * 12.92 : 1.055 * expf(logf(x) / sRGBGammaCurve) - 0.055; - } - static inline float igamma2 (float x) - { - return x <= 0.03928 ? x / 12.92 : expf(logf((x + 0.055) / 1.055) * sRGBGammaCurve); - } - // gamma function with adjustable parameters - static inline double gamma (double x, double gamma, double start, double slope, double mul, double add) - { - return (x <= start ? x*slope : exp(log(x) / gamma) * mul - add); - } - static inline double igamma (double x, double gamma, double start, double slope, double mul, double add) - { - return (x <= start * slope ? x / slope : exp(log((x + add) / mul) * gamma) ); - } - static inline float gamma (float x, float gamma, float start, float slope, float mul, float add) - { - return (x <= start ? x*slope : xexpf(xlogf(x) / gamma) * mul - add); - } - static inline float igamma (float x, float gamma, float start, float slope, float mul, float add) - { - return (x <= start * slope ? x / slope : xexpf(xlogf((x + add) / mul) * gamma) ); - } -#ifdef __SSE2__ - static inline vfloat igamma (vfloat x, vfloat gamma, vfloat start, vfloat slope, vfloat mul, vfloat add) - { -#if !defined(__clang__) - return (x <= start * slope ? x / slope : xexpf(xlogf((x + add) / mul) * gamma) ); -#else - return vself(vmaskf_le(x, start * slope), x / slope, xexpf(xlogf((x + add) / mul) * gamma)); -#endif - } -#endif - static inline float hlcurve (const float exp_scale, const float comp, const float hlrange, float level) - { - if (comp > 0.0) { - float val = level + (hlrange - 65536.0); - - if(val == 0.0f) { // to avoid division by zero - val = 0.000001f; - } - - float Y = val * exp_scale / hlrange; - Y *= comp; - - if(Y <= -1.0) { // to avoid log(<=0) - Y = -.999999f; - } - - float R = hlrange / (val * comp); - return log1p(Y) * R; - } else { - return exp_scale; - } - } - -public: - static void complexCurve (double ecomp, double black, double hlcompr, double hlcomprthresh, double shcompr, double br, double contr, - const std::vector& curvePoints, const std::vector& curvePoints2, - LUTu & histogram, LUTf & hlCurve, LUTf & shCurve, LUTf & outCurve, LUTu & outBeforeCCurveHistogram, ToneCurve & outToneCurve, ToneCurve & outToneCurve2, - - int skip = 1); - static void curveBW (const std::vector& curvePointsbw, const std::vector& curvePointsbw2, const LUTu & histogrambw, LUTu & outBeforeCCurveHistogrambw, - ToneCurve & customToneCurvebw1, ToneCurve & customToneCurvebw2, int skip); - - static void curveCL ( bool & clcutili, const std::vector& clcurvePoints, LUTf & clCurve, int skip); - - static void curveWavContL ( bool & wavcontlutili, const std::vector& wavclcurvePoints, LUTf & wavclCurve,/* LUTu & histogramwavcl, LUTu & outBeforeWavCLurveHistogram,*/int skip); - static void curveDehaContL ( bool & dehacontlutili, const std::vector& dehaclcurvePoints, LUTf & dehaclCurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram); - static void mapcurve ( bool & mapcontlutili, const std::vector& mapcurvePoints, LUTf & mapcurve, int skip, const LUTu & histogram, LUTu & outBeforeCurveHistogram); - - static void curveToning ( const std::vector& curvePoints, LUTf & ToningCurve, int skip); - - static void complexsgnCurve ( bool & autili, bool & butili, bool & ccutili, bool & clcutili, const std::vector& acurvePoints, - const std::vector& bcurvePoints, const std::vector& cccurvePoints, const std::vector& lccurvePoints, LUTf & aoutCurve, LUTf & boutCurve, LUTf & satCurve, LUTf & lhskCurve, - int skip = 1); - static void complexLCurve (double br, double contr, const std::vector& curvePoints, const LUTu & histogram, LUTf & outCurve, LUTu & outBeforeCCurveHistogram, int skip, bool & utili); - - static void curveLightBrightColor ( - const std::vector& curvePoints, - const std::vector& curvePoints2, - const std::vector& curvePoints3, - const LUTu & histogram, LUTu & outBeforeCCurveHistogram, - const LUTu & histogramC, LUTu & outBeforeCCurveHistogramC, - ColorAppearance & outColCurve1, - ColorAppearance & outColCurve2, - ColorAppearance & outColCurve3, - int skip = 1); - static void RGBCurve (const std::vector& curvePoints, LUTf & outCurve, int skip); - -}; - -class Curve -{ - - class HashEntry - { - public: - unsigned short smallerValue; - unsigned short higherValue; - }; -protected: - int N; - int ppn; // targeted polyline point number - double* x; - double* y; - // begin of variables used in Parametric curves only - double mc; - double mfc; - double msc; - double mhc; - // end of variables used in Parametric curves only - std::vector poly_x; // X points of the faceted curve - std::vector poly_y; // Y points of the faceted curve - std::vector dyByDx; - std::vector hash; - unsigned short hashSize; // hash table's size, between [10, 100, 1000] - - double* ypp; - - // Fields for the elementary curve polygonisation - double x1, y1, x2, y2, x3, y3; - bool firstPointIncluded; - double increment; - int nbr_points; - - static inline double p00 (double x, double prot) - { - return CurveFactory::clower (x, 2.0, prot); - } - static inline double p11 (double x, double prot) - { - return CurveFactory::cupper (x, 2.0, prot); - } - static inline double p01 (double x, double prot) - { - return x <= 0.5 ? CurveFactory::clower (x * 2, 2.0, prot) * 0.5 : 0.5 + CurveFactory::cupper ((x - 0.5) * 2, 2.0, prot) * 0.5; - } - static inline double p10 (double x, double prot) - { - return x <= 0.5 ? CurveFactory::cupper (x * 2, 2.0, prot) * 0.5 : 0.5 + CurveFactory::clower ((x - 0.5) * 2, 2.0, prot) * 0.5; - } - static inline double pfull (double x, double prot, double sh, double hl) - { - return (1 - sh) * (1 - hl) * p00(x, prot) + sh * hl * p11(x, prot) + (1 - sh) * hl * p01(x, prot) + sh * (1 - hl) * p10(x, prot); - } - - void fillHash(); - void fillDyByDx(); - -public: - Curve (); - virtual ~Curve () {}; - void AddPolygons (); - int getSize () const; // return the number of control points - void getControlPoint(int cpNum, double &x, double &y) const; - virtual double getVal (double t) const = 0; - virtual void getVal (const std::vector& t, std::vector& res) const = 0; - - virtual bool isIdentity () const = 0; -}; - -class DiagonalCurve : public Curve -{ - -protected: - DiagonalCurveType kind; - - void spline_cubic_set (); - void NURBS_set (); - -public: - DiagonalCurve (const std::vector& points, int ppn = CURVES_MIN_POLY_POINTS); - virtual ~DiagonalCurve (); - - double getVal (double t) const; - void getVal (const std::vector& t, std::vector& res) const; - bool isIdentity () const - { - return kind == DCT_Empty; - }; -}; - -class FlatCurve : public Curve -{ - -private: - FlatCurveType kind; - double* leftTangent; - double* rightTangent; - double identityValue; - bool periodic; - - void CtrlPoints_set (); - -public: - - FlatCurve (const std::vector& points, bool isPeriodic = true, int ppn = CURVES_MIN_POLY_POINTS); - virtual ~FlatCurve (); - - double getVal (double t) const; - void getVal (const std::vector& t, std::vector& res) const; - bool setIdentityValue (double iVal); - bool isIdentity () const - { - return kind == FCT_Empty; - }; -}; - -class RetinextransmissionCurve -{ -private: - LUTf luttransmission; // 0xffff range - void Set(const Curve &pCurve); - -public: - virtual ~RetinextransmissionCurve() {}; - RetinextransmissionCurve(); - - void Reset(); - void Set(const Curve *pCurve); - void Set(const std::vector &curvePoints); - float operator[](float index) const - { - return luttransmission[index]; - } - - operator bool (void) const - { - return luttransmission; - } -}; - -class RetinexgaintransmissionCurve -{ -private: - LUTf lutgaintransmission; // 0xffff range - void Set(const Curve &pCurve); - -public: - virtual ~RetinexgaintransmissionCurve() {}; - RetinexgaintransmissionCurve(); - - void Reset(); - void Set(const Curve *pCurve); - void Set(const std::vector &curvePoints); - float operator[](float index) const - { - return lutgaintransmission[index]; - } - - operator bool (void) const - { - return lutgaintransmission; - } -}; - - - -class ToneCurve -{ -public: - LUTf lutToneCurve; // 0xffff range - - virtual ~ToneCurve() {}; - - void Reset(); - void Set(const Curve &pCurve, float gamma = 0); - operator bool (void) const - { - return lutToneCurve; - } -}; - -class OpacityCurve -{ -public: - LUTf lutOpacityCurve; // 0xffff range - - virtual ~OpacityCurve() {}; - - void Reset(); - void Set(const Curve *pCurve); - void Set(const std::vector &curvePoints, bool &opautili); - - // TODO: transfer this method to the Color class... - float blend (float x, float lower, float upper) const - { - return (upper - lower) * lutOpacityCurve[x * 500.f] + lower; - } - void blend3f (float x, float lower1, float upper1, float &result1, float lower2, float upper2, float &result2, float lower3, float upper3, float &result3) const - { - float opacity = lutOpacityCurve[x * 500.f]; - result1 = (upper1 - lower1) * opacity + lower1; - result2 = (upper2 - lower2) * opacity + lower2; - result3 = (upper3 - lower3) * opacity + lower3; - } - - operator bool (void) const - { - return lutOpacityCurve; - } -}; - -class WavCurve -{ -private: - LUTf lutWavCurve; // 0xffff range - void Set(const Curve &pCurve); - -public: - float sum; - - virtual ~WavCurve() {}; - WavCurve(); - void Reset(); - void Set(const std::vector &curvePoints); - float getSum() const - { - return sum; - } - - float operator[](float index) const - { - return lutWavCurve[index]; - } - operator bool (void) const - { - return lutWavCurve; - } -}; - -class WavOpacityCurveRG -{ -private: - LUTf lutOpacityCurveRG; // 0xffff range - void Set(const Curve &pCurve); -public: - virtual ~WavOpacityCurveRG() {}; - WavOpacityCurveRG(); - - void Reset(); - // void Set(const std::vector &curvePoints, bool &opautili); - void Set(const std::vector &curvePoints); - float operator[](float index) const - { - return lutOpacityCurveRG[index]; - } - - operator bool (void) const - { - return lutOpacityCurveRG; - } -}; -class WavOpacityCurveBY -{ -private: - LUTf lutOpacityCurveBY; // 0xffff range - void Set(const Curve &pCurve); - -public: - virtual ~WavOpacityCurveBY() {}; - WavOpacityCurveBY(); - - void Reset(); - void Set(const Curve *pCurve); - void Set(const std::vector &curvePoints); - float operator[](float index) const - { - return lutOpacityCurveBY[index]; - } - - operator bool (void) const - { - return lutOpacityCurveBY; - } -}; -class WavOpacityCurveW -{ -private: - LUTf lutOpacityCurveW; // 0xffff range - void Set(const Curve &pCurve); - -public: - virtual ~WavOpacityCurveW() {}; - WavOpacityCurveW(); - - void Reset(); - void Set(const Curve *pCurve); - void Set(const std::vector &curvePoints); - float operator[](float index) const - { - return lutOpacityCurveW[index]; - } - - operator bool (void) const - { - return lutOpacityCurveW; - } -}; - -class WavOpacityCurveWL -{ -private: - LUTf lutOpacityCurveWL; // 0xffff range - void Set(const Curve &pCurve); - -public: - virtual ~WavOpacityCurveWL() {}; - WavOpacityCurveWL(); - - void Reset(); - void Set(const Curve *pCurve); - void Set(const std::vector &curvePoints); - float operator[](float index) const - { - return lutOpacityCurveWL[index]; - } - - operator bool (void) const - { - return lutOpacityCurveWL; - } -}; - -class NoiseCurve -{ -private: - LUTf lutNoiseCurve; // 0xffff range - float sum; - void Set(const Curve &pCurve); - -public: - virtual ~NoiseCurve() {}; - NoiseCurve(); - void Reset(); - void Set(const std::vector &curvePoints); - - float getSum() const - { - return sum; - } - float operator[](float index) const - { - return lutNoiseCurve[index]; - } - operator bool (void) const - { - return lutNoiseCurve; - } -}; - -class ColorGradientCurve -{ -public: - LUTf lut1; // [0.;1.] range (float values) - LUTf lut2; // [0.;1.] range (float values) - LUTf lut3; // [0.;1.] range (float values) - double low; - double high; - - virtual ~ColorGradientCurve() {}; - - void Reset(); - void SetXYZ(const Curve *pCurve, const double xyz_rgb[3][3], float satur, float lumin); - void SetXYZ(const std::vector &curvePoints, const double xyz_rgb[3][3], float satur, float lumin); - void SetRGB(const Curve *pCurve); - void SetRGB(const std::vector &curvePoints); - - /** - * @brief Get the value of Red, Green and Blue corresponding to the requested index - * @param index value in the [0 ; 1] range - * @param r corresponding red value [0 ; 65535] (return value) - * @param g corresponding green value [0 ; 65535] (return value) - * @param b corresponding blue value [0 ; 65535] (return value) - */ - void getVal(float index, float &r, float &g, float &b) const; - operator bool (void) const - { - return lut1 && lut2 && lut3; - } -}; - -class ColorAppearance -{ -public: - LUTf lutColCurve; // 0xffff range - - virtual ~ColorAppearance() {}; - - void Reset(); - void Set(const Curve &pCurve); - operator bool (void) const - { - return lutColCurve; - } -}; - -class Lightcurve : public ColorAppearance -{ -public: - void Apply(float& Li) const; -}; - -//lightness curve -inline void Lightcurve::Apply (float& Li) const -{ - - assert (lutColCurve); - - Li = lutColCurve[Li]; -} - -class Brightcurve : public ColorAppearance -{ -public: - void Apply(float& Br) const; -}; - -//brightness curve -inline void Brightcurve::Apply (float& Br) const -{ - - assert (lutColCurve); - - Br = lutColCurve[Br]; -} - -class Chromacurve : public ColorAppearance -{ -public: - void Apply(float& Cr) const; -}; - -//Chroma curve -inline void Chromacurve::Apply (float& Cr) const -{ - - assert (lutColCurve); - - Cr = lutColCurve[Cr]; -} -class Saturcurve : public ColorAppearance -{ -public: - void Apply(float& Sa) const; -}; - -//Saturation curve -inline void Saturcurve::Apply (float& Sa) const -{ - - assert (lutColCurve); - - Sa = lutColCurve[Sa]; -} - -class Colorfcurve : public ColorAppearance -{ -public: - void Apply(float& Cf) const; -}; - -//Colorfullness curve -inline void Colorfcurve::Apply (float& Cf) const -{ - - assert (lutColCurve); - - Cf = lutColCurve[Cf]; -} - - -class StandardToneCurve : public ToneCurve -{ -public: - void Apply(float& r, float& g, float& b) const; - - // Applies the tone curve to `r`, `g`, `b` arrays, starting at `r[start]` - // and ending at `r[end]` (and respectively for `b` and `g`). Uses SSE - // and requires that `r`, `g`, and `b` pointers have the same alignment. - void BatchApply( - const size_t start, const size_t end, - float *r, float *g, float *b) const; -}; - -class AdobeToneCurve : public ToneCurve -{ -private: - void RGBTone(float& r, float& g, float& b) const; // helper for tone curve - -public: - void Apply(float& r, float& g, float& b) const; -}; - -class SatAndValueBlendingToneCurve : public ToneCurve -{ -public: - void Apply(float& r, float& g, float& b) const; -}; - -class WeightedStdToneCurve : public ToneCurve -{ -private: - float Triangle(float refX, float refY, float X2) const; -#if defined( __SSE2__ ) && defined( __x86_64__ ) - vfloat Triangle(vfloat refX, vfloat refY, vfloat X2) const; -#endif -public: - void Apply(float& r, float& g, float& b) const; - void BatchApply(const size_t start, const size_t end, float *r, float *g, float *b) const; -}; - -class LuminanceToneCurve : public ToneCurve -{ -public: - void Apply(float& r, float& g, float& b) const; -}; - -class PerceptualToneCurveState -{ -public: - float Working2Prophoto[3][3]; - float Prophoto2Working[3][3]; - float cmul_contrast; - bool isProphoto; -}; - -// Tone curve whose purpose is to keep the color appearance constant, that is the curve changes contrast -// but colors appears to have the same hue and saturation as before. As contrast and saturation is tightly -// coupled in human vision saturation is modulated based on the curve's contrast, and that way the appearance -// can be kept perceptually constant (within limits). -class PerceptualToneCurve : public ToneCurve -{ -private: - static float cf_range[2]; - static float cf[1000]; - // for ciecam02 - static float f, c, nc, yb, la, xw, yw, zw, gamut; - static float n, d, nbb, ncb, cz, aw, wh, pfl, fl, pow1; - - static void cubic_spline(const float x[], const float y[], const int len, const float out_x[], float out_y[], const int out_len); - static float find_minimum_interval_halving(float (*func)(float x, void *arg), void *arg, float a, float b, float tol, int nmax); - static float find_tc_slope_fun(float k, void *arg); - static float get_curve_val(float x, float range[2], float lut[], size_t lut_size); - float calculateToneCurveContrastValue() const; -public: - static void init(); - void initApplyState(PerceptualToneCurveState & state, Glib::ustring workingSpace) const; - void BatchApply(const size_t start, const size_t end, float *r, float *g, float *b, const PerceptualToneCurveState &state) const; -}; - -// Standard tone curve -inline void StandardToneCurve::Apply (float& r, float& g, float& b) const -{ - - assert (lutToneCurve); - - r = lutToneCurve[r]; - g = lutToneCurve[g]; - b = lutToneCurve[b]; -} - -inline void StandardToneCurve::BatchApply( - const size_t start, const size_t end, - float *r, float *g, float *b) const { - assert (lutToneCurve); - assert (lutToneCurve.getClip() & LUT_CLIP_BELOW); - assert (lutToneCurve.getClip() & LUT_CLIP_ABOVE); - - // All pointers must have the same alignment for SSE usage. In the loop body below, - // we will only check `r`, assuming that the same result would hold for `g` and `b`. - assert (reinterpret_cast(r) % 16 == reinterpret_cast(g) % 16); - assert (reinterpret_cast(g) % 16 == reinterpret_cast(b) % 16); - - size_t i = start; - while (true) { - if (i >= end) { - // If we get to the end before getting to an aligned address, just return. - // (Or, for non-SSE mode, if we get to the end.) - return; -#if defined( __SSE2__ ) && defined( __x86_64__ ) - } else if (reinterpret_cast(&r[i]) % 16 == 0) { - // Otherwise, we get to the first aligned address; go to the SSE part. - break; -#endif - } - r[i] = lutToneCurve[r[i]]; - g[i] = lutToneCurve[g[i]]; - b[i] = lutToneCurve[b[i]]; - i++; - } - -#if defined( __SSE2__ ) && defined( __x86_64__ ) - 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]); - } - - // Remainder in non-SSE. - for (; i < end; ++i) { - r[i] = lutToneCurve[r[i]]; - g[i] = lutToneCurve[g[i]]; - b[i] = lutToneCurve[b[i]]; - } -#endif -} - -// 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 -{ - - assert (lutToneCurve); - - if (r >= g) { - if (g > b) { - RGBTone (r, g, b); // Case 1: r >= g > b - } else if (b > r) { - RGBTone (b, r, g); // Case 2: b > r >= g - } else if (b > g) { - RGBTone (r, b, g); // Case 3: r >= b > g - } else { // Case 4: r >= g == b - r = lutToneCurve[r]; - g = lutToneCurve[g]; - b = g; - } - } else { - if (r >= b) { - RGBTone (g, r, b); // Case 5: g > r >= b - } else if (b > g) { - RGBTone (b, g, r); // Case 6: b > g > r - } else { - RGBTone (g, b, r); // Case 7: g >= b > r - } - } -} - -inline void AdobeToneCurve::RGBTone (float& r, float& g, float& b) const -{ - float rold = r, gold = g, bold = b; - - r = lutToneCurve[rold]; - b = lutToneCurve[bold]; - g = b + ((r - b) * (gold - bold) / (rold - bold)); -} - -// Modifying the Luminance channel only -inline void LuminanceToneCurve::Apply(float &r, float &g, float &b) const -{ - assert (lutToneCurve); - - float currLuminance = r * 0.2126729f + g * 0.7151521f + b * 0.0721750f; - const float newLuminance = lutToneCurve[currLuminance]; - currLuminance = currLuminance == 0.f ? 0.00001f : currLuminance; - const float coef = newLuminance / currLuminance; - r = LIM(r * coef, 0.f, 65535.f); - g = LIM(g * coef, 0.f, 65535.f); - b = LIM(b * coef, 0.f, 65535.f); -} - -inline float WeightedStdToneCurve::Triangle(float a, float a1, float b) const -{ - if (a != b) { - float b1; - float a2 = a1 - a; - - if (b < a) { - b1 = b + a2 * b / a ; - } else { - b1 = b + a2 * (65535.f - b) / (65535.f - a); - } - - return b1; - } - - return a1; -} - -#if defined( __SSE2__ ) && defined( __x86_64__ ) -inline vfloat WeightedStdToneCurve::Triangle(vfloat a, vfloat a1, vfloat b) const -{ - vfloat a2 = a1 - a; - vmask cmask = vmaskf_lt(b, a); - vfloat b3 = vself(cmask, b, F2V(65535.f) - b); - vfloat a3 = vself(cmask, a, F2V(65535.f) - a); - return b + a2 * b3 / a3; -} -#endif - -// 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 -{ - - assert (lutToneCurve); - - r = CLIP(r); - g = CLIP(g); - b = CLIP(b); - float r1 = lutToneCurve[r]; - float g1 = Triangle(r, r1, g); - float b1 = Triangle(r, r1, b); - - float g2 = lutToneCurve[g]; - float r2 = Triangle(g, g2, r); - float b2 = Triangle(g, g2, b); - - float b3 = lutToneCurve[b]; - float r3 = Triangle(b, b3, r); - float g3 = Triangle(b, b3, g); - - 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); -} - -inline void WeightedStdToneCurve::BatchApply(const size_t start, const size_t end, float *r, float *g, float *b) const { - assert (lutToneCurve); - assert (lutToneCurve.getClip() & LUT_CLIP_BELOW); - assert (lutToneCurve.getClip() & LUT_CLIP_ABOVE); - - // All pointers must have the same alignment for SSE usage. In the loop body below, - // we will only check `r`, assuming that the same result would hold for `g` and `b`. - assert (reinterpret_cast(r) % 16 == reinterpret_cast(g) % 16); - assert (reinterpret_cast(g) % 16 == reinterpret_cast(b) % 16); - - size_t i = start; - while (true) { - if (i >= end) { - // If we get to the end before getting to an aligned address, just return. - // (Or, for non-SSE mode, if we get to the end.) - return; -#if defined( __SSE2__ ) && defined( __x86_64__ ) - } else if (reinterpret_cast(&r[i]) % 16 == 0) { - // Otherwise, we get to the first aligned address; go to the SSE part. - break; -#endif - } - Apply(r[i], g[i], b[i]); - i++; - } - -#if defined( __SSE2__ ) && defined( __x86_64__ ) - const vfloat c65535v = F2V(65535.f); - const vfloat zd5v = F2V(0.5f); - const vfloat zd25v = F2V(0.25f); - - for (; i + 3 < end; i += 4) { - vfloat r_val = LIMV(LVF(r[i]), ZEROV, c65535v); - vfloat g_val = LIMV(LVF(g[i]), ZEROV, c65535v); - vfloat b_val = LIMV(LVF(b[i]), ZEROV, c65535v); - vfloat r1 = lutToneCurve[r_val]; - vfloat g1 = Triangle(r_val, r1, g_val); - vfloat b1 = Triangle(r_val, r1, b_val); - - vfloat g2 = lutToneCurve[g_val]; - vfloat r2 = Triangle(g_val, g2, r_val); - vfloat b2 = Triangle(g_val, g2, b_val); - - vfloat b3 = lutToneCurve[b_val]; - 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)); - } - - // Remainder in non-SSE. - for (; i < end; ++i) { - Apply(r[i], g[i], b[i]); - } -#endif -} - -// 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 -{ - - assert (lutToneCurve); - - r = CLIP(r); - g = CLIP(g); - b = CLIP(b); - - const float lum = (r + g + b) / 3.f; - const float newLum = lutToneCurve[lum]; - - float h, s, v; - Color::rgb2hsvtc(r, g, b, h, s, v); - - float dV; - if (newLum >= lum) { - // Linearly targeting Value = 1 and Saturation = 0 - const float coef = (newLum - lum) / (65535.f - lum); - dV = (1.f - v) * coef; - s *= 1.f - coef; - } else { - // Linearly targeting Value = 0 - const float coef = (newLum - lum) / lum ; - dV = v * coef; - } - Color::hsv2rgbdcp(h, s, v + dV, r, g, b); -} - -} - -#undef CLIPI - -#endif From 7f89c362d0dc5264df9452ba6ccb12f601ced79e Mon Sep 17 00:00:00 2001 From: Alberto Griggio Date: Mon, 8 Jan 2018 14:54:55 +0100 Subject: [PATCH 12/19] HDR tone mapping: normalize luminance relative to the brightest point in the input (Also tweaked the default value for the "amount" slider) Fixes #4255 --- rtengine/procparams.cc | 2 +- rtengine/tmo_fattal02.cc | 13 +++++++++++-- rtgui/fattaltonemap.cc | 5 +---- 3 files changed, 13 insertions(+), 7 deletions(-) diff --git a/rtengine/procparams.cc b/rtengine/procparams.cc index 8fbab7a8d..a8178dde5 100644 --- a/rtengine/procparams.cc +++ b/rtengine/procparams.cc @@ -1468,7 +1468,7 @@ bool EPDParams::operator !=(const EPDParams& other) const FattalToneMappingParams::FattalToneMappingParams() : enabled(false), threshold(0), - amount(1) + amount(30) { } diff --git a/rtengine/tmo_fattal02.cc b/rtengine/tmo_fattal02.cc index 645451905..1e3669882 100644 --- a/rtengine/tmo_fattal02.cc +++ b/rtengine/tmo_fattal02.cc @@ -1121,13 +1121,20 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) const float min_luminance = 1.f; TMatrix ws = ICCStore::getInstance()->workingSpaceMatrix (params->icm.working); + float max_Y = 0.f; + int max_x = 0, max_y = 0; + #ifdef _OPENMP #pragma omp parallel for if (multiThread) #endif - for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { Yr (x, y) = std::max (luminance (rgb->r (y, x), rgb->g (y, x), rgb->b (y, x), ws), min_luminance); // clip really black pixels + if (Yr(x, y) > max_Y) { + max_Y = Yr(x, y); + max_x = x; + max_y = y; + } } } @@ -1170,6 +1177,8 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) const float hr = float(h2) / float(h); const float wr = float(w2) / float(w); + + const float scale = std::max(L(max_x * wr + 1, max_y * hr + 1), epsilon) * (65535.f / Yr(max_x, max_y)); #ifdef _OPENMP #pragma omp parallel for if(multiThread) @@ -1181,7 +1190,7 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) int xx = x * wr + 1; float Y = Yr (x, y); - float l = std::max (L (xx, yy), epsilon) * (65535.f / Y); + float l = std::max (L (xx, yy), epsilon) * (65535.f / Y) / scale; 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; diff --git a/rtgui/fattaltonemap.cc b/rtgui/fattaltonemap.cc index 10dba51cf..a6f4c5190 100644 --- a/rtgui/fattaltonemap.cc +++ b/rtgui/fattaltonemap.cc @@ -26,10 +26,7 @@ using namespace rtengine::procparams; FattalToneMapping::FattalToneMapping(): FoldableToolPanel(this, "fattal", M("TP_TM_FATTAL_LABEL"), true, true) { - -// setEnabledTooltipMarkup(M("TP_EPD_TOOLTIP")); - - amount = Gtk::manage(new Adjuster (M("TP_TM_FATTAL_AMOUNT"), 1., 100., 1., 0.0)); + amount = Gtk::manage(new Adjuster (M("TP_TM_FATTAL_AMOUNT"), 1., 100., 30., 0.0)); threshold = Gtk::manage(new Adjuster (M("TP_TM_FATTAL_THRESHOLD"), -100., 100., 1., 0.0)); amount->setAdjusterListener(this); From 6c6786929c4e5f5d6131962a63c1041879d3a60a Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 8 Jan 2018 15:21:56 +0100 Subject: [PATCH 13/19] Fixe race in tmo_fattal, #4255 --- rtengine/tmo_fattal02.cc | 25 ++++++++++++++++++++----- 1 file changed, 20 insertions(+), 5 deletions(-) diff --git a/rtengine/tmo_fattal02.cc b/rtengine/tmo_fattal02.cc index 1e3669882..ebb70f25f 100644 --- a/rtengine/tmo_fattal02.cc +++ b/rtengine/tmo_fattal02.cc @@ -1125,18 +1125,33 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) int max_x = 0, max_y = 0; #ifdef _OPENMP - #pragma omp parallel for if (multiThread) + #pragma omp parallel if (multiThread) +#endif +{ + float max_YThr = 0.f; + int max_xThr = 0, max_yThr = 0; +#ifdef _OPENMP + #pragma omp for #endif for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { Yr (x, y) = std::max (luminance (rgb->r (y, x), rgb->g (y, x), rgb->b (y, x), ws), min_luminance); // clip really black pixels - if (Yr(x, y) > max_Y) { - max_Y = Yr(x, y); - max_x = x; - max_y = y; + if (Yr(x, y) > max_YThr) { + max_YThr = Yr(x, y); + max_xThr = x; + max_yThr = y; } } } +#ifdef _OPENMP +#pragma omp critical +#endif + if (max_YThr > max_Y) { + max_Y = max_YThr; + max_x = max_xThr; + max_y = max_yThr; + } +} // 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() From 529a7f3003232c216c350a19215a8681d132aafc Mon Sep 17 00:00:00 2001 From: heckflosse Date: Mon, 8 Jan 2018 15:27:41 +0100 Subject: [PATCH 14/19] tmo_fattal02.cc added nowait, #4255 --- rtengine/tmo_fattal02.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rtengine/tmo_fattal02.cc b/rtengine/tmo_fattal02.cc index ebb70f25f..efa9c53d7 100644 --- a/rtengine/tmo_fattal02.cc +++ b/rtengine/tmo_fattal02.cc @@ -1131,7 +1131,7 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) float max_YThr = 0.f; int max_xThr = 0, max_yThr = 0; #ifdef _OPENMP - #pragma omp for + #pragma omp for nowait #endif for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { From 5104368c4a9af7b2a212d112b9c73f6fa346ca90 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Mon, 8 Jan 2018 17:51:31 +0100 Subject: [PATCH 15/19] Fix non-SSE2 build (fixes #4284) --- rtengine/improcfun.cc | 9 +++++---- 1 file changed, 5 insertions(+), 4 deletions(-) diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 7e8e153b1..61b1809e0 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -2170,7 +2170,9 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw const float pow1 = pow_F ( 1.64f - pow_F ( 0.29f, n ), 0.73f ); float nj, nbbj, ncbj, czj, awj, flj; Ciecam02::initcam2float (gamu, yb2, pilotout, f2, la2, xw2, yw2, zw2, nj, dj, nbbj, ncbj, czj, awj, flj); +#ifdef __SSE2__ const float reccmcz = 1.f / (c2 * czj); +#endif const float pow1n = pow_F ( 1.64f - pow_F ( 0.29f, nj ), 0.73f ); const float epsil = 0.0001f; @@ -2735,7 +2737,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw Ciecam02::jch2xyz_ciecam02float ( xx, yy, zz, J, C, h, xw2, yw2, zw2, - f2, c2, nc2, gamu, pow1n, nbbj, ncbj, flj, czj, dj, awj); + c2, nc2, gamu, pow1n, nbbj, ncbj, flj, czj, dj, awj); float x, y, z; x = xx * 655.35f; y = yy * 655.35f; @@ -2746,10 +2748,9 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw // gamut control in Lab mode; I must study how to do with cIECAM only if (gamu == 1) { - float HH, Lprov1, Chprov1; + float Lprov1, Chprov1; Lprov1 = Ll / 327.68f; Chprov1 = sqrtf (SQR (aa) + SQR (bb)) / 327.68f; - HH = xatan2f (bb, aa); float2 sincosval; if (Chprov1 == 0.0f) { @@ -3079,7 +3080,7 @@ void ImProcFunctions::ciecam_02float (CieImage* ncie, float adap, int pW, int pw Ciecam02::jch2xyz_ciecam02float ( xx, yy, zz, ncie->J_p[i][j], ncie_C_p, ncie->h_p[i][j], xw2, yw2, zw2, - f2, c2, nc2, gamu, pow1n, nbbj, ncbj, flj, czj, dj, awj); + c2, nc2, gamu, pow1n, nbbj, ncbj, flj, czj, dj, awj); float x = (float)xx * 655.35f; float y = (float)yy * 655.35f; float z = (float)zz * 655.35f; From 31ec1f4082e1962b76328662b638ac5027fa3980 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Mon, 8 Jan 2018 17:53:52 +0100 Subject: [PATCH 16/19] Upstream OBS compile fix by @mbajor --- rtengine/alignedbuffer.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/rtengine/alignedbuffer.h b/rtengine/alignedbuffer.h index 560f0884f..27a376c57 100644 --- a/rtengine/alignedbuffer.h +++ b/rtengine/alignedbuffer.h @@ -18,6 +18,8 @@ */ #ifndef _ALIGNEDBUFFER_ #define _ALIGNEDBUFFER_ + +#include #include #include From 3755f5c18e782498e84dc9148defa3236fe0c1e8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Mon, 8 Jan 2018 17:57:21 +0100 Subject: [PATCH 17/19] Fix i586 build by @heckflosse (#4284) --- rtengine/color.cc | 4 ++-- rtengine/improcfun.cc | 12 ++++++------ 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/rtengine/color.cc b/rtengine/color.cc index dbabb217b..706d0f36d 100644 --- a/rtengine/color.cc +++ b/rtengine/color.cc @@ -1786,7 +1786,7 @@ void Color::Lab2XYZ(vfloat L, vfloat a, vfloat b, vfloat &x, vfloat &y, vfloat & void Color::RGB2Lab(float *R, float *G, float *B, float *L, float *a, float *b, const float wp[3][3], int width) { -#ifdef __SSE2__ +#if defined( __SSE2__ ) && defined( __x86_64__ ) vfloat maxvalfv = F2V(MAXVALF); vfloat c116v = F2V(116.f); vfloat c5242d88v = F2V(5242.88f); @@ -1794,7 +1794,7 @@ void Color::RGB2Lab(float *R, float *G, float *B, float *L, float *a, float *b, vfloat c200v = F2V(200.f); #endif int i = 0; -#ifdef __SSE2__ +#if defined( __SSE2__ ) && defined( __x86_64__ ) for(;i < width - 3; i+=4) { const vfloat rv = LVFU(R[i]); const vfloat gv = LVFU(G[i]); diff --git a/rtengine/improcfun.cc b/rtengine/improcfun.cc index 61b1809e0..569ba42f1 100644 --- a/rtengine/improcfun.cc +++ b/rtengine/improcfun.cc @@ -54,7 +54,7 @@ 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__ +#if defined( __SSE2__ ) && defined( __x86_64__ ) vfloat cr = F2V(0.299f); vfloat cg = F2V(0.587f); vfloat cb = F2V(0.114f); @@ -62,7 +62,7 @@ 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__ +#if defined( __SSE2__ ) && defined( __x86_64__ ) for (; j < tW - 3; j+=4, tj+=4) { vfloat rv = LVF(rtemp[ti * tileSize + tj]); @@ -95,14 +95,14 @@ 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) { -#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__ +#if defined( __SSE2__ ) && defined( __x86_64__ ) for (; j < tW - 3; j+=4, tj+=4) { vfloat rv = LVF(rtemp[ti * tileSize + tj]); @@ -159,7 +159,7 @@ void proPhotoBlue(float *rtemp, float *gtemp, float *btemp, int istart, int tH, // this is a hack to avoid the blue=>black bug (Issue 2141) for (int i = istart, ti = 0; i < tH; i++, ti++) { int j = jstart, tj = 0; -#ifdef __SSE2__ +#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]); @@ -3763,7 +3763,7 @@ void ImProcFunctions::rgbProc (Imagefloat* working, LabImage* lab, PipetteBuffer } else { for (int i = istart, ti = 0; i < tH; i++, ti++) { int j = jstart, tj = 0; -#ifdef __SSE2__ +#if defined( __SSE2__ ) && defined( __x86_64__ ) for (; j < tW - 3; j+=4, tj+=4) { //brightness/contrast STVF(rtemp[ti * TS + tj], tonecurve(LVF(rtemp[ti * TS + tj]))); From e58cecbf0653491edb6f9a5e364c7cb76b6fd2b7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Fl=C3=B6ssie?= Date: Mon, 8 Jan 2018 18:17:12 +0100 Subject: [PATCH 18/19] Try to fix most warnings in the OBS builds (#4284) --- rtengine/cJSON.c | 57 ++++++++++++++++++++++++++------------------- rtengine/dcraw.cc | 2 +- rtgui/batchqueue.cc | 2 +- 3 files changed, 35 insertions(+), 26 deletions(-) diff --git a/rtengine/cJSON.c b/rtengine/cJSON.c index 8e9cdcccf..263a45113 100644 --- a/rtengine/cJSON.c +++ b/rtengine/cJSON.c @@ -38,7 +38,8 @@ const char *cJSON_GetErrorPtr(void) {return ep;} static int cJSON_strcasecmp(const char *s1,const char *s2) { - if (!s1) return (s1==s2)?0:1;if (!s2) return 1; + 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); } @@ -107,7 +108,7 @@ static const char *parse_number(cJSON *item,const char *num) } n=sign*n*pow(10.0,(scale+subscale*signsubscale)); /* number = +/- number.fraction * 10^+/- exponent */ - + item->valuedouble=n; item->valueint=(int)n; item->type=cJSON_Number; @@ -156,12 +157,12 @@ static const char *parse_string(cJSON *item,const char *str) { const char *ptr=str+1;char *ptr2;char *out;int len=0;unsigned uc,uc2; if (*str!='\"') {ep=str;return 0;} /* not a string! */ - + while (*ptr!='\"' && *ptr && ++len) if (*ptr++ == '\\') ptr++; /* Skip escaped quotes. */ - + out=(char*)cJSON_malloc(len+1); /* This is how long we need for the string, roughly. */ if (!out) return 0; - + ptr=str+1;ptr2=out; while (*ptr!='\"' && *ptr) { @@ -190,7 +191,7 @@ static const char *parse_string(cJSON *item,const char *str) } len=4;if (uc<0x80) len=1;else if (uc<0x800) len=2;else if (uc<0x10000) len=3; ptr2+=len; - + #if defined( __GNUC__ ) && __GNUC__ >= 7// silence warning #pragma GCC diagnostic push #pragma GCC diagnostic ignored "-Wimplicit-fallthrough" @@ -224,10 +225,10 @@ static const char *parse_string(cJSON *item,const char *str) static char *print_string_ptr(const char *str) { const char *ptr;char *ptr2,*out;int len=0;unsigned char token; - + 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++;} - + out=(char*)cJSON_malloc(len+3); if (!out) return 0; @@ -360,7 +361,7 @@ static char *print_array(cJSON *item,int depth,int fmt) char *out=0,*ptr,*ret;int len=5; cJSON *child=item->child; int numentries=0,i=0,fail=0; - + /* How many entries in the array? */ while (child) numentries++,child=child->next; /* Explicitly handle numentries==0 */ @@ -383,7 +384,7 @@ static char *print_array(cJSON *item,int depth,int fmt) if (ret) len+=strlen(ret)+2+(fmt?1:0); else fail=1; child=child->next; } - + /* If we didn't fail, try to malloc the output string */ if (!fail) out=(char*)cJSON_malloc(len); /* If that fails, we fail. */ @@ -396,7 +397,7 @@ static char *print_array(cJSON *item,int depth,int fmt) cJSON_free(entries); return 0; } - + /* Compose the output array. */ *out='['; ptr=out+1;*ptr=0; @@ -408,7 +409,7 @@ static char *print_array(cJSON *item,int depth,int fmt) } cJSON_free(entries); *ptr++=']';*ptr++=0; - return out; + return out; } /* Build an object from the text. */ @@ -416,11 +417,11 @@ static const char *parse_object(cJSON *item,const char *value) { cJSON *child; if (*value!='{') {ep=value;return 0;} /* not an object! */ - + item->type=cJSON_Object; value=skip(value+1); if (*value=='}') return value+1; /* empty array. */ - + item->child=child=cJSON_New_Item(); if (!item->child) return 0; value=skip(parse_string(child,skip(value))); @@ -429,7 +430,7 @@ static const char *parse_object(cJSON *item,const char *value) 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; - + while (*value==',') { cJSON *new_item; @@ -442,7 +443,7 @@ static const char *parse_object(cJSON *item,const char *value) value=skip(parse_value(child,skip(value+1))); /* skip any spacing, get the value. */ if (!value) return 0; } - + if (*value=='}') return value+1; /* end of array */ ep=value;return 0; /* malformed. */ } @@ -483,7 +484,7 @@ static char *print_object(cJSON *item,int depth,int fmt) if (str && ret) len+=strlen(ret)+strlen(str)+2+(fmt?2+depth:0); else fail=1; child=child->next; } - + /* Try to allocate the output string */ if (!fail) out=(char*)cJSON_malloc(len); if (!out) fail=1; @@ -495,7 +496,7 @@ static char *print_object(cJSON *item,int depth,int fmt) cJSON_free(names);cJSON_free(entries); return 0; } - + /* Compose the output: */ *out='{';ptr=out+1;if (fmt)*ptr++='\n';*ptr=0; for (i=0;ichild;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 *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; +} 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));} @@ -602,4 +611,4 @@ void cJSON_Minify(char *json) else *into++=*json++; // All other characters. } *into=0; // and null-terminate. -} \ No newline at end of file +} diff --git a/rtengine/dcraw.cc b/rtengine/dcraw.cc index 75891c589..eee4f34c0 100644 --- a/rtengine/dcraw.cc +++ b/rtengine/dcraw.cc @@ -3851,7 +3851,7 @@ float CLASS foveon_avg (short *pix, int range[2], float cfilt) short * CLASS foveon_make_curve (double max, double mul, double filt) { short *curve; - unsigned i, size; + size_t i, size; double x; if (!filt) filt = 0.8; diff --git a/rtgui/batchqueue.cc b/rtgui/batchqueue.cc index 6b04ed0ae..52d0af4d5 100644 --- a/rtgui/batchqueue.cc +++ b/rtgui/batchqueue.cc @@ -373,7 +373,7 @@ Glib::ustring BatchQueue::getTempFilenameForParams( const Glib::ustring &filenam { timeval tv; gettimeofday(&tv, nullptr); - char mseconds[4]; + char mseconds[11]; sprintf(mseconds, "%d", (int)(tv.tv_usec / 1000)); time_t rawtime; struct tm *timeinfo; From 9ce829e2f6151e4c794a2a4403c734a1aabffea8 Mon Sep 17 00:00:00 2001 From: heckflosse Date: Tue, 9 Jan 2018 00:43:13 +0100 Subject: [PATCH 19/19] Small speedup for fattal --- rtengine/tmo_fattal02.cc | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/rtengine/tmo_fattal02.cc b/rtengine/tmo_fattal02.cc index efa9c53d7..7ef490807 100644 --- a/rtengine/tmo_fattal02.cc +++ b/rtengine/tmo_fattal02.cc @@ -1131,7 +1131,7 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) float max_YThr = 0.f; int max_xThr = 0, max_yThr = 0; #ifdef _OPENMP - #pragma omp for nowait + #pragma omp for schedule(dynamic,16) nowait #endif for (int y = 0; y < h; y++) { for (int x = 0; x < w; x++) { @@ -1193,10 +1193,10 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) const float hr = float(h2) / float(h); const float wr = float(w2) / float(w); - const float scale = std::max(L(max_x * wr + 1, max_y * hr + 1), epsilon) * (65535.f / Yr(max_x, max_y)); + const float scale = 65535.f / std::max(L(max_x * wr + 1, max_y * hr + 1), epsilon) * (65535.f / Yr(max_x, max_y)); #ifdef _OPENMP - #pragma omp parallel for if(multiThread) + #pragma omp parallel for schedule(dynamic,16) if(multiThread) #endif for (int y = 0; y < h; y++) { int yy = y * hr + 1; @@ -1205,7 +1205,7 @@ void ImProcFunctions::ToneMapFattal02 (Imagefloat *rgb) int xx = x * wr + 1; float Y = Yr (x, y); - float l = std::max (L (xx, yy), epsilon) * (65535.f / Y) / scale; + 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;