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>
41using namespace std::literals;
47bool visit_until(
SPObject&
object, V&& visitor) {
48 if (visitor(
object))
return true;
51 if (is<SPUse>(&
object))
return false;
53 for (
auto&&
child : object.children) {
54 if (visit_until(
child, visitor))
return true;
60const char* style_from_use_element(
const char*
id,
SPDocument* document) {
61 if (!
id || !*
id || !document)
return nullptr;
64 if (!
root)
return nullptr;
66 const char* style =
nullptr;
67 Glib::ustring ident =
"#";
71 if (
auto use = cast<SPUse>(&obj)) {
75 style = use->getAttribute(
"style");
86static std::unique_ptr<SPDocument> symbols_preview_doc()
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"/>
99Cairo::RefPtr<Cairo::Surface> draw_symbol(
SPObject& symbol,
double box_w,
double box_h,
double device_scale,
SPDocument* preview_document,
bool style_from_use) {
105 auto style = repr->
attribute(
"inkscape:symbol-style");
110 if (style_from_use) {
116 auto id = symbol.
getId();
117 style = style_from_use_element(
id, symbol.
document);
144 auto invoke_hide_guard =
scope_exit([&] { preview_document->
getRoot()->invoke_hide(dkey); });
151 auto item = cast<SPItem>(object_temp);
152 g_assert(
item !=
nullptr);
162 double width = dbox->width();
163 double height = dbox->height();
179 cairo_surface_set_device_scale(s, device_scale, device_scale);
182 return Cairo::RefPtr<Cairo::Surface>(
new Cairo::Surface(s,
true));
188 cairo_set_source(cr->cobj(), check);
190 cairo_pattern_destroy(check);
198 cairo_matrix_init_translate(&m, -x, 0);
199 cairo_pattern_set_matrix(p, &m);
200 cairo_set_source(cr->cobj(), p);
202 cairo_pattern_destroy(p);
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);
212 auto x = 0.5 * device_scale;
213 auto y = 0.5 * device_scale;
214 width -= device_scale;
217 ctx->rectangle(x, y,
width, h);
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);
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);
234 ctx->fill_preserve();
235 ctx->set_source_rgb(0.5, 0.5, 0.5);
245 constexpr auto buffer = R
"A(
246 <svg xmlns="http://www.w3.org/2000/svg"
247 xmlns:xlink="http://www.w3.org/1999/xlink"
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" />
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" /> -->
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" />
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" />
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" />
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" />
306 for (
auto&& group : document->getObjectsByClass(
"group")) {
307 assert(group->getId());
308 if (group->getId() != group_id) {
309 group->deleteObject();
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();
324 const Glib::ustring& group_id,
326 Gdk::RGBA marker_color,
331 std::optional<guint32> checkerboard,
336 Cairo::RefPtr<Cairo::Surface> g_bad_marker;
340 if (marker ==
nullptr) {
341 g_warning(
"bad mname: %s", mname);
369 if (!strncmp (mstroke,
"url(", 4)) {
380 if (is<SPGradient>(linkObj)) {
403 if (
object ==
nullptr || !is<SPItem>(
object)) {
404 g_warning(
"no obj: %s", group_id.c_str());
408 Gdk::RGBA fg = marker_color;
410 fg.set_red(1 - fg.get_red());
411 fg.set_green(1 - fg.get_green());
412 fg.set_blue(1 - fg.get_blue());
415 for (
auto el : objects) {
419 el->changeCSS(
css,
"style");
426 for (
auto el :
cross) {
430 el->changeCSS(
css,
"style");
440 auto item = cast<SPItem>(
object);
445 g_warning(
"no dbox");
449 if (
auto measure = cast<SPItem>(_sandbox->
getObjectById(
"measure-marker"))) {
450 if (
auto box = measure->documentVisualBounds()) {
452 auto size = std::max(box->width(), box->height());
453 const double small = 5.0;
456 auto factor = 1 + small -
size;
462 for (
auto el :
cross) {
465 el->changeCSS(
css,
"style");
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));
485 Cairo::RefPtr<Cairo::Surface>
surface;
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);
493 auto ctx = Cairo::Context::create(
surface);
495 double sw = pixbuf->
width();
496 double sh = pixbuf->
height();
497 double sx = sw /
width;
499 auto scale = 1.0 / std::max(sx, sy);
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);
513 auto w =
image ? cairo_image_surface_get_width(
image->cobj()) : 0;
514 auto h =
image ? cairo_image_surface_get_height(
image->cobj()) : 0;
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);
525 x += 0.5 * device_scale;
526 y += 0.5 * device_scale;
527 width -= device_scale;
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);
538 ctx->fill_preserve();
542 ctx->set_line_width(1.0);
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) {
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;
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);
569 if (checkerboard_color) {
572 ctx->set_operator(Cairo::Context::Operator::SOURCE);
573 ctx->set_source(pattern);
574 ctx->rectangle(thickness, thickness,
width - 2*thickness,
height - 2*thickness);
579 ctx->rectangle(thickness / 2, thickness / 2,
width - thickness,
height - thickness);
583 ctx->set_line_width(thickness);
587 ctx->set_source(
image, thickness, thickness);
588 ctx->paint_with_alpha(image_alpha);
594object_renderer:: object_renderer() {
597Cairo::RefPtr<Cairo::Surface> object_renderer::render(
SPObject&
object,
double width,
double height,
double device_scale, object_renderer::options opt) {
599 Cairo::RefPtr<Cairo::Surface>
surface;
600 if (opt._draw_frame) {
601 width -= 2 * opt._stroke;
602 height -= 2 * opt._stroke;
606 if (is<SPSymbol>(&
object)) {
607 if (!_symbol_document) {
608 _symbol_document = symbols_preview_doc();
610 surface = draw_symbol(
object,
width,
height, device_scale, _symbol_document.get(), opt._symbol_style_from_use);
612 else if (is<SPMarker>(&
object)) {
613 const auto group =
"marker-mid";
617 std::optional<guint32> checkerboard;
624 auto invoke_hide_guard =
scope_exit([&] { _sandbox->
getRoot()->invoke_hide(dkey); });
628 object.document, drawing, checkerboard, no_clip, scale, device_scale);
630 else if (is<SPGradient>(&
object)) {
633 else if (
auto pattern = cast<SPPattern>(&
object)) {
636 else if (
auto image = cast<SPImage>(&
object)) {
640 g_warning(
"object_renderer: don't know how to render this object type");
643 if (opt._add_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);
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
Two-dimensional point with integer coordinates.
Axis-aligned rectangle that can be empty.
void setRoot(DrawingItem *root)
Class to hold image data for raster images.
cairo_surface_t * getSurfaceRaw()
Converts the pixbuf to Cairo pixel format and returns an image surface which can be used as a source.
Interface for refcounted XML nodes.
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.
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.
static std::unique_ptr< SPDocument > createNewDocFromMem(std::span< char const > buffer, bool keepalive, std::string const &filename="")
SPRoot * getRoot()
Returns our SPRoot.
SPObject * getObjectById(std::string const &id) const
Inkscape::XML::Node * getReprRoot()
SPDefs * getDefs()
Return the main defs object for the document.
Inkscape::XML::Document * getReprDoc()
Our Inkscape::XML::Document.
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
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.
Inkscape::DrawingItem * invoke_show(Inkscape::Drawing &drawing, unsigned int key, unsigned int flags)
static unsigned int display_key_new(unsigned numkeys)
Allocates unique integer keys.
SPObject is an abstract base class of all of the document nodes at the SVG document level.
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)
constexpr double SP_RGBA32_R_F(uint32_t v)
constexpr double SP_RGBA32_A_F(uint32_t v)
constexpr double SP_RGBA32_B_F(uint32_t v)
std::shared_ptr< Css const > css
_cairo_pattern cairo_pattern_t
struct _cairo_surface cairo_surface_t
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
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 ...
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.
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
SPCSSAttr * sp_repr_css_attr(Node const *repr, gchar const *attr)
Creates a new SPCSSAttr with one attribute (i.e.
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...
void sp_repr_css_set_property(SPCSSAttr *css, gchar const *name, gchar const *value)
Set a style property to a new value (e.g.
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.
Object used to temporarily set and then automatically clear reference document.
static const unsigned SP_STYLE_FLAG_ALWAYS(1<< 2)
SPCSSAttr * sp_css_attr_from_object(SPObject *object, guint const flags)
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)
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,...