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