Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
object-renderer.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
10#include "object-renderer.h"
11#include <cairo.h>
12#include <cairomm/enums.h>
13#include <cairomm/pattern.h>
14#include <cairomm/surface.h>
15#include <gdkmm/rgba.h>
16#include <glibmm/ustring.h>
17#include <optional>
18#include "colors/color.h"
19#include "display/cairo-utils.h"
20#include "document.h"
21#include "gradient-chemistry.h"
22#include "object/sp-gradient.h"
23#include "object/sp-image.h"
24#include "object/sp-marker.h"
25#include "object/sp-object.h"
26#include "object/sp-pattern.h"
27#include "object/sp-use.h"
28#include "ui/svg-renderer.h"
30#include "xml/node.h"
31#include "object/sp-defs.h"
32#include "object/sp-item.h"
33#include "object/sp-root.h"
34#include "object/sp-symbol.h"
35#include "pattern-manager.h"
36#include "display/drawing.h"
37#include "util/scope_exit.h"
39#include "ui/util.h"
41using namespace std::literals;
42
43namespace Inkscape {
44
45// traverse nodes starting from given 'object' until visitor returns object that evaluates to true
46template<typename V>
47bool visit_until(SPObject& object, V&& visitor) {
48 if (visitor(object)) return true;
49
50 // SPUse inserts referenced object as a child; skip it
51 if (is<SPUse>(&object)) return false;
52
53 for (auto&& child : object.children) {
54 if (visit_until(child, visitor)) return true;
55 }
56
57 return false;
58}
59
60const char* style_from_use_element(const char* id, SPDocument* document) {
61 if (!id || !*id || !document) return nullptr;
62
63 auto root = document->getRoot();
64 if (!root) return nullptr;
65
66 const char* style = nullptr;
67 Glib::ustring ident = "#";
68 ident += id;
69
70 visit_until(*root, [&](SPObject& obj){
71 if (auto use = cast<SPUse>(&obj)) {
72 if (auto href = Inkscape::getHrefAttribute(*use->getRepr()).second) {
73 if (ident == href) {
74 // style = use->getRepr()->attribute("style");
75 style = use->getAttribute("style");
76 return true;
77 }
78 }
79 }
80 return false;
81 });
82
83 return style;
84}
85
86static std::unique_ptr<SPDocument> symbols_preview_doc()
87{
88 constexpr auto buffer = R"A(
89<svg xmlns="http://www.w3.org/2000/svg"
90 xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
91 xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
92 xmlns:xlink="http://www.w3.org/1999/xlink">
93 <use id="the_use" xlink:href="#the_symbol"/>
94</svg>
95)A"sv;
96 return SPDocument::createNewDocFromMem(buffer, false);
97}
98
99Cairo::RefPtr<Cairo::Surface> draw_symbol(SPObject& symbol, double box_w, double box_h, double device_scale, SPDocument* preview_document, bool style_from_use) {
100 // Create a copy repr of the symbol with id="the_symbol"
101 Inkscape::XML::Node* repr = symbol.getRepr()->duplicate(preview_document->getReprDoc());
102 repr->setAttribute("id", "the_symbol");
103
104 // First look for default style stored in <symbol>
105 auto style = repr->attribute("inkscape:symbol-style");
106 if (!style) {
107 // If no default style in <symbol>, look in documents.
108
109 // Read style from <use> element pointing to this symbol?
110 if (style_from_use) {
111 // When symbols are inserted from a set into a new document, styles they may rely on
112 // are copied from original document and applied to the <use> symbol.
113 // We need to use those styles to render symbols correctly, because some symbols only
114 // define geometry and no presentation attributes and defaults (black fill, no stroke)
115 // may be completely incorrect (for instance originals may have no fill and stroke).
116 auto id = symbol.getId();
117 style = style_from_use_element(id, symbol.document);
118 }
119 else {
120 style = symbol.document->getReprRoot()->attribute("style");
121 }
122 }
123
124 // This is for display in Symbols dialog only
125 if (style) repr->setAttribute("style", style);
126
127 // reach out to the document for CSS styles, in case symbol uses some class selectors
128 SPDocument::install_reference_document scoped(preview_document, symbol.document);
129
130 preview_document->getDefs()->getRepr()->appendChild(repr);
132
133 // Uncomment this to get the preview_document documents saved (useful for debugging)
134 // FILE *fp = fopen (g_strconcat(id, ".svg", NULL), "w");
135 // sp_repr_save_stream(preview_document->getReprDoc(), fp);
136 // fclose (fp);
137
138 // Make sure preview_document is up-to-date.
139 preview_document->ensureUpToDate();
140
141 unsigned dkey = SPItem::display_key_new(1);
142 Inkscape::Drawing drawing; // New drawing for offscreen rendering.
143 drawing.setRoot(preview_document->getRoot()->invoke_show(drawing, dkey, SP_ITEM_SHOW_DISPLAY));
144 auto invoke_hide_guard = scope_exit([&] { preview_document->getRoot()->invoke_hide(dkey); });
145 // drawing.root()->setTransform(affine);
146 drawing.setExact(); // Maximum quality for blurs.
147
148 // Make sure we have symbol in preview_document
149 SPObject* object_temp = preview_document->getObjectById("the_use");
150
151 auto item = cast<SPItem>(object_temp);
152 g_assert(item != nullptr);
153
154 // We could use cache here, but it doesn't really work with the structure
155 // of this user interface and we've already cached the pixbuf in the gtklist
156 cairo_surface_t* s = nullptr;
157 // Find object's bbox in document.
158 // Note symbols can have own viewport... ignore for now.
160
161 if (dbox) {
162 double width = dbox->width();
163 double height = dbox->height();
164
165 if (width == 0.0) width = 1.0;
166 if (height == 0.0) height = 1.0;
167
168 auto scale = std::min(box_w / width, box_h / height);
169 if (scale > 1.0) {
170 scale = 1.0;
171 }
172
173 s = render_surface(drawing, scale, *dbox, Geom::IntPoint(box_w, box_h), device_scale, nullptr, true);
174 }
175
176 preview_document->getObjectByRepr(repr)->deleteObject(false);
177
178 if (s) {
179 cairo_surface_set_device_scale(s, device_scale, device_scale);
180 }
181
182 return Cairo::RefPtr<Cairo::Surface>(new Cairo::Surface(s, true));
183}
184
185void draw_gradient(const Cairo::RefPtr<Cairo::Context>& cr, SPGradient* gradient, int x, int width) {
187
188 cairo_set_source(cr->cobj(), check);
189 cr->fill_preserve();
190 cairo_pattern_destroy(check);
191
192 if (gradient) {
193 auto p = gradient->create_preview_pattern(width);
194 if (!p) {
195 return;
196 }
197 cairo_matrix_t m;
198 cairo_matrix_init_translate(&m, -x, 0);
199 cairo_pattern_set_matrix(p, &m);
200 cairo_set_source(cr->cobj(), p);
201 cr->fill();
202 cairo_pattern_destroy(p);
203 }
204}
205
206Cairo::RefPtr<Cairo::Surface> draw_gradient(SPGradient* gradient, double width, double height, double device_scale, bool stops) {
207 auto surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, width * device_scale, height * device_scale);
208 cairo_surface_set_device_scale(surface->cobj(), device_scale, device_scale);
209 auto ctx = Cairo::Context::create(surface);
210
211 auto h = stops ? height / 2 : height;
212 auto x = 0.5 * device_scale;
213 auto y = 0.5 * device_scale;
214 width -= device_scale;
215 h -= device_scale;
216
217 ctx->rectangle(x, y, width, h);
218 draw_gradient(ctx, gradient, 0, width);
219
220 // border
221 ctx->rectangle(x, y, width, h);
222 ctx->set_source_rgb(0.5, 0.5, 0.5);
223 ctx->set_line_width(1.0);
224 ctx->stroke();
225
226 if (stops) {
227 double radius = 3;
228 auto v = gradient->getVector();
229 for (auto& stop : v->vector.stops) {
230 double py = h + 2 * radius;
231 double px = std::round(stop.offset * width);
232 ctx->arc(px, py, radius, 0, 2 * M_PI);
233 ink_cairo_set_source_color(ctx->cobj(), *stop.color);
234 ctx->fill_preserve();
235 ctx->set_source_rgb(0.5, 0.5, 0.5);
236 ctx->stroke();
237 }
238 }
239
240 return surface;
241}
242
243std::unique_ptr<SPDocument> ink_markers_preview_doc(const Glib::ustring& group_id)
244{
245 constexpr auto buffer = R"A(
246 <svg xmlns="http://www.w3.org/2000/svg"
247 xmlns:xlink="http://www.w3.org/1999/xlink"
248 id="MarkerSample">
249
250 <defs id="defs">
251 <filter id="softGlow" height="1.2" width="1.2" x="0.0" y="0.0">
252 <!-- <feMorphology operator="dilate" radius="1" in="SourceAlpha" result="thicken" id="feMorphology2" /> -->
253 <!-- Use a gaussian blur to create the soft blurriness of the glow -->
254 <feGaussianBlur in="SourceAlpha" stdDeviation="3" result="blurred" id="feGaussianBlur4" />
255 <!-- Change the color -->
256 <feFlood flood-color="rgb(255,255,255)" result="glowColor" id="feFlood6" flood-opacity="0.70" />
257 <!-- Color in the glows -->
258 <feComposite in="glowColor" in2="blurred" operator="in" result="softGlow_colored" id="feComposite8" />
259 <!-- Layer the effects together -->
260 <feMerge id="feMerge14">
261 <feMergeNode in="softGlow_colored" id="feMergeNode10" />
262 <feMergeNode in="SourceGraphic" id="feMergeNode12" />
263 </feMerge>
264 </filter>
265 </defs>
266
267 <!-- cross at the end of the line to help position marker -->
268 <symbol id="cross" width="25" height="25" viewBox="0 0 25 25">
269 <path class="cross" style="mix-blend-mode:difference;stroke:#7ff;stroke-opacity:1;fill:none;display:block" d="M 0,0 M 25,25 M 10,10 15,15 M 10,15 15,10" />
270 <!-- <path class="cross" style="mix-blend-mode:difference;stroke:#7ff;stroke-width:1;stroke-opacity:1;fill:none;display:block;-inkscape-stroke:hairline" d="M 0,0 M 25,25 M 10,10 15,15 M 10,15 15,10" /> -->
271 </symbol>
272
273 <!-- very short path with 1px stroke used to measure size of marker -->
274 <path id="measure-marker" style="stroke-width:1.0;stroke-opacity:0.01;marker-start:url(#sample)" d="M 0,9999 m 0,0.1" />
275
276 <path id="line-marker-start" class="line colors" style="stroke-width:2;stroke-opacity:0.2" d="M 12.5,12.5 l 1000,0" />
277 <!-- <g id="marker-start" class="group" style="filter:url(#softGlow)"> -->
278 <g id="marker-start" class="group">
279 <path class="colors" style="stroke-width:2;stroke-opacity:0;marker-start:url(#sample)"
280 d="M 12.5,12.5 L 25,12.5"/>
281 <rect x="0" y="0" width="25" height="25" style="fill:none;stroke:none"/>
282 <use xlink:href="#cross" width="25" height="25" />
283 </g>
284
285 <path id="line-marker-mid" class="line colors" style="stroke-width:2;stroke-opacity:0.2" d="M -1000,12.5 L 1000,12.5" />
286 <g id="marker-mid" class="group">
287 <path class="colors" style="stroke-width:2;stroke-opacity:0;marker-mid:url(#sample)"
288 d="M 0,12.5 L 12.5,12.5 L 25,12.5"/>
289 <rect x="0" y="0" width="25" height="25" style="fill:none;stroke:none"/>
290 <use xlink:href="#cross" width="25" height="25" />
291 </g>
292
293 <path id="line-marker-end" class="line colors" style="stroke-width:2;stroke-opacity:0.2" d="M -1000,12.5 L 12.5,12.5" />
294 <g id="marker-end" class="group">
295 <path class="colors" style="stroke-width:2;stroke-opacity:0;marker-end:url(#sample)"
296 d="M 0,12.5 L 12.5,12.5"/>
297 <rect x="0" y="0" width="25" height="25" style="fill:none;stroke:none"/>
298 <use xlink:href="#cross" width="25" height="25" />
299 </g>
300
301 </svg>
302)A"sv;
303
304 auto document = SPDocument::createNewDocFromMem(buffer, false);
305 // only leave requested group, so nothing else gets rendered
306 for (auto&& group : document->getObjectsByClass("group")) {
307 assert(group->getId());
308 if (group->getId() != group_id) {
309 group->deleteObject();
310 }
311 }
312 auto id = "line-" + group_id;
313 for (auto&& line : document->getObjectsByClass("line")) {
314 assert(line->getId());
315 if (line->getId() != id) {
316 line->deleteObject();
317 }
318 }
319 return document;
320}
321
322
323Cairo::RefPtr<Cairo::Surface> create_marker_image(
324 const Glib::ustring& group_id,
325 SPDocument* _sandbox,
326 Gdk::RGBA marker_color,
327 Geom::IntPoint pixel_size,
328 const char* mname,
329 SPDocument* source,
330 Inkscape::Drawing& drawing,
331 std::optional<guint32> checkerboard,
332 bool no_clip,
333 double scale,
334 int device_scale)
335{
336 Cairo::RefPtr<Cairo::Surface> g_bad_marker;
337
338 // Retrieve the marker named 'mname' from the source SVG document
339 const SPObject* marker = source ? source->getObjectById(mname) : nullptr;
340 if (marker == nullptr) {
341 g_warning("bad mname: %s", mname);
342 return g_bad_marker;
343 }
344
345 SPObject *oldmarker = _sandbox->getObjectById("sample");
346 if (oldmarker) {
347 oldmarker->deleteObject(false);
348 }
349
350 // Create a copy repr of the marker with id="sample"
351 Inkscape::XML::Document *xml_doc = _sandbox->getReprDoc();
352 Inkscape::XML::Node *mrepr = marker->getRepr()->duplicate(xml_doc);
353 mrepr->setAttribute("id", "sample");
354
355 // Replace the old sample in the sandbox by the new one
356 Inkscape::XML::Node *defsrepr = _sandbox->getObjectById("defs")->getRepr();
357
358 // TODO - This causes a SIGTRAP on windows
359 defsrepr->appendChild(mrepr);
360
362
363 // If the marker color is a url link to a pattern or gradient copy that too
364 SPObject *mk = source->getObjectById(mname);
366 //const char *mfill = sp_repr_css_property(css_marker, "fill", "none");
367 const char *mstroke = sp_repr_css_property(css_marker, "fill", "none");
368
369 if (!strncmp (mstroke, "url(", 4)) {
370 SPObject *linkObj = getMarkerObj(mstroke, source);
371 if (linkObj) {
372 Inkscape::XML::Node *grepr = linkObj->getRepr()->duplicate(xml_doc);
373 SPObject *oldmarker = _sandbox->getObjectById(linkObj->getId());
374 if (oldmarker) {
375 oldmarker->deleteObject(false);
376 }
377 defsrepr->appendChild(grepr);
379
380 if (is<SPGradient>(linkObj)) {
381 SPGradient *vector = sp_gradient_get_forked_vector_if_necessary(cast<SPGradient>(linkObj), false);
382 if (vector) {
383 Inkscape::XML::Node *grepr = vector->getRepr()->duplicate(xml_doc);
384 SPObject *oldmarker = _sandbox->getObjectById(vector->getId());
385 if (oldmarker) {
386 oldmarker->deleteObject(false);
387 }
388 defsrepr->appendChild(grepr);
390 }
391 }
392 }
393 }
394
395// Uncomment this to get the sandbox documents saved (useful for debugging)
396 // FILE *fp = fopen (g_strconcat(combo_id, mname, ".svg", nullptr), "w");
397 // sp_repr_save_stream(_sandbox->getReprDoc(), fp);
398 // fclose (fp);
399
400 // object to render; note that the id is the same as that of the combo we're building
401 SPObject *object = _sandbox->getObjectById(group_id);
402
403 if (object == nullptr || !is<SPItem>(object)) {
404 g_warning("no obj: %s", group_id.c_str());
405 return g_bad_marker;
406 }
407
408 Gdk::RGBA fg = marker_color;
409 auto fgcolor = gdk_to_css_color(fg);
410 fg.set_red(1 - fg.get_red());
411 fg.set_green(1 - fg.get_green());
412 fg.set_blue(1 - fg.get_blue());
413 auto bgcolor = gdk_to_css_color(fg);
414 auto objects = _sandbox->getObjectsBySelector(".colors");
415 for (auto el : objects) {
416 if (SPCSSAttr* css = sp_repr_css_attr(el->getRepr(), "style")) {
417 sp_repr_css_set_property(css, "fill", bgcolor.c_str());
418 sp_repr_css_set_property(css, "stroke", fgcolor.c_str());
419 el->changeCSS(css, "style");
421 }
422 }
423
424 auto cross = _sandbox->getObjectsBySelector(".cross");
425 double stroke = 0.5;
426 for (auto el : cross) {
427 if (SPCSSAttr* css = sp_repr_css_attr(el->getRepr(), "style")) {
428 sp_repr_css_set_property(css, "display", checkerboard ? "block" : "none");
430 el->changeCSS(css, "style");
432 }
433 }
434
435 // SPDocument::install_reference_document scoped(_sandbox, source);
436
437 _sandbox->getRoot()->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
438 _sandbox->ensureUpToDate();
439
440 auto item = cast<SPItem>(object);
441 // Find object's bbox in document
443
444 if (!dbox) {
445 g_warning("no dbox");
446 return g_bad_marker;
447 }
448
449 if (auto measure = cast<SPItem>(_sandbox->getObjectById("measure-marker"))) {
450 if (auto box = measure->documentVisualBounds()) {
451 // check size of the marker applied to a path with stroke of 1px
452 auto size = std::max(box->width(), box->height());
453 const double small = 5.0;
454 // if too small, then scale up; clip needs to be enabled for scale to work
455 if (size > 0 && size < small) {
456 auto factor = 1 + small - size;
457 scale *= factor;
458 no_clip = false;
459
460 // adjust cross stroke
461 stroke /= factor;
462 for (auto el : cross) {
463 if (SPCSSAttr* css = sp_repr_css_attr(el->getRepr(), "style")) {
465 el->changeCSS(css, "style");
467 }
468 }
469
470 _sandbox->getRoot()->requestDisplayUpdate(SP_OBJECT_MODIFIED_FLAG);
471 _sandbox->ensureUpToDate();
472 }
473 }
474 }
475
476 /* Update to renderable state */
477 // const double device_scale = get_scale_factor();
478 guint32 bgnd_color = checkerboard.has_value() ? *checkerboard : 0;
479 auto surface = render_surface(drawing, scale, *dbox, pixel_size, device_scale, checkerboard.has_value() ? &bgnd_color : nullptr, no_clip);
480 cairo_surface_set_device_scale(surface, device_scale, device_scale);
481 return Cairo::RefPtr<Cairo::Surface>(new Cairo::Surface(surface, true));
482}
483
484Cairo::RefPtr<Cairo::Surface> render_image(const Inkscape::Pixbuf* pixbuf, int width, int height, int device_scale) {
485 Cairo::RefPtr<Cairo::Surface> surface;
486
487 if (!pixbuf || width <= 0 || height <= 0 || pixbuf->width() <= 0 || pixbuf->height() <= 0) return surface;
488
489 auto src = Cairo::RefPtr<Cairo::Surface>(new Cairo::Surface(pixbuf->getSurfaceRaw(), false));
490 surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, width * device_scale, height * device_scale);
491 cairo_surface_set_device_scale(surface->cobj(), device_scale, device_scale);
492
493 auto ctx = Cairo::Context::create(surface);
494
495 double sw = pixbuf->width();
496 double sh = pixbuf->height();
497 double sx = sw / width;
498 double sy = sh / height;
499 auto scale = 1.0 / std::max(sx, sy);
500 double dx = width - scale * sw;
501 double dy = height - scale * sh;
502
503 ctx->translate(dx / 2, dy / 2);
504 ctx->scale(scale, scale);
505 ctx->set_source(src, 0, 0);
506 ctx->set_operator(Cairo::Context::Operator::OVER);
507 ctx->paint();
508
509 return surface;
510}
511
512Cairo::RefPtr<Cairo::Surface> add_background_to_image(Cairo::RefPtr<Cairo::Surface> image, uint32_t rgb, double margin, double radius, int device_scale, std::optional<uint32_t> border) {
513 auto w = image ? cairo_image_surface_get_width(image->cobj()) : 0;
514 auto h = image ? cairo_image_surface_get_height(image->cobj()) : 0;
515 auto width = w / device_scale + 2 * margin;
516 auto height = h / device_scale + 2 * margin;
517
518 auto surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, width * device_scale, height * device_scale);
519 cairo_surface_set_device_scale(surface->cobj(), device_scale, device_scale);
520 auto ctx = Cairo::Context::create(surface);
521
522 auto x = 0;
523 auto y = 0;
524 if (border.has_value()) {
525 x += 0.5 * device_scale;
526 y += 0.5 * device_scale;
527 width -= device_scale;
528 height -= device_scale;
529 }
530 ctx->arc(x + width - radius, y + radius, radius, -M_PI_2, 0);
531 ctx->arc(x + width - radius, y + height - radius, radius, 0, M_PI_2);
532 ctx->arc(x + radius, y + height - radius, radius, M_PI_2, M_PI);
533 ctx->arc(x + radius, y + radius, radius, M_PI, 3 * M_PI_2);
534 ctx->close_path();
535
536 ctx->set_source_rgb(SP_RGBA32_R_F(rgb), SP_RGBA32_G_F(rgb), SP_RGBA32_B_F(rgb));
537 if (border.has_value()) {
538 ctx->fill_preserve();
539
540 auto b = *border;
541 ctx->set_source_rgb(SP_RGBA32_R_F(b), SP_RGBA32_G_F(b), SP_RGBA32_B_F(b));
542 ctx->set_line_width(1.0);
543 ctx->stroke();
544 }
545 else {
546 ctx->fill();
547 }
548
549 if (image) {
550 ctx->set_source(image, margin, margin);
551 ctx->paint();
552 }
553
554 return surface;
555}
556
557Cairo::RefPtr<Cairo::Surface> draw_frame(Cairo::RefPtr<Cairo::Surface> image, double image_alpha, uint32_t frame_rgba, double thickness, std::optional<uint32_t> checkerboard_color, int device_scale) {
558 if (!image) return image;
559
560 auto w = cairo_image_surface_get_width(image->cobj());
561 auto h = cairo_image_surface_get_height(image->cobj());
562 auto width = w / device_scale + 2 * thickness;
563 auto height = h / device_scale + 2 * thickness;
564
565 auto surface = Cairo::ImageSurface::create(Cairo::Surface::Format::ARGB32, width * device_scale, height * device_scale);
566 cairo_surface_set_device_scale(surface->cobj(), device_scale, device_scale);
567 auto ctx = Cairo::Context::create(surface);
568
569 if (checkerboard_color) {
570 Cairo::RefPtr<Cairo::Pattern> pattern(new Cairo::Pattern(ink_cairo_pattern_create_checkerboard(*checkerboard_color)));
571 ctx->save();
572 ctx->set_operator(Cairo::Context::Operator::SOURCE);
573 ctx->set_source(pattern);
574 ctx->rectangle(thickness, thickness, width - 2*thickness, height - 2*thickness);
575 ctx->fill();
576 ctx->restore();
577 }
578
579 ctx->rectangle(thickness / 2, thickness / 2, width - thickness, height - thickness);
580
581 if (thickness > 0) {
582 ctx->set_source_rgba(SP_RGBA32_R_F(frame_rgba), SP_RGBA32_G_F(frame_rgba), SP_RGBA32_B_F(frame_rgba), SP_RGBA32_A_F(frame_rgba));
583 ctx->set_line_width(thickness);
584 ctx->stroke();
585 }
586
587 ctx->set_source(image, thickness, thickness);
588 ctx->paint_with_alpha(image_alpha);
589
590 return surface;
591}
592
593
594object_renderer:: object_renderer() {
595}
596
597Cairo::RefPtr<Cairo::Surface> object_renderer::render(SPObject& object, double width, double height, double device_scale, object_renderer::options opt) {
598
599 Cairo::RefPtr<Cairo::Surface> surface;
600 if (opt._draw_frame) {
601 width -= 2 * opt._stroke;
602 height -= 2 * opt._stroke;
603 }
604 if (width <= 0 || height <= 0) return surface;
605
606 if (is<SPSymbol>(&object)) {
607 if (!_symbol_document) {
608 _symbol_document = symbols_preview_doc();
609 }
610 surface = draw_symbol(object, width, height, device_scale, _symbol_document.get(), opt._symbol_style_from_use);
611 }
612 else if (is<SPMarker>(&object)) {
613 const auto group = "marker-mid";
614 if (!_sandbox) {
615 _sandbox = ink_markers_preview_doc(group);
616 }
617 std::optional<guint32> checkerboard; // rgb background color
618 bool no_clip = true;
619 double scale = 1.0;
620
621 unsigned const dkey = SPItem::display_key_new(1);
622 Inkscape::Drawing drawing; // New drawing for offscreen rendering.
623 drawing.setRoot(_sandbox->getRoot()->invoke_show(drawing, dkey, SP_ITEM_SHOW_DISPLAY));
624 auto invoke_hide_guard = scope_exit([&] { _sandbox->getRoot()->invoke_hide(dkey); });
625 drawing.setExact(); // Maximum quality for blurs.
626
627 surface = create_marker_image(group, _sandbox.get(), opt._foreground, Geom::IntPoint(width, height), object.getId(),
628 object.document, drawing, checkerboard, no_clip, scale, device_scale);
629 }
630 else if (is<SPGradient>(&object)) {
631 surface = draw_gradient(cast<SPGradient>(&object), width, height, device_scale, false);
632 }
633 else if (auto pattern = cast<SPPattern>(&object)) {
634 surface = PatternManager::get().get_image(pattern, width, height, device_scale);
635 }
636 else if (auto image = cast<SPImage>(&object)) {
637 surface = render_image(image->pixbuf.get(), width, height, device_scale);
638 }
639 else {
640 g_warning("object_renderer: don't know how to render this object type");
641 }
642
643 if (opt._add_background) {
644 surface = add_background_to_image(surface, opt._background, opt._margin, opt._radius, device_scale);
645 }
646
647 // extra decorators: frame, opacity change, checkerboard background
648 if (opt._draw_frame || opt._image_opacity != 1 || opt._checkerboard.has_value()) {
649 surface = draw_frame(surface, opt._image_opacity, opt._frame_rgba, opt._stroke, opt._checkerboard, device_scale);
650 }
651
652 return surface;
653}
654
655} // namespace
double scale
Definition aa.cpp:228
cairo_pattern_t * ink_cairo_pattern_create_checkerboard(guint32 rgba, bool use_alpha)
void ink_cairo_set_source_color(Cairo::RefPtr< Cairo::Context > &ctx, Colors::Color const &color, bool to_srgb)
Set the source color of the Cairo context.
Cairo integration helpers.
Cairo::RefPtr< Cairo::ImageSurface > surface
Definition canvas.cpp:137
int margin
Definition canvas.cpp:166
Two-dimensional point with integer coordinates.
Definition int-point.h:57
Axis-aligned rectangle that can be empty.
Definition rect.h:203
void setRoot(DrawingItem *root)
Definition drawing.cpp:67
Class to hold image data for raster images.
Definition cairo-utils.h:31
int width() const
cairo_surface_t * getSurfaceRaw()
Converts the pixbuf to Cairo pixel format and returns an image surface which can be used as a source.
int height() const
Interface for refcounted XML nodes.
Definition node.h:80
virtual void appendChild(Node *child)=0
Append a node as the last child of this node.
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
virtual Node * duplicate(Document *doc) const =0
Create a duplicate of this node.
virtual char const * attribute(char const *key) const =0
Get the string representation of a node's attribute.
Typed SVG document implementation.
Definition document.h:101
static std::unique_ptr< SPDocument > createNewDocFromMem(std::span< char const > buffer, bool keepalive, std::string const &filename="")
Definition document.cpp:709
SPRoot * getRoot()
Returns our SPRoot.
Definition document.h:200
SPObject * getObjectById(std::string const &id) const
Inkscape::XML::Node * getReprRoot()
Definition document.h:206
SPDefs * getDefs()
Return the main defs object for the document.
Definition document.cpp:247
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
Definition document.h:211
int ensureUpToDate(unsigned int object_modified_tag=0)
Repeatedly works on getting the document updated, since sometimes it takes more than one pass to get ...
SPObject * getObjectByRepr(Inkscape::XML::Node *repr) const
std::vector< SPObject * > getObjectsBySelector(Glib::ustring const &selector) const
Gradient.
Definition sp-gradient.h:86
SPGradient * getVector(bool force_private=false)
Returns private vector of given gradient (the gradient at the end of the href chain which has stops),...
cairo_pattern_t * create_preview_pattern(double width)
Geom::OptRect documentVisualBounds() const
Get item's visual bbox in document coordinate system.
Definition sp-item.cpp:1026
Inkscape::DrawingItem * invoke_show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags)
Definition sp-item.cpp:1277
static unsigned int display_key_new(unsigned numkeys)
Allocates unique integer keys.
Definition sp-item.cpp:1254
SPObject is an abstract base class of all of the document nodes at the SVG document level.
Definition sp-object.h:160
SPDocument * document
Definition sp-object.h:188
SPObject * firstChild()
Definition sp-object.h:315
char const * getId() const
Returns the objects current ID string.
void deleteObject(bool propagate, bool propagate_descendants)
Deletes an object, unparenting it from its parent.
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
void requestDisplayUpdate(unsigned int flags)
Queues an deferred update of this object's display.
constexpr double SP_RGBA32_G_F(uint32_t v)
Definition utils.h:47
constexpr double SP_RGBA32_R_F(uint32_t v)
Definition utils.h:43
constexpr double SP_RGBA32_A_F(uint32_t v)
Definition utils.h:55
constexpr double SP_RGBA32_B_F(uint32_t v)
Definition utils.h:51
const double w
Definition conic-4.cpp:19
RootCluster root
std::shared_ptr< Css const > css
Geom::IntPoint size
Colors::Color stroke
unsigned int guint32
_cairo_pattern cairo_pattern_t
struct _cairo_surface cairo_surface_t
SVG drawing for display.
SPGradient * sp_gradient_get_forked_vector_if_necessary(SPGradient *gradient, bool force_vector)
Obtain the vector from the gradient.
std::unique_ptr< Magick::Image > image
SPItem * item
size_t v
Piecewise< SBasis > cross(Piecewise< D2< SBasis > > const &a, Piecewise< D2< SBasis > > const &b)
static R & release(R &r)
Decrements the reference count of a anchored object.
Helper class to stream background task notifications as a series of messages.
Cairo::RefPtr< Cairo::Surface > add_background_to_image(Cairo::RefPtr< Cairo::Surface > image, uint32_t rgb, double margin, double radius, int device_scale, std::optional< uint32_t > border=std::optional< uint32_t >())
std::unique_ptr< SPDocument > ink_markers_preview_doc(const Glib::ustring &group_id)
Returns a new document containing default start, mid, and end markers.
void draw_gradient(const Cairo::RefPtr< Cairo::Context > &cr, SPGradient *gradient, int x, int width)
Renders a preview of a gradient into the passed context.
std::pair< char const *, char const * > getHrefAttribute(XML::Node const &node)
Get the 'href' or 'xlink:href' (fallback) attribute from an XML node.
Cairo::RefPtr< Cairo::Surface > create_marker_image(const Glib::ustring &group_id, SPDocument *_sandbox, Gdk::RGBA marker_color, Geom::IntPoint pixel_size, const char *mname, SPDocument *source, Inkscape::Drawing &drawing, std::optional< guint32 > checkerboard, bool no_clip, double scale, int device_scale)
Creates a copy of the marker named mname, determines its visible and renderable area in the bounding ...
Ocnode * child[8]
Definition quantize.cpp:33
RGB rgb
Definition quantize.cpp:36
void sp_repr_css_set_property_double(SPCSSAttr *css, gchar const *name, double value)
Set a style property to a new float value (e.g.
Definition repr-css.cpp:223
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
Definition repr-css.cpp:76
SPCSSAttr * sp_repr_css_attr(Node const *repr, gchar const *attr)
Creates a new SPCSSAttr with one attribute (i.e.
Definition repr-css.cpp:88
char const * sp_repr_css_property(SPCSSAttr *css, gchar const *name, gchar const *defval)
Returns a character string of the value of a given style property or a default value if the attribute...
Definition repr-css.cpp:147
void sp_repr_css_set_property(SPCSSAttr *css, gchar const *name, gchar const *value)
Set a style property to a new value (e.g.
Definition repr-css.cpp:190
Run code on scope exit.
SVG <image> implementation.
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
SVG <pattern> implementation.
SPRoot: SVG <svg> implementation.
SPObject * getMarkerObj(gchar const *n, SPDocument *doc)
Extract the actual name of the link e.g.
Widgets used in the stroke style dialog.
Interface for XML documents.
Definition document.h:43
Object used to temporarily set and then automatically clear reference document.
Definition document.h:319
static const unsigned SP_STYLE_FLAG_ALWAYS(1<< 2)
SPCSSAttr * sp_css_attr_from_object(SPObject *object, guint const flags)
Definition style.cpp:1427
cairo_surface_t * render_surface(Inkscape::Drawing &drawing, double scale_factor, Geom::Rect const &dbox, Geom::IntPoint pixsize, double device_scale, const guint32 *checkerboard_color, bool no_clip)
Preview cache.
double border
double height
double width
Glib::ustring gdk_to_css_color(const Gdk::RGBA &color)
These GUI related color conversions allow us to convert from SVG xml attributes to Gdk colors,...
Definition util.cpp:325
Interface for XML nodes.