Blender  V2.93
gpencil_trace_utils.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2020 Blender Foundation
17  * All rights reserved.
18  */
19 
24 #include <stdio.h>
25 #include <stdlib.h>
26 #include <string.h>
27 
28 #include "MEM_guardedalloc.h"
29 
30 #include "BLI_blenlib.h"
31 #include "BLI_math.h"
32 
33 #include "BKE_gpencil.h"
34 #include "BKE_gpencil_geom.h"
35 #include "BKE_image.h"
36 #include "BKE_main.h"
37 #include "BKE_material.h"
38 
39 #include "DNA_gpencil_types.h"
40 #include "DNA_image_types.h"
41 #include "DNA_material_types.h"
42 #include "DNA_object_types.h"
43 
44 #include "IMB_imbuf.h"
45 #include "IMB_imbuf_types.h"
46 
47 #include "gpencil_trace.h"
48 
54 void ED_gpencil_trace_bitmap_print(FILE *f, const potrace_bitmap_t *bm)
55 {
56  int32_t x, y;
57  int32_t xx, yy;
58  int32_t d;
59  int32_t sw, sh;
60 
61  sw = bm->w < 79 ? bm->w : 79;
62  sh = bm->w < 79 ? bm->h : bm->h * sw * 44 / (79 * bm->w);
63 
64  for (yy = sh - 1; yy >= 0; yy--) {
65  for (xx = 0; xx < sw; xx++) {
66  d = 0;
67  for (x = xx * bm->w / sw; x < (xx + 1) * bm->w / sw; x++) {
68  for (y = yy * bm->h / sh; y < (yy + 1) * bm->h / sh; y++) {
69  if (BM_GET(bm, x, y)) {
70  d++;
71  }
72  }
73  }
74  fputc(d ? '*' : ' ', f);
75  }
76  fputc('\n', f);
77  }
78 }
79 
87 {
88  potrace_bitmap_t *bm;
89  int32_t dy = (w + BM_WORDBITS - 1) / BM_WORDBITS;
90 
91  bm = (potrace_bitmap_t *)MEM_mallocN(sizeof(potrace_bitmap_t), __func__);
92  if (!bm) {
93  return NULL;
94  }
95  bm->w = w;
96  bm->h = h;
97  bm->dy = dy;
98  bm->map = (potrace_word *)calloc(h, dy * BM_WORDSIZE);
99  if (!bm->map) {
100  free(bm);
101  return NULL;
102  }
103 
104  return bm;
105 }
106 
111 void ED_gpencil_trace_bitmap_free(const potrace_bitmap_t *bm)
112 {
113  if (bm != NULL) {
114  free(bm->map);
115  }
116  MEM_SAFE_FREE(bm);
117 }
118 
123 void ED_gpencil_trace_bitmap_invert(const potrace_bitmap_t *bm)
124 {
125  int32_t dy = bm->dy;
126  int32_t y;
127  int32_t i;
128  potrace_word *p;
129 
130  if (dy < 0) {
131  dy = -dy;
132  }
133 
134  for (y = 0; y < bm->h; y++) {
135  p = bm_scanline(bm, y);
136  for (i = 0; i < dy; i++) {
137  p[i] ^= BM_ALLBITS;
138  }
139  }
140 }
141 
148 static void pixel_at_index(const ImBuf *ibuf, const int32_t idx, float r_col[4])
149 {
150  BLI_assert(idx < (ibuf->x * ibuf->y));
151 
152  if (ibuf->rect_float) {
153  const float *frgba = &ibuf->rect_float[idx * 4];
154  copy_v4_v4(r_col, frgba);
155  }
156  else {
157  unsigned char *cp = (unsigned char *)(ibuf->rect + idx);
158  r_col[0] = (float)cp[0] / 255.0f;
159  r_col[1] = (float)cp[1] / 255.0f;
160  r_col[2] = (float)cp[2] / 255.0f;
161  r_col[3] = (float)cp[3] / 255.0f;
162  }
163 }
164 
171  const potrace_bitmap_t *bm,
172  const float threshold)
173 {
174  float rgba[4];
175  int32_t pixel = 0;
176 
177  for (uint32_t y = 0; y < ibuf->y; y++) {
178  for (uint32_t x = 0; x < ibuf->x; x++) {
179  pixel = (ibuf->x * y) + x;
180  pixel_at_index(ibuf, pixel, rgba);
181  /* Get a BW color. */
182  mul_v3_fl(rgba, rgba[3]);
183  float color = (rgba[0] + rgba[1] + rgba[2]) / 3.0f;
184  int32_t bw = (color > threshold) ? 0 : 1;
185  BM_PUT(bm, x, y, bw);
186  }
187  }
188 }
189 
190 /* Helper to add point to stroke. */
191 static void add_point(bGPDstroke *gps, float scale, const int32_t offset[2], float x, float y)
192 {
193  int32_t idx = gps->totpoints;
194  if (gps->totpoints == 0) {
195  gps->points = MEM_callocN(sizeof(bGPDspoint), "gp_stroke_points");
196  }
197  else {
198  gps->points = MEM_recallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
199  }
200  bGPDspoint *pt = &gps->points[idx];
201  pt->x = (x - offset[0]) * scale;
202  pt->y = 0;
203  pt->z = (y - offset[1]) * scale;
204  pt->pressure = 1.0f;
205  pt->strength = 1.0f;
206 
207  gps->totpoints++;
208 }
209 
210 /* helper to generate all points of curve. */
211 static void add_bezier(bGPDstroke *gps,
212  float scale,
213  int32_t offset[2],
214  int32_t resolution,
215  float bcp1[2],
216  float bcp2[2],
217  float bcp3[2],
218  float bcp4[2],
219  const bool skip)
220 {
221  const float step = 1.0f / (float)(resolution - 1);
222  float a = 0.0f;
223 
224  for (int32_t i = 0; i < resolution; i++) {
225  if ((!skip) || (i > 0)) {
226  float fpt[3];
227  interp_v2_v2v2v2v2_cubic(fpt, bcp1, bcp2, bcp3, bcp4, a);
228  add_point(gps, scale, offset, fpt[0], fpt[1]);
229  }
230  a += step;
231  }
232 }
233 
243  potrace_state_t *st,
244  Object *ob,
245  bGPDframe *gpf,
246  int32_t offset[2],
247  const float scale,
248  const float sample,
249  const int32_t resolution,
250  const int32_t thickness)
251 {
252 #define MAX_LENGTH 100.0f
253  /* Find materials and create them if not found. */
254  int32_t mat_fill_idx = BKE_gpencil_material_find_index_by_name_prefix(ob, "Stroke");
255  int32_t mat_mask_idx = BKE_gpencil_material_find_index_by_name_prefix(ob, "Holdout");
256 
257  const float default_color[4] = {0.0f, 0.0f, 0.0f, 1.0f};
258  /* Stroke and Fill material. */
259  if (mat_fill_idx == -1) {
260  int32_t new_idx;
261  Material *mat_gp = BKE_gpencil_object_material_new(bmain, ob, "Stroke", &new_idx);
262  MaterialGPencilStyle *gp_style = mat_gp->gp_style;
263 
264  copy_v4_v4(gp_style->stroke_rgba, default_color);
265  gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
266  gp_style->flag |= GP_MATERIAL_FILL_SHOW;
267  mat_fill_idx = ob->totcol - 1;
268  }
269  /* Holdout material. */
270  if (mat_mask_idx == -1) {
271  int32_t new_idx;
272  Material *mat_gp = BKE_gpencil_object_material_new(bmain, ob, "Holdout", &new_idx);
273  MaterialGPencilStyle *gp_style = mat_gp->gp_style;
274 
275  copy_v4_v4(gp_style->stroke_rgba, default_color);
276  copy_v4_v4(gp_style->fill_rgba, default_color);
277  gp_style->flag |= GP_MATERIAL_STROKE_SHOW;
278  gp_style->flag |= GP_MATERIAL_FILL_SHOW;
279  gp_style->flag |= GP_MATERIAL_IS_STROKE_HOLDOUT;
280  gp_style->flag |= GP_MATERIAL_IS_FILL_HOLDOUT;
281  mat_mask_idx = ob->totcol - 1;
282  }
283 
284  potrace_path_t *path = st->plist;
285  int n, *tag;
286  potrace_dpoint_t(*c)[3];
287 
288  /* There isn't any rule here, only the result of lots of testing to get a value that gets
289  * good results using the Potrace data. */
290  const float scalef = 0.008f * scale;
291  /* Draw each curve. */
292  path = st->plist;
293  while (path != NULL) {
294  n = path->curve.n;
295  tag = path->curve.tag;
296  c = path->curve.c;
297  int mat_idx = path->sign == '+' ? mat_fill_idx : mat_mask_idx;
298  /* Create a new stroke. */
299  bGPDstroke *gps = BKE_gpencil_stroke_add(gpf, mat_idx, 0, thickness, false);
300  /* Last point that is equals to start point. */
301  float start_point[2], last[2];
302  start_point[0] = c[n - 1][2].x;
303  start_point[1] = c[n - 1][2].y;
304 
305  for (int32_t i = 0; i < n; i++) {
306  switch (tag[i]) {
307  case POTRACE_CORNER: {
308  if (gps->totpoints == 0) {
309  add_point(gps, scalef, offset, c[n - 1][2].x, c[n - 1][2].y);
310  }
311  else {
312  add_point(gps, scalef, offset, last[0], last[1]);
313  }
314 
315  add_point(gps, scalef, offset, c[i][1].x, c[i][1].y);
316 
317  add_point(gps, scalef, offset, c[i][2].x, c[i][2].y);
318 
319  last[0] = c[i][2].x;
320  last[1] = c[i][2].y;
321  break;
322  }
323  case POTRACE_CURVETO: {
324  float cp1[2], cp2[2], cp3[2], cp4[2];
325  if (gps->totpoints == 0) {
326  cp1[0] = start_point[0];
327  cp1[1] = start_point[1];
328  }
329  else {
330  copy_v2_v2(cp1, last);
331  }
332 
333  cp2[0] = c[i][0].x;
334  cp2[1] = c[i][0].y;
335 
336  cp3[0] = c[i][1].x;
337  cp3[1] = c[i][1].y;
338 
339  cp4[0] = c[i][2].x;
340  cp4[1] = c[i][2].y;
341 
342  add_bezier(gps,
343  scalef,
344  offset,
345  resolution,
346  cp1,
347  cp2,
348  cp3,
349  cp4,
350  (gps->totpoints == 0) ? false : true);
351  copy_v2_v2(last, cp4);
352  break;
353  }
354  default:
355  break;
356  }
357  }
358  /* In some situations, Potrace can produce a wrong data and generate a very
359  * long stroke. Here the length is checked and removed if the length is too big. */
360  float length = BKE_gpencil_stroke_length(gps, true);
361  if (length <= MAX_LENGTH) {
362  bGPdata *gpd = ob->data;
363  if (sample > 0.0f) {
364  /* Resample stroke. Don't need to call to BKE_gpencil_stroke_geometry_update() because
365  * the sample function already call that. */
366  BKE_gpencil_stroke_sample(gpd, gps, sample, false);
367  }
368  else {
370  }
371  }
372  else {
373  /* Remove too long strokes. */
374  BLI_remlink(&gpf->strokes, gps);
376  }
377 
378  path = path->next;
379  }
380 #undef MAX_LENGTH
381 }
typedef float(TangentPoint)[2]
struct bGPDstroke * BKE_gpencil_stroke_add(struct bGPDframe *gpf, int mat_idx, int totpoints, short thickness, const bool insert_at_head)
Definition: gpencil.c:871
struct Material * BKE_gpencil_object_material_new(struct Main *bmain, struct Object *ob, const char *name, int *r_index)
Definition: gpencil.c:1873
void BKE_gpencil_free_stroke(struct bGPDstroke *gps)
Definition: gpencil.c:401
int BKE_gpencil_material_find_index_by_name_prefix(struct Object *ob, const char *name_prefix)
Definition: gpencil.c:3031
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)
Definition: gpencil_geom.c:429
General operations, lookup, etc. for materials.
#define BLI_assert(a)
Definition: BLI_assert.h:58
void BLI_kdtree_nd_() free(KDTree *tree)
Definition: kdtree_impl.h:116
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:133
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE void copy_v2_v2(float r[2], const float a[2])
MINLINE void mul_v3_fl(float r[3], float f)
void interp_v2_v2v2v2v2_cubic(float p[2], const float v1[2], const float v2[2], const float v3[2], const float v4[2], const float u)
Definition: math_vector.c:168
@ 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)
ATTR_WARN_UNUSED_RESULT BMesh * bm
SIMD_FORCE_INLINE const btScalar & w() const
Return the w value.
Definition: btQuadWord.h:119
SIMD_FORCE_INLINE btScalar length(const btQuaternion &q)
Return the length of a quaternion.
Definition: btQuaternion.h:895
#define BM_ALLBITS
Definition: gpencil_trace.h:39
#define BM_WORDBITS
Definition: gpencil_trace.h:37
#define bm_scanline(bm, y)
Definition: gpencil_trace.h:41
#define BM_PUT(bm, x, y, b)
Definition: gpencil_trace.h:56
#define BM_WORDSIZE
Definition: gpencil_trace.h:36
#define BM_GET(bm, x, y)
Definition: gpencil_trace.h:52
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)
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
static unsigned c
Definition: RandGen.cpp:97
static unsigned a[3]
Definition: RandGen.cpp:92
static void sample(SocketReader *reader, int x, int y, float color[4])
unsigned int uint32_t
Definition: stdint.h:83
signed int int32_t
Definition: stdint.h:80
unsigned int * rect
float * rect_float
Definition: BKE_main.h:116
struct MaterialGPencilStyle * gp_style
void * data
ListBase strokes
bGPDspoint * points