Fix #130276: Trace operator needs to tag data after curves replacement
1. Trace operator was missing a topology cache tag after replacing the stroke data. This caused an invalid normals cache and crash in drawing code. 2. The drawings must not be manipulated outside the main thread. Job data now stores a curves geometry array that is used to update the drawings at the end of the job. 3. Since the job data now stores an array it has to be allocated using MEM_new. Co-authored-by: Falk David <falk@blender.org> Pull Request: https://projects.blender.org/blender/blender/pulls/130313
This commit is contained in:
@@ -81,6 +81,8 @@ struct TraceJob {
|
||||
Object *ob_grease_pencil;
|
||||
bke::greasepencil::Layer *layer;
|
||||
|
||||
Array<bke::CurvesGeometry> traced_curves;
|
||||
|
||||
bool was_ob_created;
|
||||
bool use_current_frame;
|
||||
|
||||
@@ -175,9 +177,7 @@ static int ensure_background_material(Main *bmain, Object *ob, const StringRefNu
|
||||
return index;
|
||||
}
|
||||
|
||||
static bool grease_pencil_trace_image(TraceJob &trace_job,
|
||||
const ImBuf &ibuf,
|
||||
bke::greasepencil::Drawing &drawing)
|
||||
static bke::CurvesGeometry grease_pencil_trace_image(TraceJob &trace_job, const ImBuf &ibuf)
|
||||
{
|
||||
/* Trace the image. */
|
||||
potrace_bitmap_t *bm = image_trace::image_to_bitmap(ibuf, [&](const ColorGeometry4f &color) {
|
||||
@@ -218,17 +218,18 @@ static bool grease_pencil_trace_image(TraceJob &trace_job,
|
||||
/* Remove hole attribute */
|
||||
attributes.remove(hole_attribute_id);
|
||||
|
||||
drawing.strokes_for_write() = trace_curves;
|
||||
/* Uniform radius for all trace curves. */
|
||||
drawing.radii_for_write().fill(trace_job.radius);
|
||||
bke::SpanAttributeWriter<float> radii = attributes.lookup_or_add_for_write_only_span<float>(
|
||||
"radius", bke::AttrDomain::Point);
|
||||
radii.span.fill(trace_job.radius);
|
||||
radii.finish();
|
||||
|
||||
return true;
|
||||
return trace_curves;
|
||||
}
|
||||
|
||||
static void trace_start_job(void *customdata, wmJobWorkerStatus *worker_status)
|
||||
{
|
||||
TraceJob &trace_job = *static_cast<TraceJob *>(customdata);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(trace_job.ob_grease_pencil->data);
|
||||
|
||||
trace_job.stop = &worker_status->stop;
|
||||
trace_job.do_update = &worker_status->do_update;
|
||||
@@ -240,21 +241,16 @@ static void trace_start_job(void *customdata, wmJobWorkerStatus *worker_status)
|
||||
|
||||
/* Single Image. */
|
||||
if (trace_job.image->source == IMA_SRC_FILE || trace_job.mode == TraceMode::Single) {
|
||||
void *lock;
|
||||
ImageUser &iuser = *trace_job.ob_active->iuser;
|
||||
trace_job.traced_curves.reinitialize(1);
|
||||
|
||||
iuser.framenr = ((trace_job.frame_number == 0) || (trace_job.frame_number > iuser.frames)) ?
|
||||
init_frame :
|
||||
trace_job.frame_number;
|
||||
void *lock;
|
||||
ImBuf *ibuf = BKE_image_acquire_ibuf(trace_job.image, &iuser, &lock);
|
||||
if (ibuf) {
|
||||
/* Create frame. */
|
||||
bke::greasepencil::Drawing *drawing = grease_pencil.get_drawing_at(*trace_job.layer,
|
||||
trace_job.frame_target);
|
||||
if (drawing == nullptr) {
|
||||
drawing = grease_pencil.insert_frame(*trace_job.layer, trace_job.frame_target);
|
||||
}
|
||||
grease_pencil_trace_image(trace_job, *ibuf, *drawing);
|
||||
trace_job.traced_curves.first() = grease_pencil_trace_image(trace_job, *ibuf);
|
||||
BKE_image_release_ibuf(trace_job.image, ibuf, lock);
|
||||
*(trace_job.progress) = 1.0f;
|
||||
}
|
||||
@@ -262,12 +258,15 @@ static void trace_start_job(void *customdata, wmJobWorkerStatus *worker_status)
|
||||
/* Image sequence. */
|
||||
else if (trace_job.image->type == IMA_TYPE_IMAGE) {
|
||||
ImageUser &iuser = *trace_job.ob_active->iuser;
|
||||
for (int frame_number = init_frame; frame_number <= iuser.frames; frame_number++) {
|
||||
const int num_frames = iuser.frames - init_frame + 1;
|
||||
trace_job.traced_curves.reinitialize(num_frames);
|
||||
for (const int i : IndexRange(num_frames)) {
|
||||
if (G.is_break) {
|
||||
trace_job.was_canceled = true;
|
||||
break;
|
||||
}
|
||||
|
||||
const int frame_number = init_frame + i;
|
||||
*(trace_job.progress) = float(frame_number) / float(iuser.frames);
|
||||
worker_status->do_update = true;
|
||||
|
||||
@@ -276,15 +275,7 @@ static void trace_start_job(void *customdata, wmJobWorkerStatus *worker_status)
|
||||
void *lock;
|
||||
ImBuf *ibuf = BKE_image_acquire_ibuf(trace_job.image, &iuser, &lock);
|
||||
if (ibuf) {
|
||||
/* Create frame. */
|
||||
// bGPDframe *gpf = BKE_gpencil_layer_frame_get(trace_job.gpl, i, GP_GETFRAME_ADD_NEW);
|
||||
bke::greasepencil::Drawing *drawing = grease_pencil.get_drawing_at(*trace_job.layer,
|
||||
frame_number);
|
||||
if (drawing == nullptr) {
|
||||
drawing = grease_pencil.insert_frame(*trace_job.layer, frame_number);
|
||||
}
|
||||
grease_pencil_trace_image(trace_job, *ibuf, *drawing);
|
||||
|
||||
trace_job.traced_curves[i] = grease_pencil_trace_image(trace_job, *ibuf);
|
||||
BKE_image_release_ibuf(trace_job.image, ibuf, lock);
|
||||
}
|
||||
}
|
||||
@@ -300,6 +291,42 @@ static void trace_end_job(void *customdata)
|
||||
TraceJob &trace_job = *static_cast<TraceJob *>(customdata);
|
||||
GreasePencil &grease_pencil = *static_cast<GreasePencil *>(trace_job.ob_grease_pencil->data);
|
||||
|
||||
/* Update all the drawings once the job is done and we're executing in the main thread again.
|
||||
* Changing drawing array or updating the drawing geometry is not thread-safe. */
|
||||
switch (trace_job.mode) {
|
||||
case TraceMode::Single: {
|
||||
BLI_assert(trace_job.traced_curves.size() == 1);
|
||||
bke::greasepencil::Drawing *drawing = grease_pencil.get_drawing_at(*trace_job.layer,
|
||||
trace_job.frame_target);
|
||||
if (drawing == nullptr) {
|
||||
drawing = grease_pencil.insert_frame(*trace_job.layer, trace_job.frame_target);
|
||||
}
|
||||
BLI_assert(drawing != nullptr);
|
||||
drawing->strokes_for_write() = trace_job.traced_curves.first();
|
||||
drawing->tag_topology_changed();
|
||||
break;
|
||||
}
|
||||
case TraceMode::Sequence: {
|
||||
const ImageUser &iuser = *trace_job.ob_active->iuser;
|
||||
const int init_frame = std::max((trace_job.use_current_frame) ? trace_job.frame_target : 0,
|
||||
0);
|
||||
const int num_frames = iuser.frames - init_frame + 1;
|
||||
BLI_assert(trace_job.traced_curves.size() == num_frames);
|
||||
for (const int i : IndexRange(num_frames)) {
|
||||
const int frame_number = init_frame + i;
|
||||
bke::greasepencil::Drawing *drawing = grease_pencil.get_drawing_at(*trace_job.layer,
|
||||
frame_number);
|
||||
if (drawing == nullptr) {
|
||||
drawing = grease_pencil.insert_frame(*trace_job.layer, frame_number);
|
||||
}
|
||||
BLI_assert(drawing != nullptr);
|
||||
drawing->strokes_for_write() = trace_job.traced_curves[i];
|
||||
drawing->tag_topology_changed();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/* If canceled, delete all previously created object and data-block. */
|
||||
if ((trace_job.was_canceled) && (trace_job.was_ob_created) && (trace_job.ob_grease_pencil)) {
|
||||
BKE_id_delete(trace_job.bmain, &trace_job.ob_grease_pencil->id);
|
||||
@@ -320,7 +347,7 @@ static void trace_end_job(void *customdata)
|
||||
static void trace_free_job(void *customdata)
|
||||
{
|
||||
TraceJob *tj = static_cast<TraceJob *>(customdata);
|
||||
MEM_freeN(tj);
|
||||
MEM_delete(tj);
|
||||
}
|
||||
|
||||
/* Trace Image to Grease Pencil. */
|
||||
@@ -343,7 +370,7 @@ static bool grease_pencil_trace_image_poll(bContext *C)
|
||||
|
||||
static int grease_pencil_trace_image_exec(bContext *C, wmOperator *op)
|
||||
{
|
||||
TraceJob *job = static_cast<TraceJob *>(MEM_mallocN(sizeof(TraceJob), "TraceJob"));
|
||||
TraceJob *job = MEM_new<TraceJob>("TraceJob");
|
||||
job->C = C;
|
||||
job->owner = CTX_data_active_object(C);
|
||||
job->wm = CTX_wm_manager(C);
|
||||
|
||||
Reference in New Issue
Block a user