Changeset View
Changeset View
Standalone View
Standalone View
source/blender/editors/sculpt_paint/paint_image_proj.c
| Show First 20 Lines • Show All 360 Lines • ▼ Show 20 Lines | #endif | ||||
| bool reproject_ibuf_free_float; | bool reproject_ibuf_free_float; | ||||
| bool reproject_ibuf_free_uchar; | bool reproject_ibuf_free_uchar; | ||||
| /* threads */ | /* threads */ | ||||
| int thread_tot; | int thread_tot; | ||||
| int bucketMin[2]; | int bucketMin[2]; | ||||
| int bucketMax[2]; | int bucketMax[2]; | ||||
| /** must lock threads while accessing these. */ | /** must lock threads while accessing these. */ | ||||
| int context_bucket_index; | |||||
| int context_bucket_x, context_bucket_y; | int context_bucket_x, context_bucket_y; | ||||
| struct CurveMapping *cavity_curve; | struct CurveMapping *cavity_curve; | ||||
| BlurKernel *blurkernel; | BlurKernel *blurkernel; | ||||
| /* -------------------------------------------------------------------- */ | /* -------------------------------------------------------------------- */ | ||||
| /* Vars shared between multiple views (keep last) */ | /* Vars shared between multiple views (keep last) */ | ||||
| /** | /** | ||||
| * This data is owned by ``ProjStrokeHandle.ps_views[0]``, | * This data is owned by ``ProjStrokeHandle.ps_views[0]``, | ||||
| * all other views re-use the data. | * all other views re-use the data. | ||||
| */ | */ | ||||
| #define PROJ_PAINT_STATE_SHARED_MEMCPY(ps_dst, ps_src) \ | #define PROJ_PAINT_STATE_SHARED_MEMCPY(ps_dst, ps_src) \ | ||||
| MEMCPY_STRUCT_AFTER(ps_dst, ps_src, is_shared_user) | MEMCPY_STRUCT_AFTER(ps_dst, ps_src, is_shared_user) | ||||
| #define PROJ_PAINT_STATE_SHARED_CLEAR(ps) MEMSET_STRUCT_AFTER(ps, 0, is_shared_user) | #define PROJ_PAINT_STATE_SHARED_CLEAR(ps) MEMSET_STRUCT_AFTER(ps, 0, is_shared_user) | ||||
| bool is_shared_user; | bool is_shared_user; | ||||
| ProjPaintImage *projImages; | ProjPaintImage *projImages; | ||||
| bool proj_images_touch_any; | |||||
| /** cavity amount for vertices. */ | /** cavity amount for vertices. */ | ||||
| float *cavities; | float *cavities; | ||||
| #ifndef PROJ_DEBUG_NOSEAMBLEED | #ifndef PROJ_DEBUG_NOSEAMBLEED | ||||
| /** store info about faces, if they are initialized etc*/ | /** store info about faces, if they are initialized etc*/ | ||||
| ushort *faceSeamFlags; | ushort *faceSeamFlags; | ||||
| /** save the winding of the face in uv space, | /** save the winding of the face in uv space, | ||||
| * helps as an extra validation step for seam detection. */ | * helps as an extra validation step for seam detection. */ | ||||
| ▲ Show 20 Lines • Show All 2,570 Lines • ▼ Show 20 Lines | static void project_paint_face_init(const ProjPaintState *ps, | ||||
| const rctf *bucket_bounds, | const rctf *bucket_bounds, | ||||
| ImBuf *ibuf, | ImBuf *ibuf, | ||||
| ImBuf **tmpibuf) | ImBuf **tmpibuf) | ||||
| { | { | ||||
| /* Projection vars, to get the 3D locations into screen space */ | /* Projection vars, to get the 3D locations into screen space */ | ||||
| MemArena *arena = ps->arena_mt[thread_index]; | MemArena *arena = ps->arena_mt[thread_index]; | ||||
| LinkNode **bucketPixelNodes = ps->bucketRect + bucket_index; | LinkNode **bucketPixelNodes = ps->bucketRect + bucket_index; | ||||
| LinkNode *bucketFaceNodes = ps->bucketFaces[bucket_index]; | LinkNode *bucketFaceNodes = ps->bucketFaces[bucket_index]; | ||||
| bool threaded = (ps->thread_tot > 1); | const bool threaded = (ps->thread_tot > 1); | ||||
| TileInfo tinf = { | TileInfo tinf = { | ||||
| ps->tile_lock, | ps->tile_lock, | ||||
| ps->do_masking, | ps->do_masking, | ||||
| IMAPAINT_TILE_NUMBER(ibuf->x), | IMAPAINT_TILE_NUMBER(ibuf->x), | ||||
| tmpibuf, | tmpibuf, | ||||
| ps->projImages + image_index, | ps->projImages + image_index, | ||||
| }; | }; | ||||
| ▲ Show 20 Lines • Show All 514 Lines • ▼ Show 20 Lines | for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { | ||||
| 0, | 0, | ||||
| clip_rect, | clip_rect, | ||||
| bucket_bounds, | bucket_bounds, | ||||
| ibuf, | ibuf, | ||||
| &tmpibuf); | &tmpibuf); | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| /* More complicated loop, switch between images */ | /* More complicated loop, switch between images */ | ||||
| for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { | for (node = ps->bucketFaces[bucket_index]; node; node = node->next) { | ||||
| tri_index = POINTER_AS_INT(node->link); | tri_index = POINTER_AS_INT(node->link); | ||||
| /* Image context switching */ | /* Image context switching */ | ||||
| tpage = project_paint_face_paint_image(ps, tri_index); | tpage = project_paint_face_paint_image(ps, tri_index); | ||||
| if (tpage_last != tpage) { | if (tpage_last != tpage) { | ||||
| tpage_last = tpage; | tpage_last = tpage; | ||||
| ▲ Show 20 Lines • Show All 1,122 Lines • ▼ Show 20 Lines | while (tot--) { | ||||
| pr++; | pr++; | ||||
| } | } | ||||
| } | } | ||||
| static bool partial_redraw_array_merge(ImagePaintPartialRedraw *pr, | static bool partial_redraw_array_merge(ImagePaintPartialRedraw *pr, | ||||
| ImagePaintPartialRedraw *pr_other, | ImagePaintPartialRedraw *pr_other, | ||||
| int tot) | int tot) | ||||
| { | { | ||||
| bool touch = 0; | bool touch = false; | ||||
| while (tot--) { | while (tot--) { | ||||
| pr->x1 = min_ii(pr->x1, pr_other->x1); | pr->x1 = min_ii(pr->x1, pr_other->x1); | ||||
| pr->y1 = min_ii(pr->y1, pr_other->y1); | pr->y1 = min_ii(pr->y1, pr_other->y1); | ||||
| pr->x2 = max_ii(pr->x2, pr_other->x2); | pr->x2 = max_ii(pr->x2, pr_other->x2); | ||||
| pr->y2 = max_ii(pr->y2, pr_other->y2); | pr->y2 = max_ii(pr->y2, pr_other->y2); | ||||
| if (pr->x2 != -1) { | if (pr->x2 != -1) { | ||||
| touch = 1; | touch = true; | ||||
| } | } | ||||
| pr++; | pr++; | ||||
| pr_other++; | pr_other++; | ||||
| } | } | ||||
| return touch; | return touch; | ||||
| } | } | ||||
| Show All 9 Lines | static bool project_image_refresh_tagged(ProjPaintState *ps) | ||||
| for (a = 0, projIma = ps->projImages; a < ps->image_tot; a++, projIma++) { | for (a = 0, projIma = ps->projImages; a < ps->image_tot; a++, projIma++) { | ||||
| if (projIma->touch) { | if (projIma->touch) { | ||||
| /* look over each bound cell */ | /* look over each bound cell */ | ||||
| for (i = 0; i < PROJ_BOUNDBOX_SQUARED; i++) { | for (i = 0; i < PROJ_BOUNDBOX_SQUARED; i++) { | ||||
| pr = &(projIma->partRedrawRect[i]); | pr = &(projIma->partRedrawRect[i]); | ||||
| if (pr->x2 != -1) { /* TODO - use 'enabled' ? */ | if (pr->x2 != -1) { /* TODO - use 'enabled' ? */ | ||||
| set_imapaintpartial(pr); | set_imapaintpartial(pr); | ||||
| imapaint_image_update(NULL, projIma->ima, projIma->ibuf, true); | imapaint_image_update(NULL, projIma->ima, projIma->ibuf, true); | ||||
| redraw = 1; | redraw = true; | ||||
| } | } | ||||
| partial_redraw_single_init(pr); | partial_redraw_single_init(pr); | ||||
| } | } | ||||
| /* clear for reuse */ | /* clear for reuse */ | ||||
| projIma->touch = 0; | projIma->touch = false; | ||||
| } | } | ||||
| } | } | ||||
| return redraw; | return redraw; | ||||
| } | } | ||||
| /* run this per painting onto each mouse location */ | /* run this per painting onto each mouse location */ | ||||
| static bool project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2]) | static bool project_bucket_iter_init(ProjPaintState *ps, const float mval_f[2]) | ||||
| Show All 11 Lines | if (ps->source == PROJ_SRC_VIEW) { | ||||
| max_brush[0] = mval_f[0] + radius; | max_brush[0] = mval_f[0] + radius; | ||||
| max_brush[1] = mval_f[1] + radius; | max_brush[1] = mval_f[1] + radius; | ||||
| /* offset to make this a valid bucket index */ | /* offset to make this a valid bucket index */ | ||||
| project_paint_bucket_bounds(ps, min_brush, max_brush, ps->bucketMin, ps->bucketMax); | project_paint_bucket_bounds(ps, min_brush, max_brush, ps->bucketMin, ps->bucketMax); | ||||
| /* mouse outside the model areas? */ | /* mouse outside the model areas? */ | ||||
| if (ps->bucketMin[0] == ps->bucketMax[0] || ps->bucketMin[1] == ps->bucketMax[1]) { | if (ps->bucketMin[0] == ps->bucketMax[0] || ps->bucketMin[1] == ps->bucketMax[1]) { | ||||
| return 0; | return false; | ||||
| } | } | ||||
| ps->context_bucket_x = ps->bucketMin[0]; | ps->context_bucket_x = ps->bucketMin[0]; | ||||
| ps->context_bucket_y = ps->bucketMin[1]; | ps->context_bucket_y = ps->bucketMin[1]; | ||||
| } | } | ||||
| else { /* reproject: PROJ_SRC_* */ | else { /* reproject: PROJ_SRC_* */ | ||||
| ps->bucketMin[0] = 0; | ps->bucketMin[0] = 0; | ||||
| ps->bucketMin[1] = 0; | ps->bucketMin[1] = 0; | ||||
| ps->bucketMax[0] = ps->buckets_x; | ps->bucketMax[0] = ps->buckets_x; | ||||
| ps->bucketMax[1] = ps->buckets_y; | ps->bucketMax[1] = ps->buckets_y; | ||||
| ps->context_bucket_x = 0; | ps->context_bucket_x = 0; | ||||
| ps->context_bucket_y = 0; | ps->context_bucket_y = 0; | ||||
| } | } | ||||
| return 1; | ps->context_bucket_index = ps->bucketMin[1] * ps->buckets_x + ps->bucketMin[0]; | ||||
| return true; | |||||
| } | } | ||||
| static bool project_bucket_iter_next(ProjPaintState *ps, | static bool project_bucket_iter_check(ProjPaintState *ps, | ||||
| int *bucket_index, | int bucket_index, | ||||
| rctf *bucket_bounds, | rctf *bucket_bounds, | ||||
| const float mval[2]) | const float mval[2]) | ||||
| { | { | ||||
| const int diameter = 2 * ps->brush_size; | const int diameter = (int)(2.0f * ps->brush_size); | ||||
| const int bucket_y = bucket_index / ps->buckets_x; | |||||
| if (ps->thread_tot > 1) { | const int bucket_x = bucket_index - (bucket_y * ps->buckets_x); | ||||
| BLI_thread_lock(LOCK_CUSTOM1); | |||||
| /* Note: we should not need to have to check for buckt_y value here, iterator is supposed to have | |||||
| * proper start/end values for that. */ | |||||
| BLI_assert(bucket_y >= ps->bucketMin[1] && bucket_y < ps->bucketMax[1]); | |||||
| if (bucket_x < ps->bucketMin[0] || bucket_x >= ps->bucketMax[0]) { | |||||
| return false; | |||||
| } | } | ||||
| // printf("%d %d\n", ps->context_bucket_x, ps->context_bucket_y); | |||||
| for (; ps->context_bucket_y < ps->bucketMax[1]; ps->context_bucket_y++) { | |||||
| for (; ps->context_bucket_x < ps->bucketMax[0]; ps->context_bucket_x++) { | |||||
| /* use bucket_bounds for project_bucket_isect_circle and project_bucket_init*/ | /* use bucket_bounds for project_bucket_isect_circle and project_bucket_init*/ | ||||
| project_bucket_bounds(ps, ps->context_bucket_x, ps->context_bucket_y, bucket_bounds); | project_bucket_bounds(ps, bucket_x, bucket_y, bucket_bounds); | ||||
| if ((ps->source != PROJ_SRC_VIEW) || | if ((ps->source != PROJ_SRC_VIEW) || | ||||
| project_bucket_isect_circle(mval, (float)(diameter * diameter), bucket_bounds)) { | project_bucket_isect_circle(mval, (float)(diameter * diameter), bucket_bounds)) { | ||||
| *bucket_index = ps->context_bucket_x + (ps->context_bucket_y * ps->buckets_x); | return true; | ||||
| ps->context_bucket_x++; | |||||
| if (ps->thread_tot > 1) { | |||||
| BLI_thread_unlock(LOCK_CUSTOM1); | |||||
| } | |||||
| return 1; | |||||
| } | |||||
| } | |||||
| ps->context_bucket_x = ps->bucketMin[0]; | |||||
| } | |||||
| if (ps->thread_tot > 1) { | |||||
| BLI_thread_unlock(LOCK_CUSTOM1); | |||||
| } | } | ||||
| return 0; | return false; | ||||
| } | } | ||||
| /* Each thread gets one of these, also used as an argument to pass to project_paint_op */ | /* Each thread gets one of these, also used as an argument to pass to project_paint_op */ | ||||
| typedef struct ProjectHandle { | typedef struct ProjectHandle { | ||||
| /* array of partial redraws */ | |||||
| ProjPaintImage *projImages; | |||||
| struct ImagePool *pool; | |||||
| /* args */ | /* args */ | ||||
| ProjPaintState *ps; | |||||
| float prevmval[2]; | float prevmval[2]; | ||||
| float mval[2]; | float mval[2]; | ||||
| /* Annoying but we need to have image bounds per thread, | /* Annoying but we need to have image bounds per thread, | ||||
| * then merge into ps->projectPartialRedraws. */ | * then merge into ps->projectPartialRedraws. */ | ||||
| /* array of partial redraws */ | /* Data used for tools that need post-processing (currently, smear and soften ones). */ | ||||
| ProjPaintImage *projImages; | LinkNode *pixels; | ||||
| LinkNode *pixels_f; | |||||
| /* thread settings */ | MemArena *memarena; | ||||
| int thread_index; | |||||
| struct ImagePool *pool; | |||||
| } ProjectHandle; | } ProjectHandle; | ||||
| static void do_projectpaint_clone(ProjPaintState *ps, ProjPixel *projPixel, float mask) | static void do_projectpaint_clone(ProjPaintState *ps, ProjPixel *projPixel, float mask) | ||||
| { | { | ||||
| const unsigned char *clone_pt = ((ProjPixelClone *)projPixel)->clonepx.ch; | const unsigned char *clone_pt = ((ProjPixelClone *)projPixel)->clonepx.ch; | ||||
| if (clone_pt[3]) { | if (clone_pt[3]) { | ||||
| unsigned char clone_rgba[4]; | unsigned char clone_rgba[4]; | ||||
| ▲ Show 20 Lines • Show All 299 Lines • ▼ Show 20 Lines | |||||
| { | { | ||||
| cell->x1 = min_ii(cell->x1, (int)projPixel->x_px); | cell->x1 = min_ii(cell->x1, (int)projPixel->x_px); | ||||
| cell->y1 = min_ii(cell->y1, (int)projPixel->y_px); | cell->y1 = min_ii(cell->y1, (int)projPixel->y_px); | ||||
| cell->x2 = max_ii(cell->x2, (int)projPixel->x_px + 1); | cell->x2 = max_ii(cell->x2, (int)projPixel->x_px + 1); | ||||
| cell->y2 = max_ii(cell->y2, (int)projPixel->y_px + 1); | cell->y2 = max_ii(cell->y2, (int)projPixel->y_px + 1); | ||||
| } | } | ||||
| /* Run this for single and multi-threaded painting. */ | static void project_paint_func(void *__restrict userdata, | ||||
| static void do_projectpaint_thread(TaskPool *__restrict UNUSED(pool), | const int bucket_index, | ||||
| void *ph_v, | const TaskParallelTLS *__restrict tls) | ||||
| int UNUSED(threadid)) | |||||
| { | { | ||||
| /* First unpack args from the struct */ | /* Shared data. */ | ||||
| ProjPaintState *ps = ((ProjectHandle *)ph_v)->ps; | ProjPaintState *ps = userdata; | ||||
| ProjPaintImage *projImages = ((ProjectHandle *)ph_v)->projImages; | |||||
| const float *lastpos = ((ProjectHandle *)ph_v)->prevmval; | |||||
| const float *pos = ((ProjectHandle *)ph_v)->mval; | |||||
| const int thread_index = ((ProjectHandle *)ph_v)->thread_index; | |||||
| struct ImagePool *pool = ((ProjectHandle *)ph_v)->pool; | |||||
| /* Done with args from ProjectHandle */ | |||||
| LinkNode *node; | |||||
| ProjPixel *projPixel; | |||||
| Brush *brush = ps->brush; | Brush *brush = ps->brush; | ||||
| const short tool = ps->tool; | |||||
| /* Task-local data. */ | |||||
| const int thread_index = tls->thread_id; | |||||
| ProjectHandle *ph = tls->userdata_chunk; | |||||
| const float *lastpos = ph->prevmval; | |||||
| const float *pos = ph->mval; | |||||
| struct ImagePool *pool = ph->pool; | |||||
| rctf bucket_bounds; | |||||
| if (!project_bucket_iter_check(ps, bucket_index, &bucket_bounds, pos)) { | |||||
| /* This bucket is not affected by our current position, early abort. */ | |||||
| return; | |||||
| } | |||||
| int last_index = -1; | int last_index = -1; | ||||
| ProjPaintImage *last_projIma = NULL; | ProjPaintImage *last_projIma = NULL; | ||||
| ImagePaintPartialRedraw *last_partial_redraw_cell; | ImagePaintPartialRedraw *last_partial_redraw_cell; | ||||
| float dist_sq, dist; | |||||
| float falloff; | |||||
| int bucket_index; | |||||
| bool is_floatbuf = false; | bool is_floatbuf = false; | ||||
| const short tool = ps->tool; | |||||
| rctf bucket_bounds; | |||||
| /* for smear only */ | |||||
| float pos_ofs[2] = {0}; | |||||
| float co[2]; | |||||
| unsigned short mask_short; | |||||
| const float brush_alpha = BKE_brush_alpha_get(ps->scene, brush); | const float brush_alpha = BKE_brush_alpha_get(ps->scene, brush); | ||||
| const float brush_radius = ps->brush_size; | const float brush_radius = ps->brush_size; | ||||
| /* avoid a square root with every dist comparison */ | /* avoid a square root with every dist comparison */ | ||||
| const float brush_radius_sq = brush_radius * brush_radius; | const float brush_radius_sq = brush_radius * brush_radius; | ||||
| const bool lock_alpha = ELEM(brush->blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA) ? | const bool lock_alpha = ELEM(brush->blend, IMB_BLEND_ERASE_ALPHA, IMB_BLEND_ADD_ALPHA) ? | ||||
| 0 : | false : | ||||
| (brush->flag & BRUSH_LOCK_ALPHA) != 0; | (brush->flag & BRUSH_LOCK_ALPHA) != 0; | ||||
| LinkNode *smearPixels = NULL; | /* for smear only */ | ||||
| LinkNode *smearPixels_f = NULL; | float pos_diff[2] = {0}; | ||||
| /* mem arena for this brush projection only */ | |||||
| MemArena *smearArena = NULL; | |||||
| LinkNode *softenPixels = NULL; | |||||
| LinkNode *softenPixels_f = NULL; | |||||
| /* mem arena for this brush projection only */ | |||||
| MemArena *softenArena = NULL; | |||||
| if (tool == PAINT_TOOL_SMEAR) { | if (tool == PAINT_TOOL_SMEAR) { | ||||
| pos_ofs[0] = pos[0] - lastpos[0]; | pos_diff[0] = pos[0] - lastpos[0]; | ||||
| pos_ofs[1] = pos[1] - lastpos[1]; | pos_diff[1] = pos[1] - lastpos[1]; | ||||
| } | |||||
| if (ph->memarena == NULL) { | |||||
| BLI_assert(ph->projImages == NULL); | |||||
| /* Only done once, that init we cannot do from caller of BLI_task_parallel_range()... */ | |||||
| MemArena *memarena = ph->memarena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), __func__); | |||||
| const size_t size_proj_images = (size_t)ps->image_tot * sizeof(*ph->projImages); | |||||
| ph->projImages = BLI_memarena_alloc(memarena, size_proj_images); | |||||
| memcpy(ph->projImages, ps->projImages, size_proj_images); | |||||
| smearArena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "paint smear arena"); | /* image bounds */ | ||||
| const size_t size_part_redraw_rect = sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED; | |||||
| for (int i = 0; i < ps->image_tot; i++) { | |||||
| ph->projImages[i].partRedrawRect = BLI_memarena_alloc(memarena, size_part_redraw_rect); | |||||
| memcpy(ph->projImages[i].partRedrawRect, | |||||
| ps->projImages[i].partRedrawRect, | |||||
| size_part_redraw_rect); | |||||
| } | } | ||||
| else if (tool == PAINT_TOOL_SOFTEN) { | |||||
| softenArena = BLI_memarena_new(MEM_SIZE_OPTIMAL(1 << 16), "paint soften arena"); | |||||
| } | } | ||||
| ProjPaintImage *projImages = ph->projImages; | |||||
| /* printf("brush bounds %d %d %d %d\n", | /* printf("brush bounds %d %d %d %d\n", | ||||
| * bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]); */ | * bucketMin[0], bucketMin[1], bucketMax[0], bucketMax[1]); */ | ||||
| while (project_bucket_iter_next(ps, &bucket_index, &bucket_bounds, pos)) { | |||||
| /* Check this bucket and its faces are initialized */ | /* Check this bucket and its faces are initialized */ | ||||
| if (ps->bucketFlags[bucket_index] == PROJ_BUCKET_NULL) { | if (ps->bucketFlags[bucket_index] == PROJ_BUCKET_NULL) { | ||||
| rctf clip_rect = bucket_bounds; | rctf clip_rect = bucket_bounds; | ||||
| clip_rect.xmin -= PROJ_PIXEL_TOLERANCE; | clip_rect.xmin -= PROJ_PIXEL_TOLERANCE; | ||||
| clip_rect.xmax += PROJ_PIXEL_TOLERANCE; | clip_rect.xmax += PROJ_PIXEL_TOLERANCE; | ||||
| clip_rect.ymin -= PROJ_PIXEL_TOLERANCE; | clip_rect.ymin -= PROJ_PIXEL_TOLERANCE; | ||||
| clip_rect.ymax += PROJ_PIXEL_TOLERANCE; | clip_rect.ymax += PROJ_PIXEL_TOLERANCE; | ||||
| /* No pixels initialized */ | /* No pixels initialized */ | ||||
| /* Note that this is handling TLS-like data, but that has to be alive beyond that specific | |||||
| * threaded session (it's valid for a whole stroke). So we keep it inside shared | |||||
| * ProjPaintState data, using thread_id as index to get a persistent memarena from ps to | |||||
| * allocate from... */ | |||||
| project_bucket_init(ps, thread_index, bucket_index, &clip_rect, &bucket_bounds); | project_bucket_init(ps, thread_index, bucket_index, &clip_rect, &bucket_bounds); | ||||
| } | } | ||||
| if (ps->source != PROJ_SRC_VIEW) { | if (ps->source != PROJ_SRC_VIEW) { | ||||
| /* Re-Projection, simple, no brushes! */ | /* Re-Projection, simple, no brushes! */ | ||||
| for (LinkNode *node = ps->bucketRect[bucket_index]; node; node = node->next) { | |||||
| for (node = ps->bucketRect[bucket_index]; node; node = node->next) { | ProjPixel *projPixel = (ProjPixel *)node->link; | ||||
| projPixel = (ProjPixel *)node->link; | |||||
| /* copy of code below */ | /* copy of code below */ | ||||
| if (last_index != projPixel->image_index) { | if (last_index != projPixel->image_index) { | ||||
| last_index = projPixel->image_index; | last_index = projPixel->image_index; | ||||
| last_projIma = projImages + last_index; | last_projIma = projImages + last_index; | ||||
| last_projIma->touch = 1; | last_projIma->touch = true; | ||||
| is_floatbuf = (last_projIma->ibuf->rect_float != NULL); | is_floatbuf = (last_projIma->ibuf->rect_float != NULL); | ||||
| } | } | ||||
| /* end copy */ | /* end copy */ | ||||
| /* fill tools */ | /* fill tools */ | ||||
| if (ps->source == PROJ_SRC_VIEW_FILL) { | if (ps->source == PROJ_SRC_VIEW_FILL) { | ||||
| if (brush->flag & BRUSH_USE_GRADIENT) { | if (brush->flag & BRUSH_USE_GRADIENT) { | ||||
| /* these could probably be cached instead of being done per pixel */ | /* these could probably be cached instead of being done per pixel */ | ||||
| float tangent[2]; | float tangent[2]; | ||||
| float line_len_sq_inv, line_len; | float line_len_sq_inv, line_len; | ||||
| float f; | float f; | ||||
| float color_f[4]; | float color_f[4]; | ||||
| float p[2] = {projPixel->projCoSS[0] - lastpos[0], | float p[2] = {projPixel->projCoSS[0] - lastpos[0], projPixel->projCoSS[1] - lastpos[1]}; | ||||
| projPixel->projCoSS[1] - lastpos[1]}; | |||||
| sub_v2_v2v2(tangent, pos, lastpos); | sub_v2_v2v2(tangent, pos, lastpos); | ||||
| line_len = len_squared_v2(tangent); | line_len = len_squared_v2(tangent); | ||||
| line_len_sq_inv = 1.0f / line_len; | line_len_sq_inv = 1.0f / line_len; | ||||
| line_len = sqrtf(line_len); | line_len = sqrtf(line_len); | ||||
| switch (brush->gradient_fill_mode) { | switch (brush->gradient_fill_mode) { | ||||
| case BRUSH_GRADIENT_LINEAR: { | case BRUSH_GRADIENT_LINEAR: { | ||||
| f = dot_v2v2(p, tangent) * line_len_sq_inv; | f = dot_v2v2(p, tangent) * line_len_sq_inv; | ||||
| break; | break; | ||||
| } | } | ||||
| case BRUSH_GRADIENT_RADIAL: | case BRUSH_GRADIENT_RADIAL: | ||||
| default: { | default: { | ||||
| f = len_v2(p) / line_len; | f = len_v2(p) / line_len; | ||||
| break; | break; | ||||
| } | } | ||||
| } | } | ||||
| BKE_colorband_evaluate(brush->gradient, f, color_f); | BKE_colorband_evaluate(brush->gradient, f, color_f); | ||||
| color_f[3] *= ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; | color_f[3] *= ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; | ||||
| if (is_floatbuf) { | if (is_floatbuf) { | ||||
| /* convert to premultipied */ | /* convert to premultipied */ | ||||
| mul_v3_fl(color_f, color_f[3]); | mul_v3_fl(color_f, color_f[3]); | ||||
| IMB_blend_color_float( | IMB_blend_color_float( | ||||
| projPixel->pixel.f_pt, projPixel->origColor.f_pt, color_f, ps->blend); | projPixel->pixel.f_pt, projPixel->origColor.f_pt, color_f, ps->blend); | ||||
| } | } | ||||
| else { | else { | ||||
| linearrgb_to_srgb_v3_v3(color_f, color_f); | linearrgb_to_srgb_v3_v3(color_f, color_f); | ||||
| if (ps->dither > 0.0f) { | if (ps->dither > 0.0f) { | ||||
| float_to_byte_dither_v3( | float_to_byte_dither_v3( | ||||
| projPixel->newColor.ch, color_f, ps->dither, projPixel->x_px, projPixel->y_px); | projPixel->newColor.ch, color_f, ps->dither, projPixel->x_px, projPixel->y_px); | ||||
| } | } | ||||
| else { | else { | ||||
| unit_float_to_uchar_clamp_v3(projPixel->newColor.ch, color_f); | unit_float_to_uchar_clamp_v3(projPixel->newColor.ch, color_f); | ||||
| } | } | ||||
| projPixel->newColor.ch[3] = unit_float_to_uchar_clamp(color_f[3]); | projPixel->newColor.ch[3] = unit_float_to_uchar_clamp(color_f[3]); | ||||
| IMB_blend_color_byte(projPixel->pixel.ch_pt, | IMB_blend_color_byte(projPixel->pixel.ch_pt, | ||||
| projPixel->origColor.ch_pt, | projPixel->origColor.ch_pt, | ||||
| projPixel->newColor.ch, | projPixel->newColor.ch, | ||||
| ps->blend); | ps->blend); | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| if (is_floatbuf) { | if (is_floatbuf) { | ||||
| float newColor_f[4]; | float newColor_f[4]; | ||||
| newColor_f[3] = ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; | newColor_f[3] = ((float)projPixel->mask) * (1.0f / 65535.0f) * brush->alpha; | ||||
| copy_v3_v3(newColor_f, ps->paint_color_linear); | copy_v3_v3(newColor_f, ps->paint_color_linear); | ||||
| IMB_blend_color_float( | IMB_blend_color_float( | ||||
| projPixel->pixel.f_pt, projPixel->origColor.f_pt, newColor_f, ps->blend); | projPixel->pixel.f_pt, projPixel->origColor.f_pt, newColor_f, ps->blend); | ||||
| } | } | ||||
| else { | else { | ||||
| float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); | float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); | ||||
| projPixel->newColor.ch[3] = mask * 255 * brush->alpha; | projPixel->newColor.ch[3] = mask * 255 * brush->alpha; | ||||
| rgb_float_to_uchar(projPixel->newColor.ch, ps->paint_color); | rgb_float_to_uchar(projPixel->newColor.ch, ps->paint_color); | ||||
| IMB_blend_color_byte(projPixel->pixel.ch_pt, | IMB_blend_color_byte(projPixel->pixel.ch_pt, | ||||
| projPixel->origColor.ch_pt, | projPixel->origColor.ch_pt, | ||||
| projPixel->newColor.ch, | projPixel->newColor.ch, | ||||
| ps->blend); | ps->blend); | ||||
| } | } | ||||
| } | } | ||||
| if (lock_alpha) { | if (lock_alpha) { | ||||
| if (is_floatbuf) { | if (is_floatbuf) { | ||||
| /* slightly more involved case since floats are in premultiplied space we need | /* slightly more involved case since floats are in premultiplied space we need | ||||
| * to make sure alpha is consistent, see T44627 */ | * to make sure alpha is consistent, see T44627 */ | ||||
| float rgb_straight[4]; | float rgb_straight[4]; | ||||
| premul_to_straight_v4_v4(rgb_straight, projPixel->pixel.f_pt); | premul_to_straight_v4_v4(rgb_straight, projPixel->pixel.f_pt); | ||||
| rgb_straight[3] = projPixel->origColor.f_pt[3]; | rgb_straight[3] = projPixel->origColor.f_pt[3]; | ||||
| straight_to_premul_v4_v4(projPixel->pixel.f_pt, rgb_straight); | straight_to_premul_v4_v4(projPixel->pixel.f_pt, rgb_straight); | ||||
| } | } | ||||
| else { | else { | ||||
| projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; | projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; | ||||
| } | } | ||||
| } | } | ||||
| last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; | last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; | ||||
| image_paint_partial_redraw_expand(last_partial_redraw_cell, projPixel); | image_paint_partial_redraw_expand(last_partial_redraw_cell, projPixel); | ||||
| } | } | ||||
| else { | else { | ||||
| if (is_floatbuf) { | if (is_floatbuf) { | ||||
| if (UNLIKELY(ps->reproject_ibuf->rect_float == NULL)) { | if (UNLIKELY(ps->reproject_ibuf->rect_float == NULL)) { | ||||
| IMB_float_from_rect(ps->reproject_ibuf); | IMB_float_from_rect(ps->reproject_ibuf); | ||||
| ps->reproject_ibuf_free_float = true; | ps->reproject_ibuf_free_float = true; | ||||
| } | } | ||||
| bicubic_interpolation_color(ps->reproject_ibuf, | bicubic_interpolation_color(ps->reproject_ibuf, | ||||
| NULL, | NULL, | ||||
| projPixel->newColor.f, | projPixel->newColor.f, | ||||
| projPixel->projCoSS[0], | projPixel->projCoSS[0], | ||||
| projPixel->projCoSS[1]); | projPixel->projCoSS[1]); | ||||
| if (projPixel->newColor.f[3]) { | if (projPixel->newColor.f[3]) { | ||||
| float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); | float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); | ||||
| mul_v4_v4fl(projPixel->newColor.f, projPixel->newColor.f, mask); | mul_v4_v4fl(projPixel->newColor.f, projPixel->newColor.f, mask); | ||||
| blend_color_mix_float( | blend_color_mix_float( | ||||
| projPixel->pixel.f_pt, projPixel->origColor.f_pt, projPixel->newColor.f); | projPixel->pixel.f_pt, projPixel->origColor.f_pt, projPixel->newColor.f); | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| if (UNLIKELY(ps->reproject_ibuf->rect == NULL)) { | if (UNLIKELY(ps->reproject_ibuf->rect == NULL)) { | ||||
| IMB_rect_from_float(ps->reproject_ibuf); | IMB_rect_from_float(ps->reproject_ibuf); | ||||
| ps->reproject_ibuf_free_uchar = true; | ps->reproject_ibuf_free_uchar = true; | ||||
| } | } | ||||
| bicubic_interpolation_color(ps->reproject_ibuf, | bicubic_interpolation_color(ps->reproject_ibuf, | ||||
| projPixel->newColor.ch, | projPixel->newColor.ch, | ||||
| NULL, | NULL, | ||||
| projPixel->projCoSS[0], | projPixel->projCoSS[0], | ||||
| projPixel->projCoSS[1]); | projPixel->projCoSS[1]); | ||||
| if (projPixel->newColor.ch[3]) { | if (projPixel->newColor.ch[3]) { | ||||
| float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); | float mask = ((float)projPixel->mask) * (1.0f / 65535.0f); | ||||
| projPixel->newColor.ch[3] *= mask; | projPixel->newColor.ch[3] *= mask; | ||||
| blend_color_mix_byte( | blend_color_mix_byte( | ||||
| projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, projPixel->newColor.ch); | projPixel->pixel.ch_pt, projPixel->origColor.ch_pt, projPixel->newColor.ch); | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| /* Normal brush painting */ | /* Normal brush painting */ | ||||
| for (LinkNode *node = ps->bucketRect[bucket_index]; node; node = node->next) { | |||||
| ProjPixel *projPixel = (ProjPixel *)node->link; | |||||
| for (node = ps->bucketRect[bucket_index]; node; node = node->next) { | const float dist_sq = len_squared_v2v2(projPixel->projCoSS, pos); | ||||
| projPixel = (ProjPixel *)node->link; | |||||
| dist_sq = len_squared_v2v2(projPixel->projCoSS, pos); | |||||
| /*if (dist < radius) {*/ /* correct but uses a sqrtf */ | |||||
| if (dist_sq <= brush_radius_sq) { | if (dist_sq <= brush_radius_sq) { | ||||
| dist = sqrtf(dist_sq); | const float dist = sqrtf(dist_sq); | ||||
| falloff = BKE_brush_curve_strength_clamped(ps->brush, dist, brush_radius); | const float falloff = BKE_brush_curve_strength_clamped(ps->brush, dist, brush_radius); | ||||
| if (falloff > 0.0f) { | if (falloff > 0.0f) { | ||||
| float texrgb[3]; | float texrgb[3]; | ||||
| float mask; | float mask; | ||||
| /* Extra mask for normal, layer stencil, .. */ | /* Extra mask for normal, layer stencil, .. */ | ||||
| float custom_mask = ((float)projPixel->mask) * (1.0f / 65535.0f); | float custom_mask = ((float)projPixel->mask) * (1.0f / 65535.0f); | ||||
| /* Mask texture. */ | /* Mask texture. */ | ||||
| if (ps->is_maskbrush) { | if (ps->is_maskbrush) { | ||||
| float texmask = BKE_brush_sample_masktex( | float texmask = BKE_brush_sample_masktex( | ||||
| ps->scene, ps->brush, projPixel->projCoSS, thread_index, pool); | ps->scene, ps->brush, projPixel->projCoSS, thread_index, pool); | ||||
| CLAMP(texmask, 0.0f, 1.0f); | CLAMP(texmask, 0.0f, 1.0f); | ||||
| custom_mask *= texmask; | custom_mask *= texmask; | ||||
| } | } | ||||
| /* Color texture (alpha used as mask). */ | /* Color texture (alpha used as mask). */ | ||||
| if (ps->is_texbrush) { | if (ps->is_texbrush) { | ||||
| MTex *mtex = &brush->mtex; | MTex *mtex = &brush->mtex; | ||||
| float samplecos[3]; | float samplecos[3]; | ||||
| float texrgba[4]; | float texrgba[4]; | ||||
| /* taking 3d copy to account for 3D mapping too. | /* taking 3d copy to account for 3D mapping too. | ||||
| * It gets concatenated during sampling */ | * It gets concatenated during sampling */ | ||||
| if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { | if (mtex->brush_map_mode == MTEX_MAP_MODE_3D) { | ||||
| copy_v3_v3(samplecos, projPixel->worldCoSS); | copy_v3_v3(samplecos, projPixel->worldCoSS); | ||||
| } | } | ||||
| else { | else { | ||||
| copy_v2_v2(samplecos, projPixel->projCoSS); | copy_v2_v2(samplecos, projPixel->projCoSS); | ||||
| samplecos[2] = 0.0f; | samplecos[2] = 0.0f; | ||||
| } | } | ||||
| /* note, for clone and smear, | /* note, for clone and smear, | ||||
| * we only use the alpha, could be a special function */ | * we only use the alpha, could be a special function */ | ||||
| BKE_brush_sample_tex_3d(ps->scene, brush, samplecos, texrgba, thread_index, pool); | BKE_brush_sample_tex_3d(ps->scene, brush, samplecos, texrgba, thread_index, pool); | ||||
| copy_v3_v3(texrgb, texrgba); | copy_v3_v3(texrgb, texrgba); | ||||
| custom_mask *= texrgba[3]; | custom_mask *= texrgba[3]; | ||||
| } | } | ||||
| else { | else { | ||||
| zero_v3(texrgb); | zero_v3(texrgb); | ||||
| } | } | ||||
| if (ps->do_masking) { | if (ps->do_masking) { | ||||
| /* masking to keep brush contribution to a pixel limited. note we do not do | /* masking to keep brush contribution to a pixel limited. note we do not do | ||||
| * a simple max(mask, mask_accum), as this is very sensitive to spacing and | * a simple max(mask, mask_accum), as this is very sensitive to spacing and | ||||
| * gives poor results for strokes crossing themselves. | * gives poor results for strokes crossing themselves. | ||||
| * | * | ||||
| * Instead we use a formula that adds up but approaches brush_alpha slowly | * Instead we use a formula that adds up but approaches brush_alpha slowly | ||||
| * and never exceeds it, which gives nice smooth results. */ | * and never exceeds it, which gives nice smooth results. */ | ||||
| float mask_accum = *projPixel->mask_accum; | float mask_accum = *projPixel->mask_accum; | ||||
| float max_mask = brush_alpha * custom_mask * falloff * 65535.0f; | float max_mask = brush_alpha * custom_mask * falloff * 65535.0f; | ||||
| if (brush->flag & BRUSH_ACCUMULATE) { | if (brush->flag & BRUSH_ACCUMULATE) { | ||||
| mask = mask_accum + max_mask; | mask = mask_accum + max_mask; | ||||
| } | } | ||||
| else { | else { | ||||
| mask = mask_accum + (max_mask - mask_accum * falloff); | mask = mask_accum + (max_mask - mask_accum * falloff); | ||||
| } | } | ||||
| mask = min_ff(mask, 65535.0f); | mask = min_ff(mask, 65535.0f); | ||||
| mask_short = (unsigned short)mask; | const ushort mask_short = (ushort)mask; | ||||
| if (mask_short > *projPixel->mask_accum) { | if (mask_short > *projPixel->mask_accum) { | ||||
| *projPixel->mask_accum = mask_short; | *projPixel->mask_accum = mask_short; | ||||
| mask = mask_short * (1.0f / 65535.0f); | mask = mask_short * (1.0f / 65535.0f); | ||||
| } | } | ||||
| else { | else { | ||||
| /* Go onto the next pixel */ | /* Go onto the next pixel */ | ||||
| continue; | continue; | ||||
| } | } | ||||
| } | } | ||||
| else { | else { | ||||
| mask = brush_alpha * custom_mask * falloff; | mask = brush_alpha * custom_mask * falloff; | ||||
| } | } | ||||
| if (mask > 0.0f) { | if (mask > 0.0f) { | ||||
| /* copy of code above */ | /* copy of code above */ | ||||
| if (last_index != projPixel->image_index) { | if (last_index != projPixel->image_index) { | ||||
| last_index = projPixel->image_index; | last_index = projPixel->image_index; | ||||
| last_projIma = projImages + last_index; | last_projIma = projImages + last_index; | ||||
| last_projIma->touch = 1; | last_projIma->touch = true; | ||||
| is_floatbuf = (last_projIma->ibuf->rect_float != NULL); | is_floatbuf = (last_projIma->ibuf->rect_float != NULL); | ||||
| } | } | ||||
| /* end copy */ | /* end copy */ | ||||
| /* validate undo tile, since we will modify t*/ | /* validate undo tile, since we will modify t*/ | ||||
| *projPixel->valid = true; | *projPixel->valid = true; | ||||
| last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; | last_partial_redraw_cell = last_projIma->partRedrawRect + projPixel->bb_cell_index; | ||||
| image_paint_partial_redraw_expand(last_partial_redraw_cell, projPixel); | image_paint_partial_redraw_expand(last_partial_redraw_cell, projPixel); | ||||
| /* texrgb is not used for clone, smear or soften */ | /* texrgb is not used for clone, smear or soften */ | ||||
| switch (tool) { | switch (tool) { | ||||
| case PAINT_TOOL_CLONE: | case PAINT_TOOL_CLONE: | ||||
| if (is_floatbuf) { | if (is_floatbuf) { | ||||
| do_projectpaint_clone_f(ps, projPixel, mask); | do_projectpaint_clone_f(ps, projPixel, mask); | ||||
| } | } | ||||
| else { | else { | ||||
| do_projectpaint_clone(ps, projPixel, mask); | do_projectpaint_clone(ps, projPixel, mask); | ||||
| } | } | ||||
| break; | break; | ||||
| case PAINT_TOOL_SMEAR: | case PAINT_TOOL_SMEAR: { | ||||
| sub_v2_v2v2(co, projPixel->projCoSS, pos_ofs); | float co[2]; | ||||
| sub_v2_v2v2(co, projPixel->projCoSS, pos_diff); | |||||
| if (is_floatbuf) { | if (is_floatbuf) { | ||||
| do_projectpaint_smear_f(ps, projPixel, mask, smearArena, &smearPixels_f, co); | do_projectpaint_smear_f(ps, projPixel, mask, ph->memarena, &ph->pixels_f, co); | ||||
| } | } | ||||
| else { | else { | ||||
| do_projectpaint_smear(ps, projPixel, mask, smearArena, &smearPixels, co); | do_projectpaint_smear(ps, projPixel, mask, ph->memarena, &ph->pixels, co); | ||||
| } | } | ||||
| break; | break; | ||||
| } | |||||
| case PAINT_TOOL_SOFTEN: | case PAINT_TOOL_SOFTEN: | ||||
| if (is_floatbuf) { | if (is_floatbuf) { | ||||
| do_projectpaint_soften_f(ps, projPixel, mask, softenArena, &softenPixels_f); | do_projectpaint_soften_f(ps, projPixel, mask, ph->memarena, &ph->pixels); | ||||
| } | } | ||||
| else { | else { | ||||
| do_projectpaint_soften(ps, projPixel, mask, softenArena, &softenPixels); | do_projectpaint_soften(ps, projPixel, mask, ph->memarena, &ph->pixels); | ||||
| } | } | ||||
| break; | break; | ||||
| case PAINT_TOOL_MASK: | case PAINT_TOOL_MASK: | ||||
| if (is_floatbuf) { | if (is_floatbuf) { | ||||
| do_projectpaint_mask_f(ps, projPixel, mask); | do_projectpaint_mask_f(ps, projPixel, mask); | ||||
| } | } | ||||
| else { | else { | ||||
| do_projectpaint_mask(ps, projPixel, mask); | do_projectpaint_mask(ps, projPixel, mask); | ||||
| } | } | ||||
| break; | break; | ||||
| default: | default: | ||||
| if (is_floatbuf) { | if (is_floatbuf) { | ||||
| do_projectpaint_draw_f(ps, projPixel, texrgb, mask); | do_projectpaint_draw_f(ps, projPixel, texrgb, mask); | ||||
| } | } | ||||
| else { | else { | ||||
| do_projectpaint_draw( | do_projectpaint_draw( | ||||
| ps, projPixel, texrgb, mask, ps->dither, projPixel->x_px, projPixel->y_px); | ps, projPixel, texrgb, mask, ps->dither, projPixel->x_px, projPixel->y_px); | ||||
| } | } | ||||
| break; | break; | ||||
| } | } | ||||
| if (lock_alpha) { | if (lock_alpha) { | ||||
| if (is_floatbuf) { | if (is_floatbuf) { | ||||
| /* slightly more involved case since floats are in premultiplied space we need | /* slightly more involved case since floats are in premultiplied space we need | ||||
| * to make sure alpha is consistent, see T44627 */ | * to make sure alpha is consistent, see T44627 */ | ||||
| float rgb_straight[4]; | float rgb_straight[4]; | ||||
| premul_to_straight_v4_v4(rgb_straight, projPixel->pixel.f_pt); | premul_to_straight_v4_v4(rgb_straight, projPixel->pixel.f_pt); | ||||
| rgb_straight[3] = projPixel->origColor.f_pt[3]; | rgb_straight[3] = projPixel->origColor.f_pt[3]; | ||||
| straight_to_premul_v4_v4(projPixel->pixel.f_pt, rgb_straight); | straight_to_premul_v4_v4(projPixel->pixel.f_pt, rgb_straight); | ||||
| } | } | ||||
| else { | else { | ||||
| projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; | projPixel->pixel.ch_pt[3] = projPixel->origColor.ch_pt[3]; | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| /* done painting */ | /* done painting */ | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| } | } | ||||
| if (tool == PAINT_TOOL_SMEAR) { | static void project_paint_finalize(void *__restrict userdata, void *__restrict userdata_chunk) | ||||
| { | |||||
| for (node = smearPixels; node; node = node->next) { /* this wont run for a float image */ | /* Shared data. */ | ||||
| projPixel = node->link; | ProjPaintState *ps = userdata; | ||||
| *projPixel->pixel.uint_pt = ((ProjPixelClone *)projPixel)->clonepx.uint; | const short tool = ps->tool; | ||||
| } | |||||
| for (node = smearPixels_f; node; node = node->next) { | /* Task-local data. */ | ||||
| projPixel = node->link; | ProjectHandle *ph = userdata_chunk; | ||||
| copy_v4_v4(projPixel->pixel.f_pt, ((ProjPixelClone *)projPixel)->clonepx.f); | |||||
| } | |||||
| BLI_memarena_free(smearArena); | if (ph->memarena == NULL) { | ||||
| /* That task/thread had no time to be initialized, i.e. it did absolutely nothing, we can | |||||
| * return early. */ | |||||
| return; | |||||
| } | } | ||||
| else if (tool == PAINT_TOOL_SOFTEN) { | |||||
| for (node = softenPixels; node; node = node->next) { /* this wont run for a float image */ | if (ELEM(tool, PAINT_TOOL_SMEAR, PAINT_TOOL_SOFTEN)) { | ||||
| projPixel = node->link; | LinkNode *node; | ||||
| for (node = ph->pixels; node; node = node->next) { | |||||
| ProjPixel *projPixel = node->link; | |||||
| switch (tool) { | |||||
| case PAINT_TOOL_SMEAR: | |||||
| *projPixel->pixel.uint_pt = ((ProjPixelClone *)projPixel)->clonepx.uint; | |||||
| break; | |||||
| case PAINT_TOOL_SOFTEN: | |||||
| *projPixel->pixel.uint_pt = projPixel->newColor.uint; | *projPixel->pixel.uint_pt = projPixel->newColor.uint; | ||||
| break; | |||||
| } | |||||
| } | } | ||||
| for (node = softenPixels_f; node; node = node->next) { | for (node = ph->pixels_f; node; node = node->next) { | ||||
| projPixel = node->link; | ProjPixel *projPixel = node->link; | ||||
| switch (tool) { | |||||
| case PAINT_TOOL_SMEAR: | |||||
| copy_v4_v4(projPixel->pixel.f_pt, ((ProjPixelClone *)projPixel)->clonepx.f); | |||||
| break; | |||||
| case PAINT_TOOL_SOFTEN: | |||||
| copy_v4_v4(projPixel->pixel.f_pt, projPixel->newColor.f); | copy_v4_v4(projPixel->pixel.f_pt, projPixel->newColor.f); | ||||
| break; | |||||
| } | } | ||||
| BLI_memarena_free(softenArena); | |||||
| } | } | ||||
| } | } | ||||
| static bool project_paint_op(void *state, const float lastpos[2], const float pos[2]) | /* Move threaded images buckets back into shared ones. */ | ||||
| { | for (int i = 0; i < ps->image_tot; i++) { | ||||
| /* First unpack args from the struct */ | const bool touch = partial_redraw_array_merge( | ||||
| ProjPaintState *ps = (ProjPaintState *)state; | ps->projImages[i].partRedrawRect, ph->projImages[i].partRedrawRect, PROJ_BOUNDBOX_SQUARED); | ||||
| bool touch_any = false; | |||||
| ProjectHandle handles[BLENDER_MAX_THREADS]; | |||||
| TaskScheduler *scheduler = NULL; | |||||
| TaskPool *task_pool = NULL; | |||||
| int a, i; | |||||
| struct ImagePool *image_pool; | if (touch) { | ||||
| ps->projImages[i].touch = true; | |||||
| if (!project_bucket_iter_init(ps, pos)) { | ps->proj_images_touch_any = true; | ||||
| return touch_any; | |||||
| } | } | ||||
| if (ps->thread_tot > 1) { | |||||
| scheduler = BLI_task_scheduler_get(); | |||||
| task_pool = BLI_task_pool_create_suspended(scheduler, NULL); | |||||
| } | } | ||||
| image_pool = BKE_image_pool_new(); | BLI_memarena_free(ph->memarena); | ||||
| ph->memarena = NULL; | |||||
| /* get the threads running */ | |||||
| for (a = 0; a < ps->thread_tot; a++) { | |||||
| /* set defaults in handles */ | |||||
| // memset(&handles[a], 0, sizeof(BakeShade)); | |||||
| handles[a].ps = ps; | |||||
| copy_v2_v2(handles[a].mval, pos); | |||||
| copy_v2_v2(handles[a].prevmval, lastpos); | |||||
| /* thread specific */ | |||||
| handles[a].thread_index = a; | |||||
| handles[a].projImages = BLI_memarena_alloc(ps->arena_mt[a], | |||||
| ps->image_tot * sizeof(ProjPaintImage)); | |||||
| memcpy(handles[a].projImages, ps->projImages, ps->image_tot * sizeof(ProjPaintImage)); | |||||
| /* image bounds */ | |||||
| for (i = 0; i < ps->image_tot; i++) { | |||||
| handles[a].projImages[i].partRedrawRect = BLI_memarena_alloc( | |||||
| ps->arena_mt[a], sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); | |||||
| memcpy(handles[a].projImages[i].partRedrawRect, | |||||
| ps->projImages[i].partRedrawRect, | |||||
| sizeof(ImagePaintPartialRedraw) * PROJ_BOUNDBOX_SQUARED); | |||||
| } | } | ||||
| handles[a].pool = image_pool; | static bool project_paint_op(void *state, const float lastpos[2], const float pos[2]) | ||||
| { | |||||
| if (task_pool != NULL) { | /* First unpack args from the struct */ | ||||
| BLI_task_pool_push( | ProjPaintState *ps = (ProjPaintState *)state; | ||||
| task_pool, do_projectpaint_thread, &handles[a], false, TASK_PRIORITY_HIGH); | ps->proj_images_touch_any = false; | ||||
| } | |||||
| } | |||||
| if (task_pool != NULL) { /* wait for everything to be done */ | if (!project_bucket_iter_init(ps, pos)) { | ||||
| BLI_task_pool_work_and_wait(task_pool); | return ps->proj_images_touch_any; | ||||
| BLI_task_pool_free(task_pool); | |||||
| } | |||||
| else { | |||||
| do_projectpaint_thread(NULL, &handles[0], 0); | |||||
| } | } | ||||
| BKE_image_pool_free(image_pool); | ProjectHandle ph_tls = {0}; | ||||
| copy_v2_v2(ph_tls.mval, pos); | |||||
| copy_v2_v2(ph_tls.prevmval, lastpos); | |||||
| ph_tls.pool = BKE_image_pool_new(); | |||||
| /* All other data need to be initialized from within callback func... */ | |||||
| const int bucket_index_start = ps->bucketMin[0] + ps->bucketMin[1] * ps->buckets_x; | |||||
| const int bucket_index_end = ps->bucketMax[0] + (ps->bucketMax[1] - 1) * ps->buckets_x; | |||||
| TaskParallelSettings settings; | |||||
| BLI_parallel_range_settings_defaults(&settings); | |||||
| settings.scheduling_mode = TASK_SCHEDULING_DYNAMIC; | |||||
| settings.use_threading = ps->thread_tot > 1; | |||||
| settings.min_iter_per_thread = 1; | |||||
| settings.userdata_chunk = &ph_tls; | |||||
| settings.userdata_chunk_size = sizeof(ph_tls); | |||||
| settings.func_finalize = project_paint_finalize; | |||||
| /* move threaded bounds back into ps->projectPartialRedraws */ | BLI_task_parallel_range(bucket_index_start, bucket_index_end, ps, project_paint_func, &settings); | ||||
| for (i = 0; i < ps->image_tot; i++) { | |||||
| int touch = 0; | |||||
| for (a = 0; a < ps->thread_tot; a++) { | |||||
| touch |= partial_redraw_array_merge(ps->projImages[i].partRedrawRect, | |||||
| handles[a].projImages[i].partRedrawRect, | |||||
| PROJ_BOUNDBOX_SQUARED); | |||||
| } | |||||
| if (touch) { | BKE_image_pool_free(ph_tls.pool); | ||||
| ps->projImages[i].touch = 1; | |||||
| touch_any = 1; | |||||
| } | |||||
| } | |||||
| /* calculate pivot for rotation around seletion if needed */ | /* calculate pivot for rotation around seletion if needed */ | ||||
| if (U.uiflag & USER_ORBIT_SELECTION) { | if (U.uiflag & USER_ORBIT_SELECTION) { | ||||
| float w[3]; | float w[3]; | ||||
| int tri_index; | int tri_index; | ||||
| tri_index = project_paint_PickFace(ps, pos, w); | tri_index = project_paint_PickFace(ps, pos, w); | ||||
| Show All 11 Lines | if (tri_index != -1) { | ||||
| ups->average_stroke_counter++; | ups->average_stroke_counter++; | ||||
| mul_m4_v3(ps->obmat, world); | mul_m4_v3(ps->obmat, world); | ||||
| add_v3_v3(ups->average_stroke_accum, world); | add_v3_v3(ups->average_stroke_accum, world); | ||||
| ups->last_stroke_valid = true; | ups->last_stroke_valid = true; | ||||
| } | } | ||||
| } | } | ||||
| return touch_any; | return ps->proj_images_touch_any; | ||||
| } | } | ||||
| static void paint_proj_stroke_ps(const bContext *UNUSED(C), | static void paint_proj_stroke_ps(const bContext *UNUSED(C), | ||||
| void *ps_handle_p, | void *ps_handle_p, | ||||
| const float prev_pos[2], | const float prev_pos[2], | ||||
| const float pos[2], | const float pos[2], | ||||
| const bool eraser, | const bool eraser, | ||||
| float pressure, | float pressure, | ||||
| ▲ Show 20 Lines • Show All 1,102 Lines • Show Last 20 Lines | |||||