diff options
author | Antony Riakiotakis <kalast@gmail.com> | 2014-07-21 14:02:05 +0400 |
---|---|---|
committer | Antony Riakiotakis <kalast@gmail.com> | 2014-07-21 14:02:05 +0400 |
commit | f745564e4ee791e4faf804b09ce975b882f4f8d9 (patch) | |
tree | 90ebbe363ccd925cedc652c9bb018ce552b5a2ab /source/blender/editors/sculpt_paint/paint_image.c | |
parent | 8489b94e07f9e73bd3c9c3e4f6a91f1f0a259827 (diff) |
GSOC 2013 paint
Yep, at last it's here!
There are a few minor issues remaining but development can go on in
master after discussion at blender institute.
For full list of features see:
http://wiki.blender.org/index.php/Dev:Ref/Release_Notes/2.72/Painting
Thanks to Sergey and Campbell for the extensive review and to the
countless artists that have given their input and reported issues during
development.
Diffstat (limited to 'source/blender/editors/sculpt_paint/paint_image.c')
-rw-r--r-- | source/blender/editors/sculpt_paint/paint_image.c | 687 |
1 files changed, 605 insertions, 82 deletions
diff --git a/source/blender/editors/sculpt_paint/paint_image.c b/source/blender/editors/sculpt_paint/paint_image.c index 34232c51ff7..88b9f5bb296 100644 --- a/source/blender/editors/sculpt_paint/paint_image.c +++ b/source/blender/editors/sculpt_paint/paint_image.c @@ -43,6 +43,7 @@ #include "BLI_math.h" #include "BLI_blenlib.h" #include "BLI_utildefines.h" +#include "BLI_threads.h" #include "PIL_time.h" @@ -59,15 +60,21 @@ #include "BKE_brush.h" #include "BKE_image.h" #include "BKE_main.h" +#include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_node.h" #include "BKE_paint.h" +#include "BKE_report.h" +#include "BKE_scene.h" +#include "BKE_texture.h" +#include "BKE_colortools.h" #include "BKE_editmesh.h" #include "UI_view2d.h" #include "ED_image.h" +#include "ED_mesh.h" #include "ED_object.h" #include "ED_paint.h" #include "ED_screen.h" @@ -82,6 +89,9 @@ #include "GPU_draw.h" +#include "BIF_gl.h" +#include "BIF_glutil.h" + #include "IMB_colormanagement.h" #include "paint_intern.h" @@ -102,14 +112,27 @@ typedef struct UndoImageTile { int x, y; + Image *ima; short source, use_float; char gen_type; + bool valid; } UndoImageTile; /* this is a static resource for non-globality, * Maybe it should be exposed as part of the * paint operation, but for now just give a public interface */ static ImagePaintPartialRedraw imapaintpartial = {0, 0, 0, 0, 0}; +static SpinLock undolock; + +void image_undo_init_locks(void) +{ + BLI_spin_init(&undolock); +} + +void image_undo_end_locks(void) +{ + BLI_spin_end(&undolock); +} ImagePaintPartialRedraw *get_imapaintpartial(void) { @@ -122,26 +145,53 @@ void set_imapaintpartial(struct ImagePaintPartialRedraw *ippr) } /* UNDO */ +typedef enum { + COPY = 0, + RESTORE = 1, + RESTORE_COPY = 2 +} CopyMode; -static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, int restore) +static void undo_copy_tile(UndoImageTile *tile, ImBuf *tmpibuf, ImBuf *ibuf, CopyMode mode) { - /* copy or swap contents of tile->rect and region in ibuf->rect */ - IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE, - tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); + if (mode == COPY) { + /* copy or swap contents of tile->rect and region in ibuf->rect */ + IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE, + tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); - if (ibuf->rect_float) { - SWAP(float *, tmpibuf->rect_float, tile->rect.fp); + if (ibuf->rect_float) { + SWAP(float *, tmpibuf->rect_float, tile->rect.fp); + } + else { + SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); + } } else { - SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); - } - - if (restore) + if (mode == RESTORE_COPY) + IMB_rectcpy(tmpibuf, ibuf, 0, 0, tile->x * IMAPAINT_TILE_SIZE, + tile->y * IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); + /* swap to the tmpbuf for easy copying */ + if (ibuf->rect_float) { + SWAP(float *, tmpibuf->rect_float, tile->rect.fp); + } + else { + SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); + } + IMB_rectcpy(ibuf, tmpibuf, tile->x * IMAPAINT_TILE_SIZE, tile->y * IMAPAINT_TILE_SIZE, 0, 0, IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE); + + if (mode == RESTORE) { + if (ibuf->rect_float) { + SWAP(float *, tmpibuf->rect_float, tile->rect.fp); + } + else { + SWAP(unsigned int *, tmpibuf->rect, tile->rect.uint); + } + } + } } -void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask) +void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsigned short **mask, bool validate) { ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); UndoImageTile *tile; @@ -160,6 +210,8 @@ void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsi *mask = tile->mask; } + if (validate) + tile->valid = true; return tile->rect.pt; } @@ -170,7 +222,7 @@ void *image_undo_find_tile(Image *ima, ImBuf *ibuf, int x_tile, int y_tile, unsi return NULL; } -void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile) +void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, int y_tile, unsigned short **mask, bool **valid, bool proj) { ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); UndoImageTile *tile; @@ -179,10 +231,14 @@ void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, void *data; /* check if tile is already pushed */ - data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, NULL); - if (data) - return data; - + + /* in projective painting we keep accounting of tiles, so if we need one pushed, just push! */ + if (!proj) { + data = image_undo_find_tile(ima, ibuf, x_tile, y_tile, mask, true); + if (data) + return data; + } + if (*tmpibuf == NULL) *tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, IB_rectfloat | IB_rect); @@ -191,6 +247,11 @@ void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, tile->x = x_tile; tile->y = y_tile; + /* add mask explicitly here */ + if (mask) + *mask = tile->mask = MEM_callocN(sizeof(unsigned short) * IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE, + "UndoImageTile.mask"); + allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4; allocsize *= (ibuf->rect_float) ? sizeof(float) : sizeof(char); tile->rect.pt = MEM_mapallocN(allocsize, "UndeImageTile.rect"); @@ -200,12 +261,23 @@ void *image_undo_push_tile(Image *ima, ImBuf *ibuf, ImBuf **tmpibuf, int x_tile, tile->gen_type = ima->gen_type; tile->source = ima->source; tile->use_float = use_float; + tile->valid = true; + tile->ima = ima; - undo_copy_tile(tile, *tmpibuf, ibuf, 0); - undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, allocsize); + if (valid) + *valid = &tile->valid; + + undo_copy_tile(tile, *tmpibuf, ibuf, COPY); + if (proj) + BLI_spin_lock(&undolock); + + undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, allocsize); BLI_addtail(lb, tile); - + + if (proj) + BLI_spin_unlock(&undolock); + return tile->rect.pt; } @@ -222,6 +294,33 @@ void image_undo_remove_masks(void) } } +static void image_undo_restore_runtime(ListBase *lb) +{ + ImBuf *ibuf, *tmpibuf; + UndoImageTile *tile; + + tmpibuf = IMB_allocImBuf(IMAPAINT_TILE_SIZE, IMAPAINT_TILE_SIZE, 32, + IB_rectfloat | IB_rect); + + for (tile = lb->first; tile; tile = tile->next) { + Image *ima = tile->ima; + ibuf = BKE_image_acquire_ibuf(ima, NULL, NULL); + + undo_copy_tile(tile, tmpibuf, ibuf, RESTORE); + + GPU_free_image(ima); /* force OpenGL reload (maybe partial update will operate better?) */ + if (ibuf->rect_float) + ibuf->userflags |= IB_RECT_INVALID; /* force recreate of char rect */ + if (ibuf->mipmap[0]) + ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ + ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + + BKE_image_release_ibuf(ima, ibuf, NULL); + } + + IMB_freeImBuf(tmpibuf); +} + void ED_image_undo_restore(bContext *C, ListBase *lb) { Main *bmain = CTX_data_main(C); @@ -273,7 +372,7 @@ void ED_image_undo_restore(bContext *C, ListBase *lb) continue; } - undo_copy_tile(tile, tmpibuf, ibuf, 1); + undo_copy_tile(tile, tmpibuf, ibuf, RESTORE_COPY); GPU_free_image(ima); /* force OpenGL reload */ if (ibuf->rect_float) @@ -282,6 +381,8 @@ void ED_image_undo_restore(bContext *C, ListBase *lb) ibuf->userflags |= IB_MIPMAP_INVALID; /* force mipmap recreatiom */ ibuf->userflags |= IB_DISPLAY_BUFFER_INVALID; + DAG_id_tag_update(&ima->id, 0); + BKE_image_release_ibuf(ima, ibuf, NULL); } @@ -296,6 +397,42 @@ void ED_image_undo_free(ListBase *lb) MEM_freeN(tile->rect.pt); } +static void image_undo_end(void) +{ + ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); + UndoImageTile *tile; + int deallocsize = 0; + int allocsize = IMAPAINT_TILE_SIZE * IMAPAINT_TILE_SIZE * 4; + + /* first dispose of invalid tiles (may happen due to drag dot for instance) */ + for (tile = lb->first; tile;) { + if (!tile->valid) { + UndoImageTile *tmp_tile = tile->next; + deallocsize += allocsize * ((tile->use_float) ? sizeof(float) : sizeof(char)); + MEM_freeN(tile->rect.pt); + BLI_freelinkN (lb, tile); + tile = tmp_tile; + } + else { + tile = tile->next; + } + } + + /* don't forget to remove the size of deallocated tiles */ + undo_paint_push_count_alloc(UNDO_PAINT_IMAGE, -deallocsize); + + ED_undo_paint_push_end(UNDO_PAINT_IMAGE); +} + +static void image_undo_invalidate(void) +{ + UndoImageTile *tile; + ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); + + for (tile = lb->first; tile; tile = tile->next) + tile->valid = false; +} + /* Imagepaint Partial Redraw & Dirty Region */ void ED_imapaint_clear_partial_redraw(void) @@ -344,7 +481,7 @@ void ED_imapaint_dirty_region(Image *ima, ImBuf *ibuf, int x, int y, int w, int for (ty = tiley; ty <= tileh; ty++) for (tx = tilex; tx <= tilew; tx++) - image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty); + image_undo_push_tile(ima, ibuf, &tmpibuf, tx, ty, NULL, NULL, false); ibuf->userflags |= IB_BITMAPDIRTY; @@ -373,6 +510,70 @@ void imapaint_image_update(SpaceImage *sima, Image *image, ImBuf *ibuf, short te } } +/* paint blur kernels */ +BlurKernel *paint_new_blur_kernel(Brush *br) +{ + int i, j; + BlurKernel *kernel = MEM_mallocN(sizeof(BlurKernel), "blur kernel"); + int pixel_len = br->blur_kernel_radius; + BlurKernelType type = br->blur_mode; + + kernel->side = pixel_len * 2 + 1; + kernel->side_squared = kernel->side * kernel->side; + kernel->wdata = MEM_mallocN(sizeof(float) * kernel->side_squared, "blur kernel data"); + kernel->pixel_len = pixel_len; + + switch (type) { + case KERNEL_BOX: + for (i = 0; i < kernel->side_squared; i++) + kernel->wdata[i] = 1.0; + break; + + case KERNEL_GAUSSIAN: + { + float standard_dev = pixel_len / 3.0; /* at standard deviation of 3.0 kernel is at about zero */ + int i_term = pixel_len + 1; + + /* make the necessary adjustment to the value for use in the normal distribution formula */ + standard_dev = standard_dev * standard_dev * 2; + + kernel->wdata[pixel_len + pixel_len * kernel->side] = 1.0; + /* fill in all four quadrants at once */ + for (i = 0; i < i_term; i++) { + for (j = 0; j < pixel_len; j++) { + float idist = pixel_len - i; + float jdist = pixel_len - j; + + float value = exp((idist * idist + jdist * jdist) / standard_dev); + + kernel->wdata[i + j * kernel->side] = + kernel->wdata[(kernel->side - j - 1) + i * kernel->side] = + kernel->wdata[(kernel->side - i - 1) + (kernel->side - j - 1) * kernel->side] = + kernel->wdata[j + (kernel->side - i - 1) * kernel->side] = + value; + } + } + + break; + } + + default: + printf("unidentified kernel type, aborting\n"); + MEM_freeN(kernel->wdata); + MEM_freeN(kernel); + return NULL; + break; + } + + return kernel; +} + +void paint_delete_blur_kernel(BlurKernel *kernel) +{ + if (kernel->wdata) + MEM_freeN(kernel->wdata); +} + /************************ image paint poll ************************/ static Brush *image_paint_brush(bContext *C) @@ -432,11 +633,57 @@ typedef struct PaintOperation { void *custom_paint; float prevmouse[2]; + float startmouse[2]; double starttime; + void *cursor; ViewContext vc; } PaintOperation; +bool paint_use_opacity_masking(Brush *brush) +{ + return (brush->flag & BRUSH_AIRBRUSH) || + (brush->flag & BRUSH_DRAG_DOT) || + (brush->flag & BRUSH_ANCHORED) || + (brush->imagepaint_tool == PAINT_TOOL_SMEAR) || + (brush->imagepaint_tool == PAINT_TOOL_SOFTEN) || + (brush->imagepaint_tool == PAINT_TOOL_FILL) || + (brush->flag & BRUSH_USE_GRADIENT) || + (brush->mtex.tex && !ELEM(brush->mtex.brush_map_mode, MTEX_MAP_MODE_TILED, MTEX_MAP_MODE_STENCIL, MTEX_MAP_MODE_3D)) ? + false : true; +} + +void paint_brush_color_get(struct Scene *scene, struct Brush *br, bool color_correction, bool invert, float distance, + float pressure, float color[3], struct ColorManagedDisplay *display) +{ + if (invert) + copy_v3_v3(color, BKE_brush_secondary_color_get(scene, br)); + else { + if (br->flag & BRUSH_USE_GRADIENT) { + switch (br->gradient_stroke_mode) { + case BRUSH_GRADIENT_PRESSURE: + do_colorband(br->gradient, pressure, color); + break; + case BRUSH_GRADIENT_SPACING_REPEAT: + { + float coord = fmod(distance / br->gradient_spacing, 1.0); + do_colorband(br->gradient, coord, color); + break; + } + case BRUSH_GRADIENT_SPACING_CLAMP: + { + do_colorband(br->gradient, distance / br->gradient_spacing, color); + break; + } + } + } + else + copy_v3_v3(color, BKE_brush_color_get(scene, br)); + } + if (color_correction) + IMB_colormanagement_display_to_scene_linear_v3(color, display); +} + void paint_brush_init_tex(Brush *brush) { /* init mtex nodes */ @@ -462,26 +709,54 @@ void paint_brush_exit_tex(Brush *brush) } } +static void gradient_draw_line(bContext *UNUSED(C), int x, int y, void *customdata) +{ + PaintOperation *pop = (PaintOperation *)customdata; + + if (pop) { + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + + glLineWidth(4.0); + glColor4ub(0, 0, 0, 255); + sdrawline(x, y, pop->startmouse[0], pop->startmouse[1]); + glLineWidth(2.0); + glColor4ub(255, 255, 255, 255); + sdrawline(x, y, pop->startmouse[0], pop->startmouse[1]); + glLineWidth(1.0); + + glDisable(GL_BLEND); + glDisable(GL_LINE_SMOOTH); + } +} + -static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, float mouse[2]) +static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, const float mouse[2]) { Scene *scene = CTX_data_scene(C); ToolSettings *settings = scene->toolsettings; PaintOperation *pop = MEM_callocN(sizeof(PaintOperation), "PaintOperation"); /* caller frees */ + Brush *brush = BKE_paint_brush(&settings->imapaint.paint); int mode = RNA_enum_get(op->ptr, "mode"); view3d_set_viewcontext(C, &pop->vc); - pop->prevmouse[0] = mouse[0]; - pop->prevmouse[1] = mouse[1]; + copy_v2_v2(pop->prevmouse, mouse); + copy_v2_v2(pop->startmouse, mouse); + + if ((brush->imagepaint_tool == PAINT_TOOL_FILL) && (brush->flag & BRUSH_USE_GRADIENT)) { + pop->cursor = WM_paint_cursor_activate(CTX_wm_manager(C), image_paint_poll, gradient_draw_line, pop); + } /* initialize from context */ if (CTX_wm_region_view3d(C)) { + Object *ob = OBACT; + paint_proj_mesh_data_ensure(C, ob, op); pop->mode = PAINT_MODE_3D_PROJECT; - pop->custom_paint = paint_proj_new_stroke(C, OBACT, pop->prevmouse, mode); + pop->custom_paint = paint_proj_new_stroke(C, ob, mouse, mode); } else { pop->mode = PAINT_MODE_2D; - pop->custom_paint = paint_2d_new_stroke(C, op); + pop->custom_paint = paint_2d_new_stroke(C, op, mode); } if (!pop->custom_paint) { @@ -491,52 +766,69 @@ static PaintOperation *texture_paint_init(bContext *C, wmOperator *op, float mou settings->imapaint.flag |= IMAGEPAINT_DRAWING; ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name, - ED_image_undo_restore, ED_image_undo_free); - - { - UnifiedPaintSettings *ups = &settings->unified_paint_settings; - ups->stroke_active = true; - } + ED_image_undo_restore, ED_image_undo_free, NULL); return pop; } +/* restore painting image to previous state. Used for anchored and drag-dot style brushes*/ +static void paint_stroke_restore(void) +{ + ListBase *lb = undo_paint_push_get_list(UNDO_PAINT_IMAGE); + image_undo_restore_runtime(lb); + image_undo_invalidate(); +} + static void paint_stroke_update_step(bContext *C, struct PaintStroke *stroke, PointerRNA *itemptr) { PaintOperation *pop = paint_stroke_mode_data(stroke); Scene *scene = CTX_data_scene(C); - Brush *brush = BKE_paint_brush(&scene->toolsettings->imapaint.paint); + ToolSettings *toolsettings = CTX_data_tool_settings(C); + UnifiedPaintSettings *ups = &toolsettings->unified_paint_settings; + Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); + + float alphafac = (brush->flag & BRUSH_ACCUMULATE) ? ups->overlap_factor : 1.0f; /* initial brush values. Maybe it should be considered moving these to stroke system */ - float startsize = (float)BKE_brush_size_get(scene, brush); float startalpha = BKE_brush_alpha_get(scene, brush); float mouse[2]; float pressure; + float size; + float distance = paint_stroke_distance_get(stroke); int eraser; RNA_float_get_array(itemptr, "mouse", mouse); pressure = RNA_float_get(itemptr, "pressure"); eraser = RNA_boolean_get(itemptr, "pen_flip"); + size = max_ff(1.0f, RNA_float_get(itemptr, "size")); + + /* stroking with fill tool only acts on stroke end */ + if (brush->imagepaint_tool == PAINT_TOOL_FILL) { + copy_v2_v2(pop->prevmouse, mouse); + return; + } if (BKE_brush_use_alpha_pressure(scene, brush)) - BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure)); - if (BKE_brush_use_size_pressure(scene, brush)) - BKE_brush_size_set(scene, brush, (int)max_ff(1.0f, startsize * pressure)); + BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * pressure * alphafac)); + else + BKE_brush_alpha_set(scene, brush, max_ff(0.0f, startalpha * alphafac)); + + if ((brush->flag & BRUSH_DRAG_DOT) || (brush->flag & BRUSH_ANCHORED)) { + paint_stroke_restore(); + } if (pop->mode == PAINT_MODE_3D_PROJECT) { - paint_proj_stroke(C, pop->custom_paint, pop->prevmouse, mouse); + paint_proj_stroke(C, pop->custom_paint, pop->prevmouse, mouse, pressure, distance, size); } else { - paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser); + paint_2d_stroke(pop->custom_paint, pop->prevmouse, mouse, eraser, pressure, distance, size); } - pop->prevmouse[0] = mouse[0]; - pop->prevmouse[1] = mouse[1]; + copy_v2_v2(pop->prevmouse, mouse); /* restore brush values */ BKE_brush_alpha_set(scene, brush, startalpha); - BKE_brush_size_set(scene, brush, startsize); } static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, bool final) @@ -554,11 +846,39 @@ static void paint_stroke_redraw(const bContext *C, struct PaintStroke *stroke, b static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) { Scene *scene = CTX_data_scene(C); - ToolSettings *settings = scene->toolsettings; + ToolSettings *toolsettings = scene->toolsettings; PaintOperation *pop = paint_stroke_mode_data(stroke); + Brush *brush = BKE_paint_brush(&toolsettings->imapaint.paint); - settings->imapaint.flag &= ~IMAGEPAINT_DRAWING; + toolsettings->imapaint.flag &= ~IMAGEPAINT_DRAWING; + if (brush->imagepaint_tool == PAINT_TOOL_FILL) { + if (brush->flag & BRUSH_USE_GRADIENT) { + if (pop->mode == PAINT_MODE_2D) { + paint_2d_gradient_fill(C, brush, pop->startmouse, pop->prevmouse, pop->custom_paint); + } + else { + paint_proj_stroke(C, pop->custom_paint, pop->startmouse, pop->prevmouse, 1.0, 0.0, BKE_brush_size_get(scene, brush)); + /* two redraws, one for GPU update, one for notification */ + paint_proj_redraw(C, pop->custom_paint, false); + paint_proj_redraw(C, pop->custom_paint, true); + } + } + else { + if (pop->mode == PAINT_MODE_2D) { + float color[3]; + + srgb_to_linearrgb_v3_v3(color, BKE_brush_color_get(scene, brush)); + paint_2d_bucket_fill(C, color, brush, pop->prevmouse, pop->custom_paint); + } + else { + paint_proj_stroke(C, pop->custom_paint, pop->startmouse, pop->prevmouse, 1.0, 0.0, BKE_brush_size_get(scene, brush)); + /* two redraws, one for GPU update, one for notification */ + paint_proj_redraw(C, pop->custom_paint, false); + paint_proj_redraw(C, pop->custom_paint, true); + } + } + } if (pop->mode == PAINT_MODE_3D_PROJECT) { paint_proj_stroke_done(pop->custom_paint); } @@ -566,7 +886,11 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) paint_2d_stroke_done(pop->custom_paint); } - ED_undo_paint_push_end(UNDO_PAINT_IMAGE); + if (pop->cursor) { + WM_paint_cursor_end(CTX_wm_manager(C), pop->cursor); + } + + image_undo_end(); /* duplicate warning, see texpaint_init */ #if 0 @@ -576,43 +900,41 @@ static void paint_stroke_done(const bContext *C, struct PaintStroke *stroke) BKE_reportf(op->reports, RPT_WARNING, "Packed MultiLayer files cannot be painted: %s", pop->s.warnpackedfile); #endif MEM_freeN(pop); - - { - UnifiedPaintSettings *ups = &scene->toolsettings->unified_paint_settings; - ups->stroke_active = false; - } -} - -static bool paint_stroke_test_start(bContext *UNUSED(C), wmOperator *UNUSED(op), const float UNUSED(mouse[2])) -{ - return true; } - -static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event) +static bool paint_stroke_test_start(bContext *C, wmOperator *op, const float mouse[2]) { PaintOperation *pop; - float mouse[2]; - int retval; /* TODO Should avoid putting this here. Instead, last position should be requested * from stroke system. */ - mouse[0] = event->mval[0]; - mouse[1] = event->mval[1]; if (!(pop = texture_paint_init(C, op, mouse))) { - return OPERATOR_CANCELLED; + return false; } - op->customdata = paint_stroke_new(C, NULL, paint_stroke_test_start, + paint_stroke_set_mode_data(op->customdata, pop); + + return true; +} + + +static int paint_invoke(bContext *C, wmOperator *op, const wmEvent *event) +{ + int retval; + + op->customdata = paint_stroke_new(C, op, NULL, paint_stroke_test_start, paint_stroke_update_step, paint_stroke_redraw, paint_stroke_done, event->type); - paint_stroke_set_mode_data(op->customdata, pop); + + if ((retval = op->type->modal(C, op, event)) == OPERATOR_FINISHED) { + paint_stroke_data_free(op); + return OPERATOR_FINISHED; + } /* add modal handler */ WM_event_add_modal_handler(C, op); - retval = op->type->modal(C, op, event); OPERATOR_RETVAL_CHECK(retval); BLI_assert(retval == OPERATOR_RUNNING_MODAL); @@ -637,12 +959,10 @@ static int paint_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } - op->customdata = paint_stroke_new(C, NULL, paint_stroke_test_start, + op->customdata = paint_stroke_new(C, op, NULL, paint_stroke_test_start, paint_stroke_update_step, paint_stroke_redraw, paint_stroke_done, 0); - paint_stroke_set_mode_data(op->customdata, pop); - /* frees op->customdata */ paint_stroke_exec(C, op); @@ -686,9 +1006,9 @@ int get_imapaint_zoom(bContext *C, float *zoomx, float *zoomy) if (!rv3d) { SpaceImage *sima = CTX_wm_space_image(C); - ARegion *ar = CTX_wm_region(C); if (sima->mode == SI_MODE_PAINT) { + ARegion *ar = CTX_wm_region(C); ED_space_image_get_zoom(sima, ar, zoomx, zoomy); return 1; @@ -847,17 +1167,39 @@ void PAINT_OT_grab_clone(wmOperatorType *ot) typedef struct { bool show_cursor; short event_type; -} SampleColorData; + float initcolor[3]; + bool sample_palette; +} SampleColorData; + + +static void sample_color_update_header(SampleColorData *data, bContext *C) +{ +#define HEADER_LENGTH 150 + char msg[HEADER_LENGTH]; + ScrArea *sa = CTX_wm_area(C); + + if (sa) { + BLI_snprintf(msg, HEADER_LENGTH, + "Sample color for %s", + !data->sample_palette ? + "Brush. Use Left Click to sample for palette instead" : + "Palette. Use Left Click to sample more colors"); + ED_area_headerprint(sa, msg); + } + +#undef HEADER_LENGTH +} static int sample_color_exec(bContext *C, wmOperator *op) { Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); + PaintMode mode = BKE_paintmode_get_active_from_context(C); ARegion *ar = CTX_wm_region(C); wmWindow *win = CTX_wm_window(C); bool show_cursor = ((paint->flags & PAINT_SHOW_BRUSH) != 0); int location[2]; - + bool use_palette; paint->flags &= ~PAINT_SHOW_BRUSH; /* force redraw without cursor */ @@ -865,7 +1207,9 @@ static int sample_color_exec(bContext *C, wmOperator *op) WM_redraw_windows(C); RNA_int_get_array(op->ptr, "location", location); - paint_sample_color(C, ar, location[0], location[1]); + use_palette = RNA_boolean_get(op->ptr, "palette"); + + paint_sample_color(C, ar, location[0], location[1], mode == PAINT_TEXTURE_PROJECTIVE, use_palette); if (show_cursor) { paint->flags |= PAINT_SHOW_BRUSH; @@ -878,7 +1222,9 @@ static int sample_color_exec(bContext *C, wmOperator *op) static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event) { + Scene *scene = CTX_data_scene(C); Paint *paint = BKE_paint_get_active_from_context(C); + PaintMode mode = BKE_paintmode_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); SampleColorData *data = MEM_mallocN(sizeof(SampleColorData), "sample color custom data"); ARegion *ar = CTX_wm_region(C); @@ -886,18 +1232,24 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event data->event_type = event->type; data->show_cursor = ((paint->flags & PAINT_SHOW_BRUSH) != 0); + copy_v3_v3(data->initcolor, BKE_brush_color_get(scene, brush)); + data->sample_palette = false; op->customdata = data; paint->flags &= ~PAINT_SHOW_BRUSH; + sample_color_update_header(data, C); + + WM_event_add_modal_handler(C, op); + /* force redraw without cursor */ WM_paint_cursor_tag_redraw(win, ar); WM_redraw_windows(C); RNA_int_set_array(op->ptr, "location", event->mval); - paint_sample_color(C, ar, event->mval[0], event->mval[1]); + + paint_sample_color(C, ar, event->mval[0], event->mval[1], mode == PAINT_TEXTURE_PROJECTIVE, false); WM_cursor_modal_set(win, BC_EYEDROPPER_CURSOR); - WM_event_add_modal_handler(C, op); WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); return OPERATOR_RUNNING_MODAL; @@ -905,17 +1257,27 @@ static int sample_color_invoke(bContext *C, wmOperator *op, const wmEvent *event static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event) { + Scene *scene = CTX_data_scene(C); SampleColorData *data = op->customdata; Paint *paint = BKE_paint_get_active_from_context(C); Brush *brush = BKE_paint_brush(paint); + PaintMode mode = BKE_paintmode_get_active_from_context(C); if ((event->type == data->event_type) && (event->val == KM_RELEASE)) { + ScrArea *sa = CTX_wm_area(C); + if (data->show_cursor) { paint->flags |= PAINT_SHOW_BRUSH; } + if (data->sample_palette) { + BKE_brush_color_set(scene, brush, data->initcolor); + RNA_boolean_set(op->ptr, "palette", true); + } WM_cursor_modal_restore(CTX_wm_window(C)); MEM_freeN(data); + ED_area_headerprint(sa, NULL); + return OPERATOR_FINISHED; } @@ -924,10 +1286,22 @@ static int sample_color_modal(bContext *C, wmOperator *op, const wmEvent *event) { ARegion *ar = CTX_wm_region(C); RNA_int_set_array(op->ptr, "location", event->mval); - paint_sample_color(C, ar, event->mval[0], event->mval[1]); + paint_sample_color(C, ar, event->mval[0], event->mval[1], mode == PAINT_TEXTURE_PROJECTIVE, false); WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, brush); break; } + + case LEFTMOUSE: + if (event->val == KM_PRESS) { + ARegion *ar = CTX_wm_region(C); + RNA_int_set_array(op->ptr, "location", event->mval); + paint_sample_color(C, ar, event->mval[0], event->mval[1], mode == PAINT_TEXTURE_PROJECTIVE, true); + if (!data->sample_palette) { + data->sample_palette = true; + sample_color_update_header(data, C); + } + } + break; } return OPERATOR_RUNNING_MODAL; @@ -956,6 +1330,7 @@ void PAINT_OT_sample_color(wmOperatorType *ot) /* properties */ RNA_def_int_vector(ot->srna, "location", 2, NULL, 0, INT_MAX, "Location", "Cursor location in region coordinates", 0, 16384); + RNA_def_boolean(ot->srna, "palette", 0, "Palette", "Add color to palette"); } /******************** texture paint toggle operator ********************/ @@ -973,13 +1348,84 @@ static int texture_paint_toggle_poll(bContext *C) return 1; } + +/* Make sure that active object has a material, and assign UVs and image layers if they do not exist */ +void paint_proj_mesh_data_ensure(bContext *C, Object *ob, wmOperator *op) +{ + Mesh *me; + int layernum; + bool add_material = false; + ImagePaintSettings *imapaint = &(CTX_data_tool_settings(C)->imapaint); + Brush *br = BKE_paint_brush(&imapaint->paint); + + /* no material, add one */ + if (ob->totcol == 0) { + add_material = true; + } + else { + /* there may be material slots but they may be empty, check */ + bool has_material = false; + int i; + + for (i = 1; i < ob->totcol + 1; i++) { + Material *ma = give_current_material(ob, i); + if (ma) { + has_material = true; + if (!ma->texpaintslot) { + proj_paint_add_slot(C, MAP_COL, ma); + } + } + } + + if (!has_material) + add_material = true; + } + + if (add_material) { + Material *ma = BKE_material_add(CTX_data_main(C), "Material"); + /* no material found, just assign to first slot */ + assign_material(ob, ma, 1, BKE_MAT_ASSIGN_USERPREF); + proj_paint_add_slot(C, MAP_COL, ma); + } + + me = BKE_mesh_from_object(ob); + layernum = CustomData_number_of_layers(&me->pdata, CD_MTEXPOLY); + + if (layernum == 0) { + BKE_reportf(op->reports, RPT_WARNING, "Object did not have UV map. Recommend manual unwrap"); + + ED_mesh_uv_texture_add(me, "UVMap", true); + } + + /* Make sure we have a stencil to paint on! */ + if (br->imagepaint_tool == PAINT_TOOL_MASK) { + imapaint->flag |= IMAGEPAINT_PROJECT_LAYER_STENCIL; + + if (imapaint->stencil == NULL) { + int width; + int height; + Main *bmain = CTX_data_main(C); + float color[4] = {0.0, 0.0, 0.0, 1.0}; + + /* should not be allowed, but just in case */ + if (imapaint->slot_xresolution_default == 0) + imapaint->slot_xresolution_default = 1024; + if (imapaint->slot_yresolution_default == 0) + imapaint->slot_yresolution_default = 1024; + + width = imapaint->slot_xresolution_default; + height = imapaint->slot_yresolution_default; + imapaint->stencil = BKE_image_add_generated(bmain, width, height, "Stencil", 32, false, IMA_GENTYPE_BLANK, color); + } + } +} + static int texture_paint_toggle_exec(bContext *C, wmOperator *op) { Scene *scene = CTX_data_scene(C); Object *ob = CTX_data_active_object(C); const int mode_flag = OB_MODE_TEXTURE_PAINT; const bool is_mode_set = (ob->mode & mode_flag) != 0; - Mesh *me; if (!is_mode_set) { if (!ED_object_mode_compat_set(C, ob, mode_flag, op->reports)) { @@ -987,8 +1433,6 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op) } } - me = BKE_mesh_from_object(ob); - if (ob->mode & mode_flag) { ob->mode &= ~mode_flag; @@ -999,11 +1443,36 @@ static int texture_paint_toggle_exec(bContext *C, wmOperator *op) toggle_paint_cursor(C, 0); } else { - ob->mode |= mode_flag; + bScreen *sc; + Main *bmain = CTX_data_main(C); + Material *ma; + + bool use_nodes = BKE_scene_use_new_shading_nodes(scene); + /* This has to stay here to regenerate the texture paint + * cache in case we are loading a file */ + BKE_texpaint_slots_refresh_object(ob, use_nodes); + + paint_proj_mesh_data_ensure(C, ob, op); + + /* set the current material active paint slot on image editor */ + ma = give_current_material(ob, ob->actcol); + + if (ma->tot_slots > 0) { + for (sc = bmain->screen.first; sc; sc = sc->id.next) { + ScrArea *sa; + for (sa = sc->areabase.first; sa; sa = sa->next) { + SpaceLink *sl; + for (sl = sa->spacedata.first; sl; sl = sl->next) { + if (sl->spacetype == SPACE_IMAGE) { + SpaceImage *sima = (SpaceImage *)sl; + ED_space_image_set(sima, scene, scene->obedit, ma->texpaintslot[ma->paint_active_slot].ima); + } + } + } + } + } - if (me->mtface == NULL) - me->mtface = CustomData_add_layer(&me->fdata, CD_MTFACE, CD_DEFAULT, - NULL, me->totface); + ob->mode |= mode_flag; BKE_paint_init(&scene->toolsettings->imapaint.paint, PAINT_CURSOR_TEXTURE_PAINT); @@ -1035,6 +1504,60 @@ void PAINT_OT_texture_paint_toggle(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; } + +static int brush_colors_flip_exec(bContext *C, wmOperator *UNUSED(op)) +{ + UnifiedPaintSettings *ups = &CTX_data_tool_settings(C)->unified_paint_settings; + Brush *br = image_paint_brush(C); + if (ups->flag & UNIFIED_PAINT_COLOR) { + swap_v3_v3(ups->rgb, ups->secondary_rgb); + } + else if (br) { + swap_v3_v3(br->rgb, br->secondary_rgb); + } + WM_event_add_notifier(C, NC_BRUSH | NA_EDITED, br); + + return OPERATOR_FINISHED; +} + +static int brush_colors_flip_poll(bContext *C) +{ + if (image_paint_poll(C)) { + Brush *br = image_paint_brush(C); + if (br->imagepaint_tool == PAINT_TOOL_DRAW) + return 1; + } + + return 0; +} + +void PAINT_OT_brush_colors_flip(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Brush Colors Flip"; + ot->idname = "PAINT_OT_brush_colors_flip"; + ot->description = "Toggle foreground and background brush colors"; + + /* api callbacks */ + ot->exec = brush_colors_flip_exec; + ot->poll = brush_colors_flip_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; +} + + +void ED_imapaint_bucket_fill(struct bContext *C, float color[3], wmOperator *op) +{ + ED_undo_paint_push_begin(UNDO_PAINT_IMAGE, op->type->name, + ED_image_undo_restore, ED_image_undo_free, NULL); + + paint_2d_bucket_fill(C, color, NULL, NULL, NULL); + + ED_undo_paint_push_end(UNDO_PAINT_IMAGE); +} + + static int texture_paint_poll(bContext *C) { if (texture_paint_toggle_poll(C)) |