diff options
author | Antonio Vazquez <blendergit@gmail.com> | 2020-10-28 17:34:43 +0300 |
---|---|---|
committer | Antonio Vazquez <blendergit@gmail.com> | 2020-10-28 17:36:01 +0300 |
commit | a5d237ac85c7ed67edad5e75f4cc3f8a67a30776 (patch) | |
tree | fc7b9b534dada9ed48743a1f41ec883dc6aaed2f /source | |
parent | 73378c2ba2d5845c0023077f4ebc32b78f5aa625 (diff) |
GPencil: New support to trace sequence images
Now it's possible to trace a sequence of images and not just a single one
When the trace is for more than one image, a bacth job is started to process all frames.
Note: All trace data is generated by Potrace library.
Differential revision: https://developer.blender.org/D9316
Diffstat (limited to 'source')
-rw-r--r-- | source/blender/editors/gpencil/gpencil_trace.h | 4 | ||||
-rw-r--r-- | source/blender/editors/gpencil/gpencil_trace_ops.c | 306 | ||||
-rw-r--r-- | source/blender/windowmanager/WM_api.h | 1 |
3 files changed, 242 insertions, 69 deletions
diff --git a/source/blender/editors/gpencil/gpencil_trace.h b/source/blender/editors/gpencil/gpencil_trace.h index 3adde7651cd..85eb4e0609f 100644 --- a/source/blender/editors/gpencil/gpencil_trace.h +++ b/source/blender/editors/gpencil/gpencil_trace.h @@ -56,6 +56,10 @@ struct bGPDframe; #define BM_INV(bm, x, y) (bm_safe(bm, x, y) ? BM_UINV(bm, x, y) : 0) #define BM_PUT(bm, x, y, b) (bm_safe(bm, x, y) ? BM_UPUT(bm, x, y, b) : 0) +/* Trace modes */ +#define GPENCIL_TRACE_MODE_SINGLE 0 +#define GPENCIL_TRACE_MODE_SEQUENCE 1 + void ED_gpencil_trace_bitmap_print(FILE *f, const potrace_bitmap_t *bm); potrace_bitmap_t *ED_gpencil_trace_bitmap_new(int32_t w, int32_t h); diff --git a/source/blender/editors/gpencil/gpencil_trace_ops.c b/source/blender/editors/gpencil/gpencil_trace_ops.c index 4391abee5a1..0abc6193841 100644 --- a/source/blender/editors/gpencil/gpencil_trace_ops.c +++ b/source/blender/editors/gpencil/gpencil_trace_ops.c @@ -35,8 +35,10 @@ #include "BKE_context.h" #include "BKE_duplilist.h" +#include "BKE_global.h" #include "BKE_gpencil.h" #include "BKE_image.h" +#include "BKE_lib_id.h" #include "BKE_main.h" #include "BKE_material.h" #include "BKE_object.h" @@ -45,11 +47,15 @@ #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" +#include "UI_interface.h" +#include "UI_resources.h" + #include "WM_api.h" #include "WM_types.h" #include "RNA_access.h" #include "RNA_define.h" +#include "RNA_enum_types.h" #include "IMB_imbuf.h" #include "IMB_imbuf_types.h" @@ -61,34 +67,50 @@ #include "gpencil_trace.h" #include "potracelib.h" +typedef struct TraceJob { + /* from wmJob */ + struct Object *owner; + short *stop, *do_update; + float *progress; + + bContext *C; + wmWindowManager *wm; + Main *bmain; + Scene *scene; + View3D *v3d; + Base *base_active; + Object *ob_active; + Image *image; + Object *ob_gpencil; + bGPdata *gpd; + bGPDlayer *gpl; + + bool was_ob_created; + + int32_t frame_target; + float threshold; + float scale; + float sample; + int32_t resolution; + int32_t thickness; + int32_t turnpolicy; + int32_t mode; + + bool success; + bool was_canceled; +} TraceJob; + /** * Trace a image. - * \param C: Context - * \param op: Operator - * \param ob: Grease pencil object, can be NULL - * \param ima: Image + * \param ibuf: Image Ibuffer * \param gpf: Destination frame */ -static bool gpencil_trace_image( - bContext *C, wmOperator *op, Object *ob, Image *ima, bGPDframe *gpf) +static bool gpencil_trace_image(TraceJob *trace_job, ImBuf *ibuf, bGPDframe *gpf) { - Main *bmain = CTX_data_main(C); - potrace_bitmap_t *bm = NULL; potrace_param_t *param = NULL; potrace_state_t *st = NULL; - const float threshold = RNA_float_get(op->ptr, "threshold"); - const float scale = RNA_float_get(op->ptr, "scale"); - const float sample = RNA_float_get(op->ptr, "sample"); - const int32_t resolution = RNA_int_get(op->ptr, "resolution"); - const int32_t thickness = RNA_int_get(op->ptr, "thickness"); - const int32_t turnpolicy = RNA_enum_get(op->ptr, "turnpolicy"); - - ImBuf *ibuf; - void *lock; - ibuf = BKE_image_acquire_ibuf(ima, NULL, &lock); - /* Create an empty BW bitmap. */ bm = ED_gpencil_trace_bitmap_new(ibuf->x, ibuf->y); if (!bm) { @@ -101,10 +123,10 @@ static bool gpencil_trace_image( return false; } param->turdsize = 0; - param->turnpolicy = turnpolicy; + param->turnpolicy = trace_job->turnpolicy; /* Load BW bitmap with image. */ - ED_gpencil_trace_image_to_bitmap(ibuf, bm, threshold); + ED_gpencil_trace_image_to_bitmap(ibuf, bm, trace_job->threshold); /* Trace the bitmap. */ st = potrace_trace(param, bm); @@ -128,23 +150,26 @@ static bool gpencil_trace_image( * Really, there isn't documented in Potrace about how the scale is calculated, * but after doing a lot of tests, it looks is using a VGA resolution (640) as a base. * Maybe there are others ways to get the right scale conversion, but this solution works. */ - float scale_potrace = scale * (640.0f / (float)ibuf->x) * ((float)ibuf->x / (float)ibuf->y); + float scale_potrace = trace_job->scale * (640.0f / (float)ibuf->x) * + ((float)ibuf->x / (float)ibuf->y); if (ibuf->x > ibuf->y) { scale_potrace *= (float)ibuf->y / (float)ibuf->x; } - ED_gpencil_trace_data_to_strokes( - bmain, st, ob, gpf, offset, scale_potrace, sample, resolution, thickness); + ED_gpencil_trace_data_to_strokes(trace_job->bmain, + st, + trace_job->ob_gpencil, + gpf, + offset, + scale_potrace, + trace_job->sample, + trace_job->resolution, + trace_job->thickness); /* Free memory. */ potrace_state_free(st); potrace_param_free(param); - /* Release ibuf. */ - if (ibuf) { - BKE_image_release_ibuf(ima, ibuf, lock); - } - return true; } @@ -157,68 +182,192 @@ static bool gpencil_trace_image_poll(bContext *C) return false; } + Image *image = (Image *)ob->data; + if (!ELEM(image->source, IMA_SRC_FILE, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE)) { + CTX_wm_operator_poll_msg_set(C, "No valid image format selected"); + return false; + } + return true; } -static int gpencil_trace_image_exec(bContext *C, wmOperator *op) +static void trace_initialize_job_data(TraceJob *trace_job) { - Main *bmain = CTX_data_main(C); - Scene *scene = CTX_data_scene(C); - View3D *v3d = CTX_wm_view3d(C); - Base *base_active = CTX_data_active_base(C); - Object *ob_active = base_active->object; - Image *image = (Image *)ob_active->data; - bool ob_created = false; - - const int32_t frame_target = CFRA; - Object *ob_gpencil = (Object *)RNA_pointer_get(op->ptr, "target").data; - /* Create a new grease pencil object. */ - if (ob_gpencil == NULL) { - ushort local_view_bits = (v3d && v3d->localvd) ? v3d->local_view_uuid : 0; - ob_gpencil = ED_gpencil_add_object(C, ob_active->loc, local_view_bits); + if (trace_job->ob_gpencil == NULL) { + ushort local_view_bits = (trace_job->v3d && trace_job->v3d->localvd) ? + trace_job->v3d->local_view_uuid : + 0; + trace_job->ob_gpencil = ED_gpencil_add_object( + trace_job->C, trace_job->ob_active->loc, local_view_bits); /* Apply image rotation. */ - copy_v3_v3(ob_gpencil->rot, ob_active->rot); + copy_v3_v3(trace_job->ob_gpencil->rot, trace_job->ob_active->rot); /* Grease pencil is rotated 90 degrees in X axis by default. */ - ob_gpencil->rot[0] -= DEG2RADF(90.0f); - ob_created = true; + trace_job->ob_gpencil->rot[0] -= DEG2RADF(90.0f); + trace_job->was_ob_created = true; /* Apply image Scale. */ - copy_v3_v3(ob_gpencil->scale, ob_active->scale); + copy_v3_v3(trace_job->ob_gpencil->scale, trace_job->ob_active->scale); + /* The default display size of the image is 5.0 and this is used as scale = 1.0. */ + mul_v3_fl(trace_job->ob_gpencil->scale, trace_job->ob_active->empty_drawsize / 5.0f); } - if ((ob_gpencil == NULL) || (ob_gpencil->type != OB_GPENCIL)) { - BKE_report(op->reports, RPT_ERROR, "Target grease pencil object not valid"); - return OPERATOR_CANCELLED; + /* Create Layer. */ + trace_job->gpd = (bGPdata *)trace_job->ob_gpencil->data; + trace_job->gpl = BKE_gpencil_layer_active_get(trace_job->gpd); + if (trace_job->gpl == NULL) { + trace_job->gpl = BKE_gpencil_layer_addnew(trace_job->gpd, DATA_("Trace"), true); } +} - /* Create Layer. */ - bGPdata *gpd = (bGPdata *)ob_gpencil->data; - bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); - if (gpl == NULL) { - gpl = BKE_gpencil_layer_addnew(gpd, DATA_("Trace"), true); +static void trace_start_job(void *customdata, short *stop, short *do_update, float *progress) +{ + TraceJob *trace_job = customdata; + + trace_job->stop = stop; + trace_job->do_update = do_update; + trace_job->progress = progress; + trace_job->was_canceled = false; + + G.is_break = false; + + /* Single Image. */ + + if ((trace_job->image->source == IMA_SRC_FILE) || + (trace_job->mode == GPENCIL_TRACE_MODE_SINGLE)) { + void *lock; + ImBuf *ibuf = BKE_image_acquire_ibuf(trace_job->image, NULL, &lock); + if (ibuf) { + /* Create frame. */ + bGPDframe *gpf = BKE_gpencil_layer_frame_get( + trace_job->gpl, trace_job->frame_target, GP_GETFRAME_ADD_NEW); + gpencil_trace_image(trace_job, ibuf, gpf); + BKE_image_release_ibuf(trace_job->image, ibuf, lock); + *(trace_job->progress) = 1.0f; + } + } + /* Image sequence. */ + else if (trace_job->image->type == IMA_TYPE_IMAGE) { + ImageUser *iuser = trace_job->ob_active->iuser; + for (int i = 0; i < iuser->frames; i++) { + if (G.is_break) { + trace_job->was_canceled = true; + break; + } + + *(trace_job->progress) = (float)i / (float)iuser->frames; + *do_update = true; + + iuser->framenr = i + 1; + + 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, trace_job->frame_target + i, GP_GETFRAME_ADD_NEW); + gpencil_trace_image(trace_job, ibuf, gpf); + + BKE_image_release_ibuf(trace_job->image, ibuf, lock); + } + } } - /* Create frame. */ - bGPDframe *gpf = BKE_gpencil_layer_frame_get(gpl, frame_target, GP_GETFRAME_ADD_NEW); - gpencil_trace_image(C, op, ob_gpencil, image, gpf); + trace_job->success = !trace_job->was_canceled; + *do_update = true; + *stop = 0; +} - /* Back to active base. */ - ED_object_base_activate(C, base_active); +static void trace_end_job(void *customdata) +{ + TraceJob *trace_job = customdata; + + /* If canceled, delete all previously created object and datablock. */ + if ((trace_job->was_canceled) && (trace_job->was_ob_created) && (trace_job->ob_gpencil)) { + bGPdata *gpd = trace_job->ob_gpencil->data; + BKE_id_delete(trace_job->bmain, &trace_job->ob_gpencil->id); + BKE_id_delete(trace_job->bmain, &gpd->id); + } - /* notifiers */ - if (ob_created) { - DEG_relations_tag_update(bmain); + if (trace_job->success) { + DEG_relations_tag_update(trace_job->bmain); + + DEG_id_tag_update(&trace_job->scene->id, ID_RECALC_SELECT); + DEG_id_tag_update(&trace_job->gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + + WM_main_add_notifier(NC_OBJECT | NA_ADDED, NULL); + WM_main_add_notifier(NC_SCENE | ND_OB_ACTIVE, trace_job->scene); } +} - DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); - DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); +static void trace_free_job(void *customdata) +{ + TraceJob *tj = customdata; + MEM_freeN(tj); +} - WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL); - WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); +static int gpencil_trace_image_exec(bContext *C, wmOperator *op) +{ + TraceJob *job = MEM_mallocN(sizeof(TraceJob), "TraceJob"); + job->C = C; + job->owner = CTX_data_active_object(C); + job->wm = CTX_wm_manager(C); + job->bmain = CTX_data_main(C); + Scene *scene = CTX_data_scene(C); + job->scene = scene; + job->v3d = CTX_wm_view3d(C); + job->base_active = CTX_data_active_base(C); + job->ob_active = job->base_active->object; + job->image = (Image *)job->ob_active->data; + job->frame_target = CFRA; + + job->ob_gpencil = (Object *)RNA_pointer_get(op->ptr, "target").data; + job->was_ob_created = false; + + job->threshold = RNA_float_get(op->ptr, "threshold"); + job->scale = RNA_float_get(op->ptr, "scale"); + job->sample = RNA_float_get(op->ptr, "sample"); + job->resolution = RNA_int_get(op->ptr, "resolution"); + job->thickness = RNA_int_get(op->ptr, "thickness"); + job->turnpolicy = RNA_enum_get(op->ptr, "turnpolicy"); + job->mode = RNA_enum_get(op->ptr, "mode"); + + trace_initialize_job_data(job); + + /* Back to active base. */ + ED_object_base_activate(job->C, job->base_active); + + if (job->image->source == IMA_SRC_FILE) { + short stop = 0, do_update = true; + float progress; + trace_start_job(job, &stop, &do_update, &progress); + trace_end_job(job); + trace_free_job(job); + } + else { + wmJob *wm_job = WM_jobs_get(job->wm, + CTX_wm_window(C), + job->scene, + "Trace Image", + WM_JOB_PROGRESS, + WM_JOB_TYPE_TRACE_IMAGE); + + WM_jobs_customdata_set(wm_job, job, trace_free_job); + WM_jobs_timer(wm_job, 0.1, NC_GEOM | ND_DATA, NC_GEOM | ND_DATA); + WM_jobs_callbacks(wm_job, trace_start_job, NULL, NULL, trace_end_job); + + WM_jobs_start(CTX_wm_manager(C), wm_job); + } return OPERATOR_FINISHED; } +static int gpencil_trace_image_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + /* Show popup dialog to allow editing. */ + /* FIXME: hard-coded dimensions here are just arbitrary. */ + return WM_operator_props_dialog_popup(C, op, 250); +} + static bool rna_GPencil_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value) { return ((Object *)value.owner_id)->type == OB_GPENCIL; @@ -257,12 +406,19 @@ void GPENCIL_OT_trace_image(wmOperatorType *ot) {0, NULL, 0, NULL, NULL}, }; + static const EnumPropertyItem trace_modes[] = { + {GPENCIL_TRACE_MODE_SINGLE, "SINGLE", 0, "Single", "Trace actual image"}, + {GPENCIL_TRACE_MODE_SEQUENCE, "SEQUENCE", 0, "Sequence", "Trace full sequence"}, + {0, NULL, 0, NULL, NULL}, + }; + /* identifiers */ ot->name = "Trace Image to Grease Pencil"; ot->idname = "GPENCIL_OT_trace_image"; ot->description = "Extract Grease Pencil strokes from image"; /* callbacks */ + ot->invoke = gpencil_trace_image_invoke; ot->exec = gpencil_trace_image_exec; ot->poll = gpencil_trace_image_poll; @@ -270,9 +426,15 @@ void GPENCIL_OT_trace_image(wmOperatorType *ot) ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; /* properties */ - prop = RNA_def_pointer_runtime(ot->srna, "target", &RNA_Object, "Target", ""); + prop = RNA_def_pointer_runtime(ot->srna, + "target", + &RNA_Object, + "Target Object", + "Target grease pencil object name. Leave empty for new object"); RNA_def_property_poll_runtime(prop, rna_GPencil_object_poll); + RNA_def_property_flag(prop, PROP_SKIP_SAVE); + RNA_def_int(ot->srna, "thickness", 10, 1, 1000, "Thickness", "", 1, 1000); RNA_def_int( ot->srna, "resolution", 5, 1, 20, "Resolution", "Resolution of the generated curves", 1, 20); @@ -310,4 +472,10 @@ void GPENCIL_OT_trace_image(wmOperatorType *ot) POTRACE_TURNPOLICY_MINORITY, "Turn Policy", "Determines how to resolve ambiguities during decomposition of bitmaps into paths"); + RNA_def_enum(ot->srna, + "mode", + trace_modes, + GPENCIL_TRACE_MODE_SINGLE, + "Mode", + "Determines if trace simple image or full sequence"); } diff --git a/source/blender/windowmanager/WM_api.h b/source/blender/windowmanager/WM_api.h index 8d4ef29af74..b81ef14f21c 100644 --- a/source/blender/windowmanager/WM_api.h +++ b/source/blender/windowmanager/WM_api.h @@ -729,6 +729,7 @@ enum { WM_JOB_TYPE_LIGHT_BAKE, WM_JOB_TYPE_FSMENU_BOOKMARK_VALIDATE, WM_JOB_TYPE_QUADRIFLOW_REMESH, + WM_JOB_TYPE_TRACE_IMAGE, /* add as needed, bake, seq proxy build * if having hard coded values is a problem */ }; |