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