Blender  V3.3
gpencil_io_import_svg.cc
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 "BLI_math.h"
9 #include "BLI_math_vec_types.hh"
10 #include "BLI_span.hh"
11 
12 #include "DNA_gpencil_types.h"
13 
14 #include "BKE_gpencil.h"
15 #include "BKE_gpencil_geom.h"
16 
17 #include "DEG_depsgraph.h"
18 #include "DEG_depsgraph_query.h"
19 
20 #include "ED_gpencil.h"
21 
22 #include "gpencil_io.h"
23 #include "gpencil_io_import_svg.hh"
24 
25 /* Custom flags for NanoSVG. */
26 #define NANOSVG_ALL_COLOR_KEYWORDS
27 #define NANOSVG_IMPLEMENTATION
28 
29 #include "nanosvg.h"
30 
32 
33 namespace blender::io::gpencil {
34 
35 /* Constructor. */
36 GpencilImporterSVG::GpencilImporterSVG(const char *filepath, const GpencilIOParams *iparams)
37  : GpencilImporter(iparams)
38 {
39  filepath_set(filepath);
40 }
41 
43 {
44  bool result = true;
45  NSVGimage *svg_data = nullptr;
46  svg_data = nsvgParseFromFile(filepath_, "mm", 96.0f);
47  if (svg_data == nullptr) {
48  std::cout << " Could not open SVG.\n ";
49  return false;
50  }
51 
52  /* Create grease pencil object. */
54  if (params_.ob == nullptr) {
55  std::cout << "Unable to create new object.\n";
56  nsvgDelete(svg_data);
57 
58  return false;
59  }
60  gpd_ = (bGPdata *)params_.ob->data;
61 
62  /* Grease pencil is rotated 90 degrees in X axis by default. */
63  float matrix[4][4];
64  const float3 scale = float3(params_.scale);
65  unit_m4(matrix);
66  rotate_m4(matrix, 'X', DEG2RADF(-90.0f));
67  rescale_m4(matrix, scale);
68 
69  /* Loop all shapes. */
70  char prv_id[70] = {"*"};
71  int prefix = 0;
72  for (NSVGshape *shape = svg_data->shapes; shape; shape = shape->next) {
73  char *layer_id = (shape->id_parent[0] == '\0') ? BLI_sprintfN("Layer_%03d", prefix) :
74  BLI_sprintfN("%s", shape->id_parent);
75  if (!STREQ(prv_id, layer_id)) {
76  prefix++;
77  MEM_freeN(layer_id);
78  layer_id = (shape->id_parent[0] == '\0') ? BLI_sprintfN("Layer_%03d", prefix) :
79  BLI_sprintfN("%s", shape->id_parent);
80  strcpy(prv_id, layer_id);
81  }
82 
83  /* Check if the layer exist and create if needed. */
85  &gpd_->layers, layer_id, offsetof(bGPDlayer, info));
86  if (gpl == nullptr) {
87  gpl = BKE_gpencil_layer_addnew(gpd_, layer_id, true, false);
88  /* Disable lights. */
89  gpl->flag &= ~GP_LAYER_USE_LIGHTS;
90  }
91  MEM_freeN(layer_id);
92 
93  /* Check frame. */
95  /* Create materials. */
96  bool is_stroke = (bool)shape->stroke.type;
97  bool is_fill = (bool)shape->fill.type;
98  if ((!is_stroke) && (!is_fill)) {
99  is_stroke = true;
100  }
101 
102  /* Create_shape materials. */
103  const char *const mat_names[] = {"Stroke", "Fill", "Both"};
104  int index = 0;
105  if ((is_stroke) && (!is_fill)) {
106  index = 0;
107  }
108  else if ((!is_stroke) && (is_fill)) {
109  index = 1;
110  }
111  else if ((is_stroke) && (is_fill)) {
112  index = 2;
113  }
114  int32_t mat_index = create_material(mat_names[index], is_stroke, is_fill);
115 
116  /* Loop all paths to create the stroke data. */
117  for (NSVGpath *path = shape->paths; path; path = path->next) {
118  create_stroke(gpd_, gpf, shape, path, mat_index, matrix);
119  }
120  }
121 
122  /* Free SVG memory. */
123  nsvgDelete(svg_data);
124 
125  /* Calculate bounding box and move all points to new origin center. */
126  float gp_center[3];
127  BKE_gpencil_centroid_3d(gpd_, gp_center);
128 
129  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd_->layers) {
130  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
131  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
132  for (bGPDspoint &pt : MutableSpan(gps->points, gps->totpoints)) {
133  sub_v3_v3(&pt.x, gp_center);
134  }
135  /* Calc stroke bounding box. */
137  }
138  }
139  }
140 
141  return result;
142 }
143 
144 void GpencilImporterSVG::create_stroke(bGPdata *gpd,
145  bGPDframe *gpf,
146  NSVGshape *shape,
147  NSVGpath *path,
148  const int32_t mat_index,
149  const float matrix[4][4])
150 {
151  const bool is_stroke = (bool)shape->stroke.type;
152  const bool is_fill = (bool)shape->fill.type;
153 
154  const int edges = params_.resolution;
155  const float step = 1.0f / (float)(edges - 1);
156 
157  const int totpoints = (path->npts / 3) * params_.resolution;
158 
159  bGPDstroke *gps = BKE_gpencil_stroke_new(mat_index, totpoints, 1.0f);
160  BLI_addtail(&gpf->strokes, gps);
161 
162  if (path->closed == '1') {
163  gps->flag |= GP_STROKE_CYCLIC;
164  }
165  if (is_stroke) {
166  gps->thickness = shape->strokeWidth * params_.scale;
167  }
168  /* Apply Fill vertex color. */
169  if (is_fill) {
170  NSVGpaint fill = shape->fill;
171  convert_color(fill.color, gps->vert_color_fill);
172  gps->fill_opacity_fac = gps->vert_color_fill[3];
173  gps->vert_color_fill[3] = 1.0f;
174  }
175 
176  int start_index = 0;
177  for (int i = 0; i < path->npts - 1; i += 3) {
178  float *p = &path->pts[i * 2];
179  float a = 0.0f;
180  for (int v = 0; v < edges; v++) {
181  bGPDspoint *pt = &gps->points[start_index];
182  pt->strength = shape->opacity;
183  pt->pressure = 1.0f;
184  pt->z = 0.0f;
185  /* TODO(antoniov): Can be improved loading curve data instead of loading strokes. */
186  interp_v2_v2v2v2v2_cubic(&pt->x, &p[0], &p[2], &p[4], &p[6], a);
187 
188  /* Scale from millimeters. */
189  mul_v3_fl(&pt->x, 0.001f);
190  mul_m4_v3(matrix, &pt->x);
191 
192  /* Apply color to vertex color. */
193  if (is_fill) {
194  NSVGpaint fill = shape->fill;
195  convert_color(fill.color, pt->vert_color);
196  }
197  if (is_stroke) {
198  NSVGpaint stroke = shape->stroke;
199  convert_color(stroke.color, pt->vert_color);
200  gps->fill_opacity_fac = pt->vert_color[3];
201  }
202  pt->vert_color[3] = 1.0f;
203 
204  a += step;
205  start_index++;
206  }
207  }
208 
209  /* Cleanup and recalculate geometry. */
210  BKE_gpencil_stroke_merge_distance(gpd, gpf, gps, 0.001f, true);
212 }
213 
214 /* Unpack internal NanoSVG color. */
215 static void unpack_nano_color(const unsigned int pack, float r_col[4])
216 {
217  unsigned char rgb_u[4];
218 
219  rgb_u[0] = ((pack) >> 0) & 0xFF;
220  rgb_u[1] = ((pack) >> 8) & 0xFF;
221  rgb_u[2] = ((pack) >> 16) & 0xFF;
222  rgb_u[3] = ((pack) >> 24) & 0xFF;
223 
224  r_col[0] = (float)rgb_u[0] / 255.0f;
225  r_col[1] = (float)rgb_u[1] / 255.0f;
226  r_col[2] = (float)rgb_u[2] / 255.0f;
227  r_col[3] = (float)rgb_u[3] / 255.0f;
228 }
229 
230 void GpencilImporterSVG::convert_color(const int32_t color, float r_linear_rgba[4])
231 {
232  float rgba[4];
234 
235  srgb_to_linearrgb_v3_v3(r_linear_rgba, rgba);
236  r_linear_rgba[3] = rgba[3];
237 }
238 
239 } // namespace blender::io::gpencil
typedef float(TangentPoint)[2]
struct bGPDlayer * BKE_gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive, bool add_to_header)
Definition: gpencil.c:621
struct bGPDstroke * BKE_gpencil_stroke_new(int mat_idx, int totpoints, short thickness)
Definition: gpencil.c:756
struct bGPDframe * BKE_gpencil_layer_frame_get(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew)
Definition: gpencil.c:1232
@ GP_GETFRAME_ADD_NEW
Definition: BKE_gpencil.h:341
void BKE_gpencil_centroid_3d(struct bGPdata *gpd, float r_centroid[3])
void BKE_gpencil_stroke_geometry_update(struct bGPdata *gpd, struct bGPDstroke *gps)
void BKE_gpencil_stroke_boundingbox_calc(struct bGPDstroke *gps)
void BKE_gpencil_stroke_merge_distance(struct bGPdata *gpd, struct bGPDframe *gpf, struct bGPDstroke *gps, float threshold, bool use_unselected)
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
void * BLI_findstring(const struct ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE void srgb_to_linearrgb_v3_v3(float linear[3], const float srgb[3])
void unit_m4(float m[4][4])
Definition: rct.c:1090
void rescale_m4(float mat[4][4], const float scale[3])
Definition: math_matrix.c:2362
void mul_m4_v3(const float M[4][4], float r[3])
Definition: math_matrix.c:729
void rotate_m4(float mat[4][4], char axis, float angle)
Definition: math_matrix.c:2325
#define DEG2RADF(_deg)
MINLINE void sub_v3_v3(float r[3], const float a[3])
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 mul_v3_fl(float r[3], float f)
size_t size_t char * BLI_sprintfN(const char *__restrict format,...) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_PRINTF_FORMAT(1
#define STREQ(a, b)
@ GP_STROKE_CYCLIC
@ GP_LAYER_USE_LIGHTS
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 const BMVert * v
void filepath_set(const char *filepath)
GpencilImporterSVG(const char *filepath, const struct GpencilIOParams *iparams)
int32_t create_material(const char *name, bool stroke, bool fill)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
static unsigned a[3]
Definition: RandGen.cpp:78
static void unpack_nano_color(const unsigned int pack, float r_col[4])
static const pxr::TfToken rgba("rgba", pxr::TfToken::Immortal)
signed int int32_t
Definition: stdint.h:77
int32_t resolution
Definition: gpencil_io.h:37
Object * ob
Definition: gpencil_io.h:23
void * data
ListBase strokes
float vert_color[4]
ListBase layers