From e12c08e8d170b7ca40f204a5b0423c23a9fbc2c1 Mon Sep 17 00:00:00 2001 From: Campbell Barton Date: Wed, 17 Apr 2019 06:17:24 +0200 Subject: ClangFormat: apply to source, most of intern Apply clang format as proposed in T53211. For details on usage and instructions for migrating branches without conflicts, see: https://wiki.blender.org/wiki/Tools/ClangFormat --- .../blender/editors/sculpt_paint/paint_image_2d.c | 3129 +++++++++++--------- 1 file changed, 1652 insertions(+), 1477 deletions(-) (limited to 'source/blender/editors/sculpt_paint/paint_image_2d.c') diff --git a/source/blender/editors/sculpt_paint/paint_image_2d.c b/source/blender/editors/sculpt_paint/paint_image_2d.c index cad75efbf56..5390f18304a 100644 --- a/source/blender/editors/sculpt_paint/paint_image_2d.c +++ b/source/blender/editors/sculpt_paint/paint_image_2d.c @@ -30,7 +30,6 @@ #include "DNA_space_types.h" #include "DNA_object_types.h" - #include "BLI_math_color_blend.h" #include "BLI_stack.h" #include "BLI_bitmap.h" @@ -57,7 +56,6 @@ #include "UI_view2d.h" - #include "GPU_draw.h" #include "paint_intern.h" @@ -67,1677 +65,1854 @@ /* Defines and Structs */ typedef struct BrushPainterCache { - bool use_float; /* need float imbuf? */ - bool use_color_correction; /* use color correction for float */ - bool invert; - - bool is_texbrush; - bool is_maskbrush; - - int lastdiameter; - float last_tex_rotation; - float last_mask_rotation; - float last_pressure; - - ImBuf *ibuf; - ImBuf *texibuf; - unsigned short *curve_mask; - unsigned short *tex_mask; - unsigned short *tex_mask_old; - unsigned int tex_mask_old_w; - unsigned int tex_mask_old_h; + bool use_float; /* need float imbuf? */ + bool use_color_correction; /* use color correction for float */ + bool invert; + + bool is_texbrush; + bool is_maskbrush; + + int lastdiameter; + float last_tex_rotation; + float last_mask_rotation; + float last_pressure; + + ImBuf *ibuf; + ImBuf *texibuf; + unsigned short *curve_mask; + unsigned short *tex_mask; + unsigned short *tex_mask_old; + unsigned int tex_mask_old_w; + unsigned int tex_mask_old_h; } BrushPainterCache; typedef struct BrushPainter { - Scene *scene; - Brush *brush; + Scene *scene; + Brush *brush; - float lastpaintpos[2]; /* position of last paint op */ - float startpaintpos[2]; /* position of first paint */ + float lastpaintpos[2]; /* position of last paint op */ + float startpaintpos[2]; /* position of first paint */ - short firsttouch; /* first paint op */ + short firsttouch; /* first paint op */ - struct ImagePool *pool; /* image pool */ - rctf tex_mapping; /* texture coordinate mapping */ - rctf mask_mapping; /* mask texture coordinate mapping */ + struct ImagePool *pool; /* image pool */ + rctf tex_mapping; /* texture coordinate mapping */ + rctf mask_mapping; /* mask texture coordinate mapping */ - BrushPainterCache cache; + BrushPainterCache cache; } BrushPainter; typedef struct ImagePaintRegion { - int destx, desty; - int srcx, srcy; - int width, height; + int destx, desty; + int srcx, srcy; + int width, height; } ImagePaintRegion; typedef struct ImagePaintState { - BrushPainter *painter; - SpaceImage *sima; - View2D *v2d; - Scene *scene; - bScreen *screen; - struct ImagePool *image_pool; - - Brush *brush; - short tool, blend; - Image *image; - ImBuf *canvas; - ImBuf *clonecanvas; - const char *warnpackedfile; - const char *warnmultifile; - - bool do_masking; - - /* viewport texture paint only, but _not_ project paint */ - Object *ob; - int faceindex; - float uv[2]; - int do_facesel; - int symmetry; - - bool need_redraw; - - BlurKernel *blurkernel; + BrushPainter *painter; + SpaceImage *sima; + View2D *v2d; + Scene *scene; + bScreen *screen; + struct ImagePool *image_pool; + + Brush *brush; + short tool, blend; + Image *image; + ImBuf *canvas; + ImBuf *clonecanvas; + const char *warnpackedfile; + const char *warnmultifile; + + bool do_masking; + + /* viewport texture paint only, but _not_ project paint */ + Object *ob; + int faceindex; + float uv[2]; + int do_facesel; + int symmetry; + + bool need_redraw; + + BlurKernel *blurkernel; } ImagePaintState; - static BrushPainter *brush_painter_2d_new(Scene *scene, Brush *brush, bool invert) { - BrushPainter *painter = MEM_callocN(sizeof(BrushPainter), "BrushPainter"); + BrushPainter *painter = MEM_callocN(sizeof(BrushPainter), "BrushPainter"); - painter->brush = brush; - painter->scene = scene; - painter->firsttouch = 1; - painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ - painter->cache.invert = invert; + painter->brush = brush; + painter->scene = scene; + painter->firsttouch = 1; + painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ + painter->cache.invert = invert; - return painter; + return painter; } - -static void brush_painter_2d_require_imbuf(BrushPainter *painter, bool use_float, bool use_color_correction) +static void brush_painter_2d_require_imbuf(BrushPainter *painter, + bool use_float, + bool use_color_correction) { - Brush *brush = painter->brush; - - if ((painter->cache.use_float != use_float)) { - if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf); - if (painter->cache.curve_mask) MEM_freeN(painter->cache.curve_mask); - if (painter->cache.tex_mask) MEM_freeN(painter->cache.tex_mask); - if (painter->cache.tex_mask_old) MEM_freeN(painter->cache.tex_mask_old); - painter->cache.ibuf = NULL; - painter->cache.curve_mask = NULL; - painter->cache.tex_mask = NULL; - painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ - } - - painter->cache.use_float = use_float; - painter->cache.use_color_correction = use_float && use_color_correction; - painter->cache.is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? true : false; - painter->cache.is_maskbrush = (brush->mask_mtex.tex) ? true : false; + Brush *brush = painter->brush; + + if ((painter->cache.use_float != use_float)) { + if (painter->cache.ibuf) + IMB_freeImBuf(painter->cache.ibuf); + if (painter->cache.curve_mask) + MEM_freeN(painter->cache.curve_mask); + if (painter->cache.tex_mask) + MEM_freeN(painter->cache.tex_mask); + if (painter->cache.tex_mask_old) + MEM_freeN(painter->cache.tex_mask_old); + painter->cache.ibuf = NULL; + painter->cache.curve_mask = NULL; + painter->cache.tex_mask = NULL; + painter->cache.lastdiameter = -1; /* force ibuf create in refresh */ + } + + painter->cache.use_float = use_float; + painter->cache.use_color_correction = use_float && use_color_correction; + painter->cache.is_texbrush = (brush->mtex.tex && brush->imagepaint_tool == PAINT_TOOL_DRAW) ? + true : + false; + painter->cache.is_maskbrush = (brush->mask_mtex.tex) ? true : false; } static void brush_painter_2d_free(BrushPainter *painter) { - if (painter->cache.ibuf) IMB_freeImBuf(painter->cache.ibuf); - if (painter->cache.texibuf) IMB_freeImBuf(painter->cache.texibuf); - if (painter->cache.curve_mask) MEM_freeN(painter->cache.curve_mask); - if (painter->cache.tex_mask) MEM_freeN(painter->cache.tex_mask); - if (painter->cache.tex_mask_old) MEM_freeN(painter->cache.tex_mask_old); - MEM_freeN(painter); + if (painter->cache.ibuf) + IMB_freeImBuf(painter->cache.ibuf); + if (painter->cache.texibuf) + IMB_freeImBuf(painter->cache.texibuf); + if (painter->cache.curve_mask) + MEM_freeN(painter->cache.curve_mask); + if (painter->cache.tex_mask) + MEM_freeN(painter->cache.tex_mask); + if (painter->cache.tex_mask_old) + MEM_freeN(painter->cache.tex_mask_old); + MEM_freeN(painter); } static void brush_imbuf_tex_co(rctf *mapping, int x, int y, float texco[3]) { - texco[0] = mapping->xmin + x * mapping->xmax; - texco[1] = mapping->ymin + y * mapping->ymax; - texco[2] = 0.0f; + texco[0] = mapping->xmin + x * mapping->xmax; + texco[1] = mapping->ymin + y * mapping->ymax; + texco[2] = 0.0f; } /* create a mask with the mask texture */ static unsigned short *brush_painter_mask_ibuf_new(BrushPainter *painter, int size) { - Scene *scene = painter->scene; - Brush *brush = painter->brush; - rctf mask_mapping = painter->mask_mapping; - struct ImagePool *pool = painter->pool; - - float texco[3]; - unsigned short *mask, *m; - int x, y, thread = 0; - - mask = MEM_mallocN(sizeof(unsigned short) * size * size, "brush_painter_mask"); - m = mask; - - for (y = 0; y < size; y++) { - for (x = 0; x < size; x++, m++) { - float res; - brush_imbuf_tex_co(&mask_mapping, x, y, texco); - res = BKE_brush_sample_masktex(scene, brush, texco, thread, pool); - *m = (unsigned short)(65535.0f * res); - } - } - - return mask; + Scene *scene = painter->scene; + Brush *brush = painter->brush; + rctf mask_mapping = painter->mask_mapping; + struct ImagePool *pool = painter->pool; + + float texco[3]; + unsigned short *mask, *m; + int x, y, thread = 0; + + mask = MEM_mallocN(sizeof(unsigned short) * size * size, "brush_painter_mask"); + m = mask; + + for (y = 0; y < size; y++) { + for (x = 0; x < size; x++, m++) { + float res; + brush_imbuf_tex_co(&mask_mapping, x, y, texco); + res = BKE_brush_sample_masktex(scene, brush, texco, thread, pool); + *m = (unsigned short)(65535.0f * res); + } + } + + return mask; } /* update rectangular section of the brush image */ -static void brush_painter_mask_imbuf_update( - BrushPainter *painter, unsigned short *tex_mask_old, - int origx, int origy, int w, int h, int xt, int yt, int diameter) +static void brush_painter_mask_imbuf_update(BrushPainter *painter, + unsigned short *tex_mask_old, + int origx, + int origy, + int w, + int h, + int xt, + int yt, + int diameter) { - Scene *scene = painter->scene; - Brush *brush = painter->brush; - rctf tex_mapping = painter->mask_mapping; - struct ImagePool *pool = painter->pool; - unsigned short res; - - bool use_texture_old = (tex_mask_old != NULL); - - int x, y, thread = 0; - - unsigned short *tex_mask = painter->cache.tex_mask; - unsigned short *tex_mask_cur = painter->cache.tex_mask_old; - - /* fill pixels */ - for (y = origy; y < h; y++) { - for (x = origx; x < w; x++) { - /* sample texture */ - float texco[3]; - - /* handle byte pixel */ - unsigned short *b = tex_mask + (y * diameter + x); - unsigned short *t = tex_mask_cur + (y * diameter + x); - - if (!use_texture_old) { - brush_imbuf_tex_co(&tex_mapping, x, y, texco); - res = (unsigned short)(65535.0f * BKE_brush_sample_masktex(scene, brush, texco, thread, pool)); - } - - /* read from old texture buffer */ - if (use_texture_old) { - res = *(tex_mask_old + ((y - origy + yt) * painter->cache.tex_mask_old_w + (x - origx + xt))); - } - - /* write to new texture mask */ - *t = res; - /* write to mask image buffer */ - *b = res; - } - } + Scene *scene = painter->scene; + Brush *brush = painter->brush; + rctf tex_mapping = painter->mask_mapping; + struct ImagePool *pool = painter->pool; + unsigned short res; + + bool use_texture_old = (tex_mask_old != NULL); + + int x, y, thread = 0; + + unsigned short *tex_mask = painter->cache.tex_mask; + unsigned short *tex_mask_cur = painter->cache.tex_mask_old; + + /* fill pixels */ + for (y = origy; y < h; y++) { + for (x = origx; x < w; x++) { + /* sample texture */ + float texco[3]; + + /* handle byte pixel */ + unsigned short *b = tex_mask + (y * diameter + x); + unsigned short *t = tex_mask_cur + (y * diameter + x); + + if (!use_texture_old) { + brush_imbuf_tex_co(&tex_mapping, x, y, texco); + res = (unsigned short)(65535.0f * + BKE_brush_sample_masktex(scene, brush, texco, thread, pool)); + } + + /* read from old texture buffer */ + if (use_texture_old) { + res = *(tex_mask_old + + ((y - origy + yt) * painter->cache.tex_mask_old_w + (x - origx + xt))); + } + + /* write to new texture mask */ + *t = res; + /* write to mask image buffer */ + *b = res; + } + } } - /** * Update the brush mask image by trying to reuse the cached texture result. * This can be considerably faster for brushes that change size due to pressure or * textures that stick to the surface where only part of the pixels are new */ -static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, const float pos[2], int diameter) +static void brush_painter_mask_imbuf_partial_update(BrushPainter *painter, + const float pos[2], + int diameter) { - BrushPainterCache *cache = &painter->cache; - unsigned short *tex_mask_old; - int destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; - - /* create brush image buffer if it didn't exist yet */ - if (!cache->tex_mask) - cache->tex_mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); - - /* create new texture image buffer with coordinates relative to old */ - tex_mask_old = cache->tex_mask_old; - cache->tex_mask_old = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); - - if (tex_mask_old) { - ImBuf maskibuf; - ImBuf maskibuf_old; - maskibuf.x = maskibuf.y = diameter; - maskibuf_old.x = cache->tex_mask_old_w; - maskibuf_old.y = cache->tex_mask_old_h; - - srcx = srcy = 0; - w = cache->tex_mask_old_w; - h = cache->tex_mask_old_h; - destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); - desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); - - /* hack, use temporary rects so that clipping works */ - IMB_rectclip(&maskibuf, &maskibuf_old, &destx, &desty, &srcx, &srcy, &w, &h); - } - else { - srcx = srcy = 0; - destx = desty = 0; - w = h = 0; - } - - x1 = min_ii(destx, diameter); - y1 = min_ii(desty, diameter); - x2 = min_ii(destx + w, diameter); - y2 = min_ii(desty + h, diameter); - - /* blend existing texture in new position */ - if ((x1 < x2) && (y1 < y2)) - brush_painter_mask_imbuf_update(painter, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter); - - if (tex_mask_old) - MEM_freeN(tex_mask_old); - - /* sample texture in new areas */ - if ((0 < x1) && (0 < diameter)) - brush_painter_mask_imbuf_update(painter, NULL, 0, 0, x1, diameter, 0, 0, diameter); - if ((x2 < diameter) && (0 < diameter)) - brush_painter_mask_imbuf_update(painter, NULL, x2, 0, diameter, diameter, 0, 0, diameter); - if ((x1 < x2) && (0 < y1)) - brush_painter_mask_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0, diameter); - if ((x1 < x2) && (y2 < diameter)) - brush_painter_mask_imbuf_update(painter, NULL, x1, y2, x2, diameter, 0, 0, diameter); - - /* through with sampling, now update sizes */ - cache->tex_mask_old_w = diameter; - cache->tex_mask_old_h = diameter; + BrushPainterCache *cache = &painter->cache; + unsigned short *tex_mask_old; + int destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; + + /* create brush image buffer if it didn't exist yet */ + if (!cache->tex_mask) + cache->tex_mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, + "brush_painter_mask"); + + /* create new texture image buffer with coordinates relative to old */ + tex_mask_old = cache->tex_mask_old; + cache->tex_mask_old = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, + "brush_painter_mask"); + + if (tex_mask_old) { + ImBuf maskibuf; + ImBuf maskibuf_old; + maskibuf.x = maskibuf.y = diameter; + maskibuf_old.x = cache->tex_mask_old_w; + maskibuf_old.y = cache->tex_mask_old_h; + + srcx = srcy = 0; + w = cache->tex_mask_old_w; + h = cache->tex_mask_old_h; + destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); + desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); + + /* hack, use temporary rects so that clipping works */ + IMB_rectclip(&maskibuf, &maskibuf_old, &destx, &desty, &srcx, &srcy, &w, &h); + } + else { + srcx = srcy = 0; + destx = desty = 0; + w = h = 0; + } + + x1 = min_ii(destx, diameter); + y1 = min_ii(desty, diameter); + x2 = min_ii(destx + w, diameter); + y2 = min_ii(desty + h, diameter); + + /* blend existing texture in new position */ + if ((x1 < x2) && (y1 < y2)) + brush_painter_mask_imbuf_update(painter, tex_mask_old, x1, y1, x2, y2, srcx, srcy, diameter); + + if (tex_mask_old) + MEM_freeN(tex_mask_old); + + /* sample texture in new areas */ + if ((0 < x1) && (0 < diameter)) + brush_painter_mask_imbuf_update(painter, NULL, 0, 0, x1, diameter, 0, 0, diameter); + if ((x2 < diameter) && (0 < diameter)) + brush_painter_mask_imbuf_update(painter, NULL, x2, 0, diameter, diameter, 0, 0, diameter); + if ((x1 < x2) && (0 < y1)) + brush_painter_mask_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0, diameter); + if ((x1 < x2) && (y2 < diameter)) + brush_painter_mask_imbuf_update(painter, NULL, x1, y2, x2, diameter, 0, 0, diameter); + + /* through with sampling, now update sizes */ + cache->tex_mask_old_w = diameter; + cache->tex_mask_old_h = diameter; } /* create a mask with the falloff strength */ -static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, int diameter, float radius) +static unsigned short *brush_painter_curve_mask_new(BrushPainter *painter, + int diameter, + float radius) { - Brush *brush = painter->brush; + Brush *brush = painter->brush; - int xoff = -radius; - int yoff = -radius; + int xoff = -radius; + int yoff = -radius; - unsigned short *mask, *m; - int x, y; + unsigned short *mask, *m; + int x, y; - mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); - m = mask; + mask = MEM_mallocN(sizeof(unsigned short) * diameter * diameter, "brush_painter_mask"); + m = mask; - for (y = 0; y < diameter; y++) { - for (x = 0; x < diameter; x++, m++) { - float xy[2] = {x + xoff, y + yoff}; - float len = len_v2(xy); + for (y = 0; y < diameter; y++) { + for (x = 0; x < diameter; x++, m++) { + float xy[2] = {x + xoff, y + yoff}; + float len = len_v2(xy); - *m = (unsigned short)(65535.0f * BKE_brush_curve_strength_clamped(brush, len, radius)); - } - } + *m = (unsigned short)(65535.0f * BKE_brush_curve_strength_clamped(brush, len, radius)); + } + } - return mask; + return mask; } - /* create imbuf with brush color */ -static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, int size, float pressure, float distance) +static ImBuf *brush_painter_imbuf_new(BrushPainter *painter, + int size, + float pressure, + float distance) { - Scene *scene = painter->scene; - Brush *brush = painter->brush; - - const char *display_device = scene->display_settings.display_device; - struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); - - rctf tex_mapping = painter->tex_mapping; - struct ImagePool *pool = painter->pool; - - bool use_color_correction = painter->cache.use_color_correction; - bool use_float = painter->cache.use_float; - bool is_texbrush = painter->cache.is_texbrush; - - int x, y, thread = 0; - float brush_rgb[3]; - - /* allocate image buffer */ - ImBuf *ibuf = IMB_allocImBuf(size, size, 32, (use_float) ? IB_rectfloat : IB_rect); - - /* get brush color */ - if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { - paint_brush_color_get(scene, brush, use_color_correction, painter->cache.invert, distance, pressure, brush_rgb, display); - } - else { - brush_rgb[0] = 1.0f; - brush_rgb[1] = 1.0f; - brush_rgb[2] = 1.0f; - } - - /* fill image buffer */ - for (y = 0; y < size; y++) { - for (x = 0; x < size; x++) { - /* sample texture and multiply with brush color */ - float texco[3], rgba[4]; - - if (is_texbrush) { - brush_imbuf_tex_co(&tex_mapping, x, y, texco); - BKE_brush_sample_tex_3d(scene, brush, texco, rgba, thread, pool); - /* TODO(sergey): Support texture paint color space. */ - if (!use_float) { - IMB_colormanagement_scene_linear_to_display_v3(rgba, display); - } - mul_v3_v3(rgba, brush_rgb); - } - else { - copy_v3_v3(rgba, brush_rgb); - rgba[3] = 1.0f; - } - - if (use_float) { - /* write to float pixel */ - float *dstf = ibuf->rect_float + (y * size + x) * 4; - mul_v3_v3fl(dstf, rgba, rgba[3]); /* premultiply */ - dstf[3] = rgba[3]; - } - else { - /* write to byte pixel */ - unsigned char *dst = (unsigned char *)ibuf->rect + (y * size + x) * 4; - - rgb_float_to_uchar(dst, rgba); - dst[3] = unit_float_to_uchar_clamp(rgba[3]); - } - } - } - - return ibuf; + Scene *scene = painter->scene; + Brush *brush = painter->brush; + + const char *display_device = scene->display_settings.display_device; + struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); + + rctf tex_mapping = painter->tex_mapping; + struct ImagePool *pool = painter->pool; + + bool use_color_correction = painter->cache.use_color_correction; + bool use_float = painter->cache.use_float; + bool is_texbrush = painter->cache.is_texbrush; + + int x, y, thread = 0; + float brush_rgb[3]; + + /* allocate image buffer */ + ImBuf *ibuf = IMB_allocImBuf(size, size, 32, (use_float) ? IB_rectfloat : IB_rect); + + /* get brush color */ + if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { + paint_brush_color_get(scene, + brush, + use_color_correction, + painter->cache.invert, + distance, + pressure, + brush_rgb, + display); + } + else { + brush_rgb[0] = 1.0f; + brush_rgb[1] = 1.0f; + brush_rgb[2] = 1.0f; + } + + /* fill image buffer */ + for (y = 0; y < size; y++) { + for (x = 0; x < size; x++) { + /* sample texture and multiply with brush color */ + float texco[3], rgba[4]; + + if (is_texbrush) { + brush_imbuf_tex_co(&tex_mapping, x, y, texco); + BKE_brush_sample_tex_3d(scene, brush, texco, rgba, thread, pool); + /* TODO(sergey): Support texture paint color space. */ + if (!use_float) { + IMB_colormanagement_scene_linear_to_display_v3(rgba, display); + } + mul_v3_v3(rgba, brush_rgb); + } + else { + copy_v3_v3(rgba, brush_rgb); + rgba[3] = 1.0f; + } + + if (use_float) { + /* write to float pixel */ + float *dstf = ibuf->rect_float + (y * size + x) * 4; + mul_v3_v3fl(dstf, rgba, rgba[3]); /* premultiply */ + dstf[3] = rgba[3]; + } + else { + /* write to byte pixel */ + unsigned char *dst = (unsigned char *)ibuf->rect + (y * size + x) * 4; + + rgb_float_to_uchar(dst, rgba); + dst[3] = unit_float_to_uchar_clamp(rgba[3]); + } + } + } + + return ibuf; } /* update rectangular section of the brush image */ static void brush_painter_imbuf_update( - BrushPainter *painter, ImBuf *oldtexibuf, - int origx, int origy, int w, int h, int xt, int yt) + BrushPainter *painter, ImBuf *oldtexibuf, int origx, int origy, int w, int h, int xt, int yt) { - Scene *scene = painter->scene; - Brush *brush = painter->brush; - - const char *display_device = scene->display_settings.display_device; - struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); - - rctf tex_mapping = painter->tex_mapping; - struct ImagePool *pool = painter->pool; - - bool use_color_correction = painter->cache.use_color_correction; - bool use_float = painter->cache.use_float; - bool is_texbrush = painter->cache.is_texbrush; - bool use_texture_old = (oldtexibuf != NULL); - - int x, y, thread = 0; - float brush_rgb[3]; - - ImBuf *ibuf = painter->cache.ibuf; - ImBuf *texibuf = painter->cache.texibuf; - - /* get brush color */ - if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { - paint_brush_color_get(scene, brush, use_color_correction, painter->cache.invert, 0.0, 1.0, brush_rgb, display); - } - else { - brush_rgb[0] = 1.0f; - brush_rgb[1] = 1.0f; - brush_rgb[2] = 1.0f; - } - - /* fill pixels */ - for (y = origy; y < h; y++) { - for (x = origx; x < w; x++) { - /* sample texture and multiply with brush color */ - float texco[3], rgba[4]; - - if (!use_texture_old) { - if (is_texbrush) { - brush_imbuf_tex_co(&tex_mapping, x, y, texco); - BKE_brush_sample_tex_3d(scene, brush, texco, rgba, thread, pool); - /* TODO(sergey): Support texture paint color space. */ - if (!use_float) { - IMB_colormanagement_scene_linear_to_display_v3(rgba, display); - } - mul_v3_v3(rgba, brush_rgb); - } - else { - copy_v3_v3(rgba, brush_rgb); - rgba[3] = 1.0f; - } - } - - if (use_float) { - /* handle float pixel */ - float *bf = ibuf->rect_float + (y * ibuf->x + x) * 4; - float *tf = texibuf->rect_float + (y * texibuf->x + x) * 4; - - /* read from old texture buffer */ - if (use_texture_old) { - const float *otf = oldtexibuf->rect_float + ((y - origy + yt) * oldtexibuf->x + (x - origx + xt)) * 4; - copy_v4_v4(rgba, otf); - } - - /* write to new texture buffer */ - copy_v4_v4(tf, rgba); - - /* output premultiplied float image, mf was already premultiplied */ - mul_v3_v3fl(bf, rgba, rgba[3]); - bf[3] = rgba[3]; - } - else { - unsigned char crgba[4]; - - /* handle byte pixel */ - unsigned char *b = (unsigned char *)ibuf->rect + (y * ibuf->x + x) * 4; - unsigned char *t = (unsigned char *)texibuf->rect + (y * texibuf->x + x) * 4; - - /* read from old texture buffer */ - if (use_texture_old) { - unsigned char *ot = (unsigned char *)oldtexibuf->rect + ((y - origy + yt) * oldtexibuf->x + (x - origx + xt)) * 4; - crgba[0] = ot[0]; - crgba[1] = ot[1]; - crgba[2] = ot[2]; - crgba[3] = ot[3]; - } - else - rgba_float_to_uchar(crgba, rgba); - - /* write to new texture buffer */ - t[0] = crgba[0]; - t[1] = crgba[1]; - t[2] = crgba[2]; - t[3] = crgba[3]; - - /* write to brush image buffer */ - b[0] = crgba[0]; - b[1] = crgba[1]; - b[2] = crgba[2]; - b[3] = crgba[3]; - } - } - } + Scene *scene = painter->scene; + Brush *brush = painter->brush; + + const char *display_device = scene->display_settings.display_device; + struct ColorManagedDisplay *display = IMB_colormanagement_display_get_named(display_device); + + rctf tex_mapping = painter->tex_mapping; + struct ImagePool *pool = painter->pool; + + bool use_color_correction = painter->cache.use_color_correction; + bool use_float = painter->cache.use_float; + bool is_texbrush = painter->cache.is_texbrush; + bool use_texture_old = (oldtexibuf != NULL); + + int x, y, thread = 0; + float brush_rgb[3]; + + ImBuf *ibuf = painter->cache.ibuf; + ImBuf *texibuf = painter->cache.texibuf; + + /* get brush color */ + if (brush->imagepaint_tool == PAINT_TOOL_DRAW) { + paint_brush_color_get( + scene, brush, use_color_correction, painter->cache.invert, 0.0, 1.0, brush_rgb, display); + } + else { + brush_rgb[0] = 1.0f; + brush_rgb[1] = 1.0f; + brush_rgb[2] = 1.0f; + } + + /* fill pixels */ + for (y = origy; y < h; y++) { + for (x = origx; x < w; x++) { + /* sample texture and multiply with brush color */ + float texco[3], rgba[4]; + + if (!use_texture_old) { + if (is_texbrush) { + brush_imbuf_tex_co(&tex_mapping, x, y, texco); + BKE_brush_sample_tex_3d(scene, brush, texco, rgba, thread, pool); + /* TODO(sergey): Support texture paint color space. */ + if (!use_float) { + IMB_colormanagement_scene_linear_to_display_v3(rgba, display); + } + mul_v3_v3(rgba, brush_rgb); + } + else { + copy_v3_v3(rgba, brush_rgb); + rgba[3] = 1.0f; + } + } + + if (use_float) { + /* handle float pixel */ + float *bf = ibuf->rect_float + (y * ibuf->x + x) * 4; + float *tf = texibuf->rect_float + (y * texibuf->x + x) * 4; + + /* read from old texture buffer */ + if (use_texture_old) { + const float *otf = oldtexibuf->rect_float + + ((y - origy + yt) * oldtexibuf->x + (x - origx + xt)) * 4; + copy_v4_v4(rgba, otf); + } + + /* write to new texture buffer */ + copy_v4_v4(tf, rgba); + + /* output premultiplied float image, mf was already premultiplied */ + mul_v3_v3fl(bf, rgba, rgba[3]); + bf[3] = rgba[3]; + } + else { + unsigned char crgba[4]; + + /* handle byte pixel */ + unsigned char *b = (unsigned char *)ibuf->rect + (y * ibuf->x + x) * 4; + unsigned char *t = (unsigned char *)texibuf->rect + (y * texibuf->x + x) * 4; + + /* read from old texture buffer */ + if (use_texture_old) { + unsigned char *ot = (unsigned char *)oldtexibuf->rect + + ((y - origy + yt) * oldtexibuf->x + (x - origx + xt)) * 4; + crgba[0] = ot[0]; + crgba[1] = ot[1]; + crgba[2] = ot[2]; + crgba[3] = ot[3]; + } + else + rgba_float_to_uchar(crgba, rgba); + + /* write to new texture buffer */ + t[0] = crgba[0]; + t[1] = crgba[1]; + t[2] = crgba[2]; + t[3] = crgba[3]; + + /* write to brush image buffer */ + b[0] = crgba[0]; + b[1] = crgba[1]; + b[2] = crgba[2]; + b[3] = crgba[3]; + } + } + } } /* update the brush image by trying to reuse the cached texture result. this * can be considerably faster for brushes that change size due to pressure or * textures that stick to the surface where only part of the pixels are new */ -static void brush_painter_imbuf_partial_update(BrushPainter *painter, const float pos[2], int diameter) +static void brush_painter_imbuf_partial_update(BrushPainter *painter, + const float pos[2], + int diameter) { - BrushPainterCache *cache = &painter->cache; - ImBuf *oldtexibuf, *ibuf; - int imbflag, destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; - - /* create brush image buffer if it didn't exist yet */ - imbflag = (cache->use_float) ? IB_rectfloat : IB_rect; - if (!cache->ibuf) - cache->ibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); - ibuf = cache->ibuf; - - /* create new texture image buffer with coordinates relative to old */ - oldtexibuf = cache->texibuf; - cache->texibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); - - if (oldtexibuf) { - srcx = srcy = 0; - w = oldtexibuf->x; - h = oldtexibuf->y; - destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); - desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); - - IMB_rectclip(cache->texibuf, oldtexibuf, &destx, &desty, &srcx, &srcy, &w, &h); - } - else { - srcx = srcy = 0; - destx = desty = 0; - w = h = 0; - } - - x1 = min_ii(destx, ibuf->x); - y1 = min_ii(desty, ibuf->y); - x2 = min_ii(destx + w, ibuf->x); - y2 = min_ii(desty + h, ibuf->y); - - /* blend existing texture in new position */ - if ((x1 < x2) && (y1 < y2)) - brush_painter_imbuf_update(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy); - - if (oldtexibuf) - IMB_freeImBuf(oldtexibuf); - - /* sample texture in new areas */ - if ((0 < x1) && (0 < ibuf->y)) - brush_painter_imbuf_update(painter, NULL, 0, 0, x1, ibuf->y, 0, 0); - if ((x2 < ibuf->x) && (0 < ibuf->y)) - brush_painter_imbuf_update(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0); - if ((x1 < x2) && (0 < y1)) - brush_painter_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0); - if ((x1 < x2) && (y2 < ibuf->y)) - brush_painter_imbuf_update(painter, NULL, x1, y2, x2, ibuf->y, 0, 0); + BrushPainterCache *cache = &painter->cache; + ImBuf *oldtexibuf, *ibuf; + int imbflag, destx, desty, srcx, srcy, w, h, x1, y1, x2, y2; + + /* create brush image buffer if it didn't exist yet */ + imbflag = (cache->use_float) ? IB_rectfloat : IB_rect; + if (!cache->ibuf) + cache->ibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); + ibuf = cache->ibuf; + + /* create new texture image buffer with coordinates relative to old */ + oldtexibuf = cache->texibuf; + cache->texibuf = IMB_allocImBuf(diameter, diameter, 32, imbflag); + + if (oldtexibuf) { + srcx = srcy = 0; + w = oldtexibuf->x; + h = oldtexibuf->y; + destx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]) + (diameter / 2 - w / 2); + desty = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]) + (diameter / 2 - h / 2); + + IMB_rectclip(cache->texibuf, oldtexibuf, &destx, &desty, &srcx, &srcy, &w, &h); + } + else { + srcx = srcy = 0; + destx = desty = 0; + w = h = 0; + } + + x1 = min_ii(destx, ibuf->x); + y1 = min_ii(desty, ibuf->y); + x2 = min_ii(destx + w, ibuf->x); + y2 = min_ii(desty + h, ibuf->y); + + /* blend existing texture in new position */ + if ((x1 < x2) && (y1 < y2)) + brush_painter_imbuf_update(painter, oldtexibuf, x1, y1, x2, y2, srcx, srcy); + + if (oldtexibuf) + IMB_freeImBuf(oldtexibuf); + + /* sample texture in new areas */ + if ((0 < x1) && (0 < ibuf->y)) + brush_painter_imbuf_update(painter, NULL, 0, 0, x1, ibuf->y, 0, 0); + if ((x2 < ibuf->x) && (0 < ibuf->y)) + brush_painter_imbuf_update(painter, NULL, x2, 0, ibuf->x, ibuf->y, 0, 0); + if ((x1 < x2) && (0 < y1)) + brush_painter_imbuf_update(painter, NULL, x1, 0, x2, y1, 0, 0); + if ((x1 < x2) && (y2 < ibuf->y)) + brush_painter_imbuf_update(painter, NULL, x1, y2, x2, ibuf->y, 0, 0); } -static void brush_painter_2d_tex_mapping(ImagePaintState *s, int diameter, const float startpos[2], const float pos[2], const float mouse[2], int mapmode, rctf *mapping) +static void brush_painter_2d_tex_mapping(ImagePaintState *s, + int diameter, + const float startpos[2], + const float pos[2], + const float mouse[2], + int mapmode, + rctf *mapping) { - float invw = 1.0f / (float)s->canvas->x; - float invh = 1.0f / (float)s->canvas->y; - int xmin, ymin, xmax, ymax; - int ipos[2]; - - /* find start coordinate of brush in canvas */ - ipos[0] = (int)floorf((pos[0] - diameter / 2) + 1.0f); - ipos[1] = (int)floorf((pos[1] - diameter / 2) + 1.0f); - - if (mapmode == MTEX_MAP_MODE_STENCIL) { - /* map from view coordinates of brush to region coordinates */ - UI_view2d_view_to_region(s->v2d, ipos[0] * invw, ipos[1] * invh, &xmin, &ymin); - UI_view2d_view_to_region(s->v2d, (ipos[0] + diameter) * invw, (ipos[1] + diameter) * invh, &xmax, &ymax); - - /* output mapping from brush ibuf x/y to region coordinates */ - mapping->xmin = xmin; - mapping->ymin = ymin; - mapping->xmax = (xmax - xmin) / (float)diameter; - mapping->ymax = (ymax - ymin) / (float)diameter; - } - else if (mapmode == MTEX_MAP_MODE_3D) { - /* 3D mapping, just mapping to canvas 0..1 */ - mapping->xmin = 2.0f * (ipos[0] * invw - 0.5f); - mapping->ymin = 2.0f * (ipos[1] * invh - 0.5f); - mapping->xmax = 2.0f * invw; - mapping->ymax = 2.0f * invh; - } - else if (ELEM(mapmode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_RANDOM)) { - /* view mapping */ - mapping->xmin = mouse[0] - diameter * 0.5f + 0.5f; - mapping->ymin = mouse[1] - diameter * 0.5f + 0.5f; - mapping->xmax = 1.0f; - mapping->ymax = 1.0f; - } - else /* if (mapmode == MTEX_MAP_MODE_TILED) */ { - mapping->xmin = (int)(-diameter * 0.5) + (int)floorf(pos[0]) - (int)floorf(startpos[0]); - mapping->ymin = (int)(-diameter * 0.5) + (int)floorf(pos[1]) - (int)floorf(startpos[1]); - mapping->xmax = 1.0f; - mapping->ymax = 1.0f; - } + float invw = 1.0f / (float)s->canvas->x; + float invh = 1.0f / (float)s->canvas->y; + int xmin, ymin, xmax, ymax; + int ipos[2]; + + /* find start coordinate of brush in canvas */ + ipos[0] = (int)floorf((pos[0] - diameter / 2) + 1.0f); + ipos[1] = (int)floorf((pos[1] - diameter / 2) + 1.0f); + + if (mapmode == MTEX_MAP_MODE_STENCIL) { + /* map from view coordinates of brush to region coordinates */ + UI_view2d_view_to_region(s->v2d, ipos[0] * invw, ipos[1] * invh, &xmin, &ymin); + UI_view2d_view_to_region( + s->v2d, (ipos[0] + diameter) * invw, (ipos[1] + diameter) * invh, &xmax, &ymax); + + /* output mapping from brush ibuf x/y to region coordinates */ + mapping->xmin = xmin; + mapping->ymin = ymin; + mapping->xmax = (xmax - xmin) / (float)diameter; + mapping->ymax = (ymax - ymin) / (float)diameter; + } + else if (mapmode == MTEX_MAP_MODE_3D) { + /* 3D mapping, just mapping to canvas 0..1 */ + mapping->xmin = 2.0f * (ipos[0] * invw - 0.5f); + mapping->ymin = 2.0f * (ipos[1] * invh - 0.5f); + mapping->xmax = 2.0f * invw; + mapping->ymax = 2.0f * invh; + } + else if (ELEM(mapmode, MTEX_MAP_MODE_VIEW, MTEX_MAP_MODE_RANDOM)) { + /* view mapping */ + mapping->xmin = mouse[0] - diameter * 0.5f + 0.5f; + mapping->ymin = mouse[1] - diameter * 0.5f + 0.5f; + mapping->xmax = 1.0f; + mapping->ymax = 1.0f; + } + else /* if (mapmode == MTEX_MAP_MODE_TILED) */ { + mapping->xmin = (int)(-diameter * 0.5) + (int)floorf(pos[0]) - (int)floorf(startpos[0]); + mapping->ymin = (int)(-diameter * 0.5) + (int)floorf(pos[1]) - (int)floorf(startpos[1]); + mapping->xmax = 1.0f; + mapping->ymax = 1.0f; + } } -static void brush_painter_2d_refresh_cache(ImagePaintState *s, BrushPainter *painter, const float pos[2], const float mouse[2], float pressure, float distance, float size) +static void brush_painter_2d_refresh_cache(ImagePaintState *s, + BrushPainter *painter, + const float pos[2], + const float mouse[2], + float pressure, + float distance, + float size) { - const Scene *scene = painter->scene; - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - Brush *brush = painter->brush; - BrushPainterCache *cache = &painter->cache; - const int diameter = 2 * size; - - bool do_random = false; - bool do_partial_update = false; - bool update_color = ( - (brush->flag & BRUSH_USE_GRADIENT) && - ((ELEM(brush->gradient_stroke_mode, - BRUSH_GRADIENT_SPACING_REPEAT, - BRUSH_GRADIENT_SPACING_CLAMP)) || - (cache->last_pressure != pressure))); - float tex_rotation = -brush->mtex.rot; - float mask_rotation = -brush->mask_mtex.rot; - - painter->pool = BKE_image_pool_new(); - - /* determine how can update based on textures used */ - if (painter->cache.is_texbrush) { - if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { - tex_rotation += ups->brush_rotation; - } - else if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) - do_random = true; - else if (!((brush->flag & BRUSH_ANCHORED) || update_color)) - do_partial_update = true; - - brush_painter_2d_tex_mapping( - s, diameter, painter->startpaintpos, pos, mouse, - brush->mtex.brush_map_mode, &painter->tex_mapping); - } - - if (painter->cache.is_maskbrush) { - bool renew_maxmask = false; - bool do_partial_update_mask = false; - /* invalidate case for all mapping modes */ - if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { - mask_rotation += ups->brush_rotation_sec; - } - else if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) { - renew_maxmask = true; - } - else if (!(brush->flag & BRUSH_ANCHORED)) { - do_partial_update_mask = true; - renew_maxmask = true; - } - /* explicitly disable partial update even if it has been enabled above */ - if (brush->mask_pressure) { - do_partial_update_mask = false; - renew_maxmask = true; - } - - if ((diameter != cache->lastdiameter) || - (mask_rotation != cache->last_mask_rotation) || - renew_maxmask) - { - if (cache->tex_mask) { - MEM_freeN(cache->tex_mask); - cache->tex_mask = NULL; - } - - brush_painter_2d_tex_mapping( - s, diameter, painter->startpaintpos, pos, mouse, - brush->mask_mtex.brush_map_mode, &painter->mask_mapping); - - if (do_partial_update_mask) - brush_painter_mask_imbuf_partial_update(painter, pos, diameter); - else - cache->tex_mask = brush_painter_mask_ibuf_new(painter, diameter); - cache->last_mask_rotation = mask_rotation; - } - } - - /* curve mask can only change if the size changes */ - if (diameter != cache->lastdiameter) { - if (cache->curve_mask) { - MEM_freeN(cache->curve_mask); - cache->curve_mask = NULL; - } - - cache->curve_mask = brush_painter_curve_mask_new(painter, diameter, size); - } - - /* detect if we need to recreate image brush buffer */ - if ((diameter != cache->lastdiameter) || - (tex_rotation != cache->last_tex_rotation) || - do_random || - update_color) - { - if (cache->ibuf) { - IMB_freeImBuf(cache->ibuf); - cache->ibuf = NULL; - } - - if (do_partial_update) { - /* do partial update of texture */ - brush_painter_imbuf_partial_update(painter, pos, diameter); - } - else { - /* create brush from scratch */ - cache->ibuf = brush_painter_imbuf_new(painter, diameter, pressure, distance); - } - - cache->lastdiameter = diameter; - cache->last_tex_rotation = tex_rotation; - cache->last_pressure = pressure; - } - else if (do_partial_update) { - /* do only partial update of texture */ - int dx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]); - int dy = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]); - - if ((dx != 0) || (dy != 0)) { - brush_painter_imbuf_partial_update(painter, pos, diameter); - } - } - - BKE_image_pool_free(painter->pool); - painter->pool = NULL; + const Scene *scene = painter->scene; + UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; + Brush *brush = painter->brush; + BrushPainterCache *cache = &painter->cache; + const int diameter = 2 * size; + + bool do_random = false; + bool do_partial_update = false; + bool update_color = ((brush->flag & BRUSH_USE_GRADIENT) && + ((ELEM(brush->gradient_stroke_mode, + BRUSH_GRADIENT_SPACING_REPEAT, + BRUSH_GRADIENT_SPACING_CLAMP)) || + (cache->last_pressure != pressure))); + float tex_rotation = -brush->mtex.rot; + float mask_rotation = -brush->mask_mtex.rot; + + painter->pool = BKE_image_pool_new(); + + /* determine how can update based on textures used */ + if (painter->cache.is_texbrush) { + if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { + tex_rotation += ups->brush_rotation; + } + else if (brush->mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) + do_random = true; + else if (!((brush->flag & BRUSH_ANCHORED) || update_color)) + do_partial_update = true; + + brush_painter_2d_tex_mapping(s, + diameter, + painter->startpaintpos, + pos, + mouse, + brush->mtex.brush_map_mode, + &painter->tex_mapping); + } + + if (painter->cache.is_maskbrush) { + bool renew_maxmask = false; + bool do_partial_update_mask = false; + /* invalidate case for all mapping modes */ + if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_VIEW) { + mask_rotation += ups->brush_rotation_sec; + } + else if (brush->mask_mtex.brush_map_mode == MTEX_MAP_MODE_RANDOM) { + renew_maxmask = true; + } + else if (!(brush->flag & BRUSH_ANCHORED)) { + do_partial_update_mask = true; + renew_maxmask = true; + } + /* explicitly disable partial update even if it has been enabled above */ + if (brush->mask_pressure) { + do_partial_update_mask = false; + renew_maxmask = true; + } + + if ((diameter != cache->lastdiameter) || (mask_rotation != cache->last_mask_rotation) || + renew_maxmask) { + if (cache->tex_mask) { + MEM_freeN(cache->tex_mask); + cache->tex_mask = NULL; + } + + brush_painter_2d_tex_mapping(s, + diameter, + painter->startpaintpos, + pos, + mouse, + brush->mask_mtex.brush_map_mode, + &painter->mask_mapping); + + if (do_partial_update_mask) + brush_painter_mask_imbuf_partial_update(painter, pos, diameter); + else + cache->tex_mask = brush_painter_mask_ibuf_new(painter, diameter); + cache->last_mask_rotation = mask_rotation; + } + } + + /* curve mask can only change if the size changes */ + if (diameter != cache->lastdiameter) { + if (cache->curve_mask) { + MEM_freeN(cache->curve_mask); + cache->curve_mask = NULL; + } + + cache->curve_mask = brush_painter_curve_mask_new(painter, diameter, size); + } + + /* detect if we need to recreate image brush buffer */ + if ((diameter != cache->lastdiameter) || (tex_rotation != cache->last_tex_rotation) || + do_random || update_color) { + if (cache->ibuf) { + IMB_freeImBuf(cache->ibuf); + cache->ibuf = NULL; + } + + if (do_partial_update) { + /* do partial update of texture */ + brush_painter_imbuf_partial_update(painter, pos, diameter); + } + else { + /* create brush from scratch */ + cache->ibuf = brush_painter_imbuf_new(painter, diameter, pressure, distance); + } + + cache->lastdiameter = diameter; + cache->last_tex_rotation = tex_rotation; + cache->last_pressure = pressure; + } + else if (do_partial_update) { + /* do only partial update of texture */ + int dx = (int)floorf(painter->lastpaintpos[0]) - (int)floorf(pos[0]); + int dy = (int)floorf(painter->lastpaintpos[1]) - (int)floorf(pos[1]); + + if ((dx != 0) || (dy != 0)) { + brush_painter_imbuf_partial_update(painter, pos, diameter); + } + } + + BKE_image_pool_free(painter->pool); + painter->pool = NULL; } /* keep these functions in sync */ static void paint_2d_ibuf_rgb_get(ImBuf *ibuf, int x, int y, float r_rgb[4]) { - if (ibuf->rect_float) { - const float *rrgbf = ibuf->rect_float + (ibuf->x * y + x) * 4; - copy_v4_v4(r_rgb, rrgbf); - } - else { - unsigned char *rrgb = (unsigned char *)ibuf->rect + (ibuf->x * y + x) * 4; - straight_uchar_to_premul_float(r_rgb, rrgb); - } + if (ibuf->rect_float) { + const float *rrgbf = ibuf->rect_float + (ibuf->x * y + x) * 4; + copy_v4_v4(r_rgb, rrgbf); + } + else { + unsigned char *rrgb = (unsigned char *)ibuf->rect + (ibuf->x * y + x) * 4; + straight_uchar_to_premul_float(r_rgb, rrgb); + } } -static void paint_2d_ibuf_rgb_set(ImBuf *ibuf, int x, int y, const bool is_torus, const float rgb[4]) +static void paint_2d_ibuf_rgb_set( + ImBuf *ibuf, int x, int y, const bool is_torus, const float rgb[4]) { - if (is_torus) { - x %= ibuf->x; - if (x < 0) x += ibuf->x; - y %= ibuf->y; - if (y < 0) y += ibuf->y; - } - - if (ibuf->rect_float) { - float *rrgbf = ibuf->rect_float + (ibuf->x * y + x) * 4; - float map_alpha = (rgb[3] == 0.0f) ? rrgbf[3] : rrgbf[3] / rgb[3]; - - mul_v3_v3fl(rrgbf, rgb, map_alpha); - rrgbf[3] = rgb[3]; - } - else { - unsigned char straight[4]; - unsigned char *rrgb = (unsigned char *)ibuf->rect + (ibuf->x * y + x) * 4; - - premul_float_to_straight_uchar(straight, rgb); - rrgb[0] = straight[0]; - rrgb[1] = straight[1]; - rrgb[2] = straight[2]; - rrgb[3] = straight[3]; - } + if (is_torus) { + x %= ibuf->x; + if (x < 0) + x += ibuf->x; + y %= ibuf->y; + if (y < 0) + y += ibuf->y; + } + + if (ibuf->rect_float) { + float *rrgbf = ibuf->rect_float + (ibuf->x * y + x) * 4; + float map_alpha = (rgb[3] == 0.0f) ? rrgbf[3] : rrgbf[3] / rgb[3]; + + mul_v3_v3fl(rrgbf, rgb, map_alpha); + rrgbf[3] = rgb[3]; + } + else { + unsigned char straight[4]; + unsigned char *rrgb = (unsigned char *)ibuf->rect + (ibuf->x * y + x) * 4; + + premul_float_to_straight_uchar(straight, rgb); + rrgb[0] = straight[0]; + rrgb[1] = straight[1]; + rrgb[2] = straight[2]; + rrgb[3] = straight[3]; + } } static void paint_2d_ibuf_tile_convert(ImBuf *ibuf, int *x, int *y, short tile) { - if (tile & PAINT_TILE_X) { - *x %= ibuf->x; - if (*x < 0) *x += ibuf->x; - } - if (tile & PAINT_TILE_Y) { - *y %= ibuf->y; - if (*y < 0) *y += ibuf->y; - } + if (tile & PAINT_TILE_X) { + *x %= ibuf->x; + if (*x < 0) + *x += ibuf->x; + } + if (tile & PAINT_TILE_Y) { + *y %= ibuf->y; + if (*y < 0) + *y += ibuf->y; + } } - static float paint_2d_ibuf_add_if(ImBuf *ibuf, int x, int y, float *outrgb, short tile, float w) { - float inrgb[4]; - - if (tile) paint_2d_ibuf_tile_convert(ibuf, &x, &y, tile); - /* need to also do clipping here always since tiled coordinates - * are not always within bounds */ - if (x < ibuf->x && x >= 0 && y < ibuf->y && y >= 0) { - paint_2d_ibuf_rgb_get(ibuf, x, y, inrgb); - } - else return 0; - - mul_v4_fl(inrgb, w); - add_v4_v4(outrgb, inrgb); - - return w; + float inrgb[4]; + + if (tile) + paint_2d_ibuf_tile_convert(ibuf, &x, &y, tile); + /* need to also do clipping here always since tiled coordinates + * are not always within bounds */ + if (x < ibuf->x && x >= 0 && y < ibuf->y && y >= 0) { + paint_2d_ibuf_rgb_get(ibuf, x, y, inrgb); + } + else + return 0; + + mul_v4_fl(inrgb, w); + add_v4_v4(outrgb, inrgb); + + return w; } -static void paint_2d_lift_soften(ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, int *pos, const short tile) +static void paint_2d_lift_soften( + ImagePaintState *s, ImBuf *ibuf, ImBuf *ibufb, int *pos, const short tile) { - bool sharpen = (s->painter->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0)); - float threshold = s->brush->sharp_threshold; - int x, y, xi, yi, xo, yo, xk, yk; - float count; - int out_off[2], in_off[2], dim[2]; - int diff_pos[2]; - float outrgb[4]; - float rgba[4]; - BlurKernel *kernel = s->blurkernel; - - dim[0] = ibufb->x; - dim[1] = ibufb->y; - in_off[0] = pos[0]; - in_off[1] = pos[1]; - out_off[0] = out_off[1] = 0; - - if (!tile) { - IMB_rectclip( - ibuf, ibufb, &in_off[0], &in_off[1], &out_off[0], - &out_off[1], &dim[0], &dim[1]); - - if ((dim[0] == 0) || (dim[1] == 0)) - return; - } - - /* find offset inside mask buffers to sample them */ - sub_v2_v2v2_int(diff_pos, out_off, in_off); - - for (y = 0; y < dim[1]; y++) { - for (x = 0; x < dim[0]; x++) { - /* get input pixel */ - xi = in_off[0] + x; - yi = in_off[1] + y; - - count = 0.0; - if (tile) { - paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, tile); - if (xi < ibuf->x && xi >= 0 && yi < ibuf->y && yi >= 0) - paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba); - else - zero_v4(rgba); - } - else { - /* coordinates have been clipped properly here, it should be safe to do this */ - paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba); - } - zero_v4(outrgb); - - for (yk = 0; yk < kernel->side; yk++) { - for (xk = 0; xk < kernel->side; xk++) { - count += paint_2d_ibuf_add_if( - ibuf, xi + xk - kernel->pixel_len, - yi + yk - kernel->pixel_len, outrgb, tile, - kernel->wdata[xk + yk * kernel->side]); - } - } - - if (count > 0.0f) { - mul_v4_fl(outrgb, 1.0f / (float)count); - - if (sharpen) { - /* subtract blurred image from normal image gives high pass filter */ - sub_v3_v3v3(outrgb, rgba, outrgb); - - /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid - * colored speckles appearing in final image, and also to check for threshold */ - outrgb[0] = outrgb[1] = outrgb[2] = IMB_colormanagement_get_luminance(outrgb); - if (fabsf(outrgb[0]) > threshold) { - float mask = BKE_brush_alpha_get(s->scene, s->brush); - float alpha = rgba[3]; - rgba[3] = outrgb[3] = mask; - - /* add to enhance edges */ - blend_color_add_float(outrgb, rgba, outrgb); - outrgb[3] = alpha; - } - else - copy_v4_v4(outrgb, rgba); - } - } - else - copy_v4_v4(outrgb, rgba); - /* write into brush buffer */ - xo = out_off[0] + x; - yo = out_off[1] + y; - paint_2d_ibuf_rgb_set(ibufb, xo, yo, 0, outrgb); - } - } + bool sharpen = (s->painter->cache.invert ^ ((s->brush->flag & BRUSH_DIR_IN) != 0)); + float threshold = s->brush->sharp_threshold; + int x, y, xi, yi, xo, yo, xk, yk; + float count; + int out_off[2], in_off[2], dim[2]; + int diff_pos[2]; + float outrgb[4]; + float rgba[4]; + BlurKernel *kernel = s->blurkernel; + + dim[0] = ibufb->x; + dim[1] = ibufb->y; + in_off[0] = pos[0]; + in_off[1] = pos[1]; + out_off[0] = out_off[1] = 0; + + if (!tile) { + IMB_rectclip(ibuf, ibufb, &in_off[0], &in_off[1], &out_off[0], &out_off[1], &dim[0], &dim[1]); + + if ((dim[0] == 0) || (dim[1] == 0)) + return; + } + + /* find offset inside mask buffers to sample them */ + sub_v2_v2v2_int(diff_pos, out_off, in_off); + + for (y = 0; y < dim[1]; y++) { + for (x = 0; x < dim[0]; x++) { + /* get input pixel */ + xi = in_off[0] + x; + yi = in_off[1] + y; + + count = 0.0; + if (tile) { + paint_2d_ibuf_tile_convert(ibuf, &xi, &yi, tile); + if (xi < ibuf->x && xi >= 0 && yi < ibuf->y && yi >= 0) + paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba); + else + zero_v4(rgba); + } + else { + /* coordinates have been clipped properly here, it should be safe to do this */ + paint_2d_ibuf_rgb_get(ibuf, xi, yi, rgba); + } + zero_v4(outrgb); + + for (yk = 0; yk < kernel->side; yk++) { + for (xk = 0; xk < kernel->side; xk++) { + count += paint_2d_ibuf_add_if(ibuf, + xi + xk - kernel->pixel_len, + yi + yk - kernel->pixel_len, + outrgb, + tile, + kernel->wdata[xk + yk * kernel->side]); + } + } + + if (count > 0.0f) { + mul_v4_fl(outrgb, 1.0f / (float)count); + + if (sharpen) { + /* subtract blurred image from normal image gives high pass filter */ + sub_v3_v3v3(outrgb, rgba, outrgb); + + /* now rgba_ub contains the edge result, but this should be converted to luminance to avoid + * colored speckles appearing in final image, and also to check for threshold */ + outrgb[0] = outrgb[1] = outrgb[2] = IMB_colormanagement_get_luminance(outrgb); + if (fabsf(outrgb[0]) > threshold) { + float mask = BKE_brush_alpha_get(s->scene, s->brush); + float alpha = rgba[3]; + rgba[3] = outrgb[3] = mask; + + /* add to enhance edges */ + blend_color_add_float(outrgb, rgba, outrgb); + outrgb[3] = alpha; + } + else + copy_v4_v4(outrgb, rgba); + } + } + else + copy_v4_v4(outrgb, rgba); + /* write into brush buffer */ + xo = out_off[0] + x; + yo = out_off[1] + y; + paint_2d_ibuf_rgb_set(ibufb, xo, yo, 0, outrgb); + } + } } -static void paint_2d_set_region(ImagePaintRegion *region, int destx, int desty, int srcx, int srcy, int width, int height) +static void paint_2d_set_region( + ImagePaintRegion *region, int destx, int desty, int srcx, int srcy, int width, int height) { - region->destx = destx; - region->desty = desty; - region->srcx = srcx; - region->srcy = srcy; - region->width = width; - region->height = height; + region->destx = destx; + region->desty = desty; + region->srcx = srcx; + region->srcy = srcy; + region->width = width; + region->height = height; } -static int paint_2d_torus_split_region(ImagePaintRegion region[4], ImBuf *dbuf, ImBuf *sbuf, short tile) +static int paint_2d_torus_split_region(ImagePaintRegion region[4], + ImBuf *dbuf, + ImBuf *sbuf, + short tile) { - int destx = region->destx; - int desty = region->desty; - int srcx = region->srcx; - int srcy = region->srcy; - int width = region->width; - int height = region->height; - int origw, origh, w, h, tot = 0; - - /* convert destination and source coordinates to be within image */ - if (tile & PAINT_TILE_X) { - destx = destx % dbuf->x; - if (destx < 0) destx += dbuf->x; - srcx = srcx % sbuf->x; - if (srcx < 0) srcx += sbuf->x; - } - if (tile & PAINT_TILE_Y) { - desty = desty % dbuf->y; - if (desty < 0) desty += dbuf->y; - srcy = srcy % sbuf->y; - if (srcy < 0) srcy += sbuf->y; - } - /* clip width of blending area to destination imbuf, to avoid writing the - * same pixel twice */ - origw = w = (width > dbuf->x) ? dbuf->x : width; - origh = h = (height > dbuf->y) ? dbuf->y : height; - - /* clip within image */ - IMB_rectclip(dbuf, sbuf, &destx, &desty, &srcx, &srcy, &w, &h); - paint_2d_set_region(®ion[tot++], destx, desty, srcx, srcy, w, h); - - /* do 3 other rects if needed */ - if ((tile & PAINT_TILE_X) && w < origw) - paint_2d_set_region(®ion[tot++], (destx + w) % dbuf->x, desty, (srcx + w) % sbuf->x, srcy, origw - w, h); - if ((tile & PAINT_TILE_Y) && h < origh) - paint_2d_set_region(®ion[tot++], destx, (desty + h) % dbuf->y, srcx, (srcy + h) % sbuf->y, w, origh - h); - if ((tile & PAINT_TILE_X) && (tile & PAINT_TILE_Y) && (w < origw) && (h < origh)) - paint_2d_set_region(®ion[tot++], (destx + w) % dbuf->x, (desty + h) % dbuf->y, (srcx + w) % sbuf->x, (srcy + h) % sbuf->y, origw - w, origh - h); - - return tot; + int destx = region->destx; + int desty = region->desty; + int srcx = region->srcx; + int srcy = region->srcy; + int width = region->width; + int height = region->height; + int origw, origh, w, h, tot = 0; + + /* convert destination and source coordinates to be within image */ + if (tile & PAINT_TILE_X) { + destx = destx % dbuf->x; + if (destx < 0) + destx += dbuf->x; + srcx = srcx % sbuf->x; + if (srcx < 0) + srcx += sbuf->x; + } + if (tile & PAINT_TILE_Y) { + desty = desty % dbuf->y; + if (desty < 0) + desty += dbuf->y; + srcy = srcy % sbuf->y; + if (srcy < 0) + srcy += sbuf->y; + } + /* clip width of blending area to destination imbuf, to avoid writing the + * same pixel twice */ + origw = w = (width > dbuf->x) ? dbuf->x : width; + origh = h = (height > dbuf->y) ? dbuf->y : height; + + /* clip within image */ + IMB_rectclip(dbuf, sbuf, &destx, &desty, &srcx, &srcy, &w, &h); + paint_2d_set_region(®ion[tot++], destx, desty, srcx, srcy, w, h); + + /* do 3 other rects if needed */ + if ((tile & PAINT_TILE_X) && w < origw) + paint_2d_set_region( + ®ion[tot++], (destx + w) % dbuf->x, desty, (srcx + w) % sbuf->x, srcy, origw - w, h); + if ((tile & PAINT_TILE_Y) && h < origh) + paint_2d_set_region( + ®ion[tot++], destx, (desty + h) % dbuf->y, srcx, (srcy + h) % sbuf->y, w, origh - h); + if ((tile & PAINT_TILE_X) && (tile & PAINT_TILE_Y) && (w < origw) && (h < origh)) + paint_2d_set_region(®ion[tot++], + (destx + w) % dbuf->x, + (desty + h) % dbuf->y, + (srcx + w) % sbuf->x, + (srcy + h) % sbuf->y, + origw - w, + origh - h); + + return tot; } static void paint_2d_lift_smear(ImBuf *ibuf, ImBuf *ibufb, int *pos, short tile) { - ImagePaintRegion region[4]; - int a, tot; - - paint_2d_set_region(region, 0, 0, pos[0], pos[1], ibufb->x, ibufb->y); - tot = paint_2d_torus_split_region(region, ibufb, ibuf, tile); - - for (a = 0; a < tot; a++) - IMB_rectblend( - ibufb, ibufb, ibuf, NULL, NULL, 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, false); + ImagePaintRegion region[4]; + int a, tot; + + paint_2d_set_region(region, 0, 0, pos[0], pos[1], ibufb->x, ibufb->y); + tot = paint_2d_torus_split_region(region, ibufb, ibuf, tile); + + for (a = 0; a < tot; a++) + IMB_rectblend(ibufb, + ibufb, + ibuf, + NULL, + NULL, + 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, + false); } static ImBuf *paint_2d_lift_clone(ImBuf *ibuf, ImBuf *ibufb, int *pos) { - /* note: allocImbuf returns zero'd memory, so regions outside image will - * have zero alpha, and hence not be blended onto the image */ - int w = ibufb->x, h = ibufb->y, destx = 0, desty = 0, srcx = pos[0], srcy = pos[1]; - ImBuf *clonebuf = IMB_allocImBuf(w, h, ibufb->planes, ibufb->flags); - - IMB_rectclip(clonebuf, ibuf, &destx, &desty, &srcx, &srcy, &w, &h); - IMB_rectblend( - clonebuf, clonebuf, ibufb, NULL, NULL, NULL, 0, destx, desty, destx, desty, destx, desty, w, h, - IMB_BLEND_COPY_ALPHA, false); - IMB_rectblend( - clonebuf, clonebuf, ibuf, NULL, NULL, NULL, 0, destx, desty, destx, desty, srcx, srcy, w, h, - IMB_BLEND_COPY_RGB, false); - - return clonebuf; + /* note: allocImbuf returns zero'd memory, so regions outside image will + * have zero alpha, and hence not be blended onto the image */ + int w = ibufb->x, h = ibufb->y, destx = 0, desty = 0, srcx = pos[0], srcy = pos[1]; + ImBuf *clonebuf = IMB_allocImBuf(w, h, ibufb->planes, ibufb->flags); + + IMB_rectclip(clonebuf, ibuf, &destx, &desty, &srcx, &srcy, &w, &h); + IMB_rectblend(clonebuf, + clonebuf, + ibufb, + NULL, + NULL, + NULL, + 0, + destx, + desty, + destx, + desty, + destx, + desty, + w, + h, + IMB_BLEND_COPY_ALPHA, + false); + IMB_rectblend(clonebuf, + clonebuf, + ibuf, + NULL, + NULL, + NULL, + 0, + destx, + desty, + destx, + desty, + srcx, + srcy, + w, + h, + IMB_BLEND_COPY_RGB, + false); + + return clonebuf; } static void paint_2d_convert_brushco(ImBuf *ibufb, const float pos[2], int ipos[2]) { - ipos[0] = (int)floorf((pos[0] - ibufb->x / 2)); - ipos[1] = (int)floorf((pos[1] - ibufb->y / 2)); + ipos[0] = (int)floorf((pos[0] - ibufb->x / 2)); + ipos[1] = (int)floorf((pos[1] - ibufb->y / 2)); } -static void paint_2d_do_making_brush( - ImagePaintState *s, - ImagePaintRegion *region, - unsigned short *curveb, - unsigned short *texmaskb, - ImBuf *frombuf, - float mask_max, - short blend, - int tilex, int tiley, - int tilew, int tileh) +static void paint_2d_do_making_brush(ImagePaintState *s, + ImagePaintRegion *region, + unsigned short *curveb, + unsigned short *texmaskb, + ImBuf *frombuf, + float mask_max, + short blend, + int tilex, + int tiley, + int tilew, + int tileh) { - ImBuf tmpbuf; - IMB_initImBuf(&tmpbuf, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0); - - ListBase *undo_tiles = ED_image_undo_get_tiles(); - - for (int ty = tiley; ty <= tileh; ty++) { - for (int tx = tilex; tx <= tilew; tx++) { - /* retrieve original pixels + mask from undo buffer */ - unsigned short *mask; - int origx = region->destx - tx * IMAPAINT_TILE_SIZE; - int origy = region->desty - ty * IMAPAINT_TILE_SIZE; - - if (s->canvas->rect_float) - tmpbuf.rect_float = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false); - else - tmpbuf.rect = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false); - - IMB_rectblend( - s->canvas, &tmpbuf, frombuf, mask, - curveb, texmaskb, mask_max, - region->destx, region->desty, - origx, origy, - region->srcx, region->srcy, - region->width, region->height, - blend, ((s->brush->flag & BRUSH_ACCUMULATE) != 0)); - } - } + ImBuf tmpbuf; + IMB_initImBuf(&tmpbuf, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, 0); + + ListBase *undo_tiles = ED_image_undo_get_tiles(); + + for (int ty = tiley; ty <= tileh; ty++) { + for (int tx = tilex; tx <= tilew; tx++) { + /* retrieve original pixels + mask from undo buffer */ + unsigned short *mask; + int origx = region->destx - tx * IMAPAINT_TILE_SIZE; + int origy = region->desty - ty * IMAPAINT_TILE_SIZE; + + if (s->canvas->rect_float) + tmpbuf.rect_float = image_undo_find_tile( + undo_tiles, s->image, s->canvas, tx, ty, &mask, false); + else + tmpbuf.rect = image_undo_find_tile(undo_tiles, s->image, s->canvas, tx, ty, &mask, false); + + IMB_rectblend(s->canvas, + &tmpbuf, + frombuf, + mask, + curveb, + texmaskb, + mask_max, + region->destx, + region->desty, + origx, + origy, + region->srcx, + region->srcy, + region->width, + region->height, + blend, + ((s->brush->flag & BRUSH_ACCUMULATE) != 0)); + } + } } typedef struct Paint2DForeachData { - ImagePaintState *s; - ImagePaintRegion *region; - unsigned short *curveb; - unsigned short *texmaskb; - ImBuf *frombuf; - float mask_max; - short blend; - int tilex; - int tilew; + ImagePaintState *s; + ImagePaintRegion *region; + unsigned short *curveb; + unsigned short *texmaskb; + ImBuf *frombuf; + float mask_max; + short blend; + int tilex; + int tilew; } Paint2DForeachData; -static void paint_2d_op_foreach_do( - void *__restrict data_v, - const int iter, - const ParallelRangeTLS *__restrict UNUSED(tls)) +static void paint_2d_op_foreach_do(void *__restrict data_v, + const int iter, + const ParallelRangeTLS *__restrict UNUSED(tls)) { - Paint2DForeachData *data = (Paint2DForeachData *)data_v; - paint_2d_do_making_brush( - data->s, data->region, data->curveb, - data->texmaskb, data->frombuf, data->mask_max, - data->blend, - data->tilex, iter, - data->tilew, iter); + Paint2DForeachData *data = (Paint2DForeachData *)data_v; + paint_2d_do_making_brush(data->s, + data->region, + data->curveb, + data->texmaskb, + data->frombuf, + data->mask_max, + data->blend, + data->tilex, + iter, + data->tilew, + iter); } -static int paint_2d_op(void *state, ImBuf *ibufb, unsigned short *curveb, unsigned short *texmaskb, const float lastpos[2], const float pos[2]) +static int paint_2d_op(void *state, + ImBuf *ibufb, + unsigned short *curveb, + unsigned short *texmaskb, + const float lastpos[2], + const float pos[2]) { - ImagePaintState *s = ((ImagePaintState *)state); - ImBuf *clonebuf = NULL, *frombuf; - ImagePaintRegion region[4]; - short tile = s->symmetry & (PAINT_TILE_X | PAINT_TILE_Y); - short blend = s->blend; - const float *offset = s->brush->clone.offset; - float liftpos[2]; - float mask_max = BKE_brush_alpha_get(s->scene, s->brush); - int bpos[2], blastpos[2], bliftpos[2]; - int a, tot; - - paint_2d_convert_brushco(ibufb, pos, bpos); - - /* lift from canvas */ - if (s->tool == PAINT_TOOL_SOFTEN) { - paint_2d_lift_soften(s, s->canvas, ibufb, bpos, tile); - blend = IMB_BLEND_INTERPOLATE; - } - else if (s->tool == PAINT_TOOL_SMEAR) { - if (lastpos[0] == pos[0] && lastpos[1] == pos[1]) - return 0; - - paint_2d_convert_brushco(ibufb, lastpos, blastpos); - paint_2d_lift_smear(s->canvas, ibufb, blastpos, tile); - blend = IMB_BLEND_INTERPOLATE; - } - else if (s->tool == PAINT_TOOL_CLONE && s->clonecanvas) { - liftpos[0] = pos[0] - offset[0] * s->canvas->x; - liftpos[1] = pos[1] - offset[1] * s->canvas->y; - - paint_2d_convert_brushco(ibufb, liftpos, bliftpos); - clonebuf = paint_2d_lift_clone(s->clonecanvas, ibufb, bliftpos); - } - - frombuf = (clonebuf) ? clonebuf : ibufb; - - if (tile) { - paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); - tot = paint_2d_torus_split_region(region, s->canvas, frombuf, tile); - } - else { - paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); - tot = 1; - } - - /* blend into canvas */ - for (a = 0; a < tot; a++) { - ED_imapaint_dirty_region( - s->image, s->canvas, - region[a].destx, region[a].desty, - region[a].width, region[a].height, true); - - if (s->do_masking) { - /* masking, find original pixels tiles from undo buffer to composite over */ - int tilex, tiley, tilew, tileh; - - imapaint_region_tiles( - s->canvas, region[a].destx, region[a].desty, - region[a].width, region[a].height, - &tilex, &tiley, &tilew, &tileh); - - if (tiley == tileh) { - paint_2d_do_making_brush( - s, ®ion[a], curveb, texmaskb, frombuf, - mask_max, blend, tilex, tiley, tilew, tileh); - } - else { - Paint2DForeachData data; - data.s = s; - data.region = ®ion[a]; - data.curveb = curveb; - data.texmaskb = texmaskb; - data.frombuf = frombuf; - data.mask_max = mask_max; - data.blend = blend; - data.tilex = tilex; - data.tilew = tilew; - - ParallelRangeSettings settings; - BLI_parallel_range_settings_defaults(&settings); - BLI_task_parallel_range( - tiley, tileh + 1, &data, - paint_2d_op_foreach_do, - &settings); - - } - } - else { - /* no masking, composite brush directly onto canvas */ - IMB_rectblend_threaded( - s->canvas, s->canvas, frombuf, NULL, curveb, texmaskb, mask_max, - 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, false); - } - } - - if (clonebuf) IMB_freeImBuf(clonebuf); - - return 1; + ImagePaintState *s = ((ImagePaintState *)state); + ImBuf *clonebuf = NULL, *frombuf; + ImagePaintRegion region[4]; + short tile = s->symmetry & (PAINT_TILE_X | PAINT_TILE_Y); + short blend = s->blend; + const float *offset = s->brush->clone.offset; + float liftpos[2]; + float mask_max = BKE_brush_alpha_get(s->scene, s->brush); + int bpos[2], blastpos[2], bliftpos[2]; + int a, tot; + + paint_2d_convert_brushco(ibufb, pos, bpos); + + /* lift from canvas */ + if (s->tool == PAINT_TOOL_SOFTEN) { + paint_2d_lift_soften(s, s->canvas, ibufb, bpos, tile); + blend = IMB_BLEND_INTERPOLATE; + } + else if (s->tool == PAINT_TOOL_SMEAR) { + if (lastpos[0] == pos[0] && lastpos[1] == pos[1]) + return 0; + + paint_2d_convert_brushco(ibufb, lastpos, blastpos); + paint_2d_lift_smear(s->canvas, ibufb, blastpos, tile); + blend = IMB_BLEND_INTERPOLATE; + } + else if (s->tool == PAINT_TOOL_CLONE && s->clonecanvas) { + liftpos[0] = pos[0] - offset[0] * s->canvas->x; + liftpos[1] = pos[1] - offset[1] * s->canvas->y; + + paint_2d_convert_brushco(ibufb, liftpos, bliftpos); + clonebuf = paint_2d_lift_clone(s->clonecanvas, ibufb, bliftpos); + } + + frombuf = (clonebuf) ? clonebuf : ibufb; + + if (tile) { + paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); + tot = paint_2d_torus_split_region(region, s->canvas, frombuf, tile); + } + else { + paint_2d_set_region(region, bpos[0], bpos[1], 0, 0, frombuf->x, frombuf->y); + tot = 1; + } + + /* blend into canvas */ + for (a = 0; a < tot; a++) { + ED_imapaint_dirty_region(s->image, + s->canvas, + region[a].destx, + region[a].desty, + region[a].width, + region[a].height, + true); + + if (s->do_masking) { + /* masking, find original pixels tiles from undo buffer to composite over */ + int tilex, tiley, tilew, tileh; + + imapaint_region_tiles(s->canvas, + region[a].destx, + region[a].desty, + region[a].width, + region[a].height, + &tilex, + &tiley, + &tilew, + &tileh); + + if (tiley == tileh) { + paint_2d_do_making_brush( + s, ®ion[a], curveb, texmaskb, frombuf, mask_max, blend, tilex, tiley, tilew, tileh); + } + else { + Paint2DForeachData data; + data.s = s; + data.region = ®ion[a]; + data.curveb = curveb; + data.texmaskb = texmaskb; + data.frombuf = frombuf; + data.mask_max = mask_max; + data.blend = blend; + data.tilex = tilex; + data.tilew = tilew; + + ParallelRangeSettings settings; + BLI_parallel_range_settings_defaults(&settings); + BLI_task_parallel_range(tiley, tileh + 1, &data, paint_2d_op_foreach_do, &settings); + } + } + else { + /* no masking, composite brush directly onto canvas */ + IMB_rectblend_threaded(s->canvas, + s->canvas, + frombuf, + NULL, + curveb, + texmaskb, + mask_max, + 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, + false); + } + } + + if (clonebuf) + IMB_freeImBuf(clonebuf); + + return 1; } - static int paint_2d_canvas_set(ImagePaintState *s, Image *ima) { - ImBuf *ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL); - - /* verify that we can paint and set canvas */ - if (ima == NULL) { - return 0; - } - else if (BKE_image_has_packedfile(ima) && ima->rr) { - s->warnpackedfile = ima->id.name + 2; - return 0; - } - else if (ibuf && ibuf->channels != 4) { - s->warnmultifile = ima->id.name + 2; - return 0; - } - else if (!ibuf || !(ibuf->rect || ibuf->rect_float)) - return 0; - - s->image = ima; - s->canvas = ibuf; - - /* set clone canvas */ - if (s->tool == PAINT_TOOL_CLONE) { - ima = s->brush->clone.image; - ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL); - - if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) { - BKE_image_release_ibuf(ima, ibuf, NULL); - BKE_image_release_ibuf(s->image, s->canvas, NULL); - return 0; - } - - s->clonecanvas = ibuf; - - /* temporarily add float rect for cloning */ - if (s->canvas->rect_float && !s->clonecanvas->rect_float) { - IMB_float_from_rect(s->clonecanvas); - } - else if (!s->canvas->rect_float && !s->clonecanvas->rect) - IMB_rect_from_float(s->clonecanvas); - } - - /* set masking */ - s->do_masking = paint_use_opacity_masking(s->brush); - - return 1; + ImBuf *ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL); + + /* verify that we can paint and set canvas */ + if (ima == NULL) { + return 0; + } + else if (BKE_image_has_packedfile(ima) && ima->rr) { + s->warnpackedfile = ima->id.name + 2; + return 0; + } + else if (ibuf && ibuf->channels != 4) { + s->warnmultifile = ima->id.name + 2; + return 0; + } + else if (!ibuf || !(ibuf->rect || ibuf->rect_float)) + return 0; + + s->image = ima; + s->canvas = ibuf; + + /* set clone canvas */ + if (s->tool == PAINT_TOOL_CLONE) { + ima = s->brush->clone.image; + ibuf = BKE_image_acquire_ibuf(ima, s->sima ? &s->sima->iuser : NULL, NULL); + + if (!ima || !ibuf || !(ibuf->rect || ibuf->rect_float)) { + BKE_image_release_ibuf(ima, ibuf, NULL); + BKE_image_release_ibuf(s->image, s->canvas, NULL); + return 0; + } + + s->clonecanvas = ibuf; + + /* temporarily add float rect for cloning */ + if (s->canvas->rect_float && !s->clonecanvas->rect_float) { + IMB_float_from_rect(s->clonecanvas); + } + else if (!s->canvas->rect_float && !s->clonecanvas->rect) + IMB_rect_from_float(s->clonecanvas); + } + + /* set masking */ + s->do_masking = paint_use_opacity_masking(s->brush); + + return 1; } 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); + BKE_image_release_ibuf(s->image, s->canvas, NULL); + BKE_image_release_ibuf(s->brush->clone.image, s->clonecanvas, NULL); - if (s->blurkernel) { - paint_delete_blur_kernel(s->blurkernel); - MEM_freeN(s->blurkernel); - } + if (s->blurkernel) { + paint_delete_blur_kernel(s->blurkernel); + MEM_freeN(s->blurkernel); + } - image_undo_remove_masks(); + image_undo_remove_masks(); } -void paint_2d_stroke(void *ps, const float prev_mval[2], const float mval[2], const bool eraser, float pressure, float distance, float size) +void paint_2d_stroke(void *ps, + const float prev_mval[2], + const float mval[2], + const bool eraser, + float pressure, + float distance, + float size) { - float newuv[2], olduv[2]; - ImagePaintState *s = ps; - BrushPainter *painter = s->painter; - ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL); - const bool is_data = (ibuf && ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA); + float newuv[2], olduv[2]; + ImagePaintState *s = ps; + BrushPainter *painter = s->painter; + ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL); + const bool is_data = (ibuf && ibuf->colormanage_flag & IMB_COLORMANAGE_IS_DATA); - if (!ibuf) - return; + if (!ibuf) + return; - s->blend = s->brush->blend; - if (eraser) - s->blend = IMB_BLEND_ERASE_ALPHA; + s->blend = s->brush->blend; + if (eraser) + s->blend = IMB_BLEND_ERASE_ALPHA; - UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &newuv[0], &newuv[1]); - UI_view2d_region_to_view(s->v2d, prev_mval[0], prev_mval[1], &olduv[0], &olduv[1]); + UI_view2d_region_to_view(s->v2d, mval[0], mval[1], &newuv[0], &newuv[1]); + UI_view2d_region_to_view(s->v2d, prev_mval[0], prev_mval[1], &olduv[0], &olduv[1]); - newuv[0] *= ibuf->x; - newuv[1] *= ibuf->y; + newuv[0] *= ibuf->x; + newuv[1] *= ibuf->y; - olduv[0] *= ibuf->x; - olduv[1] *= ibuf->y; + olduv[0] *= ibuf->x; + olduv[1] *= ibuf->y; - if (painter->firsttouch) { - float startuv[2]; + if (painter->firsttouch) { + float startuv[2]; - UI_view2d_region_to_view(s->v2d, 0, 0, &startuv[0], &startuv[1]); + UI_view2d_region_to_view(s->v2d, 0, 0, &startuv[0], &startuv[1]); - /* paint exactly once on first touch */ - painter->startpaintpos[0] = startuv[0] * ibuf->x; - painter->startpaintpos[1] = startuv[1] * ibuf->y; + /* paint exactly once on first touch */ + painter->startpaintpos[0] = startuv[0] * ibuf->x; + painter->startpaintpos[1] = startuv[1] * ibuf->y; - painter->firsttouch = 0; - copy_v2_v2(painter->lastpaintpos, newuv); - } - else { - copy_v2_v2(painter->lastpaintpos, olduv); - } + painter->firsttouch = 0; + copy_v2_v2(painter->lastpaintpos, newuv); + } + else { + copy_v2_v2(painter->lastpaintpos, olduv); + } - /* OCIO_TODO: float buffers are now always linear, so always use color correction - * this should probably be changed when texture painting color space is supported - */ - brush_painter_2d_require_imbuf(painter, (ibuf->rect_float != NULL), !is_data); + /* OCIO_TODO: float buffers are now always linear, so always use color correction + * this should probably be changed when texture painting color space is supported + */ + brush_painter_2d_require_imbuf(painter, (ibuf->rect_float != NULL), !is_data); - brush_painter_2d_refresh_cache(s, painter, newuv, mval, pressure, distance, size); + brush_painter_2d_refresh_cache(s, painter, newuv, mval, pressure, distance, size); - if (paint_2d_op(s, painter->cache.ibuf, painter->cache.curve_mask, painter->cache.tex_mask, olduv, newuv)) - s->need_redraw = true; + if (paint_2d_op(s, + painter->cache.ibuf, + painter->cache.curve_mask, + painter->cache.tex_mask, + olduv, + newuv)) + s->need_redraw = true; - BKE_image_release_ibuf(s->image, ibuf, NULL); + BKE_image_release_ibuf(s->image, ibuf, NULL); } void *paint_2d_new_stroke(bContext *C, wmOperator *op, int mode) { - Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; - Brush *brush = BKE_paint_brush(&settings->imapaint.paint); + Scene *scene = CTX_data_scene(C); + ToolSettings *settings = scene->toolsettings; + Brush *brush = BKE_paint_brush(&settings->imapaint.paint); - ImagePaintState *s = MEM_callocN(sizeof(ImagePaintState), "ImagePaintState"); + ImagePaintState *s = MEM_callocN(sizeof(ImagePaintState), "ImagePaintState"); - s->sima = CTX_wm_space_image(C); - s->v2d = &CTX_wm_region(C)->v2d; - s->scene = scene; - s->screen = CTX_wm_screen(C); + s->sima = CTX_wm_space_image(C); + s->v2d = &CTX_wm_region(C)->v2d; + s->scene = scene; + s->screen = CTX_wm_screen(C); - s->brush = brush; - s->tool = brush->imagepaint_tool; - s->blend = brush->blend; + s->brush = brush; + s->tool = brush->imagepaint_tool; + s->blend = brush->blend; - s->image = s->sima->image; - s->symmetry = settings->imapaint.paint.symmetry_flags; + s->image = s->sima->image; + s->symmetry = settings->imapaint.paint.symmetry_flags; - if (!paint_2d_canvas_set(s, s->image)) { - if (s->warnmultifile) - BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint"); - if (s->warnpackedfile) - BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted"); + if (!paint_2d_canvas_set(s, s->image)) { + if (s->warnmultifile) + BKE_report(op->reports, RPT_WARNING, "Image requires 4 color channels to paint"); + if (s->warnpackedfile) + BKE_report(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted"); - MEM_freeN(s); - return NULL; - } + MEM_freeN(s); + return NULL; + } - if (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) { - s->blurkernel = paint_new_blur_kernel(brush, false); - } + if (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) { + s->blurkernel = paint_new_blur_kernel(brush, false); + } - paint_brush_init_tex(s->brush); + paint_brush_init_tex(s->brush); - /* create painter */ - s->painter = brush_painter_2d_new(scene, s->brush, mode == BRUSH_STROKE_INVERT); + /* create painter */ + s->painter = brush_painter_2d_new(scene, s->brush, mode == BRUSH_STROKE_INVERT); - return s; + return s; } void paint_2d_redraw(const bContext *C, void *ps, bool final) { - ImagePaintState *s = ps; - - if (s->need_redraw) { - ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL); - - imapaint_image_update(s->sima, s->image, ibuf, false); - ED_imapaint_clear_partial_redraw(); - - BKE_image_release_ibuf(s->image, ibuf, NULL); - - s->need_redraw = false; - } - else if (!final) { - return; - } - - if (final) { - if (s->image && !(s->sima && s->sima->lock)) - GPU_free_image(s->image); - - /* compositor listener deals with updating */ - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image); - DEG_id_tag_update(&s->image->id, 0); - } - else { - if (!s->sima || !s->sima->lock) - ED_region_tag_redraw(CTX_wm_region(C)); - else - WM_event_add_notifier(C, NC_IMAGE | NA_PAINTING, s->image); - } + ImagePaintState *s = ps; + + if (s->need_redraw) { + ImBuf *ibuf = BKE_image_acquire_ibuf(s->image, s->sima ? &s->sima->iuser : NULL, NULL); + + imapaint_image_update(s->sima, s->image, ibuf, false); + ED_imapaint_clear_partial_redraw(); + + BKE_image_release_ibuf(s->image, ibuf, NULL); + + s->need_redraw = false; + } + else if (!final) { + return; + } + + if (final) { + if (s->image && !(s->sima && s->sima->lock)) + GPU_free_image(s->image); + + /* compositor listener deals with updating */ + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, s->image); + DEG_id_tag_update(&s->image->id, 0); + } + else { + if (!s->sima || !s->sima->lock) + ED_region_tag_redraw(CTX_wm_region(C)); + else + WM_event_add_notifier(C, NC_IMAGE | NA_PAINTING, s->image); + } } void paint_2d_stroke_done(void *ps) { - ImagePaintState *s = ps; + ImagePaintState *s = ps; - paint_2d_canvas_free(s); - brush_painter_2d_free(s->painter); - paint_brush_exit_tex(s->brush); + paint_2d_canvas_free(s); + brush_painter_2d_free(s->painter); + paint_brush_exit_tex(s->brush); - MEM_freeN(s); + MEM_freeN(s); } -static void paint_2d_fill_add_pixel_byte( - const int x_px, const int y_px, ImBuf *ibuf, BLI_Stack *stack, BLI_bitmap *touched, - const float color[4], float threshold_sq) +static void paint_2d_fill_add_pixel_byte(const int x_px, + const int y_px, + ImBuf *ibuf, + BLI_Stack *stack, + BLI_bitmap *touched, + const float color[4], + float threshold_sq) { - size_t coordinate; + size_t coordinate; - if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) - return; + if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) + return; - coordinate = ((size_t)y_px) * ibuf->x + x_px; + coordinate = ((size_t)y_px) * ibuf->x + x_px; - if (!BLI_BITMAP_TEST(touched, coordinate)) { - float color_f[4]; - unsigned char *color_b = (unsigned char *)(ibuf->rect + coordinate); - rgba_uchar_to_float(color_f, color_b); - straight_to_premul_v4(color_f); + if (!BLI_BITMAP_TEST(touched, coordinate)) { + float color_f[4]; + unsigned char *color_b = (unsigned char *)(ibuf->rect + coordinate); + rgba_uchar_to_float(color_f, color_b); + straight_to_premul_v4(color_f); - if (len_squared_v4v4(color_f, color) <= threshold_sq) { - BLI_stack_push(stack, &coordinate); - } - BLI_BITMAP_SET(touched, coordinate, true); - } + if (len_squared_v4v4(color_f, color) <= threshold_sq) { + BLI_stack_push(stack, &coordinate); + } + BLI_BITMAP_SET(touched, coordinate, true); + } } -static void paint_2d_fill_add_pixel_float( - const int x_px, const int y_px, ImBuf *ibuf, BLI_Stack *stack, BLI_bitmap *touched, - const float color[4], float threshold_sq) +static void paint_2d_fill_add_pixel_float(const int x_px, + const int y_px, + ImBuf *ibuf, + BLI_Stack *stack, + BLI_bitmap *touched, + const float color[4], + float threshold_sq) { - size_t coordinate; + size_t coordinate; - if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) - return; + if (x_px >= ibuf->x || x_px < 0 || y_px >= ibuf->y || y_px < 0) + return; - coordinate = ((size_t)y_px) * ibuf->x + x_px; + coordinate = ((size_t)y_px) * ibuf->x + x_px; - if (!BLI_BITMAP_TEST(touched, coordinate)) { - if (len_squared_v4v4(ibuf->rect_float + 4 * coordinate, color) <= threshold_sq) { - BLI_stack_push(stack, &coordinate); - } - BLI_BITMAP_SET(touched, coordinate, true); - } + if (!BLI_BITMAP_TEST(touched, coordinate)) { + if (len_squared_v4v4(ibuf->rect_float + 4 * coordinate, color) <= threshold_sq) { + BLI_stack_push(stack, &coordinate); + } + BLI_BITMAP_SET(touched, coordinate, true); + } } /* this function expects linear space color values */ void paint_2d_bucket_fill( - const bContext *C, const float color[3], Brush *br, - const float mouse_init[2], - void *ps) + const bContext *C, const float color[3], Brush *br, const float mouse_init[2], void *ps) { - SpaceImage *sima = CTX_wm_space_image(C); - Image *ima = sima->image; - - ImagePaintState *s = ps; - - ImBuf *ibuf; - int x_px, y_px; - unsigned int color_b; - float color_f[4]; - float strength = br ? br->alpha : 1.0f; - - bool do_float; - - if (!ima) - return; - - ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); - - if (!ibuf) - return; - - do_float = (ibuf->rect_float != NULL); - /* first check if our image is float. If it is not we should correct the color to - * be in gamma space. strictly speaking this is not correct, but blender does not paint - * byte images in linear space */ - if (!do_float) { - linearrgb_to_srgb_uchar3((unsigned char *)&color_b, color); - *(((char *)&color_b) + 3) = strength * 255; - } - else { - copy_v3_v3(color_f, color); - color_f[3] = strength; - } - - if (!mouse_init || !br) { - /* first case, no image UV, fill the whole image */ - ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); - - if (do_float) { - for (x_px = 0; x_px < ibuf->x; x_px++) { - for (y_px = 0; y_px < ibuf->y; y_px++) { - blend_color_mix_float( - ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), - ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), color_f); - } - } - } - else { - for (x_px = 0; x_px < ibuf->x; x_px++) { - for (y_px = 0; y_px < ibuf->y; y_px++) { - blend_color_mix_byte( - (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), - (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), (unsigned char *)&color_b); - } - } - } - } - else { - /* second case, start sweeping the neighboring pixels, looking for pixels whose - * value is within the brush fill threshold from the fill color */ - BLI_Stack *stack; - BLI_bitmap *touched; - size_t coordinate; - int width = ibuf->x; - float image_init[2]; - int minx = ibuf->x, miny = ibuf->y, maxx = 0, maxy = 0; - float pixel_color[4]; - /* We are comparing to sum of three squared values - * (assumed in range [0,1]), so need to multiply... */ - float threshold_sq = br->fill_threshold * br->fill_threshold * 3; - - UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); - - x_px = image_init[0] * ibuf->x; - y_px = image_init[1] * ibuf->y; - - if (x_px >= ibuf->x || x_px < 0 || y_px > ibuf->y || y_px < 0) { - BKE_image_release_ibuf(ima, ibuf, NULL); - return; - } - - /* change image invalidation method later */ - ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); - - stack = BLI_stack_new(sizeof(size_t), __func__); - touched = BLI_BITMAP_NEW(((size_t)ibuf->x) * ibuf->y, "bucket_fill_bitmap"); - - coordinate = (((size_t)y_px) * ibuf->x + x_px); - - if (do_float) { - copy_v4_v4(pixel_color, ibuf->rect_float + 4 * coordinate); - } - else { - int pixel_color_b = *(ibuf->rect + coordinate); - rgba_uchar_to_float(pixel_color, (unsigned char *)&pixel_color_b); - straight_to_premul_v4(pixel_color); - } - - BLI_stack_push(stack, &coordinate); - BLI_BITMAP_SET(touched, coordinate, true); - - if (do_float) { - while (!BLI_stack_is_empty(stack)) { - BLI_stack_pop(stack, &coordinate); - - IMB_blend_color_float( - ibuf->rect_float + 4 * (coordinate), - ibuf->rect_float + 4 * (coordinate), - color_f, br->blend); - - /* reconstruct the coordinates here */ - x_px = coordinate % width; - y_px = coordinate / width; - - paint_2d_fill_add_pixel_float(x_px - 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_float(x_px - 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_float(x_px - 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_float(x_px, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_float(x_px, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_float(x_px + 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_float(x_px + 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_float(x_px + 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); - - if (x_px > maxx) - maxx = x_px; - if (x_px < minx) - minx = x_px; - if (y_px > maxy) - maxy = y_px; - if (x_px > miny) - miny = y_px; - } - } - else { - while (!BLI_stack_is_empty(stack)) { - BLI_stack_pop(stack, &coordinate); - - IMB_blend_color_byte( - (unsigned char *)(ibuf->rect + coordinate), - (unsigned char *)(ibuf->rect + coordinate), - (unsigned char *)&color_b, br->blend); - - /* reconstruct the coordinates here */ - x_px = coordinate % width; - y_px = coordinate / width; - - paint_2d_fill_add_pixel_byte(x_px - 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_byte(x_px - 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_byte(x_px - 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_byte(x_px, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_byte(x_px, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_byte(x_px + 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_byte(x_px + 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); - paint_2d_fill_add_pixel_byte(x_px + 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); - - if (x_px > maxx) - maxx = x_px; - if (x_px < minx) - minx = x_px; - if (y_px > maxy) - maxy = y_px; - if (x_px > miny) - miny = y_px; - } - } - - MEM_freeN(touched); - BLI_stack_free(stack); - } - - imapaint_image_update(sima, ima, ibuf, false); - ED_imapaint_clear_partial_redraw(); - - BKE_image_release_ibuf(ima, ibuf, NULL); - - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = sima->image; + + ImagePaintState *s = ps; + + ImBuf *ibuf; + int x_px, y_px; + unsigned int color_b; + float color_f[4]; + float strength = br ? br->alpha : 1.0f; + + bool do_float; + + if (!ima) + return; + + ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); + + if (!ibuf) + return; + + do_float = (ibuf->rect_float != NULL); + /* first check if our image is float. If it is not we should correct the color to + * be in gamma space. strictly speaking this is not correct, but blender does not paint + * byte images in linear space */ + if (!do_float) { + linearrgb_to_srgb_uchar3((unsigned char *)&color_b, color); + *(((char *)&color_b) + 3) = strength * 255; + } + else { + copy_v3_v3(color_f, color); + color_f[3] = strength; + } + + if (!mouse_init || !br) { + /* first case, no image UV, fill the whole image */ + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); + + if (do_float) { + for (x_px = 0; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + blend_color_mix_float(ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), + ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), + color_f); + } + } + } + else { + for (x_px = 0; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + blend_color_mix_byte((unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), + (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), + (unsigned char *)&color_b); + } + } + } + } + else { + /* second case, start sweeping the neighboring pixels, looking for pixels whose + * value is within the brush fill threshold from the fill color */ + BLI_Stack *stack; + BLI_bitmap *touched; + size_t coordinate; + int width = ibuf->x; + float image_init[2]; + int minx = ibuf->x, miny = ibuf->y, maxx = 0, maxy = 0; + float pixel_color[4]; + /* We are comparing to sum of three squared values + * (assumed in range [0,1]), so need to multiply... */ + float threshold_sq = br->fill_threshold * br->fill_threshold * 3; + + UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); + + x_px = image_init[0] * ibuf->x; + y_px = image_init[1] * ibuf->y; + + if (x_px >= ibuf->x || x_px < 0 || y_px > ibuf->y || y_px < 0) { + BKE_image_release_ibuf(ima, ibuf, NULL); + return; + } + + /* change image invalidation method later */ + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); + + stack = BLI_stack_new(sizeof(size_t), __func__); + touched = BLI_BITMAP_NEW(((size_t)ibuf->x) * ibuf->y, "bucket_fill_bitmap"); + + coordinate = (((size_t)y_px) * ibuf->x + x_px); + + if (do_float) { + copy_v4_v4(pixel_color, ibuf->rect_float + 4 * coordinate); + } + else { + int pixel_color_b = *(ibuf->rect + coordinate); + rgba_uchar_to_float(pixel_color, (unsigned char *)&pixel_color_b); + straight_to_premul_v4(pixel_color); + } + + BLI_stack_push(stack, &coordinate); + BLI_BITMAP_SET(touched, coordinate, true); + + if (do_float) { + while (!BLI_stack_is_empty(stack)) { + BLI_stack_pop(stack, &coordinate); + + IMB_blend_color_float(ibuf->rect_float + 4 * (coordinate), + ibuf->rect_float + 4 * (coordinate), + color_f, + br->blend); + + /* reconstruct the coordinates here */ + x_px = coordinate % width; + y_px = coordinate / width; + + paint_2d_fill_add_pixel_float( + x_px - 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float( + x_px - 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float( + x_px - 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float( + x_px, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float( + x_px, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float( + x_px + 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float( + x_px + 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_float( + x_px + 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + + if (x_px > maxx) + maxx = x_px; + if (x_px < minx) + minx = x_px; + if (y_px > maxy) + maxy = y_px; + if (x_px > miny) + miny = y_px; + } + } + else { + while (!BLI_stack_is_empty(stack)) { + BLI_stack_pop(stack, &coordinate); + + IMB_blend_color_byte((unsigned char *)(ibuf->rect + coordinate), + (unsigned char *)(ibuf->rect + coordinate), + (unsigned char *)&color_b, + br->blend); + + /* reconstruct the coordinates here */ + x_px = coordinate % width; + y_px = coordinate / width; + + paint_2d_fill_add_pixel_byte( + x_px - 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte( + x_px - 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte( + x_px - 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte( + x_px, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte( + x_px, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte( + x_px + 1, y_px - 1, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte( + x_px + 1, y_px, ibuf, stack, touched, pixel_color, threshold_sq); + paint_2d_fill_add_pixel_byte( + x_px + 1, y_px + 1, ibuf, stack, touched, pixel_color, threshold_sq); + + if (x_px > maxx) + maxx = x_px; + if (x_px < minx) + minx = x_px; + if (y_px > maxy) + maxy = y_px; + if (x_px > miny) + miny = y_px; + } + } + + MEM_freeN(touched); + BLI_stack_free(stack); + } + + imapaint_image_update(sima, ima, ibuf, false); + ED_imapaint_clear_partial_redraw(); + + BKE_image_release_ibuf(ima, ibuf, NULL); + + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); } void paint_2d_gradient_fill( - const bContext *C, Brush *br, - const float mouse_init[2], const float mouse_final[2], - void *ps) + const bContext *C, Brush *br, const float mouse_init[2], const float mouse_final[2], void *ps) { - SpaceImage *sima = CTX_wm_space_image(C); - Image *ima = sima->image; - ImagePaintState *s = ps; - - ImBuf *ibuf; - int x_px, y_px; - unsigned int color_b; - float color_f[4]; - float image_init[2], image_final[2]; - float tangent[2]; - float line_len_sq_inv, line_len; - - bool do_float; - - if (!ima) - return; - - ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); - - if (!ibuf) - return; - - UI_view2d_region_to_view(s->v2d, mouse_final[0], mouse_final[1], &image_final[0], &image_final[1]); - UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); - - image_final[0] *= ibuf->x; - image_final[1] *= ibuf->y; - - image_init[0] *= ibuf->x; - image_init[1] *= ibuf->y; - - /* some math to get needed gradient variables */ - sub_v2_v2v2(tangent, image_final, image_init); - line_len = len_squared_v2(tangent); - line_len_sq_inv = 1.0f / line_len; - line_len = sqrtf(line_len); - - do_float = (ibuf->rect_float != NULL); - - /* this will be substituted by something else when selection is available */ - ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); - - if (do_float) { - for (x_px = 0; x_px < ibuf->x; x_px++) { - for (y_px = 0; y_px < ibuf->y; y_px++) { - float f; - float p[2] = {x_px - image_init[0], y_px - image_init[1]}; - - switch (br->gradient_fill_mode) { - case BRUSH_GRADIENT_LINEAR: - { - f = dot_v2v2(p, tangent) * line_len_sq_inv; - break; - } - case BRUSH_GRADIENT_RADIAL: - default: - { - f = len_v2(p) / line_len; - break; - } - } - BKE_colorband_evaluate(br->gradient, f, color_f); - /* convert to premultiplied */ - mul_v3_fl(color_f, color_f[3]); - color_f[3] *= br->alpha; - IMB_blend_color_float( - ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), - ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), - color_f, br->blend); - } - } - } - else { - for (x_px = 0; x_px < ibuf->x; x_px++) { - for (y_px = 0; y_px < ibuf->y; y_px++) { - float f; - float p[2] = {x_px - image_init[0], y_px - image_init[1]}; - - switch (br->gradient_fill_mode) { - case BRUSH_GRADIENT_LINEAR: - { - f = dot_v2v2(p, tangent) * line_len_sq_inv; - break; - } - case BRUSH_GRADIENT_RADIAL: - default: - { - f = len_v2(p) / line_len; - break; - } - } - - BKE_colorband_evaluate(br->gradient, f, color_f); - linearrgb_to_srgb_v3_v3(color_f, color_f); - rgba_float_to_uchar((unsigned char *)&color_b, color_f); - ((unsigned char *)&color_b)[3] *= br->alpha; - IMB_blend_color_byte( - (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), - (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), - (unsigned char *)&color_b, br->blend); - } - } - } - - imapaint_image_update(sima, ima, ibuf, false); - ED_imapaint_clear_partial_redraw(); - - BKE_image_release_ibuf(ima, ibuf, NULL); - - WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); + SpaceImage *sima = CTX_wm_space_image(C); + Image *ima = sima->image; + ImagePaintState *s = ps; + + ImBuf *ibuf; + int x_px, y_px; + unsigned int color_b; + float color_f[4]; + float image_init[2], image_final[2]; + float tangent[2]; + float line_len_sq_inv, line_len; + + bool do_float; + + if (!ima) + return; + + ibuf = BKE_image_acquire_ibuf(ima, &sima->iuser, NULL); + + if (!ibuf) + return; + + UI_view2d_region_to_view( + s->v2d, mouse_final[0], mouse_final[1], &image_final[0], &image_final[1]); + UI_view2d_region_to_view(s->v2d, mouse_init[0], mouse_init[1], &image_init[0], &image_init[1]); + + image_final[0] *= ibuf->x; + image_final[1] *= ibuf->y; + + image_init[0] *= ibuf->x; + image_init[1] *= ibuf->y; + + /* some math to get needed gradient variables */ + sub_v2_v2v2(tangent, image_final, image_init); + line_len = len_squared_v2(tangent); + line_len_sq_inv = 1.0f / line_len; + line_len = sqrtf(line_len); + + do_float = (ibuf->rect_float != NULL); + + /* this will be substituted by something else when selection is available */ + ED_imapaint_dirty_region(ima, ibuf, 0, 0, ibuf->x, ibuf->y, false); + + if (do_float) { + for (x_px = 0; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + float f; + float p[2] = {x_px - image_init[0], y_px - image_init[1]}; + + switch (br->gradient_fill_mode) { + case BRUSH_GRADIENT_LINEAR: { + f = dot_v2v2(p, tangent) * line_len_sq_inv; + break; + } + case BRUSH_GRADIENT_RADIAL: + default: { + f = len_v2(p) / line_len; + break; + } + } + BKE_colorband_evaluate(br->gradient, f, color_f); + /* convert to premultiplied */ + mul_v3_fl(color_f, color_f[3]); + color_f[3] *= br->alpha; + IMB_blend_color_float(ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), + ibuf->rect_float + 4 * (((size_t)y_px) * ibuf->x + x_px), + color_f, + br->blend); + } + } + } + else { + for (x_px = 0; x_px < ibuf->x; x_px++) { + for (y_px = 0; y_px < ibuf->y; y_px++) { + float f; + float p[2] = {x_px - image_init[0], y_px - image_init[1]}; + + switch (br->gradient_fill_mode) { + case BRUSH_GRADIENT_LINEAR: { + f = dot_v2v2(p, tangent) * line_len_sq_inv; + break; + } + case BRUSH_GRADIENT_RADIAL: + default: { + f = len_v2(p) / line_len; + break; + } + } + + BKE_colorband_evaluate(br->gradient, f, color_f); + linearrgb_to_srgb_v3_v3(color_f, color_f); + rgba_float_to_uchar((unsigned char *)&color_b, color_f); + ((unsigned char *)&color_b)[3] *= br->alpha; + IMB_blend_color_byte((unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), + (unsigned char *)(ibuf->rect + ((size_t)y_px) * ibuf->x + x_px), + (unsigned char *)&color_b, + br->blend); + } + } + } + + imapaint_image_update(sima, ima, ibuf, false); + ED_imapaint_clear_partial_redraw(); + + BKE_image_release_ibuf(ima, ibuf, NULL); + + WM_event_add_notifier(C, NC_IMAGE | NA_EDITED, ima); } -- cgit v1.2.3