fix for float projection painting, now updating correctly.
authorAntony Riakiotakis <kalast@gmail.com>
Mon, 6 Jun 2011 22:10:05 +0000 (22:10 +0000)
committerAntony Riakiotakis <kalast@gmail.com>
Mon, 6 Jun 2011 22:10:05 +0000 (22:10 +0000)
This fix also allows for partial update of the image, speeding up painting.
The different code path implemented will be used to upload high resolution images to OpenGL when onion branch is merged.
Due to conversion of float textures to/from sRGB, corrections made to brush color sampling to take account of the image profile. This is not 100% correct yet as texture images used for projection painting strokes are not converted to/from sRGB yet(This has been decided due to loss of precision for 8-bit formats). It will have to do for now, though.

last-minute update, exr image loading is broken, will fix asap

source/blender/blenkernel/BKE_brush.h
source/blender/blenkernel/intern/brush.c
source/blender/blenlib/BLI_math_vector.h
source/blender/blenlib/BLI_utildefines.h
source/blender/blenlib/intern/math_vector_inline.c
source/blender/editors/sculpt_paint/paint_image.c
source/blender/gpu/intern/gpu_draw.c
source/blender/imbuf/IMB_imbuf.h
source/blender/imbuf/intern/divers.c

index ad736cd07bf47c81a79623bd5597ac89f9d6b9da..ebb9714cd1b7af2792f35d7d007e9152f2fa258d 100644 (file)
@@ -71,7 +71,7 @@ float brush_curve_strength(struct Brush *br, float p, const float len); /* used
 /* sampling */
 void brush_sample_tex(struct Brush *brush, float *xy, float *rgba, const int thread);
 void brush_imbuf_new(struct Brush *brush, short flt, short texfalloff, int size,
-       struct ImBuf **imbuf);
+       struct ImBuf **imbuf, int use_color_correction);
 
 /* painting */
 struct BrushPainter;
@@ -82,7 +82,7 @@ BrushPainter *brush_painter_new(struct Brush *brush);
 void brush_painter_require_imbuf(BrushPainter *painter, short flt,
        short texonly, int size);
 int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos,
-       double time, float pressure, void *user);
+       double time, float pressure, void *user, int use_color_correction);
 void brush_painter_break_stroke(BrushPainter *painter);
 void brush_painter_free(BrushPainter *painter);
 
index 25b60fef6ddc77ee8f1855c4024f24887483aafd..a4ceb62ab55dcbf2247afc76b88d0a16c9879b39 100644 (file)
@@ -521,7 +521,7 @@ void brush_sample_tex(Brush *brush, float *xy, float *rgba, const int thread)
 }
 
 
-void brush_imbuf_new(Brush *brush, short flt, short texfall, int bufsize, ImBuf **outbuf)
+void brush_imbuf_new(Brush *brush, short flt, short texfall, int bufsize, ImBuf **outbuf, int use_color_correction)
 {
        ImBuf *ibuf;
        float xy[2], dist, rgba[4], *dstf;
@@ -529,7 +529,8 @@ void brush_imbuf_new(Brush *brush, short flt, short texfall, int bufsize, ImBuf
        const int radius= brush_size(brush);
        char *dst, crgb[3];
        const float alpha= brush_alpha(brush);
-
+       float brush_rgb[3];
+    
        imbflag= (flt)? IB_rectfloat: IB_rect;
        xoff = -bufsize/2.0f + 0.5f;
        yoff = -bufsize/2.0f + 0.5f;
@@ -541,6 +542,11 @@ void brush_imbuf_new(Brush *brush, short flt, short texfall, int bufsize, ImBuf
                ibuf= IMB_allocImBuf(bufsize, bufsize, 32, imbflag);
 
        if (flt) {
+               copy_v3_v3(brush_rgb, brush->rgb);
+               if(use_color_correction){
+                       srgb_to_linearrgb_v3_v3(brush_rgb, brush_rgb);
+               }
+
                for (y=0; y < ibuf->y; y++) {
                        dstf = ibuf->rect_float + y*rowbytes;
 
@@ -551,7 +557,7 @@ void brush_imbuf_new(Brush *brush, short flt, short texfall, int bufsize, ImBuf
                                if (texfall == 0) {
                                        dist = sqrt(xy[0]*xy[0] + xy[1]*xy[1]);
 
-                                       VECCOPY(dstf, brush->rgb);
+                                       VECCOPY(dstf, brush_rgb);
                                        dstf[3]= alpha*brush_curve_strength_clamp(brush, dist, radius);
                                }
                                else if (texfall == 1) {
@@ -561,10 +567,7 @@ void brush_imbuf_new(Brush *brush, short flt, short texfall, int bufsize, ImBuf
                                        dist = sqrt(xy[0]*xy[0] + xy[1]*xy[1]);
 
                                        brush_sample_tex(brush, xy, rgba, 0);
-
-                                       dstf[0] = rgba[0]*brush->rgb[0];
-                                       dstf[1] = rgba[1]*brush->rgb[1];
-                                       dstf[2] = rgba[2]*brush->rgb[2];
+                                       mul_v3_v3v3(dstf, rgba, brush_rgb);
                                        dstf[3] = rgba[3]*alpha*brush_curve_strength_clamp(brush, dist, radius);
                                }
                        }
@@ -862,7 +865,7 @@ static void brush_painter_fixed_tex_partial_update(BrushPainter *painter, float
                brush_painter_do_partial(painter, NULL, x1, y2, x2, ibuf->y, 0, 0, pos);
 }
 
-static void brush_painter_refresh_cache(BrushPainter *painter, float *pos)
+static void brush_painter_refresh_cache(BrushPainter *painter, float *pos, int use_color_correction)
 {
        Brush *brush= painter->brush;
        BrushPainterCache *cache= &painter->cache;
@@ -889,11 +892,11 @@ static void brush_painter_refresh_cache(BrushPainter *painter, float *pos)
                size= (cache->size)? cache->size: diameter;
 
                if (brush->flag & BRUSH_FIXED_TEX) {
-                       brush_imbuf_new(brush, flt, 3, size, &cache->maskibuf);
+                       brush_imbuf_new(brush, flt, 3, size, &cache->maskibuf, use_color_correction);
                        brush_painter_fixed_tex_partial_update(painter, pos);
                }
                else
-                       brush_imbuf_new(brush, flt, 2, size, &cache->ibuf);
+                       brush_imbuf_new(brush, flt, 2, size, &cache->ibuf, use_color_correction);
 
                cache->lastsize= diameter;
                cache->lastalpha= alpha;
@@ -952,7 +955,7 @@ void brush_jitter_pos(Brush *brush, float *pos, float *jitterpos)
        }
 }
 
-int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, double time, float pressure, void *user)
+int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, double time, float pressure, void *user, int use_color_correction)
 {
        Brush *brush= painter->brush;
        int totpaintops= 0;
@@ -970,7 +973,7 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl
 
                brush_apply_pressure(painter, brush, pressure);
                if (painter->cache.enabled)
-                       brush_painter_refresh_cache(painter, pos);
+                       brush_painter_refresh_cache(painter, pos, use_color_correction);
                totpaintops += func(user, painter->cache.ibuf, pos, pos);
                
                painter->lasttime= time;
@@ -1043,7 +1046,7 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl
                                brush_jitter_pos(brush, paintpos, finalpos);
 
                                if (painter->cache.enabled)
-                                       brush_painter_refresh_cache(painter, finalpos);
+                                       brush_painter_refresh_cache(painter, finalpos, use_color_correction);
 
                                totpaintops +=
                                        func(user, painter->cache.ibuf, painter->lastpaintpos, finalpos);
@@ -1057,7 +1060,7 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl
                        brush_jitter_pos(brush, pos, finalpos);
 
                        if (painter->cache.enabled)
-                               brush_painter_refresh_cache(painter, finalpos);
+                               brush_painter_refresh_cache(painter, finalpos, use_color_correction);
 
                        totpaintops += func(user, painter->cache.ibuf, pos, finalpos);
 
@@ -1085,7 +1088,7 @@ int brush_painter_paint(BrushPainter *painter, BrushFunc func, float *pos, doubl
                                brush_jitter_pos(brush, pos, finalpos);
 
                                if (painter->cache.enabled)
-                                       brush_painter_refresh_cache(painter, finalpos);
+                                       brush_painter_refresh_cache(painter, finalpos, use_color_correction);
 
                                totpaintops +=
                                        func(user, painter->cache.ibuf, painter->lastmousepos, finalpos);
index 5f26bff0ad994db2e6d16c953ddce3618f155a77..decfa22c3e6529d4f6bc9e420687516678afd5e9 100644 (file)
@@ -58,6 +58,8 @@ MINLINE void swap_v4_v4(float a[4], float b[4]);
 
 /********************************* Arithmetic ********************************/
 
+MINLINE void add_v3_fl(float r[3], float f);
+MINLINE void add_v4_fl(float r[4], float f);
 MINLINE void add_v2_v2(float r[2], const float a[2]);
 MINLINE void add_v2_v2v2(float r[2], const float a[2], const float b[2]);
 MINLINE void add_v3_v3(float r[3], const float a[3]);
index a376d04841254608687b740486b7160a512247fd..9af55601ff7fcbd4632c73ec2bbb80c607df59a5 100644 (file)
 
 #define FTOCHAR(val) ((val)<=0.0f)? 0 : (((val)>(1.0f-0.5f/255.0f))? 255 : (char)((255.0f*(val))+0.5f))
 #define FTOUSHORT(val) ((val >= 1.0f-0.5f/65535)? 65535: (val <= 0.0f)? 0: (unsigned short)(val*65535.0f + 0.5f))
+#define F3TOCHAR3(v2,v1) (v1)[0]=FTOCHAR((v2[0])); (v1)[1]=FTOCHAR((v2[1])); (v1)[2]=FTOCHAR((v2[2]))
+#define F3TOCHAR4(v2,v1) { (v1)[0]=FTOCHAR((v2[0])); (v1)[1]=FTOCHAR((v2[1])); (v1)[2]=FTOCHAR((v2[2])); \
+                                               (v1)[3]=FTOCHAR((v2[3])); (v1)[3] = 255; }
+#define F4TOCHAR4(v2,v1) { (v1)[0]=FTOCHAR((v2[0])); (v1)[1]=FTOCHAR((v2[1])); (v1)[2]=FTOCHAR((v2[2])); \
+                                               (v1)[3]=FTOCHAR((v2[3])); (v1)[3]=FTOCHAR((v2[3])); }
+
 
 #define VECCOPY(v1,v2)          {*(v1)= *(v2); *(v1+1)= *(v2+1); *(v1+2)= *(v2+2);}
 #define VECCOPY2D(v1,v2)          {*(v1)= *(v2); *(v1+1)= *(v2+1);}
index 9f6a8afe2d5792088ac4f7b76178c949de1bb49a..e2b7c7703563c2736a66204af1414294504f63a7 100644 (file)
@@ -102,6 +102,21 @@ MINLINE void swap_v4_v4(float a[4], float b[4])
 
 /********************************* Arithmetic ********************************/
 
+MINLINE void add_v3_fl(float r[3], float f)
+{
+       r[0] += f;
+       r[1] += f;
+       r[2] += f;
+}
+
+MINLINE void add_v4_fl(float r[4], float f)
+{
+       r[0] += f;
+       r[1] += f;
+       r[2] += f;
+       r[3] += f;
+}
+
 MINLINE void add_v2_v2(float *r, const float *a)
 {
        r[0] += a[0];
index d7e8d3be66ffb691230b75a67d76bb1f32cc0abb..83ba35a2e5c6ee2257006d5efadf1490613d0b4f 100644 (file)
@@ -3692,14 +3692,26 @@ static void do_projectpaint_draw(ProjPaintState *ps, ProjPixel *projPixel, float
        }
 }
 
-static void do_projectpaint_draw_f(ProjPaintState *ps, ProjPixel *projPixel, float *rgba, float alpha, float mask) {
+static void do_projectpaint_draw_f(ProjPaintState *ps, ProjPixel *projPixel, float *rgba, float alpha, float mask, int use_color_correction) {
        if (ps->is_texbrush) {
-               rgba[0] *= ps->brush->rgb[0];
-               rgba[1] *= ps->brush->rgb[1];
-               rgba[2] *= ps->brush->rgb[2];
+               /* rgba already holds a texture result here from higher level function */
+               float rgba_br[3];
+               if(use_color_correction){
+                       srgb_to_linearrgb_v3_v3(rgba_br, ps->brush->rgb);
+                       mul_v3_v3(rgba, rgba_br);
+               }
+               else{
+                       mul_v3_v3(rgba, ps->brush->rgb);
+               }
        }
        else {
-               VECCOPY(rgba, ps->brush->rgb);
+               if(use_color_correction){
+                       srgb_to_linearrgb_v3_v3(rgba, rgba);
+               }
+               else {
+                       VECCOPY(rgba, ps->brush->rgb);
+               }
+               rgba[3] = 1.0;
        }
        
        if (ps->is_airbrush==0 && mask < 1.0f) {
@@ -3736,6 +3748,7 @@ static void *do_projectpaint_thread(void *ph_v)
        float falloff;
        int bucket_index;
        int is_floatbuf = 0;
+       int use_color_correction = 0;
        const short tool =  ps->tool;
        rctf bucket_bounds;
        
@@ -3841,6 +3854,7 @@ static void *do_projectpaint_thread(void *ph_v)
 
                                                                last_projIma->touch = 1;
                                                                is_floatbuf = last_projIma->ibuf->rect_float ? 1 : 0;
+                                                               use_color_correction = (last_projIma->ibuf->profile == IB_PROFILE_LINEAR_RGB) ? 1 : 0;
                                                        }
 
                                                        last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index;
@@ -3871,7 +3885,7 @@ static void *do_projectpaint_thread(void *ph_v)
                                                                else                            do_projectpaint_smear(ps, projPixel, alpha, mask, smearArena, &smearPixels, co);
                                                                break;
                                                        default:
-                                                               if (is_floatbuf)        do_projectpaint_draw_f(ps, projPixel, rgba, alpha, mask);
+                                                               if (is_floatbuf)        do_projectpaint_draw_f(ps, projPixel, rgba, alpha, mask, use_color_correction);
                                                                else                            do_projectpaint_draw(ps, projPixel, rgba, alpha, mask);
                                                                break;
                                                        }
@@ -3987,7 +4001,7 @@ static int project_paint_sub_stroke(ProjPaintState *ps, BrushPainter *painter, c
        // we may want to use this later 
        // brush_painter_require_imbuf(painter, ((ibuf->rect_float)? 1: 0), 0, 0);
        
-       if (brush_painter_paint(painter, project_paint_op, pos, time, pressure, ps)) {
+       if (brush_painter_paint(painter, project_paint_op, pos, time, pressure, ps, 0)) {
                return 1;
        }
        else return 0;
@@ -4058,7 +4072,6 @@ static void imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w,
 static void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short texpaint)
 {
        if(ibuf->rect_float)
-               /* TODO - should just update a portion from imapaintpartial! */
                ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */
        
        if(ibuf->mipmap[0])
@@ -4409,7 +4422,7 @@ static int imapaint_paint_sub_stroke(ImagePaintState *s, BrushPainter *painter,
 
        brush_painter_require_imbuf(painter, ((ibuf->rect_float)? 1: 0), 0, 0);
 
-       if (brush_painter_paint(painter, imapaint_paint_op, pos, time, pressure, s)) {
+       if (brush_painter_paint(painter, imapaint_paint_op, pos, time, pressure, s, ibuf->profile == IB_PROFILE_LINEAR_RGB)) {
                if (update)
                        imapaint_image_update(s->sima, image, ibuf, texpaint);
                return 1;
index 0e7df43bd344ee9fd342b56d19623dd395dbd04e..7dfbc52819ef3c8749e8efcea5e914e3c711cc29 100644 (file)
@@ -687,9 +687,25 @@ void GPU_paint_update_image(Image *ima, int x, int y, int w, int h, int mipmap)
                glGetIntegerv(GL_UNPACK_SKIP_PIXELS, &skip_pixels);
                glGetIntegerv(GL_UNPACK_SKIP_ROWS, &skip_rows);
 
-               if ((ibuf->rect==NULL) && ibuf->rect_float)
-                       IMB_rect_from_float(ibuf);
-
+               if (ibuf->rect_float){
+                       /*This case needs a whole new buffer*/
+                       if(ibuf->rect==NULL) {
+                               IMB_rect_from_float(ibuf);
+                       }
+                       else {
+                               /* Do partial drawing. 'buffer' holds only the changed part. Needed for color corrected result */
+                               float *buffer = (float *)MEM_mallocN(w*h*sizeof(float)*4, "temp_texpaint_float_buf");
+                               IMB_partial_rect_from_float(ibuf, buffer, x, y, w, h);
+                               glBindTexture(GL_TEXTURE_2D, ima->bindcode);
+                               glTexSubImage2D(GL_TEXTURE_2D, 0, x, y, w, h, GL_RGBA,
+                                       GL_FLOAT, buffer);
+                               MEM_freeN(buffer);
+                               if(ima->tpageflag & IMA_MIPMAP_COMPLETE)
+                                       ima->tpageflag &= ~IMA_MIPMAP_COMPLETE;
+                               return;
+                       }
+               }
+               
                glBindTexture(GL_TEXTURE_2D, ima->bindcode);
 
                glPixelStorei(GL_UNPACK_ROW_LENGTH, ibuf->x);
index 1eefc58d4de9769dd502a9d1ec825c435bb24757..5d61452e149e3bb875df726fb9768ced20038c3f 100644 (file)
@@ -327,7 +327,12 @@ int imb_get_anim_type(const char *name);
  */
 void IMB_de_interlace(struct ImBuf *ibuf);
 void IMB_interlace(struct ImBuf *ibuf);
+
+/* create char buffer, color corrected if necessary, for ImBufs that lack one */ 
 void IMB_rect_from_float(struct ImBuf *ibuf);
+/* create char buffer for part of the image, color corrected if necessary,
+   Changed part will be stored in buffer. This is expected to be used for texture painting updates */ 
+void IMB_partial_rect_from_float(struct ImBuf *ibuf, float *buffer, int x, int y, int w, int h);
 void IMB_float_from_rect(struct ImBuf *ibuf);
 void IMB_float_from_rect_simple(struct ImBuf *ibuf); /* no profile conversion */
 /* note, check that the conversion exists, only some are supported */
index 6b35d7df397fa31caa696bf0007976fc6ce1d82c..90ee2692cf0323417dbf2af08fbdd8ed7ad87277 100644 (file)
@@ -197,6 +197,135 @@ void IMB_rect_from_float(struct ImBuf *ibuf)
        ibuf->userflags &= ~IB_RECT_INVALID;
 }
 
+
+/* converts from linear float to sRGB byte for part of the texture, buffer will hold the changed part */
+void IMB_partial_rect_from_float(struct ImBuf *ibuf,float *buffer, int x, int y, int w, int h)
+{
+       /* indices to source and destination image pixels */
+       float *srcFloatPxl;
+       unsigned char *dstBytePxl;
+       /* buffer index will fill buffer */
+       float *bufferIndex;
+
+       /* convenience pointers to start of image buffers */
+       float *init_srcFloatPxl = (float *)ibuf->rect_float;
+       unsigned char *init_dstBytePxl = (unsigned char *) ibuf->rect;
+
+       /* Dithering factor */
+       float dither= ibuf->dither / 255.0f;
+       /* respective attributes of image */
+       short profile= ibuf->profile;
+       int channels= ibuf->channels;
+       
+       int i, j;
+       
+       /*
+               if called -only- from GPU_paint_update_image this test will never fail
+               but leaving it here for better or worse
+       */
+       if(init_srcFloatPxl==NULL || (buffer == NULL)){
+               return;
+       }
+       if(init_dstBytePxl==NULL) {
+               imb_addrectImBuf(ibuf);
+               init_dstBytePxl = (unsigned char *) ibuf->rect;
+       }
+       if(channels==1) {
+                       for (j = 0; j < h; j++){
+                               bufferIndex = buffer + w*j*4;
+                               dstBytePxl = init_dstBytePxl + (ibuf->x*(y + j) + x)*4;
+                               srcFloatPxl = init_srcFloatPxl + (ibuf->x*(y + j) + x);
+                               for(i = 0;  i < w; i++, dstBytePxl+=4, srcFloatPxl++, bufferIndex+=4) {
+                                       dstBytePxl[1]= dstBytePxl[2]= dstBytePxl[3]= dstBytePxl[0] = FTOCHAR(srcFloatPxl[0]);
+                                       bufferIndex[0] = bufferIndex[1] = bufferIndex[2] = bufferIndex[3] = srcFloatPxl[0];
+                               }
+                       }
+       }
+       else if (profile == IB_PROFILE_LINEAR_RGB) {
+               if(channels == 3) {
+                       for (j = 0; j < h; j++){
+                               bufferIndex = buffer + w*j*4;
+                               dstBytePxl = init_dstBytePxl + (ibuf->x*(y + j) + x)*4;
+                               srcFloatPxl = init_srcFloatPxl + (ibuf->x*(y + j) + x)*3;
+                               for(i = 0;  i < w; i++, dstBytePxl+=4, srcFloatPxl+=3, bufferIndex += 4) {
+                                       linearrgb_to_srgb_v3_v3(bufferIndex, srcFloatPxl);
+                                       F3TOCHAR4(bufferIndex, dstBytePxl);
+                                       bufferIndex[3]= 1.0;
+                               }
+                       }
+               }
+               else if (channels == 4) {
+                       if (dither != 0.f) {
+                               for (j = 0; j < h; j++){
+                                       bufferIndex = buffer + w*j*4;
+                                       dstBytePxl = init_dstBytePxl + (ibuf->x*(y + j) + x)*4;
+                                       srcFloatPxl = init_srcFloatPxl + (ibuf->x*(y + j) + x)*4;
+                                       for(i = 0;  i < w; i++, dstBytePxl+=4, srcFloatPxl+=4, bufferIndex+=4) {
+                                               const float d = (BLI_frand()-0.5f)*dither;
+                                               linearrgb_to_srgb_v3_v3(bufferIndex, srcFloatPxl);
+                                               bufferIndex[3] = srcFloatPxl[3];
+                                               add_v4_fl(bufferIndex, d);
+                                               F4TOCHAR4(bufferIndex, dstBytePxl);
+                                       }
+                               }
+                       } else {
+                               for (j = 0; j < h; j++){
+                                       bufferIndex = buffer + w*j*4;
+                                       dstBytePxl = init_dstBytePxl + (ibuf->x*(y + j) + x)*4;
+                                       srcFloatPxl = init_srcFloatPxl + (ibuf->x*(y + j) + x)*4;
+                                       for(i = 0;  i < w; i++, dstBytePxl+=4, srcFloatPxl+=4, bufferIndex+=4) {
+                                               linearrgb_to_srgb_v3_v3(bufferIndex, srcFloatPxl);
+                                               bufferIndex[3]= srcFloatPxl[3];
+                                               F4TOCHAR4(bufferIndex, dstBytePxl);
+                                       }
+                               }
+                       }
+               }
+       }
+       else if(ELEM(profile, IB_PROFILE_NONE, IB_PROFILE_SRGB)) {
+               if(channels==3) {
+                       for (j = 0; j < h; j++){
+                               bufferIndex = buffer + w*j*4;
+                               dstBytePxl = init_dstBytePxl + (ibuf->x*(y + j) + x)*4;
+                               srcFloatPxl = init_srcFloatPxl + (ibuf->x*(y + j) + x)*3;
+                               for(i = 0;  i < w; i++, dstBytePxl+=4, srcFloatPxl+=3, bufferIndex+=4) {
+                                       copy_v3_v3(bufferIndex, srcFloatPxl);
+                                       F3TOCHAR4(bufferIndex, dstBytePxl);
+                                       bufferIndex[3] = 1.0;
+                               }
+                       }
+               }
+               else {
+                       if (dither != 0.f) {
+                               for (j = 0; j < h; j++){
+                                       bufferIndex = buffer + w*j*4;
+                                       dstBytePxl = init_dstBytePxl + (ibuf->x*(y + j) + x)*4;
+                                       srcFloatPxl = init_srcFloatPxl + (ibuf->x*(y + j) + x)*4;
+                                       for(i = 0;  i < w; i++, dstBytePxl+=4, srcFloatPxl+=4, bufferIndex+=4) {
+                                               const float d = (BLI_frand()-0.5f)*dither;
+                                               copy_v4_v4(bufferIndex, srcFloatPxl);
+                                               add_v4_fl(bufferIndex,d);
+                                               F4TOCHAR4(bufferIndex, dstBytePxl);
+                                       }
+                               }
+                       } else {
+                               for (j = 0; j < h; j++){
+                                       bufferIndex = buffer + w*j*4;
+                                       dstBytePxl = init_dstBytePxl + (ibuf->x*(y + j) + x)*4;
+                                       srcFloatPxl = init_srcFloatPxl + (ibuf->x*(y + j) + x)*4;
+                                       for(i = 0;  i < w; i++, dstBytePxl+=4, srcFloatPxl+=4, bufferIndex+=4) {
+                                               copy_v4_v4(bufferIndex, srcFloatPxl);
+                                               F4TOCHAR4(bufferIndex, dstBytePxl);
+                                       }
+                               }
+                       }
+               }
+       }
+       /* ensure user flag is reset */
+       ibuf->userflags &= ~IB_RECT_INVALID;
+}
+
 static void imb_float_from_rect_nonlinear(struct ImBuf *ibuf, float *fbuf)
 {
        float *tof = fbuf;