Blender  V3.3
gpencil_trace_utils.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2020 Blender Foundation. All rights reserved. */
3 
8 #include <stdio.h>
9 #include <stdlib.h>
10 #include <string.h>
11 
12 #include "MEM_guardedalloc.h"
13 
14 #include "BLI_blenlib.h"
15 #include "BLI_math.h"
16 
17 #include "BKE_gpencil.h"
18 #include "BKE_gpencil_geom.h"
19 #include "BKE_image.h"
20 #include "BKE_main.h"
21 #include "BKE_material.h"
22 
23 #include "DNA_gpencil_types.h"
24 #include "DNA_image_types.h"
25 #include "DNA_material_types.h"
26 #include "DNA_object_types.h"
27 
28 #include "IMB_imbuf.h"
29 #include "IMB_imbuf_types.h"
30 
31 #include "gpencil_trace.h"
32 
33 void ED_gpencil_trace_bitmap_print(FILE *f, const potrace_bitmap_t *bm)
34 {
35  int32_t x, y;
36  int32_t xx, yy;
37  int32_t d;
38  int32_t sw, sh;
39 
40  sw = bm->w < 79 ? bm->w : 79;
41  sh = bm->w < 79 ? bm->h : bm->h * sw * 44 / (79 * bm->w);
42 
43  for (yy = sh - 1; yy >= 0; yy--) {
44  for (xx = 0; xx < sw; xx++) {
45  d = 0;
46  for (x = xx * bm->w / sw; x < (xx + 1) * bm->w / sw; x++) {
47  for (y = yy * bm->h / sh; y < (yy + 1) * bm->h / sh; y++) {
48  if (BM_GET(bm, x, y)) {
49  d++;
50  }
51  }
52  }
53  fputc(d ? '*' : ' ', f);
54  }
55  fputc('\n', f);
56  }
57 }
58 
60 {
61  potrace_bitmap_t *bm;
62  int32_t dy = (w + BM_WORDBITS - 1) / BM_WORDBITS;
63 
64  bm = (potrace_bitmap_t *)MEM_mallocN(sizeof(potrace_bitmap_t), __func__);
65  if (!bm) {
66  return NULL;
67  }
68  bm->w = w;
69  bm->h = h;
70  bm->dy = dy;
71  bm->map = (potrace_word *)calloc(h, dy * BM_WORDSIZE);
72  if (!bm->map) {
73  free(bm);
74  return NULL;
75  }
76 
77  return bm;
78 }
79 
80 void ED_gpencil_trace_bitmap_free(const potrace_bitmap_t *bm)
81 {
82  if (bm != NULL) {
83  free(bm->map);
84  }
86 }
87 
88 void ED_gpencil_trace_bitmap_invert(const potrace_bitmap_t *bm)
89 {
90  int32_t dy = bm->dy;
91  int32_t y;
92  int32_t i;
93  potrace_word *p;
94 
95  if (dy < 0) {
96  dy = -dy;
97  }
98 
99  for (y = 0; y < bm->h; y++) {
100  p = bm_scanline(bm, y);
101  for (i = 0; i < dy; i++) {
102  p[i] ^= BM_ALLBITS;
103  }
104  }
105 }
106 
113 static void pixel_at_index(const ImBuf *ibuf, const int32_t idx, float r_col[4])
114 {
115  BLI_assert(idx < (ibuf->x * ibuf->y));
116 
117  if (ibuf->rect_float) {
118  const float *frgba = &ibuf->rect_float[idx * 4];
119  copy_v4_v4(r_col, frgba);
120  }
121  else {
122  unsigned char *cp = (unsigned char *)(ibuf->rect + idx);
123  r_col[0] = (float)cp[0] / 255.0f;
124  r_col[1] = (float)cp[1] / 255.0f;
125  r_col[2] = (float)cp[2] / 255.0f;
126  r_col[3] = (float)cp[3] / 255.0f;
127  }
128 }
129 
131  const potrace_bitmap_t *bm,
132  const float threshold)
133 {
134  float rgba[4];
135  int32_t pixel = 0;
136 
137  for (uint32_t y = 0; y < ibuf->y; y++) {
138  for (uint32_t x = 0; x < ibuf->x; x++) {
139  pixel = (ibuf->x * y) + x;
140  pixel_at_index(ibuf, pixel, rgba);
141  /* Get a BW color. */
142  mul_v3_fl(rgba, rgba[3]);
143  float color = (rgba[0] + rgba[1] + rgba[2]) / 3.0f;
144  int32_t bw = (color > threshold) ? 0 : 1;
145  BM_PUT(bm, x, y, bw);
146  }
147  }
148 }
149 
150 /* Helper to add point to stroke. */
151 static void add_point(bGPDstroke *gps, float scale, const int32_t offset[2], float x, float y)
152 {
153  int32_t idx = gps->totpoints;
154  if (gps->totpoints == 0) {
155  gps->points = MEM_callocN(sizeof(bGPDspoint), "gp_stroke_points");
156  }
157  else {
158  gps->points = MEM_recallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
159  }
160  bGPDspoint *pt = &gps->points[idx];
161  pt->x = (x - offset[0]) * scale;
162  pt->y = 0;
163  pt->z = (y - offset[1]) * scale;
164  pt->pressure = 1.0f;
165  pt->strength = 1.0f;
166 
167  gps->totpoints++;
168 }
169 
170 /* helper to generate all points of curve. */
171 static void add_bezier(bGPDstroke *gps,
172  float scale,
173  int32_t offset[2],
174  int32_t resolution,
175  float bcp1[2],
176  float bcp2[2],
177  float bcp3[2],
178  float bcp4[2],
179  const bool skip)
180 {
181  const float step = 1.0f / (float)(resolution - 1);
182  float a = 0.0f;
183 
184  for (int32_t i = 0; i < resolution; i++) {
185  if ((!skip) || (i > 0)) {
186  float fpt[3];
187  interp_v2_v2v2v2v2_cubic(fpt, bcp1, bcp2, bcp3, bcp4, a);
188  add_point(gps, scale, offset, fpt[0], fpt[1]);
189  }
190  a += step;
191  }
192 }
193 
195  potrace_state_t *st,
196  Object *ob,
197  bGPDframe *gpf,
198  int32_t offset[2],
199  const float scale,
200  const float sample,
201  const int32_t resolution,
202  const int32_t thickness)
203 {
204 #define MAX_LENGTH 100.0f
205  /* Find materials and create them if not found. */
206  int32_t mat_fill_idx = BKE_gpencil_material_find_index_by_name_prefix(ob, "Stroke");
207  int32_t mat_mask_idx = BKE_gpencil_material_find_index_by_name_prefix(ob, "Holdout");
208 
209  const float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
210  /* Stroke and Fill material. */
211  if (mat_fill_idx == -1) {
212  int32_t new_idx;
213  Material *mat_gp = BKE_gpencil_object_material_new(bmain, ob, "Stroke", &new_idx);
214  MaterialGPencilStyle *gp_style = mat_gp->gp_style;
215 
216  copy_v4_v4(gp_style->stroke_rgba, default_color);
217  gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
218  gp_style->flag |= GP_MATERIAL_FILL_SHOW;
219  mat_fill_idx = ob->totcol - 1;
220  }
221  /* Holdout material. */
222  if (mat_mask_idx == -1) {
223  int32_t new_idx;
224  Material *mat_gp = BKE_gpencil_object_material_new(bmain, ob, "Holdout", &new_idx);
225  MaterialGPencilStyle *gp_style = mat_gp->gp_style;
226 
227  copy_v4_v4(gp_style->stroke_rgba, default_color);
228  copy_v4_v4(gp_style->fill_rgba, default_color);
229  gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
230  gp_style->flag |= GP_MATERIAL_FILL_SHOW;
231  gp_style->flag |= GP_MATERIAL_IS_STROKE_HOLDOUT;
232  gp_style->flag |= GP_MATERIAL_IS_FILL_HOLDOUT;
233  mat_mask_idx = ob->totcol - 1;
234  }
235 
236  int n, *tag;
237  potrace_dpoint_t(*c)[3];
238 
239  /* There isn't any rule here, only the result of lots of testing to get a value that gets
240  * good results using the Potrace data. */
241  const float scalef = 0.008f * scale;
242  /* Draw each curve. */
243  potrace_path_t *path = st->plist;
244  while (path != NULL) {
245  n = path->curve.n;
246  tag = path->curve.tag;
247  c = path->curve.c;
248  int mat_idx = path->sign == '+' ? mat_fill_idx : mat_mask_idx;
249  /* Create a new stroke. */
250  bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, mat_idx, 0, thickness, false);
251  /* Last point that is equals to start point. */
252  float start_point[2], last[2];
253  start_point[0] = c[n - 1][2].x;
254  start_point[1] = c[n - 1][2].y;
255 
256  for (int32_t i = 0; i < n; i++) {
257  switch (tag[i]) {
258  case POTRACE_CORNER: {
259  if (gps->totpoints == 0) {
260  add_point(gps, scalef, offset, c[n - 1][2].x, c[n - 1][2].y);
261  }
262  else {
263  add_point(gps, scalef, offset, last[0], last[1]);
264  }
265 
266  add_point(gps, scalef, offset, c[i][1].x, c[i][1].y);
267 
268  add_point(gps, scalef, offset, c[i][2].x, c[i][2].y);
269 
270  last[0] = c[i][2].x;
271  last[1] = c[i][2].y;
272  break;
273  }
274  case POTRACE_CURVETO: {
275  float cp1[2], cp2[2], cp3[2], cp4[2];
276  if (gps->totpoints == 0) {
277  cp1[0] = start_point[0];
278  cp1[1] = start_point[1];
279  }
280  else {
281  copy_v2_v2(cp1, last);
282  }
283 
284  cp2[0] = c[i][0].x;
285  cp2[1] = c[i][0].y;
286 
287  cp3[0] = c[i][1].x;
288  cp3[1] = c[i][1].y;
289 
290  cp4[0] = c[i][2].x;
291  cp4[1] = c[i][2].y;
292 
293  add_bezier(gps,
294  scalef,
295  offset,
296  resolution,
297  cp1,
298  cp2,
299  cp3,
300  cp4,
301  (gps->totpoints == 0) ? false : true);
302  copy_v2_v2(last, cp4);
303  break;
304  }
305  default:
306  break;
307  }
308  }
309  /* In some situations, Potrace can produce a wrong data and generate a very
310  * long stroke. Here the length is checked and removed if the length is too big. */
311  float length = BKE_gpencil_stroke_length(gps, true);
312  if (length <= MAX_LENGTH) {
313  bGPdata *gpd = ob->data;
314  if (sample > 0.0f) {
315  /* Resample stroke. Don't need to call to BKE_gpencil_stroke_geometry_update() because
316  * the sample function already call that. */
317  BKE_gpencil_stroke_sample(gpd, gps, sample, false, 0);
318  }
319  else {
321  }
322  }
323  else {
324  /* Remove too long strokes. */
325  BLI_remlink(&gpf->strokes, gps);
327  }
328 
329  path = path->next;
330  }
331 #undef MAX_LENGTH
332 }
typedef float(TangentPoint)[2]
struct Material * BKE_gpencil_object_material_new(struct Main *bmain, struct Object *ob, const char *name, int *r_index)
Definition: gpencil.c:1734
struct bGPDstroke * BKE_gpencil_stroke_add(struct bGPDframe *gpf, int mat_idx, int totpoints, short thickness, bool insert_at_head)
Definition: gpencil.c:792
void BKE_gpencil_free_stroke(struct bGPDstroke *gps)
Definition: gpencil.c:391
int BKE_gpencil_material_find_index_by_name_prefix(struct Object *ob, const char *name_prefix)
Definition: gpencil.c:2855
float BKE_gpencil_stroke_length(const struct bGPDstroke *gps, bool use_3d)
void BKE_gpencil_stroke_geometry_update(struct bGPdata *gpd, struct bGPDstroke *gps)
bool BKE_gpencil_stroke_sample(struct bGPdata *gpd, struct bGPDstroke *gps, const float dist, const bool select, const float sharp_threshold)
General operations, lookup, etc. for materials.
#define BLI_assert(a)
Definition: BLI_assert.h:46
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:102
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
MINLINE void copy_v4_v4(float r[4], const float a[4])
void interp_v2_v2v2v2v2_cubic(float p[2], const float v1[2], const float v2[2], const float v3[2], const float v4[2], float u)
Definition: math_vector.c:139
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
@ GP_MATERIAL_IS_STROKE_HOLDOUT
@ GP_MATERIAL_STROKE_SHOW
@ GP_MATERIAL_IS_FILL_HOLDOUT
@ GP_MATERIAL_FILL_SHOW
Object is a sort of wrapper for general info.
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint y
Contains defines and structs used throughout the imbuf module.
Read Guarded memory(de)allocation.
#define MEM_recallocN(vmemh, len)
#define MEM_SAFE_FREE(v)
Group Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position Clamp a value between a minimum and a maximum Vector Perform vector math operation Invert a color
ATTR_WARN_UNUSED_RESULT BMesh * bm
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition: btQuadWord.h:119
#define BM_ALLBITS
Definition: gpencil_trace.h:23
#define BM_WORDBITS
Definition: gpencil_trace.h:21
#define bm_scanline(bm, y)
Definition: gpencil_trace.h:25
#define BM_PUT(bm, x, y, b)
Definition: gpencil_trace.h:40
#define BM_WORDSIZE
Definition: gpencil_trace.h:20
#define BM_GET(bm, x, y)
Definition: gpencil_trace.h:36
void ED_gpencil_trace_data_to_strokes(Main *bmain, potrace_state_t *st, Object *ob, bGPDframe *gpf, int32_t offset[2], const float scale, const float sample, const int32_t resolution, const int32_t thickness)
static void add_point(bGPDstroke *gps, float scale, const int32_t offset[2], float x, float y)
void ED_gpencil_trace_bitmap_print(FILE *f, const potrace_bitmap_t *bm)
static void pixel_at_index(const ImBuf *ibuf, const int32_t idx, float r_col[4])
#define MAX_LENGTH
void ED_gpencil_trace_bitmap_free(const potrace_bitmap_t *bm)
static void add_bezier(bGPDstroke *gps, float scale, int32_t offset[2], int32_t resolution, float bcp1[2], float bcp2[2], float bcp3[2], float bcp4[2], const bool skip)
void ED_gpencil_trace_image_to_bitmap(ImBuf *ibuf, const potrace_bitmap_t *bm, const float threshold)
void ED_gpencil_trace_bitmap_invert(const potrace_bitmap_t *bm)
potrace_bitmap_t * ED_gpencil_trace_bitmap_new(int32_t w, int32_t h)
ccl_gpu_kernel_postfix ccl_global float int int int int float threshold
ccl_gpu_kernel_postfix ccl_global float int int int sw
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
ccl_gpu_kernel_postfix ccl_global float int int int int sh
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
static unsigned c
Definition: RandGen.cpp:83
static unsigned a[3]
Definition: RandGen.cpp:78
T length(const vec_base< T, Size > &a)
static const pxr::TfToken st("st", pxr::TfToken::Immortal)
static const pxr::TfToken rgba("rgba", pxr::TfToken::Immortal)
unsigned int uint32_t
Definition: stdint.h:80
signed int int32_t
Definition: stdint.h:77
unsigned int * rect
float * rect_float
Definition: BKE_main.h:121
struct MaterialGPencilStyle * gp_style
void * data
ListBase strokes
bGPDspoint * points