diff options
author | Antonio Vazquez <blendergit@gmail.com> | 2020-09-21 20:53:36 +0300 |
---|---|---|
committer | Antonio Vazquez <blendergit@gmail.com> | 2020-09-21 21:03:25 +0300 |
commit | 4d62bb8fe57ca431da669386d47ca185f3624c9a (patch) | |
tree | a24b120668693b3f99f99aa5c7c2175e679c3d01 /source/blender/editors/gpencil/gpencil_trace_ops.c | |
parent | d93db4f30c9c435d84ff35529036368261c9b0db (diff) |
GPencil: New Trace images using Potrace
This patch adds a new operator to convert a black and white image into
grease pencil strokes.
If the image is not B/W, an internal conversion is done.
This is the first operator using Potrace, but we expect to add more features in next Blender versions.
Reviewed By: HooglyBoogly
Maniphest Tasks: T79877
Differential Revision: https://developer.blender.org/D8951
Diffstat (limited to 'source/blender/editors/gpencil/gpencil_trace_ops.c')
-rw-r--r-- | source/blender/editors/gpencil/gpencil_trace_ops.c | 313 |
1 files changed, 313 insertions, 0 deletions
diff --git a/source/blender/editors/gpencil/gpencil_trace_ops.c b/source/blender/editors/gpencil/gpencil_trace_ops.c new file mode 100644 index 00000000000..4391abee5a1 --- /dev/null +++ b/source/blender/editors/gpencil/gpencil_trace_ops.c @@ -0,0 +1,313 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * The Original Code is Copyright (C) 2020 Blender Foundation + * All rights reserved. + */ + +/** \file + * \ingroup edgpencil + */ + +#include "MEM_guardedalloc.h" + +#include "BLI_blenlib.h" +#include "BLI_math.h" + +#include "BLT_translation.h" + +#include "DNA_gpencil_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "BKE_context.h" +#include "BKE_duplilist.h" +#include "BKE_gpencil.h" +#include "BKE_image.h" +#include "BKE_main.h" +#include "BKE_material.h" +#include "BKE_object.h" +#include "BKE_report.h" + +#include "DEG_depsgraph.h" +#include "DEG_depsgraph_query.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "RNA_access.h" +#include "RNA_define.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "ED_gpencil.h" +#include "ED_object.h" + +#include "gpencil_intern.h" +#include "gpencil_trace.h" +#include "potracelib.h" + +/** + * Trace a image. + * \param C: Context + * \param op: Operator + * \param ob: Grease pencil object, can be NULL + * \param ima: Image + * \param gpf: Destination frame + */ +static bool gpencil_trace_image( + bContext *C, wmOperator *op, Object *ob, Image *ima, 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) { + return false; + } + + /* Set tracing parameters, starting from defaults */ + param = potrace_param_default(); + if (!param) { + return false; + } + param->turdsize = 0; + param->turnpolicy = turnpolicy; + + /* Load BW bitmap with image. */ + ED_gpencil_trace_image_to_bitmap(ibuf, bm, threshold); + + /* Trace the bitmap. */ + st = potrace_trace(param, bm); + if (!st || st->status != POTRACE_STATUS_OK) { + ED_gpencil_trace_bitmap_free(bm); + if (st) { + potrace_state_free(st); + } + potrace_param_free(param); + return false; + } + /* Free BW bitmap. */ + ED_gpencil_trace_bitmap_free(bm); + + /* Convert the trace to strokes. */ + int32_t offset[2]; + offset[0] = ibuf->x / 2; + offset[1] = ibuf->y / 2; + + /* Scale correction for Potrace. + * 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); + 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); + + /* Free memory. */ + potrace_state_free(st); + potrace_param_free(param); + + /* Release ibuf. */ + if (ibuf) { + BKE_image_release_ibuf(ima, ibuf, lock); + } + + return true; +} + +/* Trace Image to Grease Pencil. */ +static bool gpencil_trace_image_poll(bContext *C) +{ + Object *ob = CTX_data_active_object(C); + if ((ob == NULL) || (ob->type != OB_EMPTY) || (ob->data == NULL)) { + CTX_wm_operator_poll_msg_set(C, "No image empty selected"); + return false; + } + + return true; +} + +static int gpencil_trace_image_exec(bContext *C, wmOperator *op) +{ + 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); + /* Apply image rotation. */ + copy_v3_v3(ob_gpencil->rot, ob_active->rot); + /* Grease pencil is rotated 90 degrees in X axis by default. */ + ob_gpencil->rot[0] -= DEG2RADF(90.0f); + ob_created = true; + /* Apply image Scale. */ + copy_v3_v3(ob_gpencil->scale, ob_active->scale); + } + + 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. */ + 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); + } + + /* 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); + + /* Back to active base. */ + ED_object_base_activate(C, base_active); + + /* notifiers */ + if (ob_created) { + DEG_relations_tag_update(bmain); + } + + DEG_id_tag_update(&scene->id, ID_RECALC_SELECT); + DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE); + + WM_event_add_notifier(C, NC_OBJECT | NA_ADDED, NULL); + WM_event_add_notifier(C, NC_SCENE | ND_OB_ACTIVE, scene); + + return OPERATOR_FINISHED; +} + +static bool rna_GPencil_object_poll(PointerRNA *UNUSED(ptr), PointerRNA value) +{ + return ((Object *)value.owner_id)->type == OB_GPENCIL; +} + +void GPENCIL_OT_trace_image(wmOperatorType *ot) +{ + PropertyRNA *prop; + + static const EnumPropertyItem turnpolicy_type[] = { + {POTRACE_TURNPOLICY_BLACK, + "BLACK", + 0, + "Black", + "Prefers to connect black (foreground) components"}, + {POTRACE_TURNPOLICY_WHITE, + "WHITE", + 0, + "White", + "Prefers to connect white (background) components"}, + {POTRACE_TURNPOLICY_LEFT, "LEFT", 0, "Left", "Always take a left turn"}, + {POTRACE_TURNPOLICY_RIGHT, "RIGHT", 0, "Right", "Always take a right turn"}, + {POTRACE_TURNPOLICY_MINORITY, + "MINORITY", + 0, + "Minority", + "Prefers to connect the color (black or white) that occurs least frequently in the local " + "neighborhood of the current position"}, + {POTRACE_TURNPOLICY_MAJORITY, + "MAJORITY", + 0, + "Majority", + "Prefers to connect the color (black or white) that occurs most frequently in the local " + "neighborhood of the current position"}, + {POTRACE_TURNPOLICY_RANDOM, "RANDOM", 0, "Random", "Choose pseudo-randomly"}, + {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->exec = gpencil_trace_image_exec; + ot->poll = gpencil_trace_image_poll; + + /* flags */ + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* properties */ + prop = RNA_def_pointer_runtime(ot->srna, "target", &RNA_Object, "Target", ""); + RNA_def_property_poll_runtime(prop, rna_GPencil_object_poll); + + 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); + + RNA_def_float(ot->srna, + "scale", + 1.0f, + 0.001f, + 100.0f, + "Scale", + "Scale of the final stroke", + 0.001f, + 100.0f); + RNA_def_float(ot->srna, + "sample", + 0.0f, + 0.0f, + 100.0f, + "Sample", + "Distance to sample points, zero to disable", + 0.0f, + 100.0f); + RNA_def_float_factor(ot->srna, + "threshold", + 0.5f, + 0.0f, + 1.0f, + "Color Threshold", + "Determine what is considered white and what black", + 0.0f, + 1.0f); + RNA_def_enum(ot->srna, + "turnpolicy", + turnpolicy_type, + POTRACE_TURNPOLICY_MINORITY, + "Turn Policy", + "Determines how to resolve ambiguities during decomposition of bitmaps into paths"); +} |