More image painting fixes:
authorBrecht Van Lommel <brechtvanlommel@pandora.be>
Tue, 30 Apr 2013 06:07:42 +0000 (06:07 +0000)
committerBrecht Van Lommel <brechtvanlommel@pandora.be>
Tue, 30 Apr 2013 06:07:42 +0000 (06:07 +0000)
* 2D image painting support for masking to limit the max contribution of a stroke
  to a pixel, to get it working compatible with projection painting. Not strictly
  a bugfix, but the inconsistency here was annoying.

* Fix python errors in Texture Mask panel in image editor, was missing overlay
  options.

* Clamp paint mask to 0..1 in case some texture exceeds it, this could give black
  pixels due to integer overflow.

release/scripts/startup/bl_ui/space_image.py
source/blender/blenkernel/BKE_brush.h
source/blender/blenkernel/intern/brush.c
source/blender/blenlib/intern/math_color_blend_inline.c
source/blender/editors/sculpt_paint/paint_image.c
source/blender/editors/sculpt_paint/paint_image_2d.c
source/blender/editors/sculpt_paint/paint_image_proj.c
source/blender/editors/sculpt_paint/paint_intern.h
source/blender/imbuf/IMB_imbuf.h
source/blender/imbuf/intern/rectop.c
source/gameengine/VideoTexture/ImageBuff.cpp

index 54a788f3e4671bbfa98a5f2a4ce5759d091dbd99..3acab7200ea7b8f2114e17e1b0fc1358a382d248 100644 (file)
@@ -763,6 +763,12 @@ class IMAGE_PT_tools_mask_texture(BrushButtonsPanel, Panel):
         col.template_ID_preview(brush, "mask_texture", new="texture.new", rows=3, cols=8)
 
         brush_mask_texture_settings(col, brush)
+
+        col = layout.column(align=True)
+        col.active = brush.brush_capabilities.has_overlay
+        col.label(text="Overlay:")
+
+        row = col.row()
         if tex_slot_alpha.map_mode != 'STENCIL':
             if brush.use_secondary_overlay:
                 row.prop(brush, "use_secondary_overlay", toggle=True, text="", icon='RESTRICT_VIEW_OFF')
index 452030d05e6d21319a38877796d1d3ef792147c2..863d7b0569359eca6a407f364743239a5853808b 100644 (file)
@@ -82,7 +82,7 @@ float BKE_brush_sample_tex_2D(const struct Scene *scene, struct Brush *brush, co
 float BKE_brush_sample_masktex(const Scene *scene, struct Brush *br, const float point[3],
                                const int thread, struct ImagePool *pool);
 void BKE_brush_imbuf_new(const struct Scene *scene, struct Brush *brush, short flt, short texfalloff, int size,
-                         struct ImBuf **imbuf, int use_color_correction);
+                         struct ImBuf **imbuf, bool use_color_correction, bool use_brush_alpha);
 
 /* texture */
 unsigned int *BKE_brush_gen_texture_cache(struct Brush *br, int half_side);
index 517180964a546370c56b35cde2abae1eb1c29760..c24aab1629cc425c7d85bf300e47b087a8201219 100644 (file)
@@ -828,14 +828,15 @@ float BKE_brush_sample_tex_2D(const Scene *scene, Brush *brush, const float xy[2
 
 /* TODO, use define for 'texfall' arg
  * NOTE: only used for 2d brushes currently! */
-void BKE_brush_imbuf_new(const Scene *scene, Brush *brush, short flt, short texfall, int bufsize, ImBuf **outbuf, int use_color_correction)
+void BKE_brush_imbuf_new(const Scene *scene, Brush *brush, short flt, short texfall, int bufsize,
+                         ImBuf **outbuf, bool use_color_correction, bool use_brush_alpha)
 {
        ImBuf *ibuf;
        float xy[2], rgba[4], *dstf;
        int x, y, rowbytes, xoff, yoff, imbflag;
        const int radius = BKE_brush_size_get(scene, brush);
        unsigned char *dst, crgb[3];
-       const float alpha = BKE_brush_alpha_get(scene, brush);
+       const float alpha = (use_brush_alpha)? BKE_brush_alpha_get(scene, brush): 1.0f;
        float brush_rgb[3];
 
        imbflag = (flt) ? IB_rectfloat : IB_rect;
index 94a474da6817c6854b86715802675752cddba28d..121750fe9191b6c62840483cce36de62436e1e91 100644 (file)
@@ -372,12 +372,17 @@ MINLINE void blend_color_erase_alpha_float(float dst[4], const float src1[4], co
 {
        if (src2[3] != 0.0f && src1[3] > 0.0f) {
                /* subtract alpha and remap RGB channels to match */
-               const float alpha = max_ff(src1[3] - src2[3], 0.0f);
-               const float map_alpha = alpha / src1[3];
+               float alpha = max_ff(src1[3] - src2[3], 0.0f);
+               float map_alpha;
 
-               dst[0] *= map_alpha;
-               dst[1] *= map_alpha;
-               dst[2] *= map_alpha;
+               if (alpha <= 0.0005f)
+                       alpha = 0.0f;
+
+               map_alpha = alpha / src1[3];
+
+               dst[0] = src1[0] * map_alpha;
+               dst[1] = src1[1] * map_alpha;
+               dst[2] = src1[2] * map_alpha;
                dst[3] = alpha;
        }
        else {
@@ -393,12 +398,17 @@ MINLINE void blend_color_add_alpha_float(float dst[4], const float src1[4], cons
 {
        if (src2[3] != 0.0f && src1[3] < 1.0f) {
                /* add alpha and remap RGB channels to match */
-               const float alpha = min_ff(src1[3] + src2[3], 1.0f);
-               const float map_alpha = (src1[3] > 0.0f) ? alpha / src1[3] : 1.0f;
+               float alpha = min_ff(src1[3] + src2[3], 1.0f);
+               float map_alpha;
+
+               if (alpha >= 1.0f - 0.0005f)
+                       alpha = 1.0f;
+
+               map_alpha = (src1[3] > 0.0f) ? alpha / src1[3] : 1.0f;
 
-               dst[0] *= map_alpha;
-               dst[1] *= map_alpha;
-               dst[2] *= map_alpha;
+               dst[0] = src1[0] * map_alpha;
+               dst[1] = src1[1] * map_alpha;
+               dst[2] = src1[2] * map_alpha;
                dst[3] = alpha;
        }
        else {
index e3adf40a7eb128c3352e3f0c3e6de96fd6a7d9bb..f059a0b1a0a3c84df44cd14a8da85932d1bdefe0 100644 (file)
 
 #include "paint_intern.h"
 
-#define IMAPAINT_TILE_BITS          6
-#define IMAPAINT_TILE_SIZE          (1 << IMAPAINT_TILE_BITS)
-
 typedef struct UndoImageTile {
        struct UndoImageTile *next, *prev;
 
@@ -115,6 +112,9 @@ typedef struct UndoImageTile {
                unsigned int *uint;
                void         *pt;
        } rect;
+
+       unsigned short *mask;
+
        int x, y;
 
        short source, use_float;
@@ -156,18 +156,45 @@ static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, int
                            tile->y * IMAPAINT_TILE_SIZE, 0, 0, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE);
 }
 
+void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask)
+{
+       ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
+       UndoImageTile *tile;
+       short use_float = ibuf->rect_float ? 1 : 0;
+
+       for (tile = lb->first; tile; tile = tile->next) {
+               if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && ima->source == tile->source) {
+                       if (tile->use_float == use_float) {
+                               if (strcmp(tile->idname, ima->id.name) == 0 && strcmp(tile->ibufname, ibuf->name) == 0) {
+                                       if (mask) {
+                                               /* allocate mask if requested */
+                                               if (!tile->mask)
+                                                       tile->mask = MEM_callocN(sizeof(unsigned short)*IMAPAINT_TILE_SIZE*IMAPAINT_TILE_SIZE, "UndoImageTile.mask");
+
+                                               *mask = tile->mask;
+                                       }
+
+                                       return tile->rect.pt;
+                               }
+                       }
+               }
+       }
+       
+       return NULL;
+}
+
 void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile)
 {
        ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
        UndoImageTile *tile;
        int allocsize;
        short use_float = ibuf->rect_float ? 1 : 0;
+       void *data;
 
-       for (tile = lb->first; tile; tile = tile->next)
-               if (tile->x == x_tile && tile->y == y_tile && ima->gen_type == tile->gen_type && ima->source == tile->source)
-                       if (tile->use_float == use_float)
-                               if (strcmp(tile->idname, ima->id.name) == 0 && strcmp(tile->ibufname, ibuf->name) == 0)
-                                       return tile->rect.pt;
+       /* check if tile is already pushed */
+       data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, NULL);
+       if (data)
+               return data;
        
        if (*tmpibuf == NULL)
                *tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect);
@@ -195,6 +222,19 @@ void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile,
        return tile->rect.pt;
 }
 
+void image_undo_remove_masks(void)
+{
+       ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE);
+       UndoImageTile *tile;
+
+       for (tile = lb->first; tile; tile = tile->next) {
+               if (tile->mask) {
+                       MEM_freeN(tile->mask);
+                       tile->mask = NULL;
+               }
+       }
+}
+
 void image_undo_restore(bContext *C, ListBase *lb)
 {
        Main *bmain = CTX_data_main(C);
@@ -276,10 +316,23 @@ void imapaint_clear_partial_redraw(void)
        memset(&imapaintpartial, 0, sizeof(imapaintpartial));
 }
 
+void imapaint_region_tiles(ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th)
+{
+       int srcx = 0, srcy = 0;
+
+       IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h);
+
+       *tw = ((x + w - 1) >> IMAPAINT_TILE_BITS);
+       *th = ((y + h - 1) >> IMAPAINT_TILE_BITS);
+       *tx = (x >> IMAPAINT_TILE_BITS);
+       *ty = (y >> IMAPAINT_TILE_BITS);
+}
+
 void imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h)
 {
        ImBuf *tmpibuf = NULL;
-       int srcx = 0, srcy = 0, origx;
+       int tilex, tiley, tilew, tileh, tx, ty;
+       int srcx = 0, srcy = 0;
 
        IMB_rectclip(ibuf, NULL, &x, &y, &srcx, &srcy, &w, &h);
 
@@ -300,14 +353,11 @@ void imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int h)
                imapaintpartial.y2 = max_ii(imapaintpartial.y2, y + h);
        }
 
-       w = ((x + w - 1) >> IMAPAINT_TILE_BITS);
-       h = ((y + h - 1) >> IMAPAINT_TILE_BITS);
-       origx = (x >> IMAPAINT_TILE_BITS);
-       y = (y >> IMAPAINT_TILE_BITS);
-       
-       for (; y <= h; y++)
-               for (x = origx; x <= w; x++)
-                       image_undo_push_tile(ima, ibuf, &tmpibuf, x, y);
+       imapaint_region_tiles(ibuf, x, y, w, h, &tilex, &tiley, &tilew, &tileh);
+
+       for (ty = tiley; ty <= tileh; ty++)
+               for (tx = tilex; tx <= tilew; tx++)
+                       image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty);
 
        ibuf->userflags |= IB_BITMAPDIRTY;
        
index 1c50b048fce05b26f30d7fa3f3cbb28321107b1c..d6a634ec4045e44487ead13227272259df2807b8 100644 (file)
@@ -133,6 +133,8 @@ typedef struct ImagePaintState {
        char *warnpackedfile;
        char *warnmultifile;
 
+       bool do_masking;
+
        /* viewport texture paint only, but _not_ project paint */
        Object *ob;
        int faceindex;
@@ -327,7 +329,7 @@ static void brush_painter_2d_tiled_tex_partial_update(BrushPainter *painter, con
                brush_painter_2d_do_partial(painter, NULL, x1, y2, x2, ibuf->y, 0, 0, pos);
 }
 
-static void brush_painter_2d_refresh_cache(BrushPainter *painter, const float pos[2], int use_color_correction)
+static void brush_painter_2d_refresh_cache(BrushPainter *painter, const float pos[2], bool use_color_correction, bool use_brush_alpha)
 {
        const Scene *scene = painter->scene;
        UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings;
@@ -347,7 +349,7 @@ static void brush_painter_2d_refresh_cache(BrushPainter *painter, const float po
        }
 
        if (diameter != cache->lastsize ||
-           alpha != cache->lastalpha ||
+           (use_brush_alpha && alpha != cache->lastalpha) ||
            brush->jitter != cache->lastjitter ||
            rotation != cache->last_rotation ||
            do_random)
@@ -365,11 +367,13 @@ static void brush_painter_2d_refresh_cache(BrushPainter *painter, const float po
                size = (cache->size) ? cache->size : diameter;
 
                if (do_tiled) {
-                       BKE_brush_imbuf_new(scene, brush, flt, 3, size, &cache->maskibuf, use_color_correction);
+                       BKE_brush_imbuf_new(scene, brush, flt, 3, size, &cache->maskibuf,
+                                           use_color_correction, use_brush_alpha);
                        brush_painter_2d_tiled_tex_partial_update(painter, pos);
                }
                else
-                       BKE_brush_imbuf_new(scene, brush, flt, 2, size, &cache->ibuf, use_color_correction);
+                       BKE_brush_imbuf_new(scene, brush, flt, 2, size, &cache->ibuf,
+                                           use_color_correction, use_brush_alpha);
 
                cache->lastsize = diameter;
                cache->lastalpha = alpha;
@@ -552,7 +556,8 @@ static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos)
        tot = paint_2d_torus_split_region(region, ibufb, ibuf);
 
        for (a = 0; a < tot; a++)
-               IMB_rectblend(ibufb, ibuf, region[a].destx, region[a].desty,
+               IMB_rectblend(ibufb, ibufb, ibuf, NULL, 0, region[a].destx, region[a].desty,
+                             region[a].destx, region[a].desty,
                              region[a].srcx, region[a].srcy,
                              region[a].width, region[a].height, IMB_BLEND_COPY_RGB);
 }
@@ -565,9 +570,9 @@ static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos)
        ImBuf *clonebuf = IMB_allocImBuf(w, h, ibufb->planes, ibufb->flags);
 
        IMB_rectclip(clonebuf, ibuf, &destx, &desty, &srcx, &srcy, &w, &h);
-       IMB_rectblend(clonebuf, ibuf, destx, desty, srcx, srcy, w, h,
+       IMB_rectblend(clonebuf, clonebuf, ibuf, NULL, 0, destx, desty, destx, desty, srcx, srcy, w, h,
                      IMB_BLEND_COPY_RGB);
-       IMB_rectblend(clonebuf, ibufb, destx, desty, destx, desty, w, h,
+       IMB_rectblend(clonebuf, clonebuf, ibufb, NULL, 0, destx, desty, destx, desty, destx, desty, w, h,
                      IMB_BLEND_COPY_ALPHA);
 
        return clonebuf;
@@ -582,12 +587,14 @@ static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[
 static int paint_2d_op(void *state, ImBuf *ibufb, const float lastpos[2], const float pos[2])
 {
        ImagePaintState *s = ((ImagePaintState *)state);
-       ImBuf *clonebuf = NULL, *frombuf;
+       ImBuf *clonebuf = NULL, *frombuf, *tmpbuf = NULL;
        ImagePaintRegion region[4];
        short torus = s->brush->flag & BRUSH_TORUS;
        short blend = s->blend;
        float *offset = s->brush->clone.offset;
        float liftpos[2];
+       float brush_alpha = BKE_brush_alpha_get(s->scene, s->brush);
+       unsigned short mask_max = (unsigned short)(brush_alpha * 65535.0f);
        int bpos[2], blastpos[2], bliftpos[2];
        int a, tot;
 
@@ -623,19 +630,55 @@ static int paint_2d_op(void *state, ImBuf *ibufb, const float lastpos[2], const
                tot = 1;
        }
 
+       if (s->do_masking)
+               tmpbuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0);
+       
        /* blend into canvas */
        for (a = 0; a < tot; a++) {
                imapaint_dirty_region(s->image, s->canvas,
                                      region[a].destx, region[a].desty,
                                      region[a].width, region[a].height);
-
-               IMB_rectblend(s->canvas, frombuf,
-                             region[a].destx, region[a].desty,
-                             region[a].srcx, region[a].srcy,
-                             region[a].width, region[a].height, blend);
+       
+               if (s->do_masking) {
+                       /* masking, find original pixels tiles from undo buffer to composite over */
+                       int tilex, tiley, tilew, tileh, tx, ty;
+
+                       imapaint_region_tiles(s->canvas, region[a].destx, region[a].desty,
+                                             region[a].width, region[a].height,
+                                                                 &tilex, &tiley, &tilew, &tileh);
+                       
+                       for (ty = tiley; ty <= tileh; ty++) {
+                               for (tx = tilex; tx <= tilew; tx++) {
+                                       /* retrieve original pixels + mask from undo buffer */
+                                       unsigned short *mask;
+                                       int origx = region[a].destx - tx * IMAPAINT_TILE_SIZE;
+                                       int origy = region[a].desty - ty * IMAPAINT_TILE_SIZE;
+
+                                       if (s->canvas->rect_float)
+                                               tmpbuf->rect_float = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask);
+                                       else
+                                               tmpbuf->rect = image_undo_find_tile(s->image, s->canvas, tx, ty, &mask);
+
+                                       IMB_rectblend(s->canvas, tmpbuf, frombuf, mask, mask_max,
+                                                                 region[a].destx, region[a].desty,
+                                                                 origx, origy,
+                                                                 region[a].srcx, region[a].srcy,
+                                                                 region[a].width, region[a].height, blend);
+                               }
+                       }
+               }
+               else {
+                       /* no masking, composite brush directly onto canvas */
+                       IMB_rectblend(s->canvas, s->canvas, frombuf, NULL, 0,
+                                                 region[a].destx, region[a].desty,
+                                                 region[a].destx, region[a].desty,
+                                                 region[a].srcx, region[a].srcy,
+                                                 region[a].width, region[a].height, blend);
+               }
        }
 
        if (clonebuf) IMB_freeImBuf(clonebuf);
+       if (tmpbuf) IMB_freeImBuf(tmpbuf);
 
        return 1;
 }
@@ -684,6 +727,11 @@ static int paint_2d_canvas_set(ImagePaintState *s, Image *ima)
                        IMB_rect_from_float(s->clonecanvas);
        }
 
+       /* set masking */
+       s->do_masking = (s->brush->flag & BRUSH_AIRBRUSH || (s->brush->mtex.tex &&
+                                        !ELEM(s->brush->mtex.brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL)))
+                                        ? false : true;
+       
        return 1;
 }
 
@@ -691,6 +739,9 @@ static void paint_2d_canvas_free(ImagePaintState *s)
 {
        BKE_image_release_ibuf(s->image, s->canvas, NULL);
        BKE_image_release_ibuf(s->brush->clone.image, s->clonecanvas, NULL);
+
+       if (s->do_masking)
+               image_undo_remove_masks();
 }
 
 int paint_2d_stroke(void *ps, const int prev_mval[2], const int mval[2], int eraser)
@@ -734,7 +785,7 @@ int paint_2d_stroke(void *ps, const int prev_mval[2], const int mval[2], int era
         */
        brush_painter_2d_require_imbuf(painter, ((ibuf->rect_float) ? 1 : 0), 0);
 
-       brush_painter_2d_refresh_cache(painter, newuv, is_data == false);
+       brush_painter_2d_refresh_cache(painter, newuv, is_data == false, s->do_masking);
 
        if (paint_2d_op(s, painter->cache.ibuf, olduv, newuv)) {
                imapaint_image_update(s->sima, s->image, ibuf, false);
index 0088e5aa3cb3d73a9250df296f8374d749636ac3..13b3629518fb111da237648b3970904b5627b80f 100644 (file)
@@ -3887,6 +3887,8 @@ static void *do_projectpaint_thread(void *ph_v)
                                                        mask *= BKE_brush_sample_masktex(ps->scene, ps->brush, projPixel->projCoSS, thread_index, pool);
                                                }
 
+                                               CLAMP(mask, 0.0f, 1.0f);
+
                                                if (ps->do_masking) {
                                                        /* masking to keep brush contribution to a pixel limited. note we do not do
                                                         * a simple max(mask, mask_accum), as this is very sensitive to spacing and
index f77d0ac4144bc7fae0bcde6572bc96c032fb04f3..0085998bd58a42f443a1b42d912e337cdcf3caee 100644 (file)
@@ -126,7 +126,9 @@ typedef struct ImagePaintPartialRedraw {
 #define IMAPAINT_CHAR_TO_FLOAT(c) ((c) / 255.0f)
 
 int image_texture_paint_poll(struct bContext *C);
+void *image_undo_find_tile(struct Image *ima, struct ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask);
 void *image_undo_push_tile(struct Image *ima, struct ImBuf *ibuf, struct ImBuf **tmpibuf, int x_tile, int y_tile);
+void image_undo_remove_masks(void);
 void image_undo_restore(struct bContext *C, struct ListBase *lb);
 void image_undo_free(struct ListBase *lb);
 void imapaint_image_update(struct SpaceImage *sima, struct Image *image, struct ImBuf *ibuf, short texpaint);
@@ -134,6 +136,7 @@ struct ImagePaintPartialRedraw *get_imapaintpartial(void);
 void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr);
 void imapaint_clear_partial_redraw(void);
 void imapaint_dirty_region(struct Image *ima, struct ImBuf *ibuf, int x, int y, int w, int h);
+void imapaint_region_tiles(struct ImBuf *ibuf, int x, int y, int w, int h, int *tx, int *ty, int *tw, int *th);
 int get_imapaint_zoom(struct bContext *C, float *zoomx, float *zoomy);
 void *paint_2d_new_stroke(struct bContext *, struct wmOperator *);
 void paint_2d_redraw(const bContext *C, void *ps, int final);
index 04179b28e562b7993c10602e5bac683e74c58024..7253a61092ccbd4c931dd1f83b777b1db45f24dd 100644 (file)
@@ -178,8 +178,10 @@ void IMB_rectclip(struct ImBuf *dbuf, struct ImBuf *sbuf, int *destx,
        int *desty, int *srcx, int *srcy, int *width, int *height);
 void IMB_rectcpy(struct ImBuf *drect, struct ImBuf *srect, int destx,
        int desty, int srcx, int srcy, int width, int height);
-void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *sbuf, int destx, 
-       int desty, int srcx, int srcy, int width, int height, IMB_BlendMode mode);
+void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *obuf, struct ImBuf *sbuf,
+       unsigned short *mask, unsigned short mask_max,
+       int destx,  int desty, int origx, int origy, int srcx, int srcy,
+       int width, int height, IMB_BlendMode mode);
 
 /**
  *
index 95426c573dbd0c167044e77da19126bd31766133..d9fc4fe172a7a9d7f280863706d854509eed876e 100644 (file)
@@ -104,7 +104,7 @@ void IMB_blend_color_float(float dst[4], float src1[4], float src2[4], IMB_Blend
 
 /* clipping */
 
-void IMB_rectclip(struct ImBuf *dbuf, struct ImBuf *sbuf, int *destx, 
+void IMB_rectclip(ImBuf *dbuf, ImBuf *sbuf, int *destx, 
                   int *desty, int *srcx, int *srcy, int *width, int *height)
 {
        int tmp;
@@ -150,43 +150,125 @@ void IMB_rectclip(struct ImBuf *dbuf, struct ImBuf *sbuf, int *destx,
        }
 }
 
+static void imb_rectclip3(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, int *destx, 
+                          int *desty, int *origx, int *origy, int *srcx, int *srcy,
+                          int *width, int *height)
+{
+       int tmp;
+
+       if (dbuf == NULL) return;
+       
+       if (*destx < 0) {
+               *srcx -= *destx;
+               *origx -= *destx;
+               *width += *destx;
+               *destx = 0;
+       }
+       if (*origx < 0) {
+               *destx -= *origx;
+               *srcx -= *origx;
+               *width += *origx;
+               *origx = 0;
+       }
+       if (*srcx < 0) {
+               *destx -= *srcx;
+               *origx -= *srcx;
+               *width += *srcx;
+               *srcx = 0;
+       }
+
+       if (*desty < 0) {
+               *srcy -= *desty;
+               *origy -= *desty;
+               *height += *desty;
+               *desty = 0;
+       }
+       if (*origy < 0) {
+               *desty -= *origy;
+               *srcy -= *origy;
+               *height += *origy;
+               *origy = 0;
+       }
+       if (*srcy < 0) {
+               *desty -= *srcy;
+               *origy -= *srcy;
+               *height += *srcy;
+               *srcy = 0;
+       }
+
+       tmp = dbuf->x - *destx;
+       if (*width > tmp) *width = tmp;
+       tmp = dbuf->y - *desty;
+       if (*height > tmp) *height = tmp;
+
+       if (obuf) {
+               tmp = obuf->x - *origx;
+               if (*width > tmp) *width = tmp;
+               tmp = obuf->y - *origy;
+               if (*height > tmp) *height = tmp;
+       }
+
+       if (sbuf) {
+               tmp = sbuf->x - *srcx;
+               if (*width > tmp) *width = tmp;
+               tmp = sbuf->y - *srcy;
+               if (*height > tmp) *height = tmp;
+       }
+
+       if ((*height <= 0) || (*width <= 0)) {
+               *width = 0;
+               *height = 0;
+       }
+}
+
 /* copy and blend */
 
-void IMB_rectcpy(struct ImBuf *dbuf, struct ImBuf *sbuf, int destx, 
+void IMB_rectcpy(ImBuf *dbuf, ImBuf *sbuf, int destx, 
                  int desty, int srcx, int srcy, int width, int height)
 {
-       IMB_rectblend(dbuf, sbuf, destx, desty, srcx, srcy, width, height,
-                     IMB_BLEND_COPY);
+       IMB_rectblend(dbuf, dbuf, sbuf, NULL, 0, destx, desty, destx, desty, srcx, srcy, width, height, IMB_BLEND_COPY);
 }
 
 typedef void (*IMB_blend_func)(unsigned char *dst, const unsigned char *src1, const unsigned char *src2);
 typedef void (*IMB_blend_func_float)(float *dst, const float *src1, const float *src2);
 
 
-void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *sbuf, int destx, 
-                   int desty, int srcx, int srcy, int width, int height, IMB_BlendMode mode)
+void IMB_rectblend(ImBuf *dbuf, ImBuf *obuf, ImBuf *sbuf, unsigned short *maskrect, unsigned short mask_max,
+                   int destx,  int desty, int origx, int origy, int srcx, int srcy, int width, int height,
+                   IMB_BlendMode mode)
 {
-       unsigned int *drect = NULL, *srect = NULL, *dr, *sr;
-       float *drectf = NULL, *srectf = NULL, *drf, *srf;
-       int do_float, do_char, srcskip, destskip, x;
+       unsigned int *drect = NULL, *orect, *srect = NULL, *dr, *or, *sr;
+       float *drectf = NULL, *orectf, *srectf = NULL, *drf, *orf, *srf;
+       unsigned short *mr;
+       int do_float, do_char, srcskip, destskip, origskip, x;
        IMB_blend_func func = NULL;
        IMB_blend_func_float func_float = NULL;
 
-       if (dbuf == NULL) return;
+       if (dbuf == NULL || obuf == NULL) return;
 
-       IMB_rectclip(dbuf, sbuf, &destx, &desty, &srcx, &srcy, &width, &height);
+       imb_rectclip3(dbuf, obuf, sbuf, &destx, &desty, &origx, &origy, &srcx, &srcy, &width, &height);
 
        if (width == 0 || height == 0) return;
        if (sbuf && sbuf->channels != 4) return;
        if (dbuf->channels != 4) return;
-       
-       do_char = (sbuf && sbuf->rect && dbuf->rect);
-       do_float = (sbuf && sbuf->rect_float && dbuf->rect_float);
 
-       if (do_char) drect = dbuf->rect + desty * dbuf->x + destx;
-       if (do_float) drectf = dbuf->rect_float + (desty * dbuf->x + destx) * 4;
+       do_char = (sbuf && sbuf->rect && dbuf->rect && obuf->rect);
+       do_float = (sbuf && sbuf->rect_float && dbuf->rect_float && obuf->rect_float);
+
+       if (do_char) {
+               drect = dbuf->rect + desty * dbuf->x + destx;
+               orect = obuf->rect + origy * obuf->x + origx;
+       }
+       if (do_float) {
+               drectf = dbuf->rect_float + (desty * dbuf->x + destx) * 4;
+               orectf = obuf->rect_float + (origy * obuf->x + origx) * 4;
+       }
+
+       if (maskrect)
+               maskrect += origy * obuf->x + origx;
 
        destskip = dbuf->x;
+       origskip = obuf->x;
 
        if (sbuf) {
                if (do_char) srect = sbuf->rect + srcy * sbuf->x + srcx;
@@ -307,24 +389,92 @@ void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *sbuf, int destx,
                for (; height > 0; height--) {
                        if (do_char) {
                                dr = drect;
+                               or = orect;
                                sr = srect;
-                               for (x = width; x > 0; x--, dr++, sr++) {
-                                       if (((unsigned char *)sr)[3])
-                                               func((unsigned char *)dr, (unsigned char *)dr, (unsigned char *)sr);
+
+                               if (maskrect) {
+                                       /* mask accumulation for painting */
+                                       mr = maskrect;
+
+                                       for (x = width; x > 0; x--, dr++, or++, sr++, mr++) {
+                                               unsigned char *src = (unsigned char*)sr;
+
+                                               if (src[3]) {
+                                                       unsigned short mask = *mr + divide_round_i((mask_max - *mr) * src[3], 255);
+
+                                                       if (mask > *mr) {
+                                                               unsigned char mask_src[4];
+
+                                                               *mr = mask;
+
+                                                               mask_src[0] = src[0];
+                                                               mask_src[1] = src[1];
+                                                               mask_src[2] = src[2];
+                                                               mask_src[3] = mask >> 8;
+
+                                                               func((unsigned char *)dr, (unsigned char *)or, mask_src);
+                                                       }
+                                               }
+                                       }
+
+                                       maskrect += origskip;
+                               }
+                               else {
+                                       /* regular blending */
+                                       for (x = width; x > 0; x--, dr++, or++, sr++) {
+                                               if (((unsigned char *)sr)[3])
+                                                       func((unsigned char *)dr, (unsigned char *)or, (unsigned char *)sr);
+                                       }
                                }
 
                                drect += destskip;
+                               orect += origskip;
                                srect += srcskip;
                        }
 
                        if (do_float) {
                                drf = drectf;
+                               orf = orectf;
                                srf = srectf;
-                               for (x = width; x > 0; x--, drf += 4, srf += 4) {
-                                       if (srf[3] != 0)
-                                               func_float(drf, drf, srf);
+
+                               if (maskrect) {
+                                       /* mask accumulation for painting */
+                                       mr = maskrect;
+
+                                       for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4, mr++) {
+                                               if (srf[3] != 0) {
+                                                       float alpha = CLAMPIS(srf[3], 0.0f, 1.0f);
+                                                       unsigned short mask = (unsigned short)(*mr + (mask_max - *mr) * alpha);
+
+                                                       if (mask > *mr) {
+                                                               float mask_srf[4];
+                                                               float new_alpha = mask * (1.0f/65535.0f);
+                                                               float map_alpha = new_alpha / srf[3];
+
+                                                               *mr = mask;
+
+                                                               mask_srf[0] = map_alpha * srf[0];
+                                                               mask_srf[1] = map_alpha * srf[1];
+                                                               mask_srf[2] = map_alpha * srf[2];
+                                                               mask_srf[3] = new_alpha;
+
+                                                               func_float(drf, orf, mask_srf);
+                                                       }
+                                               }
+                                       }
+
+                                       maskrect += origskip;
                                }
+                               else {
+                                       /* regular blending */
+                                       for (x = width; x > 0; x--, drf += 4, orf += 4, srf += 4) {
+                                               if (srf[3] != 0)
+                                                       func_float(drf, orf, srf);
+                                       }
+                               }
+
                                drectf += destskip * 4;
+                               orectf += origskip * 4;
                                srectf += srcskip * 4;
                        }
                }
@@ -333,7 +483,7 @@ void IMB_rectblend(struct ImBuf *dbuf, struct ImBuf *sbuf, int destx,
 
 /* fill */
 
-void IMB_rectfill(struct ImBuf *drect, const float col[4])
+void IMB_rectfill(ImBuf *drect, const float col[4])
 {
        int num;
 
@@ -462,7 +612,7 @@ void buf_rectfill_area(unsigned char *rect, float *rectf, int width, int height,
        }
 }
 
-void IMB_rectfill_area(struct ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2, struct ColorManagedDisplay *display)
+void IMB_rectfill_area(ImBuf *ibuf, const float col[4], int x1, int y1, int x2, int y2, struct ColorManagedDisplay *display)
 {
        if (!ibuf) return;
        buf_rectfill_area((unsigned char *) ibuf->rect, ibuf->rect_float, ibuf->x, ibuf->y, col, display,
index 8f547aa8b578ec7ef201c99d7c642b23716dafdc..eb68ea2425df993639bb9e0fce1eb17ed94e728c 100644 (file)
@@ -163,7 +163,7 @@ void ImageBuff::plot(unsigned char *img, short width, short height, short x, sho
        // assign temporarily our buffer to the ImBuf buffer, we use the same format
        tmpbuf->rect = (unsigned int*)img;
        m_imbuf->rect = m_image;
-       IMB_rectblend(m_imbuf, tmpbuf, x, y, 0, 0, width, height, (IMB_BlendMode)mode);
+       IMB_rectblend(m_imbuf, m_imbuf, tmpbuf, NULL, 0, x, y, x, y, 0, 0, width, height, (IMB_BlendMode)mode);
        // remove so that MB_freeImBuf will free our buffer
        m_imbuf->rect = NULL;
        tmpbuf->rect = NULL;
@@ -186,7 +186,7 @@ void ImageBuff::plot(ImageBuff *img, short x, short y, short mode)
        // assign temporarily our buffer to the ImBuf buffer, we use the same format
        img->m_imbuf->rect = img->m_image;
        m_imbuf->rect = m_image;
-       IMB_rectblend(m_imbuf, img->m_imbuf, x, y, 0, 0, img->m_imbuf->x, img->m_imbuf->y, (IMB_BlendMode)mode);
+       IMB_rectblend(m_imbuf, m_imbuf, img->m_imbuf, NULL, 0, x, y, x, y, 0, 0, img->m_imbuf->x, img->m_imbuf->y, (IMB_BlendMode)mode);
        // remove so that MB_freeImBuf will free our buffer
        m_imbuf->rect = NULL;
        img->m_imbuf->rect = NULL;