Merge branch 'master' into blender2.8
[blender.git] / source / blender / blenkernel / intern / colortools.c
index 5176f93f4f3921eb77ae9bcb4e1c77b57330fd4c..6c97380b9ca94ab45679ef82146b89290ca16086 100644 (file)
@@ -4,7 +4,7 @@
  * This program 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 2
- * of the License, or (at your option) any later version. 
+ * of the License, or (at your option) any later version.
  *
  * This program is distributed in the hope that it will be useful,
  * but WITHOUT ANY WARRANTY; without even the implied warranty of
@@ -43,6 +43,8 @@
 #include "BLI_blenlib.h"
 #include "BLI_math.h"
 #include "BLI_utildefines.h"
+#include "BLI_task.h"
+#include "BLI_threads.h"
 
 #include "BKE_colortools.h"
 #include "BKE_curve.h"
@@ -50,7 +52,6 @@
 
 
 #include "IMB_colormanagement.h"
-#include "IMB_imbuf.h"
 #include "IMB_imbuf_types.h"
 
 /* ********************************* color curve ********************* */
@@ -129,7 +130,7 @@ void curvemapping_free(CurveMapping *cumap)
        }
 }
 
-void curvemapping_copy_data(CurveMapping *target, CurveMapping *cumap)
+void curvemapping_copy_data(CurveMapping *target, const CurveMapping *cumap)
 {
        int a;
 
@@ -145,7 +146,7 @@ void curvemapping_copy_data(CurveMapping *target, CurveMapping *cumap)
        }
 }
 
-CurveMapping *curvemapping_copy(CurveMapping *cumap)
+CurveMapping *curvemapping_copy(const CurveMapping *cumap)
 {
        if (cumap) {
                CurveMapping *cumapn = MEM_dupallocN(cumap);
@@ -182,14 +183,14 @@ void curvemapping_set_black_white(CurveMapping *cumap, const float black[3], con
 /* ********** NOTE: requires curvemapping_changed() call after ******** */
 
 /* remove specified point */
-int curvemap_remove_point(CurveMap *cuma, CurveMapPoint *point)
+bool curvemap_remove_point(CurveMap *cuma, CurveMapPoint *point)
 {
        CurveMapPoint *cmp;
        int a, b, removed = 0;
        
        /* must have 2 points minimum */
        if (cuma->totpoint <= 2)
-               return FALSE;
+               return false;
 
        cmp = MEM_mallocN((cuma->totpoint) * sizeof(CurveMapPoint), "curve points");
 
@@ -238,23 +239,24 @@ CurveMapPoint *curvemap_insert(CurveMap *cuma, float x, float y)
 {
        CurveMapPoint *cmp = MEM_callocN((cuma->totpoint + 1) * sizeof(CurveMapPoint), "curve points");
        CurveMapPoint *newcmp = NULL;
-       int a, b, foundloc = 0;
-               
+       int a, b;
+       bool foundloc = false;
+
        /* insert fragments of the old one and the new point to the new curve */
        cuma->totpoint++;
        for (a = 0, b = 0; a < cuma->totpoint; a++) {
-               if ((x < cuma->curve[a].x) && !foundloc) {
+               if ((foundloc == false) && ((a + 1 == cuma->totpoint) || (x < cuma->curve[a].x))) {
                        cmp[a].x = x;
                        cmp[a].y = y;
                        cmp[a].flag = CUMA_SELECT;
-                       foundloc = 1;
+                       foundloc = true;
                        newcmp = &cmp[a];
                }
                else {
                        cmp[a].x = cuma->curve[b].x;
                        cmp[a].y = cuma->curve[b].y;
-                       cmp[a].flag = cuma->curve[b].flag;
-                       cmp[a].flag &= ~CUMA_SELECT; /* make sure old points don't remain selected */
+                       /* make sure old points don't remain selected */
+                       cmp[a].flag = cuma->curve[b].flag & ~CUMA_SELECT;
                        cmp[a].shorty = cuma->curve[b].shorty;
                        b++;
                }
@@ -288,10 +290,12 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope)
                case CURVE_PRESET_LINE:
                        cuma->curve[0].x = clipr->xmin;
                        cuma->curve[0].y = clipr->ymax;
-                       cuma->curve[0].flag = 0;
                        cuma->curve[1].x = clipr->xmax;
                        cuma->curve[1].y = clipr->ymin;
-                       cuma->curve[1].flag = 0;
+                       if (slope == CURVEMAP_SLOPE_POS_NEG) {
+                               cuma->curve[0].flag |= CUMA_HANDLE_VECTOR;
+                               cuma->curve[1].flag |= CUMA_HANDLE_VECTOR;
+                       }
                        break;
                case CURVE_PRESET_SHARP:
                        cuma->curve[0].x = 0;
@@ -326,8 +330,8 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope)
                                cuma->curve[i].x = i / ((float)cuma->totpoint - 1);
                                cuma->curve[i].y = 0.5;
                        }
+                       break;
                }
-               break;
                case CURVE_PRESET_ROUND:
                        cuma->curve[0].x = 0;
                        cuma->curve[0].y = 1;
@@ -363,32 +367,68 @@ void curvemap_reset(CurveMap *cuma, const rctf *clipr, int preset, int slope)
                MEM_freeN(cuma->curve);
                cuma->curve = newpoints;
        }
-       
+       else if (slope == CURVEMAP_SLOPE_POS_NEG) {
+               const int num_points = cuma->totpoint * 2 - 1;
+               CurveMapPoint *new_points = MEM_mallocN(num_points * sizeof(CurveMapPoint),
+                                                      "curve symmetric points");
+               int i;
+               for (i = 0; i < cuma->totpoint; i++) {
+                       const int src_last_point = cuma->totpoint - i - 1;
+                       const int dst_last_point = num_points - i - 1;
+                       new_points[i] = cuma->curve[src_last_point];
+                       new_points[i].x = (1.0f - cuma->curve[src_last_point].x) * 0.5f;
+                       new_points[dst_last_point] = new_points[i];
+                       new_points[dst_last_point].x = 0.5f + cuma->curve[src_last_point].x * 0.5f;
+               }
+               cuma->totpoint = num_points;
+               MEM_freeN(cuma->curve);
+               cuma->curve = new_points;
+       }
+
        if (cuma->table) {
                MEM_freeN(cuma->table);
                cuma->table = NULL;
        }
 }
 
-/* if type==1: vector, else auto */
-void curvemap_sethandle(CurveMap *cuma, int type)
+/**
+ * \param type: eBezTriple_Handle
+ */
+void curvemap_handle_set(CurveMap *cuma, int type)
 {
        int a;
        
        for (a = 0; a < cuma->totpoint; a++) {
                if (cuma->curve[a].flag & CUMA_SELECT) {
-                       if (type) cuma->curve[a].flag |= CUMA_VECTOR;
-                       else cuma->curve[a].flag &= ~CUMA_VECTOR;
+                       cuma->curve[a].flag &= ~(CUMA_HANDLE_VECTOR | CUMA_HANDLE_AUTO_ANIM);
+                       if (type == HD_VECT) {
+                               cuma->curve[a].flag |= CUMA_HANDLE_VECTOR;
+                       }
+                       else if (type == HD_AUTO_ANIM) {
+                               cuma->curve[a].flag |= CUMA_HANDLE_AUTO_ANIM;
+                       }
+                       else {
+                               /* pass */
+                       }
                }
        }
 }
 
 /* *********************** Making the tables and display ************** */
 
-/* reduced copy of garbled calchandleNurb() code in curve.c */
-static void calchandle_curvemap(BezTriple *bezt, BezTriple *prev, BezTriple *next, int UNUSED(mode))
+/**
+ * reduced copy of #calchandleNurb_intern code in curve.c
+ */
+static void calchandle_curvemap(
+        BezTriple *bezt, const BezTriple *prev, const BezTriple *next)
 {
-       float *p1, *p2, *p3, pt[3];
+       /* defines to avoid confusion */
+#define p2_h1 ((p2) - 3)
+#define p2_h2 ((p2) + 3)
+
+       const float *p1, *p3;
+       float *p2;
+       float pt[3];
        float len, len_a, len_b;
        float dvec_a[2], dvec_b[2];
 
@@ -427,7 +467,7 @@ static void calchandle_curvemap(BezTriple *bezt, BezTriple *prev, BezTriple *nex
        if (len_a == 0.0f) len_a = 1.0f;
        if (len_b == 0.0f) len_b = 1.0f;
 
-       if (bezt->h1 == HD_AUTO || bezt->h2 == HD_AUTO) { /* auto */
+       if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM) || ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) {    /* auto */
                float tvec[2];
                tvec[0] = dvec_b[0] / len_b + dvec_a[0] / len_a;
                tvec[1] = dvec_b[1] / len_b + dvec_a[1] / len_a;
@@ -435,23 +475,70 @@ static void calchandle_curvemap(BezTriple *bezt, BezTriple *prev, BezTriple *nex
                len = len_v2(tvec) * 2.5614f;
                if (len != 0.0f) {
                        
-                       if (bezt->h1 == HD_AUTO) {
+                       if (ELEM(bezt->h1, HD_AUTO, HD_AUTO_ANIM)) {
                                len_a /= len;
-                               madd_v2_v2v2fl(p2 - 3, p2, tvec, -len_a);
+                               madd_v2_v2v2fl(p2_h1, p2, tvec, -len_a);
+
+                               if ((bezt->h1 == HD_AUTO_ANIM) && next && prev) { /* keep horizontal if extrema */
+                                       const float ydiff1 = prev->vec[1][1] - bezt->vec[1][1];
+                                       const float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
+                                       if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) ||
+                                           (ydiff1 >= 0.0f && ydiff2 >= 0.0f))
+                                       {
+                                               bezt->vec[0][1] = bezt->vec[1][1];
+                                       }
+                                       else { /* handles should not be beyond y coord of two others */
+                                               if (ydiff1 <= 0.0f) {
+                                                       if (prev->vec[1][1] > bezt->vec[0][1]) {
+                                                               bezt->vec[0][1] = prev->vec[1][1];
+                                                       }
+                                               }
+                                               else {
+                                                       if (prev->vec[1][1] < bezt->vec[0][1]) {
+                                                               bezt->vec[0][1] = prev->vec[1][1];
+                                                       }
+                                               }
+                                       }
+                               }
                        }
-                       if (bezt->h2 == HD_AUTO) {
+                       if (ELEM(bezt->h2, HD_AUTO, HD_AUTO_ANIM)) {
                                len_b /= len;
-                               madd_v2_v2v2fl(p2 + 3, p2, tvec,  len_b);
+                               madd_v2_v2v2fl(p2_h2, p2, tvec,  len_b);
+
+                               if ((bezt->h2 == HD_AUTO_ANIM) && next && prev) { /* keep horizontal if extrema */
+                                       const float ydiff1 = prev->vec[1][1] - bezt->vec[1][1];
+                                       const float ydiff2 = next->vec[1][1] - bezt->vec[1][1];
+                                       if ((ydiff1 <= 0.0f && ydiff2 <= 0.0f) ||
+                                           (ydiff1 >= 0.0f && ydiff2 >= 0.0f))
+                                       {
+                                               bezt->vec[2][1] = bezt->vec[1][1];
+                                       }
+                                       else { /* handles should not be beyond y coord of two others */
+                                               if (ydiff1 <= 0.0f) {
+                                                       if (next->vec[1][1] < bezt->vec[2][1]) {
+                                                               bezt->vec[2][1] = next->vec[1][1];
+                                                       }
+                                               }
+                                               else {
+                                                       if (next->vec[1][1] > bezt->vec[2][1]) {
+                                                               bezt->vec[2][1] = next->vec[1][1];
+                                                       }
+                                               }
+                                       }
+                               }
                        }
                }
        }
 
        if (bezt->h1 == HD_VECT) {    /* vector */
-               madd_v2_v2v2fl(p2 - 3, p2, dvec_a, -1.0f / 3.0f);
+               madd_v2_v2v2fl(p2_h1, p2, dvec_a, -1.0f / 3.0f);
        }
        if (bezt->h2 == HD_VECT) {
-               madd_v2_v2v2fl(p2 + 3, p2, dvec_b,  1.0f / 3.0f);
+               madd_v2_v2v2fl(p2_h2, p2, dvec_b,  1.0f / 3.0f);
        }
+
+#undef p2_h1
+#undef p2_h2
 }
 
 /* in X, out Y. 
@@ -507,19 +594,22 @@ static void curvemap_make_table(CurveMap *cuma, const rctf *clipr)
                cuma->maxtable = max_ff(cuma->maxtable, cmp[a].x);
                bezt[a].vec[1][0] = cmp[a].x;
                bezt[a].vec[1][1] = cmp[a].y;
-               if (cmp[a].flag & CUMA_VECTOR)
+               if (cmp[a].flag & CUMA_HANDLE_VECTOR) {
                        bezt[a].h1 = bezt[a].h2 = HD_VECT;
-               else
+               }
+               else if (cmp[a].flag & CUMA_HANDLE_AUTO_ANIM) {
+                       bezt[a].h1 = bezt[a].h2 = HD_AUTO_ANIM;
+               }
+               else {
                        bezt[a].h1 = bezt[a].h2 = HD_AUTO;
+               }
        }
        
+       const BezTriple *bezt_prev = NULL;
        for (a = 0; a < cuma->totpoint; a++) {
-               if (a == 0)
-                       calchandle_curvemap(bezt, NULL, bezt + 1, 0);
-               else if (a == cuma->totpoint - 1)
-                       calchandle_curvemap(bezt + a, bezt + a - 1, NULL, 0);
-               else
-                       calchandle_curvemap(bezt + a, bezt + a - 1, bezt + a + 1, 0);
+               const BezTriple *bezt_next = (a != cuma->totpoint - 1) ? &bezt[a + 1] : NULL;
+               calchandle_curvemap(&bezt[a], bezt_prev, bezt_next);
+               bezt_prev = &bezt[a];
        }
        
        /* first and last handle need correction, instead of pointing to center of next/prev, 
@@ -576,14 +666,14 @@ static void curvemap_make_table(CurveMap *cuma, const rctf *clipr)
        /* store first and last handle for extrapolation, unit length */
        cuma->ext_in[0] = bezt[0].vec[0][0] - bezt[0].vec[1][0];
        cuma->ext_in[1] = bezt[0].vec[0][1] - bezt[0].vec[1][1];
-       range = sqrt(cuma->ext_in[0] * cuma->ext_in[0] + cuma->ext_in[1] * cuma->ext_in[1]);
+       range = sqrtf(cuma->ext_in[0] * cuma->ext_in[0] + cuma->ext_in[1] * cuma->ext_in[1]);
        cuma->ext_in[0] /= range;
        cuma->ext_in[1] /= range;
 
        a = cuma->totpoint - 1;
        cuma->ext_out[0] = bezt[a].vec[1][0] - bezt[a].vec[2][0];
        cuma->ext_out[1] = bezt[a].vec[1][1] - bezt[a].vec[2][1];
-       range = sqrt(cuma->ext_out[0] * cuma->ext_out[0] + cuma->ext_out[1] * cuma->ext_out[1]);
+       range = sqrtf(cuma->ext_out[0] * cuma->ext_out[0] + cuma->ext_out[1] * cuma->ext_out[1]);
        cuma->ext_out[0] /= range;
        cuma->ext_out[1] /= range;
        
@@ -635,6 +725,11 @@ void curvemapping_premultiply(CurveMapping *cumap, int restore)
                                MEM_freeN(cumap->cm[a].table);
                                cumap->cm[a].table = cumap->cm[a].premultable;
                                cumap->cm[a].premultable = NULL;
+
+                               copy_v2_v2(cumap->cm[a].ext_in, cumap->cm[a].premul_ext_in);
+                               copy_v2_v2(cumap->cm[a].ext_out, cumap->cm[a].premul_ext_out);
+                               zero_v2(cumap->cm[a].premul_ext_in);
+                               zero_v2(cumap->cm[a].premul_ext_out);
                        }
                        
                        cumap->flag &= ~CUMA_PREMULLED;
@@ -660,6 +755,11 @@ void curvemapping_premultiply(CurveMapping *cumap, int restore)
                                for (b = 0; b <= CM_TABLE; b++) {
                                        cumap->cm[a].table[b].y = curvemap_evaluateF(cumap->cm + 3, cumap->cm[a].table[b].y);
                                }
+
+                               copy_v2_v2(cumap->cm[a].premul_ext_in, cumap->cm[a].ext_in);
+                               copy_v2_v2(cumap->cm[a].premul_ext_out, cumap->cm[a].ext_out);
+                               mul_v2_v2(cumap->cm[a].ext_in, cumap->cm[3].ext_in);
+                               mul_v2_v2(cumap->cm[a].ext_out, cumap->cm[3].ext_out);
                        }
                        
                        cumap->flag |= CUMA_PREMULLED;
@@ -679,7 +779,7 @@ static int sort_curvepoints(const void *a1, const void *a2)
 /* ************************ more CurveMapping calls *************** */
 
 /* note; only does current curvemap! */
-void curvemapping_changed(CurveMapping *cumap, int rem_doubles)
+void curvemapping_changed(CurveMapping *cumap, const bool rem_doubles)
 {
        CurveMap *cuma = cumap->cm + cumap->cur;
        CurveMapPoint *cmp = cuma->curve;
@@ -710,6 +810,16 @@ void curvemapping_changed(CurveMapping *cumap, int rem_doubles)
                                cmp[a].y -= dy;
                        }
                }
+
+               /* ensure zoom-level respects clipping */
+               if (BLI_rctf_size_x(&cumap->curr) > BLI_rctf_size_x(&cumap->clipr)) {
+                       cumap->curr.xmin = cumap->clipr.xmin;
+                       cumap->curr.xmax = cumap->clipr.xmax;
+               }
+               if (BLI_rctf_size_y(&cumap->curr) > BLI_rctf_size_y(&cumap->clipr)) {
+                       cumap->curr.ymin = cumap->clipr.ymin;
+                       cumap->curr.ymax = cumap->clipr.ymax;
+               }
        }
        
        
@@ -722,12 +832,12 @@ void curvemapping_changed(CurveMapping *cumap, int rem_doubles)
                        dy = cmp[a].y - cmp[a + 1].y;
                        if (sqrtf(dx * dx + dy * dy) < thresh) {
                                if (a == 0) {
-                                       cmp[a + 1].flag |= CUMA_VECTOR;
+                                       cmp[a + 1].flag |= CUMA_HANDLE_VECTOR;
                                        if (cmp[a + 1].flag & CUMA_SELECT)
                                                cmp[a].flag |= CUMA_SELECT;
                                }
                                else {
-                                       cmp[a].flag |= CUMA_VECTOR;
+                                       cmp[a].flag |= CUMA_HANDLE_VECTOR;
                                        if (cmp[a].flag & CUMA_SELECT)
                                                cmp[a + 1].flag |= CUMA_SELECT;
                                }
@@ -747,7 +857,7 @@ void curvemapping_changed_all(CurveMapping *cumap)
        for (a = 0; a < CM_TOT; a++) {
                if (cumap->cm[a].curve) {
                        cumap->cur = a;
-                       curvemapping_changed(cumap, FALSE);
+                       curvemapping_changed(cumap, false);
                }
        }
 
@@ -780,7 +890,17 @@ float curvemap_evaluateF(const CurveMap *cuma, float value)
 float curvemapping_evaluateF(const CurveMapping *cumap, int cur, float value)
 {
        const CurveMap *cuma = cumap->cm + cur;
-       return curvemap_evaluateF(cuma, value);
+       float val = curvemap_evaluateF(cuma, value);
+
+       /* account for clipping */
+       if (cumap->flag & CUMA_DO_CLIP) {
+               if (val < cumap->curr.ymin)
+                       val = cumap->curr.ymin;
+               else if (val > cumap->curr.ymax)
+                       val = cumap->curr.ymax;
+       }
+
+       return val;
 }
 
 /* vector case */
@@ -835,9 +955,9 @@ void curvemapping_evaluate_premulRGB(const CurveMapping *cumap, unsigned char ve
 
        curvemapping_evaluate_premulRGBF(cumap, vecout, vecin);
 
-       vecout_byte[0] = FTOCHAR(vecout[0]);
-       vecout_byte[1] = FTOCHAR(vecout[1]);
-       vecout_byte[2] = FTOCHAR(vecout[2]);
+       vecout_byte[0] = unit_float_to_uchar_clamp(vecout[0]);
+       vecout_byte[1] = unit_float_to_uchar_clamp(vecout[1]);
+       vecout_byte[2] = unit_float_to_uchar_clamp(vecout[2]);
 }
 
 int curvemapping_RGBA_does_something(const CurveMapping *cumap)
@@ -914,13 +1034,14 @@ static void save_sample_line(Scopes *scopes, const int idx, const float fx, cons
        float yuv[3];
 
        /* vectorscope*/
-       rgb_to_yuv(rgb[0], rgb[1], rgb[2], &yuv[0], &yuv[1], &yuv[2]);
+       rgb_to_yuv(rgb[0], rgb[1], rgb[2], &yuv[0], &yuv[1], &yuv[2], BLI_YUV_ITU_BT709);
        scopes->vecscope[idx + 0] = yuv[1];
        scopes->vecscope[idx + 1] = yuv[2];
 
        /* waveform */
        switch (scopes->wavefrm_mode) {
                case SCOPES_WAVEFRM_RGB:
+               case SCOPES_WAVEFRM_RGB_PARADE:
                        scopes->waveform_1[idx + 0] = fx;
                        scopes->waveform_1[idx + 1] = rgb[0];
                        scopes->waveform_2[idx + 0] = fx;
@@ -949,8 +1070,7 @@ void BKE_histogram_update_sample_line(Histogram *hist, ImBuf *ibuf, const ColorM
                                       const ColorManagedDisplaySettings *display_settings)
 {
        int i, x, y;
-       float *fp;
-       float rgb[3];
+       const float *fp;
        unsigned char *cp;
 
        int x1 = 0.5f + hist->co[0][0] * ibuf->x;
@@ -970,9 +1090,6 @@ void BKE_histogram_update_sample_line(Histogram *hist, ImBuf *ibuf, const ColorM
        if (ibuf->rect_float)
                cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
 
-       /* persistent draw */
-       hist->flag |= HISTO_FLAG_SAMPLELINE; /* keep drawing the flag after */
-
        for (i = 0; i < 256; i++) {
                x = (int)(0.5f + x1 + (float)i * (x2 - x1) / 255.0f);
                y = (int)(0.5f + y1 + (float)i * (y2 - y1) / 255.0f);
@@ -982,20 +1099,40 @@ void BKE_histogram_update_sample_line(Histogram *hist, ImBuf *ibuf, const ColorM
                }
                else {
                        if (ibuf->rect_float) {
+                               float rgba[4];
                                fp = (ibuf->rect_float + (ibuf->channels) * (y * ibuf->x + x));
 
-                               copy_v3_v3(rgb, fp);
-                               IMB_colormanagement_processor_apply_v3(cm_processor, rgb);
+                               switch (ibuf->channels) {
+                                       case 4:
+                                               copy_v4_v4(rgba, fp);
+                                               IMB_colormanagement_processor_apply_v4(cm_processor, rgba);
+                                               break;
+                                       case 3:
+                                               copy_v3_v3(rgba, fp);
+                                               IMB_colormanagement_processor_apply_v3(cm_processor, rgba);
+                                               rgba[3] = 1.0f;
+                                               break;
+                                       case 2:
+                                               copy_v3_fl(rgba, fp[0]);
+                                               rgba[3] = fp[1];
+                                               break;
+                                       case 1:
+                                               copy_v3_fl(rgba, fp[0]);
+                                               rgba[3] = 1.0f;
+                                               break;
+                                       default:
+                                               BLI_assert(0);
+                               }
 
-                               hist->data_luma[i]  = rgb_to_luma(rgb);
-                               hist->data_r[i]     = rgb[0];
-                               hist->data_g[i]     = rgb[1];
-                               hist->data_b[i]     = rgb[2];
-                               hist->data_a[i]     = fp[3];
+                               hist->data_luma[i]  = IMB_colormanagement_get_luminance(rgba);
+                               hist->data_r[i]     = rgba[0];
+                               hist->data_g[i]     = rgba[1];
+                               hist->data_b[i]     = rgba[2];
+                               hist->data_a[i]     = rgba[3];
                        }
                        else if (ibuf->rect) {
                                cp = (unsigned char *)(ibuf->rect + y * ibuf->x + x);
-                               hist->data_luma[i]  = (float)rgb_to_luma_byte(cp) / 255.0f;
+                               hist->data_luma[i]  = (float)IMB_colormanagement_get_luminance_byte(cp) / 255.0f;
                                hist->data_r[i]     = (float)cp[0] / 255.0f;
                                hist->data_g[i]     = (float)cp[1] / 255.0f;
                                hist->data_b[i]     = (float)cp[2] / 255.0f;
@@ -1008,20 +1145,173 @@ void BKE_histogram_update_sample_line(Histogram *hist, ImBuf *ibuf, const ColorM
                IMB_colormanagement_processor_free(cm_processor);
 }
 
+/* if view_settings, it also applies this to byte buffers */
+typedef struct ScopesUpdateData {
+       Scopes *scopes;
+       const ImBuf *ibuf;
+       struct ColormanageProcessor *cm_processor;
+       const unsigned char *display_buffer;
+       const int ycc_mode;
+
+       unsigned int *bin_lum, *bin_r, *bin_g, *bin_b, *bin_a;
+} ScopesUpdateData;
+
+typedef struct ScopesUpdateDataChunk {
+       unsigned int bin_lum[256];
+       unsigned int bin_r[256];
+       unsigned int bin_g[256];
+       unsigned int bin_b[256];
+       unsigned int bin_a[256];
+       float min[3], max[3];
+} ScopesUpdateDataChunk;
+
+static void scopes_update_cb(void *__restrict userdata, 
+                             const int y,
+                             const ParallelRangeTLS *__restrict tls)
+{
+       const ScopesUpdateData *data = userdata;
+
+       Scopes *scopes = data->scopes;
+       const ImBuf *ibuf = data->ibuf;
+       struct ColormanageProcessor *cm_processor = data->cm_processor;
+       const unsigned char *display_buffer = data->display_buffer;
+       const int ycc_mode = data->ycc_mode;
+
+       ScopesUpdateDataChunk *data_chunk = tls->userdata_chunk;
+       unsigned int *bin_lum = data_chunk->bin_lum;
+       unsigned int *bin_r = data_chunk->bin_r;
+       unsigned int *bin_g = data_chunk->bin_g;
+       unsigned int *bin_b = data_chunk->bin_b;
+       unsigned int *bin_a = data_chunk->bin_a;
+       float *min = data_chunk->min;
+       float *max = data_chunk->max;
+
+       const float *rf = NULL;
+       const unsigned char *rc = NULL;
+       const int rows_per_sample_line = ibuf->y / scopes->sample_lines;
+       const int savedlines = y / rows_per_sample_line;
+       const bool do_sample_line = (savedlines < scopes->sample_lines) && (y % rows_per_sample_line) == 0;
+       const bool is_float = (ibuf->rect_float != NULL);
+
+       if (is_float)
+               rf = ibuf->rect_float + ((size_t)y) * ibuf->x * ibuf->channels;
+       else {
+               rc = display_buffer + ((size_t)y) * ibuf->x * ibuf->channels;
+       }
+
+       for (int x = 0; x < ibuf->x; x++) {
+               float rgba[4], ycc[3], luma;
+
+               if (is_float) {
+                       switch (ibuf->channels) {
+                               case 4:
+                                       copy_v4_v4(rgba, rf);
+                                       IMB_colormanagement_processor_apply_v4(cm_processor, rgba);
+                                       break;
+                               case 3:
+                                       copy_v3_v3(rgba, rf);
+                                       IMB_colormanagement_processor_apply_v3(cm_processor, rgba);
+                                       rgba[3] = 1.0f;
+                                       break;
+                               case 2:
+                                       copy_v3_fl(rgba, rf[0]);
+                                       rgba[3] = rf[1];
+                                       break;
+                               case 1:
+                                       copy_v3_fl(rgba, rf[0]);
+                                       rgba[3] = 1.0f;
+                                       break;
+                               default:
+                                       BLI_assert(0);
+                       }
+               }
+               else {
+                       for (int c = 4; c--;)
+                               rgba[c] = rc[c] * INV_255;
+               }
+
+               /* we still need luma for histogram */
+               luma = IMB_colormanagement_get_luminance(rgba);
+
+               /* check for min max */
+               if (ycc_mode == -1) {
+                       minmax_v3v3_v3(min, max, rgba);
+               }
+               else {
+                       rgb_to_ycc(rgba[0], rgba[1], rgba[2], &ycc[0], &ycc[1], &ycc[2], ycc_mode);
+                       mul_v3_fl(ycc, INV_255);
+                       minmax_v3v3_v3(min, max, ycc);
+               }
+               /* increment count for histo*/
+               bin_lum[get_bin_float(luma)]++;
+               bin_r[get_bin_float(rgba[0])]++;
+               bin_g[get_bin_float(rgba[1])]++;
+               bin_b[get_bin_float(rgba[2])]++;
+               bin_a[get_bin_float(rgba[3])]++;
+
+               /* save sample if needed */
+               if (do_sample_line) {
+                       const float fx = (float)x / (float)ibuf->x;
+                       const int idx = 2 * (ibuf->x * savedlines + x);
+                       save_sample_line(scopes, idx, fx, rgba, ycc);
+               }
+
+               rf += ibuf->channels;
+               rc += ibuf->channels;
+       }
+}
+
+static void scopes_update_finalize(void *__restrict userdata,
+                                   void *__restrict userdata_chunk)
+{
+       const ScopesUpdateData *data = userdata;
+       const ScopesUpdateDataChunk *data_chunk = userdata_chunk;
+
+       unsigned int *bin_lum = data->bin_lum;
+       unsigned int *bin_r = data->bin_r;
+       unsigned int *bin_g = data->bin_g;
+       unsigned int *bin_b = data->bin_b;
+       unsigned int *bin_a = data->bin_a;
+       const unsigned int *bin_lum_c = data_chunk->bin_lum;
+       const unsigned int *bin_r_c = data_chunk->bin_r;
+       const unsigned int *bin_g_c = data_chunk->bin_g;
+       const unsigned int *bin_b_c = data_chunk->bin_b;
+       const unsigned int *bin_a_c = data_chunk->bin_a;
+
+       float (*minmax)[2] = data->scopes->minmax;
+       const float *min = data_chunk->min;
+       const float *max = data_chunk->max;
+
+       for (int b = 256; b--;) {
+               bin_lum[b] += bin_lum_c[b];
+               bin_r[b] += bin_r_c[b];
+               bin_g[b] += bin_g_c[b];
+               bin_b[b] += bin_b_c[b];
+               bin_a[b] += bin_a_c[b];
+       }
+
+       for (int c = 3; c--;) {
+               if (min[c] < minmax[c][0])
+                       minmax[c][0] = min[c];
+               if (max[c] > minmax[c][1])
+                       minmax[c][1] = max[c];
+       }
+}
+
 void scopes_update(Scopes *scopes, ImBuf *ibuf, const ColorManagedViewSettings *view_settings,
                    const ColorManagedDisplaySettings *display_settings)
 {
-       int x, y, c;
+       int a;
        unsigned int nl, na, nr, ng, nb;
        double divl, diva, divr, divg, divb;
-       float *rf = NULL;
-       unsigned char *rc = NULL;
-       unsigned int *bin_lum, *bin_r, *bin_g, *bin_b, *bin_a;
-       int savedlines, saveline;
-       float rgba[4], ycc[3], luma;
+       const unsigned char *display_buffer = NULL;
+       unsigned int bin_lum[256] = {0},
+                    bin_r[256] = {0},
+                    bin_g[256] = {0},
+                    bin_b[256] = {0},
+                    bin_a[256] = {0};
        int ycc_mode = -1;
-       const short is_float = (ibuf->rect_float != NULL);
-
+       void *cache_handle = NULL;
        struct ColormanageProcessor *cm_processor = NULL;
 
        if (ibuf->rect == NULL && ibuf->rect_float == NULL) return;
@@ -1038,6 +1328,8 @@ void scopes_update(Scopes *scopes, ImBuf *ibuf, const ColorManagedViewSettings *
 
        switch (scopes->wavefrm_mode) {
                case SCOPES_WAVEFRM_RGB:
+                       /* fall-through */
+               case SCOPES_WAVEFRM_RGB_PARADE:
                        ycc_mode = -1;
                        break;
                case SCOPES_WAVEFRM_LUMA:
@@ -1052,24 +1344,17 @@ void scopes_update(Scopes *scopes, ImBuf *ibuf, const ColorManagedViewSettings *
                        break;
        }
 
-       /* temp table to count pix value for histogram */
-       bin_r     = MEM_callocN(256 * sizeof(unsigned int), "temp historgram bins");
-       bin_g     = MEM_callocN(256 * sizeof(unsigned int), "temp historgram bins");
-       bin_b     = MEM_callocN(256 * sizeof(unsigned int), "temp historgram bins");
-       bin_a = MEM_callocN(256 * sizeof(unsigned int), "temp historgram bins");
-       bin_lum   = MEM_callocN(256 * sizeof(unsigned int), "temp historgram bins");
-
        /* convert to number of lines with logarithmic scale */
        scopes->sample_lines = (scopes->accuracy * 0.01f) * (scopes->accuracy * 0.01f) * ibuf->y;
+       CLAMP_MIN(scopes->sample_lines, 1);
        
        if (scopes->sample_full)
                scopes->sample_lines = ibuf->y;
 
        /* scan the image */
-       savedlines = 0;
-       for (c = 0; c < 3; c++) {
-               scopes->minmax[c][0] = 25500.0f;
-               scopes->minmax[c][1] = -25500.0f;
+       for (a = 0; a < 3; a++) {
+               scopes->minmax[a][0] = 25500.0f;
+               scopes->minmax[a][1] = -25500.0f;
        }
        
        scopes->waveform_tot = ibuf->x * scopes->sample_lines;
@@ -1088,112 +1373,73 @@ void scopes_update(Scopes *scopes, ImBuf *ibuf, const ColorManagedViewSettings *
        scopes->waveform_3 = MEM_callocN(scopes->waveform_tot * 2 * sizeof(float), "waveform point channel 3");
        scopes->vecscope = MEM_callocN(scopes->waveform_tot * 2 * sizeof(float), "vectorscope point channel");
        
-       if (is_float)
-               rf = ibuf->rect_float;
-       else
-               rc = (unsigned char *)ibuf->rect;
-
-       if (ibuf->rect_float)
+       if (ibuf->rect_float) {
                cm_processor = IMB_colormanagement_display_processor_new(view_settings, display_settings);
-
-       for (y = 0; y < ibuf->y; y++) {
-               if (savedlines < scopes->sample_lines && y >= ((savedlines) * ibuf->y) / (scopes->sample_lines + 1)) {
-                       saveline = 1;
-               }
-               else {
-                       saveline = 0;
-               }
-               for (x = 0; x < ibuf->x; x++) {
-
-                       if (is_float) {
-                               copy_v4_v4(rgba, rf);
-                               IMB_colormanagement_processor_apply_v4(cm_processor, rgba);
-                       }
-                       else {
-                               for (c = 0; c < 4; c++)
-                                       rgba[c] = rc[c] * INV_255;
-                       }
-
-                       /* we still need luma for histogram */
-                       luma = rgb_to_luma(rgba);
-
-                       /* check for min max */
-                       if (ycc_mode == -1) {
-                               for (c = 0; c < 3; c++) {
-                                       if (rgba[c] < scopes->minmax[c][0]) scopes->minmax[c][0] = rgba[c];
-                                       if (rgba[c] > scopes->minmax[c][1]) scopes->minmax[c][1] = rgba[c];
-                               }
-                       }
-                       else {
-                               rgb_to_ycc(rgba[0], rgba[1], rgba[2], &ycc[0], &ycc[1], &ycc[2], ycc_mode);
-                               for (c = 0; c < 3; c++) {
-                                       ycc[c] *= INV_255;
-                                       if (ycc[c] < scopes->minmax[c][0]) scopes->minmax[c][0] = ycc[c];
-                                       if (ycc[c] > scopes->minmax[c][1]) scopes->minmax[c][1] = ycc[c];
-                               }
-                       }
-                       /* increment count for histo*/
-                       bin_lum[get_bin_float(luma)] += 1;
-                       bin_r[get_bin_float(rgba[0])] += 1;
-                       bin_g[get_bin_float(rgba[1])] += 1;
-                       bin_b[get_bin_float(rgba[2])] += 1;
-                       bin_a[get_bin_float(rgba[3])] += 1;
-
-                       /* save sample if needed */
-                       if (saveline) {
-                               const float fx = (float)x / (float)ibuf->x;
-                               const int idx = 2 * (ibuf->x * savedlines + x);
-                               save_sample_line(scopes, idx, fx, rgba, ycc);
-                       }
-
-                       rf += ibuf->channels;
-                       rc += ibuf->channels;
-               }
-               if (saveline)
-                       savedlines += 1;
        }
+       else {
+               display_buffer = (const unsigned char *)IMB_display_buffer_acquire(
+                                                           ibuf, view_settings, display_settings, &cache_handle);
+       }
+
+       /* Keep number of threads in sync with the merge parts below. */
+       ScopesUpdateData data = {
+               .scopes = scopes, .ibuf = ibuf,
+               .cm_processor = cm_processor, .display_buffer = display_buffer, .ycc_mode = ycc_mode,
+               .bin_lum = bin_lum, .bin_r = bin_r, .bin_g = bin_g, .bin_b = bin_b, .bin_a = bin_a,
+       };
+       ScopesUpdateDataChunk data_chunk = {{0}};
+       INIT_MINMAX(data_chunk.min, data_chunk.max);
+
+       ParallelRangeSettings settings;
+       BLI_parallel_range_settings_defaults(&settings);
+       settings.use_threading = (ibuf->y > 256);
+       settings.userdata_chunk = &data_chunk;
+       settings.userdata_chunk_size = sizeof(data_chunk);
+       settings.func_finalize = scopes_update_finalize;
+       BLI_task_parallel_range(0, ibuf->y,
+                               &data,
+                               scopes_update_cb,
+                               &settings);
 
        /* test for nicer distribution even - non standard, leave it out for a while */
 #if 0
-       for (x = 0; x < 256; x++) {
-               bin_lum[x] = sqrt (bin_lum[x]);
-               bin_r[x] = sqrt(bin_r[x]);
-               bin_g[x] = sqrt(bin_g[x]);
-               bin_b[x] = sqrt(bin_b[x]);
-               bin_a[x] = sqrt(bin_a[x]);
+       for (a = 0; a < 256; a++) {
+               bin_lum[a] = sqrt (bin_lum[a]);
+               bin_r[a] = sqrt(bin_r[a]);
+               bin_g[a] = sqrt(bin_g[a]);
+               bin_b[a] = sqrt(bin_b[a]);
+               bin_a[a] = sqrt(bin_a[a]);
        }
 #endif
        
        /* convert hist data to float (proportional to max count) */
        nl = na = nr = nb = ng = 0;
-       for (x = 0; x < 256; x++) {
-               if (bin_lum[x] > nl) nl = bin_lum[x];
-               if (bin_r[x]   > nr) nr = bin_r[x];
-               if (bin_g[x]   > ng) ng = bin_g[x];
-               if (bin_b[x]   > nb) nb = bin_b[x];
-               if (bin_a[x]   > na) na = bin_a[x];
+       for (a = 0; a < 256; a++) {
+               if (bin_lum[a] > nl) nl = bin_lum[a];
+               if (bin_r[a]   > nr) nr = bin_r[a];
+               if (bin_g[a]   > ng) ng = bin_g[a];
+               if (bin_b[a]   > nb) nb = bin_b[a];
+               if (bin_a[a]   > na) na = bin_a[a];
        }
-       divl = 1.0 / (double)nl;
-       diva = 1.0 / (double)na;
-       divr = 1.0 / (double)nr;
-       divg = 1.0 / (double)ng;
-       divb = 1.0 / (double)nb;
-       for (x = 0; x < 256; x++) {
-               scopes->hist.data_luma[x] = bin_lum[x] * divl;
-               scopes->hist.data_r[x] = bin_r[x] * divr;
-               scopes->hist.data_g[x] = bin_g[x] * divg;
-               scopes->hist.data_b[x] = bin_b[x] * divb;
-               scopes->hist.data_a[x] = bin_a[x] * diva;
+       divl = nl ? 1.0 / (double)nl : 1.0;
+       diva = na ? 1.0 / (double)na : 1.0;
+       divr = nr ? 1.0 / (double)nr : 1.0;
+       divg = ng ? 1.0 / (double)ng : 1.0;
+       divb = nb ? 1.0 / (double)nb : 1.0;
+       
+       for (a = 0; a < 256; a++) {
+               scopes->hist.data_luma[a] = bin_lum[a] * divl;
+               scopes->hist.data_r[a] = bin_r[a] * divr;
+               scopes->hist.data_g[a] = bin_g[a] * divg;
+               scopes->hist.data_b[a] = bin_b[a] * divb;
+               scopes->hist.data_a[a] = bin_a[a] * diva;
        }
-       MEM_freeN(bin_lum);
-       MEM_freeN(bin_r);
-       MEM_freeN(bin_g);
-       MEM_freeN(bin_b);
-       MEM_freeN(bin_a);
 
        if (cm_processor)
                IMB_colormanagement_processor_free(cm_processor);
-
+       if (cache_handle)
+               IMB_display_buffer_release(cache_handle);
+       
        scopes->ok = 1;
 }
 
@@ -1253,6 +1499,7 @@ void BKE_color_managed_view_settings_init(ColorManagedViewSettings *settings)
         *            for now use NONE to be compatible with all current files
         */
        BLI_strncpy(settings->view_transform, "Default", sizeof(settings->view_transform));
+       BLI_strncpy(settings->look, "None", sizeof(settings->look));
 
        settings->gamma = 1.0f;
        settings->exposure = 0.0f;
@@ -1261,6 +1508,7 @@ void BKE_color_managed_view_settings_init(ColorManagedViewSettings *settings)
 void BKE_color_managed_view_settings_copy(ColorManagedViewSettings *new_settings,
                                           const ColorManagedViewSettings *settings)
 {
+       BLI_strncpy(new_settings->look, settings->look, sizeof(new_settings->look));
        BLI_strncpy(new_settings->view_transform, settings->view_transform, sizeof(new_settings->view_transform));
 
        new_settings->flag = settings->flag;
@@ -1269,6 +1517,8 @@ void BKE_color_managed_view_settings_copy(ColorManagedViewSettings *new_settings
 
        if (settings->curve_mapping)
                new_settings->curve_mapping = curvemapping_copy(settings->curve_mapping);
+       else
+               new_settings->curve_mapping = NULL;
 }
 
 void BKE_color_managed_view_settings_free(ColorManagedViewSettings *settings)
@@ -1287,3 +1537,9 @@ void BKE_color_managed_colorspace_settings_copy(ColorManagedColorspaceSettings *
 {
        BLI_strncpy(colorspace_settings->name, settings->name, sizeof(colorspace_settings->name));
 }
+
+bool BKE_color_managed_colorspace_settings_equals(const ColorManagedColorspaceSettings *settings1,
+                                                  const ColorManagedColorspaceSettings *settings2)
+{
+       return STREQ(settings1->name, settings2->name);
+}