Blender  V3.3
gpencil_io_export_pdf.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_vector.h"
9 
10 #include "DNA_gpencil_types.h"
11 #include "DNA_material_types.h"
12 #include "DNA_object_types.h"
13 #include "DNA_scene_types.h"
14 #include "DNA_screen_types.h"
15 #include "DNA_view3d_types.h"
16 
17 #include "BKE_context.h"
18 #include "BKE_gpencil.h"
19 #include "BKE_gpencil_geom.h"
20 #include "BKE_main.h"
21 #include "BKE_material.h"
22 
23 #include "DEG_depsgraph.h"
24 #include "DEG_depsgraph_query.h"
25 
26 #include "ED_gpencil.h"
27 #include "ED_view3d.h"
28 
29 #ifdef WIN32
30 # include "utfconv.h"
31 #endif
32 
33 #include "UI_view2d.h"
34 
35 #include "gpencil_io.h"
36 #include "gpencil_io_export_pdf.hh"
37 
38 namespace blender ::io ::gpencil {
39 
40 static void error_handler(HPDF_STATUS error_no, HPDF_STATUS detail_no, void *UNUSED(user_data))
41 {
42  printf("ERROR: error_no=%04X, detail_no=%u\n", (HPDF_UINT)error_no, (HPDF_UINT)detail_no);
43 }
44 
45 /* Constructor. */
46 GpencilExporterPDF::GpencilExporterPDF(const char *filepath, const GpencilIOParams *iparams)
47  : GpencilExporter(iparams)
48 {
49  filepath_set(filepath);
50 
51  invert_axis_[0] = false;
52  invert_axis_[1] = false;
53 
54  pdf_ = nullptr;
55  page_ = nullptr;
56 }
57 
59 {
60  return create_document();
61 }
62 
64 {
65  return add_page();
66 }
67 
69 {
70  export_gpencil_layers();
71  return true;
72 }
73 
75 {
76  /* Support unicode character paths on Windows. */
77  HPDF_STATUS res = 0;
78 
79  /* TODO: It looks `libharu` does not support unicode. */
80 #if 0 /* `ifdef WIN32` */
81  char filepath_cstr[FILE_MAX];
82  BLI_strncpy(filepath_cstr, filepath_, FILE_MAX);
83 
84  UTF16_ENCODE(filepath_cstr);
85  std::wstring wstr(filepath_cstr_16);
86  res = HPDF_SaveToFile(pdf_, wstr.c_str());
87 
88  UTF16_UN_ENCODE(filepath_cstr);
89 #else
90  res = HPDF_SaveToFile(pdf_, filepath_);
91 #endif
92 
93  return (res == 0) ? true : false;
94 }
95 
96 bool GpencilExporterPDF::create_document()
97 {
98  pdf_ = HPDF_New(error_handler, nullptr);
99  if (!pdf_) {
100  std::cout << "error: cannot create PdfDoc object\n";
101  return false;
102  }
103  return true;
104 }
105 
106 bool GpencilExporterPDF::add_page()
107 {
108  /* Add a new page object. */
109  page_ = HPDF_AddPage(pdf_);
110  if (!pdf_) {
111  std::cout << "error: cannot create PdfPage\n";
112  return false;
113  }
114 
115  HPDF_Page_SetWidth(page_, render_x_);
116  HPDF_Page_SetHeight(page_, render_y_);
117 
118  return true;
119 }
120 
121 void GpencilExporterPDF::export_gpencil_layers()
122 {
123  /* If is doing a set of frames, the list of objects can change for each frame. */
125 
126  const bool is_normalized = ((params_.flag & GP_EXPORT_NORM_THICKNESS) != 0);
127 
128  for (ObjectZ &obz : ob_list_) {
129  Object *ob = obz.ob;
130 
131  /* Use evaluated version to get strokes with modifiers. */
132  Object *ob_eval_ = (Object *)DEG_get_evaluated_id(depsgraph_, &ob->id);
133  bGPdata *gpd_eval = (bGPdata *)ob_eval_->data;
134 
135  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd_eval->layers) {
136  if (gpl->flag & GP_LAYER_HIDE) {
137  continue;
138  }
140 
141  bGPDframe *gpf = gpl->actframe;
142  if ((gpf == nullptr) || (gpf->strokes.first == nullptr)) {
143  continue;
144  }
145 
146  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
147  if (gps->totpoints < 2) {
148  continue;
149  }
150  if (!ED_gpencil_stroke_material_visible(ob, gps)) {
151  continue;
152  }
153  /* Skip invisible lines. */
155  const float fill_opacity = fill_color_[3] * gpl->opacity;
156  const float stroke_opacity = stroke_color_[3] * stroke_average_opacity_get() *
157  gpl->opacity;
158  if ((fill_opacity < GPENCIL_ALPHA_OPACITY_THRESH) &&
159  (stroke_opacity < GPENCIL_ALPHA_OPACITY_THRESH)) {
160  continue;
161  }
162 
163  MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
164  const bool is_stroke = ((gp_style->flag & GP_MATERIAL_STROKE_SHOW) &&
165  (gp_style->stroke_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH) &&
166  (stroke_opacity > GPENCIL_ALPHA_OPACITY_THRESH));
167  const bool is_fill = ((gp_style->flag & GP_MATERIAL_FILL_SHOW) &&
168  (gp_style->fill_rgba[3] > GPENCIL_ALPHA_OPACITY_THRESH));
169 
170  if ((!is_stroke) && (!is_fill)) {
171  continue;
172  }
173 
174  /* Duplicate the stroke to apply any layer thickness change. */
175  bGPDstroke *gps_duplicate = BKE_gpencil_stroke_duplicate(gps, true, false);
176 
177  /* Apply layer thickness change. */
178  gps_duplicate->thickness += gpl->line_change;
179  /* Apply object scale to thickness. */
180  gps_duplicate->thickness *= mat4_to_scale(ob->obmat);
181  CLAMP_MIN(gps_duplicate->thickness, 1.0f);
182  /* Fill. */
183  if ((is_fill) && (params_.flag & GP_EXPORT_FILL)) {
184  /* Fill is exported as polygon for fill and stroke in a different shape. */
185  export_stroke_to_polyline(gpl, gps_duplicate, is_stroke, true, false);
186  }
187 
188  /* Stroke. */
189  if (is_stroke) {
190  if (is_normalized) {
191  export_stroke_to_polyline(gpl, gps_duplicate, is_stroke, false, true);
192  }
193  else {
195  rv3d_, gpd_, gpl, gps_duplicate, 3, diff_mat_.values);
196 
197  /* Sample stroke. */
198  if (params_.stroke_sample > 0.0f) {
199  BKE_gpencil_stroke_sample(gpd_eval, gps_perimeter, params_.stroke_sample, false, 0);
200  }
201 
202  export_stroke_to_polyline(gpl, gps_perimeter, is_stroke, false, false);
203 
204  BKE_gpencil_free_stroke(gps_perimeter);
205  }
206  }
207  BKE_gpencil_free_stroke(gps_duplicate);
208  }
209  }
210  }
211 }
212 
213 void GpencilExporterPDF::export_stroke_to_polyline(bGPDlayer *gpl,
214  bGPDstroke *gps,
215  const bool is_stroke,
216  const bool do_fill,
217  const bool normalize)
218 {
219  const bool cyclic = ((gps->flag & GP_STROKE_CYCLIC) != 0);
220  const float avg_pressure = BKE_gpencil_stroke_average_pressure_get(gps);
221 
222  /* Get the thickness in pixels using a simple 1 point stroke. */
223  bGPDstroke *gps_temp = BKE_gpencil_stroke_duplicate(gps, false, false);
224  gps_temp->totpoints = 1;
225  gps_temp->points = MEM_new<bGPDspoint>("gp_stroke_points");
226  const bGPDspoint *pt_src = &gps->points[0];
227  bGPDspoint *pt_dst = &gps_temp->points[0];
228  copy_v3_v3(&pt_dst->x, &pt_src->x);
229  pt_dst->pressure = avg_pressure;
230 
231  const float radius = stroke_point_radius_get(gpl, gps_temp);
232 
233  BKE_gpencil_free_stroke(gps_temp);
234 
235  color_set(gpl, do_fill);
236 
237  if (is_stroke && !do_fill) {
238  HPDF_Page_SetLineJoin(page_, HPDF_ROUND_JOIN);
239  HPDF_Page_SetLineWidth(page_, MAX2((radius * 2.0f) - gpl->line_change, 1.0f));
240  }
241 
242  /* Loop all points. */
243  for (const int i : IndexRange(gps->totpoints)) {
244  bGPDspoint *pt = &gps->points[i];
245  const float2 screen_co = gpencil_3D_point_to_2D(&pt->x);
246  if (i == 0) {
247  HPDF_Page_MoveTo(page_, screen_co.x, screen_co.y);
248  }
249  else {
250  HPDF_Page_LineTo(page_, screen_co.x, screen_co.y);
251  }
252  }
253  /* Close cyclic */
254  if (cyclic) {
255  HPDF_Page_ClosePath(page_);
256  }
257 
258  if (do_fill || !normalize) {
259  HPDF_Page_Fill(page_);
260  }
261  else {
262  HPDF_Page_Stroke(page_);
263  }
264 
265  HPDF_Page_GRestore(page_);
266 }
267 
268 void GpencilExporterPDF::color_set(bGPDlayer *gpl, const bool do_fill)
269 {
270  const float fill_opacity = fill_color_[3] * gpl->opacity;
271  const float stroke_opacity = stroke_color_[3] * stroke_average_opacity_get() * gpl->opacity;
272  const bool need_state = (do_fill && fill_opacity < 1.0f) || (stroke_opacity < 1.0f);
273 
274  HPDF_Page_GSave(page_);
275  HPDF_ExtGState gstate = (need_state) ? HPDF_CreateExtGState(pdf_) : nullptr;
276 
277  float col[3];
278  if (do_fill) {
281  CLAMP3(col, 0.0f, 1.0f);
282  HPDF_Page_SetRGBFill(page_, col[0], col[1], col[2]);
283  if (gstate) {
284  HPDF_ExtGState_SetAlphaFill(gstate, clamp_f(fill_opacity, 0.0f, 1.0f));
285  }
286  }
287  else {
290  CLAMP3(col, 0.0f, 1.0f);
291 
292  HPDF_Page_SetRGBFill(page_, col[0], col[1], col[2]);
293  HPDF_Page_SetRGBStroke(page_, col[0], col[1], col[2]);
294  if (gstate) {
295  HPDF_ExtGState_SetAlphaFill(gstate, clamp_f(stroke_opacity, 0.0f, 1.0f));
296  HPDF_ExtGState_SetAlphaStroke(gstate, clamp_f(stroke_opacity, 0.0f, 1.0f));
297  }
298  }
299  if (gstate) {
300  HPDF_Page_SetExtGState(page_, gstate);
301  }
302 }
303 } // namespace blender::io::gpencil
struct bGPDstroke * BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src, bool dup_points, bool dup_curve)
Definition: gpencil.c:855
#define GPENCIL_ALPHA_OPACITY_THRESH
Definition: BKE_gpencil.h:323
void BKE_gpencil_free_stroke(struct bGPDstroke *gps)
Definition: gpencil.c:391
float BKE_gpencil_stroke_average_pressure_get(struct bGPDstroke *gps)
struct bGPDstroke * BKE_gpencil_stroke_perimeter_from_view(struct RegionView3D *rv3d, struct bGPdata *gpd, const struct bGPDlayer *gpl, struct bGPDstroke *gps, int subdivisions, const float diff_mat[4][4])
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.
struct MaterialGPencilStyle * BKE_gpencil_material_settings(struct Object *ob, short act)
Definition: material.c:805
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
MINLINE float clamp_f(float value, float min, float max)
MINLINE void linearrgb_to_srgb_v3_v3(float srgb[3], const float linear[3])
float mat4_to_scale(const float M[4][4])
Definition: math_matrix.c:2185
MINLINE void copy_v3_v3(float r[3], const float a[3])
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
Definition: math_vector.c:29
static struct error_handler_data error_handler
#define FILE_MAX
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
#define UNUSED(x)
#define MAX2(a, b)
#define CLAMP3(vec, b, c)
#define CLAMP_MIN(a, b)
struct ID * DEG_get_evaluated_id(const struct Depsgraph *depsgraph, struct ID *id)
@ GP_STROKE_CYCLIC
@ GP_LAYER_HIDE
@ GP_MATERIAL_STROKE_SHOW
@ GP_MATERIAL_FILL_SHOW
Object is a sort of wrapper for general info.
void filepath_set(const char *filepath)
float stroke_point_radius_get(struct bGPDlayer *gpl, struct bGPDstroke *gps)
blender::Vector< ObjectZ > ob_list_
void prepare_stroke_export_colors(struct Object *ob, struct bGPDstroke *gps)
float2 gpencil_3D_point_to_2D(const float3 co)
void prepare_layer_export_matrix(struct Object *ob, struct bGPDlayer *gpl)
void * user_data
@ GP_EXPORT_NORM_THICKNESS
Definition: gpencil_io.h:47
@ GP_EXPORT_FILL
Definition: gpencil_io.h:45
bool ED_gpencil_stroke_material_visible(Object *ob, const bGPDstroke *gps)
uint col
vec_base< T, Size > normalize(const vec_base< T, Size > &v)
float stroke_sample
Definition: gpencil_io.h:36
uint32_t flag
Definition: gpencil_io.h:29
void * first
Definition: DNA_listBase.h:31
float obmat[4][4]
void * data
ListBase strokes
float tintcolor[4]
bGPDspoint * points
ListBase layers
float values[4][4]
Definition: BLI_float4x4.hh:13
float x
Definition: types_float2.h:15
float y
Definition: types_float2.h:15
#define UTF16_ENCODE(in8str)
Definition: utfconv.h:83
#define UTF16_UN_ENCODE(in8str)
Definition: utfconv.h:87