Merge branch 'master' into blender2.8
[blender.git] / source / blender / imbuf / intern / divers.c
index 5ba08f1dd735741fd45f2bb3d8da4b23743c128c..769d53a44ef306bd24be80108aff359eb0c10f77 100644 (file)
  *  \ingroup imbuf
  */
 
-#include "BLI_rand.h"
 #include "BLI_math.h"
 #include "BLI_utildefines.h"
 
 #include "imbuf.h"
 #include "IMB_imbuf_types.h"
 #include "IMB_imbuf.h"
-#include "IMB_allocimbuf.h"
+#include "IMB_filter.h"
+
+#include "IMB_colormanagement.h"
+#include "IMB_colormanagement_intern.h"
+
 
 #include "MEM_guardedalloc.h"
 
@@ -57,7 +60,7 @@ void IMB_de_interlace(ImBuf *ibuf)
                tbuf1 = IMB_allocImBuf(ibuf->x, ibuf->y / 2, 32, IB_rect);
                tbuf2 = IMB_allocImBuf(ibuf->x, ibuf->y / 2, 32, IB_rect);
                
-               ibuf->x *= 2;   
+               ibuf->x *= 2;
                IMB_rectcpy(tbuf1, ibuf, 0, 0, 0, 0, ibuf->x, ibuf->y);
                IMB_rectcpy(tbuf2, ibuf, 0, 0, tbuf2->x, 0, ibuf->x, ibuf->y);
        
@@ -101,118 +104,96 @@ void IMB_interlace(ImBuf *ibuf)
 /************************* Floyd-Steinberg dithering *************************/
 
 typedef struct DitherContext {
-       int *error_buf, *e;
-       int v[4], v0[4], v1[4];
-       float f;
+       float dither;
 } DitherContext;
 
-DitherContext *create_dither_context(int w, float factor)
+static DitherContext *create_dither_context(float dither)
 {
        DitherContext *di;
-       int i;
-       
-       di = MEM_callocN(sizeof(DitherContext), "dithering context");
-       di->f = factor / 16.0f;
-       di->error_buf = MEM_callocN(4 * (w + 1) * sizeof(int), "dithering error");
-       di->e = di->error_buf;
 
-       for (i = 0; i < 4; ++i)
-               di->v[i] = di->v0[i] = di->v1[i] = 1024.0f * (BLI_frand() - 0.5f);
+       di = MEM_mallocN(sizeof(DitherContext), "dithering context");
+       di->dither = dither;
 
        return di;
 }
 
 static void clear_dither_context(DitherContext *di)
 {
-       MEM_freeN(di->error_buf);
        MEM_freeN(di);
 }
 
-static void dither_finish_row(DitherContext *di)
-{
-       int i;
-
-       for (i = 0; i < 4; i++)
-               di->v[i] = di->v0[i] = di->v1[i] = 0;
 
-       di->e = di->error_buf;
-}
+/************************* Generic Buffer Conversion *************************/
 
-MINLINE unsigned char dither_value(unsigned short v_in, DitherContext *di, int i)
+MINLINE void ushort_to_byte_v4(uchar b[4], const unsigned short us[4])
 {
-       int dv, d2;
-       unsigned char v_out;
-
-       di->v[i] = v_in + (2 * di->v[i] + di->e[4]) * di->f;
-       CLAMP(di->v[i], 0, 0xFF00);
-       v_out = USHORTTOUCHAR(di->v[i]);
-       di->v[i] -= v_out << 8;
-       dv = di->v[i];
-       d2 = di->v[i] << 1;
-       di->v[i] += d2;
-       *(di->e++) = di->v[i] + di->v0[i];
-       di->v[i] += d2;
-
-       di->v0[i] = di->v[i] + di->v1[i];
-       di->v1[i] = dv;
-       di->v[i] += d2;
-
-       return v_out;
+       b[0] = unit_ushort_to_uchar(us[0]);
+       b[1] = unit_ushort_to_uchar(us[1]);
+       b[2] = unit_ushort_to_uchar(us[2]);
+       b[3] = unit_ushort_to_uchar(us[3]);
 }
 
-/************************* Generic Buffer Conversion *************************/
-
-MINLINE void ushort_to_byte_v4(uchar b[4], const unsigned short us[4])
+MINLINE unsigned char ftochar(float value)
 {
-       b[0] = USHORTTOUCHAR(us[0]);
-       b[1] = USHORTTOUCHAR(us[1]);
-       b[2] = USHORTTOUCHAR(us[2]);
-       b[3] = USHORTTOUCHAR(us[3]);
+       return unit_float_to_uchar_clamp(value);
 }
 
-MINLINE void ushort_to_byte_dither_v4(uchar b[4], const unsigned short us[4], DitherContext *di)
+MINLINE void ushort_to_byte_dither_v4(uchar b[4], const unsigned short us[4], DitherContext *di, float s, float t)
 {
-       b[0] = dither_value(us[0], di, 0);
-       b[1] = dither_value(us[1], di, 1);
-       b[2] = dither_value(us[2], di, 2);
-       b[3] = dither_value(us[3], di, 3);
+#define USHORTTOFLOAT(val) ((float)val / 65535.0f)
+       float dither_value = dither_random_value(s, t) * 0.005f * di->dither;
+
+       b[0] = ftochar(dither_value + USHORTTOFLOAT(us[0]));
+       b[1] = ftochar(dither_value + USHORTTOFLOAT(us[1]));
+       b[2] = ftochar(dither_value + USHORTTOFLOAT(us[2]));
+       b[3] = unit_ushort_to_uchar(us[3]);
+
+#undef USHORTTOFLOAT
 }
 
-MINLINE void float_to_byte_dither_v4(uchar b[4], const float f[4], DitherContext *di)
+MINLINE void float_to_byte_dither_v4(uchar b[4], const float f[4], DitherContext *di, float s, float t)
 {
-       unsigned short us[4] = {FTOUSHORT(f[0]), FTOUSHORT(f[1]), FTOUSHORT(f[2]), FTOUSHORT(f[3])};
-       ushort_to_byte_dither_v4(b, us, di);
+       float dither_value = dither_random_value(s, t) * 0.005f * di->dither;
+
+       b[0] = ftochar(dither_value + f[0]);
+       b[1] = ftochar(dither_value + f[1]);
+       b[2] = ftochar(dither_value + f[2]);
+       b[3] = unit_float_to_uchar_clamp(f[3]);
 }
 
 /* float to byte pixels, output 4-channel RGBA */
 void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from,
-                                int channels_from, float dither, int profile_to, int profile_from, int predivide,
+                                int channels_from, float dither, int profile_to, int profile_from, bool predivide,
                                 int width, int height, int stride_to, int stride_from)
 {
        float tmp[4];
        int x, y;
-       DitherContext *di;
+       DitherContext *di = NULL;
+       float inv_width = 1.0f / width;
+       float inv_height = 1.0f / height;
 
        /* we need valid profiles */
        BLI_assert(profile_to != IB_PROFILE_NONE);
        BLI_assert(profile_from != IB_PROFILE_NONE);
 
        if (dither)
-               di = create_dither_context(width, dither);
+               di = create_dither_context(dither);
 
        for (y = 0; y < height; y++) {
+               float t = y * inv_height;
+
                if (channels_from == 1) {
                        /* single channel input */
-                       const float *from = rect_from + stride_from * y;
-                       uchar *to = rect_to + stride_to * y * 4;
+                       const float *from = rect_from + ((size_t)stride_from) * y;
+                       uchar *to = rect_to + ((size_t)stride_to) * y * 4;
 
                        for (x = 0; x < width; x++, from++, to += 4)
-                               to[0] = to[1] = to[2] = to[3] = FTOCHAR(from[0]);
+                               to[0] = to[1] = to[2] = to[3] = unit_float_to_uchar_clamp(from[0]);
                }
                else if (channels_from == 3) {
                        /* RGB input */
-                       const float *from = rect_from + stride_from * y * 3;
-                       uchar *to = rect_to + stride_to * y * 4;
+                       const float *from = rect_from + ((size_t)stride_from) * y * 3;
+                       uchar *to = rect_to + ((size_t)stride_to) * y * 4;
 
                        if (profile_to == profile_from) {
                                /* no color space conversion */
@@ -240,14 +221,28 @@ void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from,
                }
                else if (channels_from == 4) {
                        /* RGBA input */
-                       const float *from = rect_from + stride_from * y * 4;
-                       uchar *to = rect_to + stride_to * y * 4;
+                       const float *from = rect_from + ((size_t)stride_from) * y * 4;
+                       uchar *to = rect_to + ((size_t)stride_to) * y * 4;
 
                        if (profile_to == profile_from) {
+                               float straight[4];
+
                                /* no color space conversion */
-                               if (dither) {
+                               if (dither && predivide) {
+                                       for (x = 0; x < width; x++, from += 4, to += 4) {
+                                               premul_to_straight_v4_v4(straight, from);
+                                               float_to_byte_dither_v4(to, straight, di, (float) x * inv_width, t);
+                                       }
+                               }
+                               else if (dither) {
                                        for (x = 0; x < width; x++, from += 4, to += 4)
-                                               float_to_byte_dither_v4(to, from, di);
+                                               float_to_byte_dither_v4(to, from, di, (float) x * inv_width, t);
+                               }
+                               else if (predivide) {
+                                       for (x = 0; x < width; x++, from += 4, to += 4) {
+                                               premul_to_straight_v4_v4(straight, from);
+                                               rgba_float_to_uchar(to, straight);
+                                       }
                                }
                                else {
                                        for (x = 0; x < width; x++, from += 4, to += 4)
@@ -257,22 +252,25 @@ void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from,
                        else if (profile_to == IB_PROFILE_SRGB) {
                                /* convert from linear to sRGB */
                                unsigned short us[4];
+                               float straight[4];
 
                                if (dither && predivide) {
                                        for (x = 0; x < width; x++, from += 4, to += 4) {
-                                               linearrgb_to_srgb_ushort4_predivide(us, from);
-                                               ushort_to_byte_dither_v4(to, us, di);
+                                               premul_to_straight_v4_v4(straight, from);
+                                               linearrgb_to_srgb_ushort4(us, from);
+                                               ushort_to_byte_dither_v4(to, us, di, (float) x * inv_width, t);
                                        }
                                }
                                else if (dither) {
                                        for (x = 0; x < width; x++, from += 4, to += 4) {
                                                linearrgb_to_srgb_ushort4(us, from);
-                                               ushort_to_byte_dither_v4(to, us, di);
+                                               ushort_to_byte_dither_v4(to, us, di, (float) x * inv_width, t);
                                        }
                                }
                                else if (predivide) {
                                        for (x = 0; x < width; x++, from += 4, to += 4) {
-                                               linearrgb_to_srgb_ushort4_predivide(us, from);
+                                               premul_to_straight_v4_v4(straight, from);
+                                               linearrgb_to_srgb_ushort4(us, from);
                                                ushort_to_byte_v4(to, us);
                                        }
                                }
@@ -288,13 +286,13 @@ void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from,
                                if (dither && predivide) {
                                        for (x = 0; x < width; x++, from += 4, to += 4) {
                                                srgb_to_linearrgb_predivide_v4(tmp, from);
-                                               float_to_byte_dither_v4(to, tmp, di);
+                                               float_to_byte_dither_v4(to, tmp, di, (float) x * inv_width, t);
                                        }
                                }
                                else if (dither) {
                                        for (x = 0; x < width; x++, from += 4, to += 4) {
                                                srgb_to_linearrgb_v4(tmp, from);
-                                               float_to_byte_dither_v4(to, tmp, di);
+                                               float_to_byte_dither_v4(to, tmp, di, (float) x * inv_width, t);
                                        }
                                }
                                else if (predivide) {
@@ -311,9 +309,84 @@ void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from,
                                }
                        }
                }
+       }
 
-               if (dither)
-                       dither_finish_row(di);
+       if (dither)
+               clear_dither_context(di);
+}
+
+
+/* float to byte pixels, output 4-channel RGBA */
+void IMB_buffer_byte_from_float_mask(uchar *rect_to, const float *rect_from,
+                                int channels_from, float dither, bool predivide,
+                                int width, int height, int stride_to, int stride_from, char *mask)
+{
+       int x, y;
+       DitherContext *di = NULL;
+       float inv_width = 1.0f / width,
+       inv_height = 1.0f / height;
+
+       if (dither)
+               di = create_dither_context(dither);
+
+       for (y = 0; y < height; y++) {
+               float t = y * inv_height;
+
+               if (channels_from == 1) {
+                       /* single channel input */
+                       const float *from = rect_from + ((size_t)stride_from) * y;
+                       uchar *to = rect_to + ((size_t)stride_to) * y * 4;
+
+                       for (x = 0; x < width; x++, from++, to += 4)
+                               if (*mask++ == FILTER_MASK_USED)
+                                       to[0] = to[1] = to[2] = to[3] = unit_float_to_uchar_clamp(from[0]);
+               }
+               else if (channels_from == 3) {
+                       /* RGB input */
+                       const float *from = rect_from + ((size_t)stride_from) * y * 3;
+                       uchar *to = rect_to + ((size_t)stride_to) * y * 4;
+
+                       for (x = 0; x < width; x++, from += 3, to += 4) {
+                               if (*mask++ == FILTER_MASK_USED) {
+                                       rgb_float_to_uchar(to, from);
+                                       to[3] = 255;
+                               }
+                       }
+               }
+               else if (channels_from == 4) {
+                       /* RGBA input */
+                       const float *from = rect_from + ((size_t)stride_from) * y * 4;
+                       uchar *to = rect_to + ((size_t)stride_to) * y * 4;
+
+                       float straight[4];
+
+                       if (dither && predivide) {
+                               for (x = 0; x < width; x++, from += 4, to += 4) {
+                                       if (*mask++ == FILTER_MASK_USED) {
+                                               premul_to_straight_v4_v4(straight, from);
+                                               float_to_byte_dither_v4(to, straight, di, (float) x * inv_width, t);
+                                       }
+                               }
+                       }
+                       else if (dither) {
+                               for (x = 0; x < width; x++, from += 4, to += 4)
+                                       if (*mask++ == FILTER_MASK_USED)
+                                               float_to_byte_dither_v4(to, from, di, (float) x * inv_width, t);
+                       }
+                       else if (predivide) {
+                               for (x = 0; x < width; x++, from += 4, to += 4) {
+                                       if (*mask++ == FILTER_MASK_USED) {
+                                               premul_to_straight_v4_v4(straight, from);
+                                               rgba_float_to_uchar(to, straight);
+                                       }
+                               }
+                       }
+                       else {
+                               for (x = 0; x < width; x++, from += 4, to += 4)
+                                       if (*mask++ == FILTER_MASK_USED)
+                                               rgba_float_to_uchar(to, from);
+                       }
+               }
        }
 
        if (dither)
@@ -322,7 +395,7 @@ void IMB_buffer_byte_from_float(uchar *rect_to, const float *rect_from,
 
 /* byte to float pixels, input and output 4-channel RGBA  */
 void IMB_buffer_float_from_byte(float *rect_to, const uchar *rect_from,
-                                int profile_to, int profile_from, int predivide,
+                                int profile_to, int profile_from, bool predivide,
                                 int width, int height, int stride_to, int stride_from)
 {
        float tmp[4];
@@ -335,7 +408,7 @@ void IMB_buffer_float_from_byte(float *rect_to, const uchar *rect_from,
        /* RGBA input */
        for (y = 0; y < height; y++) {
                const uchar *from = rect_from + stride_from * y * 4;
-               float *to = rect_to + stride_to * y * 4;
+               float *to = rect_to + ((size_t)stride_to) * y * 4;
 
                if (profile_to == profile_from) {
                        /* no color space conversion */
@@ -375,7 +448,7 @@ void IMB_buffer_float_from_byte(float *rect_to, const uchar *rect_from,
 
 /* float to float pixels, output 4-channel RGBA */
 void IMB_buffer_float_from_float(float *rect_to, const float *rect_from,
-                                 int channels_from, int profile_to, int profile_from, int predivide,
+                                 int channels_from, int profile_to, int profile_from, bool predivide,
                                  int width, int height, int stride_to, int stride_from)
 {
        int x, y;
@@ -387,8 +460,8 @@ void IMB_buffer_float_from_float(float *rect_to, const float *rect_from,
        if (channels_from == 1) {
                /* single channel input */
                for (y = 0; y < height; y++) {
-                       const float *from = rect_from + stride_from * y;
-                       float *to = rect_to + stride_to * y * 4;
+                       const float *from = rect_from + ((size_t)stride_from) * y;
+                       float *to = rect_to + ((size_t)stride_to) * y * 4;
 
                        for (x = 0; x < width; x++, from++, to += 4)
                                to[0] = to[1] = to[2] = to[3] = from[0];
@@ -397,8 +470,8 @@ void IMB_buffer_float_from_float(float *rect_to, const float *rect_from,
        else if (channels_from == 3) {
                /* RGB input */
                for (y = 0; y < height; y++) {
-                       const float *from = rect_from + stride_from * y * 3;
-                       float *to = rect_to + stride_to * y * 4;
+                       const float *from = rect_from + ((size_t)stride_from) * y * 3;
+                       float *to = rect_to + ((size_t)stride_to) * y * 4;
 
                        if (profile_to == profile_from) {
                                /* no color space conversion */
@@ -426,12 +499,12 @@ void IMB_buffer_float_from_float(float *rect_to, const float *rect_from,
        else if (channels_from == 4) {
                /* RGBA input */
                for (y = 0; y < height; y++) {
-                       const float *from = rect_from + stride_from * y * 4;
-                       float *to = rect_to + stride_to * y * 4;
+                       const float *from = rect_from + ((size_t)stride_from) * y * 4;
+                       float *to = rect_to + ((size_t)stride_to) * y * 4;
 
                        if (profile_to == profile_from) {
                                /* same profile, copy */
-                               memcpy(to, from, sizeof(float) * 4 * width);
+                               memcpy(to, from, sizeof(float) * ((size_t)4) * width);
                        }
                        else if (profile_to == IB_PROFILE_LINEAR_RGB) {
                                /* convert to sRGB to linear */
@@ -459,9 +532,123 @@ void IMB_buffer_float_from_float(float *rect_to, const float *rect_from,
        }
 }
 
+typedef struct FloatToFloatThreadData {
+       float *rect_to;
+       const float *rect_from;
+       int channels_from;
+       int profile_to;
+       int profile_from;
+       bool predivide;
+       int width;
+       int stride_to;
+       int stride_from;
+} FloatToFloatThreadData;
+
+static void imb_buffer_float_from_float_thread_do(void *data_v,
+                                                  int start_scanline,
+                                                  int num_scanlines)
+{
+       FloatToFloatThreadData *data = (FloatToFloatThreadData *)data_v;
+       size_t offset_from = ((size_t)start_scanline) * data->stride_from * data->channels_from;
+       size_t offset_to = ((size_t)start_scanline) * data->stride_to * data->channels_from;
+       IMB_buffer_float_from_float(data->rect_to + offset_to,
+                                   data->rect_from + offset_from,
+                                   data->channels_from,
+                                   data->profile_to,
+                                   data->profile_from,
+                                   data->predivide,
+                                   data->width,
+                                   num_scanlines,
+                                   data->stride_to,
+                                   data->stride_from);
+}
+
+void IMB_buffer_float_from_float_threaded(float *rect_to,
+                                          const float *rect_from,
+                                          int channels_from,
+                                          int profile_to,
+                                          int profile_from,
+                                          bool predivide,
+                                          int width,
+                                          int height,
+                                          int stride_to,
+                                          int stride_from)
+{
+       if (((size_t)width) * height < 64 * 64) {
+               IMB_buffer_float_from_float(rect_to,
+                                           rect_from,
+                                           channels_from,
+                                           profile_to,
+                                           profile_from,
+                                           predivide,
+                                           width,
+                                           height,
+                                           stride_to,
+                                           stride_from);
+       }
+       else {
+               FloatToFloatThreadData data;
+               data.rect_to = rect_to;
+               data.rect_from = rect_from;
+               data.channels_from = channels_from;
+               data.profile_to = profile_to;
+               data.profile_from = profile_from;
+               data.predivide = predivide;
+               data.width = width;
+               data.stride_to = stride_to;
+               data.stride_from = stride_from;
+               IMB_processor_apply_threaded_scanlines(
+                   height, imb_buffer_float_from_float_thread_do, &data);
+       }
+}
+
+/* float to float pixels, output 4-channel RGBA */
+void IMB_buffer_float_from_float_mask(float *rect_to, const float *rect_from, int channels_from,
+                                      int width, int height, int stride_to, int stride_from, char *mask)
+{
+       int x, y;
+
+       if (channels_from == 1) {
+               /* single channel input */
+               for (y = 0; y < height; y++) {
+                       const float *from = rect_from + ((size_t)stride_from) * y;
+                       float *to = rect_to + ((size_t)stride_to) * y * 4;
+
+                       for (x = 0; x < width; x++, from++, to += 4)
+                               if (*mask++ == FILTER_MASK_USED)
+                                       to[0] = to[1] = to[2] = to[3] = from[0];
+               }
+       }
+       else if (channels_from == 3) {
+               /* RGB input */
+               for (y = 0; y < height; y++) {
+                       const float *from = rect_from + ((size_t)stride_from) * y * 3;
+                       float *to = rect_to + ((size_t)stride_to) * y * 4;
+
+                       for (x = 0; x < width; x++, from += 3, to += 4) {
+                               if (*mask++ == FILTER_MASK_USED) {
+                                       copy_v3_v3(to, from);
+                                       to[3] = 1.0f;
+                               }
+                       }
+               }
+       }
+       else if (channels_from == 4) {
+               /* RGBA input */
+               for (y = 0; y < height; y++) {
+                       const float *from = rect_from + ((size_t)stride_from) * y * 4;
+                       float *to = rect_to + ((size_t)stride_to) * y * 4;
+
+                       for (x = 0; x < width; x++, from += 4, to += 4)
+                               if (*mask++ == FILTER_MASK_USED)
+                                       copy_v4_v4(to, from);
+               }
+       }
+}
+
 /* byte to byte pixels, input and output 4-channel RGBA */
 void IMB_buffer_byte_from_byte(uchar *rect_to, const uchar *rect_from,
-                               int profile_to, int profile_from, int predivide,
+                               int profile_to, int profile_from, bool predivide,
                                int width, int height, int stride_to, int stride_from)
 {
        float tmp[4];
@@ -473,8 +660,8 @@ void IMB_buffer_byte_from_byte(uchar *rect_to, const uchar *rect_from,
 
        /* always RGBA input */
        for (y = 0; y < height; y++) {
-               const uchar *from = rect_from + stride_from * y * 4;
-               uchar *to = rect_to + stride_to * y * 4;
+               const uchar *from = rect_from + ((size_t)stride_from) * y * 4;
+               uchar *to = rect_to + ((size_t)stride_to) * y * 4;
 
                if (profile_to == profile_from) {
                        /* same profile, copy */
@@ -521,45 +708,106 @@ void IMB_buffer_byte_from_byte(uchar *rect_to, const uchar *rect_from,
 
 void IMB_rect_from_float(ImBuf *ibuf)
 {
-       int predivide = (ibuf->flags & IB_cm_predivide);
-       int profile_from;
+       float *buffer;
+       const char *from_colorspace;
 
        /* verify we have a float buffer */
        if (ibuf->rect_float == NULL)
                return;
 
        /* create byte rect if it didn't exist yet */
-       if (ibuf->rect == NULL)
-               imb_addrectImBuf(ibuf);
-
-       /* determine profiles */
-       if (ibuf->profile == IB_PROFILE_LINEAR_RGB) {
-               profile_from = IB_PROFILE_LINEAR_RGB;
-       }
-       else if (ELEM(ibuf->profile, IB_PROFILE_SRGB, IB_PROFILE_NONE)) {
-               profile_from = IB_PROFILE_SRGB;
-       }
-       else {
-               profile_from = IB_PROFILE_SRGB; /* should never happen */
-               BLI_assert(0);
+       if (ibuf->rect == NULL) {
+               if (imb_addrectImBuf(ibuf) == 0)
+                       return;
        }
 
-       /* do conversion */
-       IMB_buffer_byte_from_float((uchar *)ibuf->rect, ibuf->rect_float,
-                                  ibuf->channels, ibuf->dither, IB_PROFILE_SRGB, profile_from, predivide,
-                                  ibuf->x, ibuf->y, ibuf->x, ibuf->x);
+       if (ibuf->float_colorspace == NULL)
+               from_colorspace = IMB_colormanagement_role_colorspace_name_get(COLOR_ROLE_SCENE_LINEAR);
+       else
+               from_colorspace = ibuf->float_colorspace->name;
+
+       buffer = MEM_dupallocN(ibuf->rect_float);
+
+       /* first make float buffer in byte space */
+       IMB_colormanagement_transform(buffer, ibuf->x, ibuf->y, ibuf->channels, from_colorspace, ibuf->rect_colorspace->name, true);
+
+       /* convert from float's premul alpha to byte's straight alpha */
+       IMB_unpremultiply_rect_float(buffer, ibuf->channels, ibuf->x, ibuf->y);
+
+       /* convert float to byte */
+       IMB_buffer_byte_from_float((unsigned char *) ibuf->rect, buffer, ibuf->channels, ibuf->dither, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
+                                  false, ibuf->x, ibuf->y, ibuf->x, ibuf->x);
+
+       MEM_freeN(buffer);
 
        /* ensure user flag is reset */
        ibuf->userflags &= ~IB_RECT_INVALID;
 }
 
+typedef struct PartialThreadData {
+       ImBuf *ibuf;
+       float *buffer;
+       uchar *rect_byte;
+       const float *rect_float;
+       int width;
+       bool is_data;
+} PartialThreadData;
+
+static void partial_rect_from_float_slice(float *buffer,
+                                          uchar *rect_byte,
+                                          ImBuf *ibuf,
+                                          const float *rect_float,
+                                          const int w,
+                                          const int h,
+                                          const bool is_data)
+{
+       const int profile_from = IB_PROFILE_LINEAR_RGB;
+       if (is_data) {
+               /* exception for non-color data, just copy float */
+               IMB_buffer_float_from_float(buffer, rect_float,
+                                           ibuf->channels, IB_PROFILE_LINEAR_RGB, IB_PROFILE_LINEAR_RGB, 0,
+                                           w, h, w, ibuf->x);
+
+               /* and do color space conversion to byte */
+               IMB_buffer_byte_from_float(rect_byte, rect_float,
+                                          4, ibuf->dither, IB_PROFILE_SRGB, profile_from, true,
+                                          w, h, ibuf->x, w);
+       }
+       else {
+               IMB_buffer_float_from_float(buffer, rect_float,
+                                           ibuf->channels, IB_PROFILE_SRGB, profile_from, true,
+                                           w, h, w, ibuf->x);
+
+               IMB_buffer_float_unpremultiply(buffer, w, h);
+               /* XXX: need to convert to image buffer's rect space */
+               IMB_buffer_byte_from_float(rect_byte, buffer,
+                                          4, ibuf->dither, IB_PROFILE_SRGB, IB_PROFILE_SRGB, 0,
+                                          w, h, ibuf->x, w);
+       }
+}
+
+static void partial_rect_from_float_thread_do(void *data_v,
+                                              int start_scanline,
+                                              int num_scanlines)
+{
+       PartialThreadData *data = (PartialThreadData *)data_v;
+       ImBuf *ibuf = data->ibuf;
+       size_t global_offset = ((size_t)ibuf->x) * start_scanline;
+       size_t local_offset = ((size_t)data->width) * start_scanline;
+       partial_rect_from_float_slice(data->buffer + local_offset * ibuf->channels,
+                                     data->rect_byte + global_offset * 4,
+                                     ibuf,
+                                     data->rect_float + global_offset * ibuf->channels,
+                                     data->width,
+                                     num_scanlines,
+                                     data->is_data);
+}
+
 /* converts from linear float to sRGB byte for part of the texture, buffer will hold the changed part */
-void IMB_partial_rect_from_float(ImBuf *ibuf, float *buffer, int x, int y, int w, int h)
+void IMB_partial_rect_from_float(ImBuf *ibuf, float *buffer, int x, int y, int w, int h, bool is_data)
 {
-       float *rect_float;
+       const float *rect_float;
        uchar *rect_byte;
-       int predivide = (ibuf->flags & IB_cm_predivide);
-       int profile_from;
 
        /* verify we have a float buffer */
        if (ibuf->rect_float == NULL || buffer == NULL)
@@ -569,160 +817,71 @@ void IMB_partial_rect_from_float(ImBuf *ibuf, float *buffer, int x, int y, int w
        if (ibuf->rect == NULL)
                imb_addrectImBuf(ibuf);
 
-       /* determine profiles */
-       if (ibuf->profile == IB_PROFILE_LINEAR_RGB) {
-               profile_from = IB_PROFILE_LINEAR_RGB;
-       }
-       else if (ELEM(ibuf->profile, IB_PROFILE_SRGB, IB_PROFILE_NONE)) {
-               profile_from = IB_PROFILE_SRGB;
+       /* do conversion */
+       rect_float = ibuf->rect_float + (x + ((size_t)y) * ibuf->x) * ibuf->channels;
+       rect_byte = (uchar *)ibuf->rect + (x + ((size_t)y) * ibuf->x) * 4;
+
+       if (((size_t)w) * h < 64 * 64) {
+               partial_rect_from_float_slice(
+                       buffer, rect_byte, ibuf, rect_float, w, h, is_data);
        }
        else {
-               profile_from = IB_PROFILE_SRGB; /* should never happen */
-               BLI_assert(0);
+               PartialThreadData data;
+               data.ibuf = ibuf;
+               data.buffer = buffer;
+               data.rect_byte = rect_byte;
+               data.rect_float = rect_float;
+               data.width = w;
+               data.is_data = is_data;
+               IMB_processor_apply_threaded_scanlines(
+                       h, partial_rect_from_float_thread_do, &data);
        }
 
-       /* do conversion */
-       rect_float = ibuf->rect_float + (x + y * ibuf->x) * ibuf->channels;
-       rect_byte = (uchar *)ibuf->rect + (x + y * ibuf->x) * 4;
-
-       IMB_buffer_float_from_float(buffer, rect_float,
-                                   ibuf->channels, IB_PROFILE_SRGB, profile_from, predivide,
-                                   w, h, w, ibuf->x);
-
-       IMB_buffer_byte_from_float(rect_byte, buffer,
-                                  4, ibuf->dither, IB_PROFILE_SRGB, IB_PROFILE_SRGB, 0,
-                                  w, h, ibuf->x, w);
-
        /* ensure user flag is reset */
        ibuf->userflags &= ~IB_RECT_INVALID;
 }
 
 void IMB_float_from_rect(ImBuf *ibuf)
 {
-       int predivide = (ibuf->flags & IB_cm_predivide);
-       int profile_from;
+       float *rect_float;
 
        /* verify if we byte and float buffers */
        if (ibuf->rect == NULL)
                return;
 
-       if (ibuf->rect_float == NULL)
-               if (imb_addrectfloatImBuf(ibuf) == 0)
-                       return;
-       
-       /* determine profiles */
-       if (ibuf->profile == IB_PROFILE_NONE)
-               profile_from = IB_PROFILE_LINEAR_RGB;
-       else
-               profile_from = IB_PROFILE_SRGB;
-       
-       /* do conversion */
-       IMB_buffer_float_from_byte(ibuf->rect_float, (uchar *)ibuf->rect,
-                                  IB_PROFILE_LINEAR_RGB, profile_from, predivide,
-                                  ibuf->x, ibuf->y, ibuf->x, ibuf->x);
-}
+       /* allocate float buffer outside of image buffer,
+        * so work-in-progress color space conversion doesn't
+        * interfere with other parts of blender
+        */
+       rect_float = ibuf->rect_float;
+       if (rect_float == NULL) {
+               size_t size;
 
-/* no profile conversion */
-void IMB_float_from_rect_simple(ImBuf *ibuf)
-{
-       int predivide = (ibuf->flags & IB_cm_predivide);
+               size = ((size_t)ibuf->x) * ibuf->y;
+               size = size * 4 * sizeof(float);
+               ibuf->channels = 4;
 
-       if (ibuf->rect_float == NULL)
-               imb_addrectfloatImBuf(ibuf);
+               rect_float = MEM_mapallocN(size, "IMB_float_from_rect");
 
-       IMB_buffer_float_from_byte(ibuf->rect_float, (uchar *)ibuf->rect,
-                                  IB_PROFILE_SRGB, IB_PROFILE_SRGB, predivide,
-                                  ibuf->x, ibuf->y, ibuf->x, ibuf->x);
-}
-
-void IMB_convert_profile(ImBuf *ibuf, int profile)
-{
-       int predivide = (ibuf->flags & IB_cm_predivide);
-       int profile_from, profile_to;
-
-       if (ibuf->profile == profile)
-               return;
-
-       /* determine profiles */
-       if (ibuf->profile == IB_PROFILE_LINEAR_RGB)
-               profile_from = IB_PROFILE_LINEAR_RGB;
-       else if (ELEM(ibuf->profile, IB_PROFILE_SRGB, IB_PROFILE_NONE))
-               profile_from = IB_PROFILE_SRGB;
-       else {
-               BLI_assert(0);
-               profile_from = IB_PROFILE_SRGB; /* dummy, should never happen */
-       }
-
-       if (profile == IB_PROFILE_LINEAR_RGB)
-               profile_to = IB_PROFILE_LINEAR_RGB;
-       else if (ELEM(profile, IB_PROFILE_SRGB, IB_PROFILE_NONE))
-               profile_to = IB_PROFILE_SRGB;
-       else {
-               BLI_assert(0);
-               profile_to = IB_PROFILE_SRGB; /* dummy, should never happen */
-       }
-       
-       /* do conversion */
-       if (ibuf->rect_float) {
-               IMB_buffer_float_from_float(ibuf->rect_float, ibuf->rect_float,
-                                           4, profile_to, profile_from, predivide,
-                                           ibuf->x, ibuf->y, ibuf->x, ibuf->x);
-       }
-
-       if (ibuf->rect) {
-               IMB_buffer_byte_from_byte((uchar *)ibuf->rect, (uchar *)ibuf->rect,
-                                         profile_to, profile_from, predivide,
-                                         ibuf->x, ibuf->y, ibuf->x, ibuf->x);
+               if (rect_float == NULL)
+                       return;
        }
 
-       /* set new profile */
-       ibuf->profile = profile;
-}
-
-/* use when you need to get a buffer with a certain profile
- * if the return  */
-float *IMB_float_profile_ensure(ImBuf *ibuf, int profile, int *alloc)
-{
-       int predivide = (ibuf->flags & IB_cm_predivide);
-       int profile_from, profile_to;
-
-       /* determine profiles */
-       if (ibuf->profile == IB_PROFILE_NONE)
-               profile_from = IB_PROFILE_LINEAR_RGB;
-       else
-               profile_from = IB_PROFILE_SRGB;
-
-       if (profile == IB_PROFILE_NONE)
-               profile_to = IB_PROFILE_LINEAR_RGB;
-       else
-               profile_to = IB_PROFILE_SRGB;
-       
-       if (profile_from == profile_to) {
-               /* simple case, just allocate the buffer and return */
-               *alloc = 0;
+       /* first, create float buffer in non-linear space */
+       IMB_buffer_float_from_byte(rect_float, (unsigned char *) ibuf->rect, IB_PROFILE_SRGB, IB_PROFILE_SRGB,
+                                  false, ibuf->x, ibuf->y, ibuf->x, ibuf->x);
 
-               if (ibuf->rect_float == NULL)
-                       IMB_float_from_rect(ibuf);
+       /* then make float be in linear space */
+       IMB_colormanagement_colorspace_to_scene_linear(rect_float, ibuf->x, ibuf->y, ibuf->channels,
+                                                      ibuf->rect_colorspace, false);
 
-               return ibuf->rect_float;
-       }
-       else {
-               /* conversion is needed, first check */
-               float *fbuf = MEM_mallocN(ibuf->x * ibuf->y * sizeof(float) * 4, "IMB_float_profile_ensure");
-               *alloc = 1;
-
-               if (ibuf->rect_float == NULL) {
-                       IMB_buffer_float_from_byte(fbuf, (uchar *)ibuf->rect,
-                                                  profile_to, profile_from, predivide,
-                                                  ibuf->x, ibuf->y, ibuf->x, ibuf->x);
-               }
-               else {
-                       IMB_buffer_float_from_float(fbuf, ibuf->rect_float,
-                                                   4, profile_to, profile_from, predivide,
-                                                   ibuf->x, ibuf->y, ibuf->x, ibuf->x);
-               }
+       /* byte buffer is straight alpha, float should always be premul */
+       IMB_premultiply_rect_float(rect_float, ibuf->channels, ibuf->x, ibuf->y);
 
-               return fbuf;
+       if (ibuf->rect_float == NULL) {
+               ibuf->rect_float = rect_float;
+               ibuf->mall |= IB_rectfloat;
+               ibuf->flags |= IB_rectfloat;
        }
 }
 
@@ -731,26 +890,46 @@ float *IMB_float_profile_ensure(ImBuf *ibuf, int profile, int *alloc)
 /* no profile conversion */
 void IMB_color_to_bw(ImBuf *ibuf)
 {
-       float *rctf = ibuf->rect_float;
+       float *rct_fl = ibuf->rect_float;
        uchar *rct = (uchar *)ibuf->rect;
-       int i;
+       size_t i;
 
-       if (rctf) {
-               for (i = ibuf->x * ibuf->y; i > 0; i--, rctf += 4)
-                       rctf[0] = rctf[1] = rctf[2] = rgb_to_grayscale(rctf);
+       if (rct_fl) {
+               for (i = ((size_t)ibuf->x) * ibuf->y; i > 0; i--, rct_fl += 4)
+                       rct_fl[0] = rct_fl[1] = rct_fl[2] = IMB_colormanagement_get_luminance(rct_fl);
        }
 
        if (rct) {
-               for (i = ibuf->x * ibuf->y; i > 0; i--, rct += 4)
-                       rct[0] = rct[1] = rct[2] = rgb_to_grayscale_byte(rct);
+               for (i = ((size_t)ibuf->x * ibuf->y); i > 0; i--, rct += 4)
+                       rct[0] = rct[1] = rct[2] = IMB_colormanagement_get_luminance_byte(rct);
        }
 }
 
 void IMB_buffer_float_clamp(float *buf, int width, int height)
 {
-       int i, total = width * height * 4;
+       size_t i, total = ((size_t)width) * height * 4;
        for (i = 0; i < total; i++) {
-               buf[i] = minf(1.0, buf[i]);
+               buf[i] = min_ff(1.0, buf[i]);
+       }
+}
+
+void IMB_buffer_float_unpremultiply(float *buf, int width, int height)
+{
+       size_t total = ((size_t)width) * height;
+       float *fp = buf;
+       while (total--) {
+               premul_to_straight_v4(fp);
+               fp += 4;
+       }
+}
+
+void IMB_buffer_float_premultiply(float *buf, int width, int height)
+{
+       size_t total = ((size_t)width) * height;
+       float *fp = buf;
+       while (total--) {
+               straight_to_premul_v4(fp);
+               fp += 4;
        }
 }
 
@@ -758,14 +937,14 @@ void IMB_buffer_float_clamp(float *buf, int width, int height)
 
 void IMB_saturation(ImBuf *ibuf, float sat)
 {
-       int i;
+       size_t i;
        unsigned char *rct = (unsigned char *)ibuf->rect;
-       float *rctf = ibuf->rect_float;
+       float *rct_fl = ibuf->rect_float;
        float hsv[3];
 
        if (rct) {
                float rgb[3];
-               for (i = ibuf->x * ibuf->y; i > 0; i--, rct += 4) {
+               for (i = ((size_t)ibuf->x) * ibuf->y; i > 0; i--, rct += 4) {
                        rgb_uchar_to_float(rgb, rct);
                        rgb_to_hsv_v(rgb, hsv);
                        hsv_to_rgb(hsv[0], hsv[1] * sat, hsv[2], rgb, rgb + 1, rgb + 2);
@@ -773,10 +952,10 @@ void IMB_saturation(ImBuf *ibuf, float sat)
                }
        }
 
-       if (rctf) {
-               for (i = ibuf->x * ibuf->y; i > 0; i--, rctf += 4) {
-                       rgb_to_hsv_v(rctf, hsv);
-                       hsv_to_rgb(hsv[0], hsv[1] * sat, hsv[2], rctf, rctf + 1, rctf + 2);
+       if (rct_fl) {
+               for (i = ((size_t)ibuf->x) * ibuf->y; i > 0; i--, rct_fl += 4) {
+                       rgb_to_hsv_v(rct_fl, hsv);
+                       hsv_to_rgb(hsv[0], hsv[1] * sat, hsv[2], rct_fl, rct_fl + 1, rct_fl + 2);
                }
        }
 }