Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
image-properties.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
10#include "image-properties.h"
11
12#include <array>
13#include <sstream>
14#include <string>
15#include <cairomm/context.h>
16#include <giomm/file.h>
17#include <glib/gi18n.h>
18#include <glibmm/convert.h>
19#include <glibmm/markup.h>
20#include <glibmm/ustring.h>
21#include <gtkmm/builder.h>
22#include <gtkmm/button.h>
23#include <gtkmm/checkbutton.h>
24#include <gtkmm/comboboxtext.h>
25#include <gtkmm/drawingarea.h>
26#include <gtkmm/entry.h>
27#include <gtkmm/enums.h>
28#include <gtkmm/grid.h>
29#include <gtkmm/label.h>
30
31#include "display/cairo-utils.h"
32#include "document-undo.h"
33#include "enums.h"
34#include "object/sp-image.h"
35#include "ui/builder-utils.h"
38#include "ui/icon-names.h"
39#include "ui/pack.h"
40#include "ui/util.h"
41#include "util/format_size.h"
44
45namespace Inkscape::UI::Widget {
46
47namespace {
48
49Cairo::RefPtr<Cairo::Surface> draw_preview(SPImage* image, double width, double height, int device_scale, uint32_t frame_color, uint32_t background) {
50 if (!image || !image->pixbuf) return Cairo::RefPtr<Cairo::Surface>();
51
52 object_renderer r;
53 object_renderer::options opt;
54 opt.frame(frame_color);
55 auto s = image->style;
56 // here for preview purposes using image's own opacity only
57 double alpha = s && s->opacity.set && !s->opacity.inherit ? SP_SCALE24_TO_FLOAT(s->opacity.value) : 1.0;
58 opt.image_opacity(alpha);
59 opt.checkerboard(background);
60 return r.render(*image, width, height, device_scale, opt);
61}
62
63void link_image(Gtk::Window* window, SPImage* image) {
64 if (!window || !image) return;
65
66 static std::string current_folder;
67 std::vector<Glib::ustring> mime_types = {
68 "image/png", "image/jpeg", "image/gif", "image/bmp", "image/tiff"
69 };
70 auto file = choose_file_open(_("Change Image"), window, mime_types, current_folder);
71 if (!file) return;
72
73 auto uri = file->get_uri();
74 setHrefAttribute(*image->getRepr(), uri);
75
76 // SPImage modifies size when href changes; trigger it now before undo concludes
77 // TODO: this needs to be fixed in SPImage
78 image->document->_updateDocument(0);
79 DocumentUndo::done(image->document, _("Change image"), INKSCAPE_ICON("shape-image"));
80}
81
82void set_rendering_mode(SPImage* image, int index) {
83 static const std::array<const char*, 5> render = {
84 "auto", "optimizeSpeed", "optimizeQuality", "crisp-edges", "pixelated"
85 }; // SPImageRendering values
86
87 if (!image || index < 0 || index >= render.size()) return;
88
90 sp_repr_css_set_property(css, "image-rendering", render[index]);
91 if (auto image_node = image->getRepr()) {
92 sp_repr_css_change(image_node, css, "style");
93 DocumentUndo::done(image->document, _("Set image rendering option"), INKSCAPE_ICON("shape-image"));
94 }
96}
97
98void set_aspect_ratio(SPImage* image, bool preserve_aspect_ratio) {
99 if (!image) return;
100 image->setAttribute("preserveAspectRatio", preserve_aspect_ratio ? "xMidYMid" : "none");
101 DocumentUndo::done(image->document, _("Preserve image aspect ratio"), INKSCAPE_ICON("shape-image"));
102}
103
104} // unnamed namespace
105
107 Glib::ObjectBase{"ImageProperties"}, WidgetVfuncsClassInit{}, // They are both needed w ClassInit
108 Gtk::Box(Gtk::Orientation::HORIZONTAL),
109 _builder(create_builder("image-properties.glade")),
110 _preview(get_widget<Gtk::DrawingArea>(_builder, "preview")),
111 _aspect(get_widget<Gtk::CheckButton>(_builder, "preserve")),
112 _stretch(get_widget<Gtk::CheckButton>(_builder, "stretch")),
113 _rendering(get_widget<Gtk::ComboBoxText>(_builder, "rendering")),
114 _embed(get_widget<Gtk::Button>(_builder, "embed"))
115{
116 auto& main = get_widget<Gtk::Grid>(_builder, "main");
117 UI::pack_start(*this, main, true, true);
118
119 // arbitrarily selected max preview size for image content:
120 _preview_max_width = 120;
122
123 _preview.set_draw_func([this](Cairo::RefPtr<Cairo::Context> const &ctx, int /*width*/, int /*height*/) {
124 if (_preview_image) {
125 ctx->set_source(_preview_image, 0, 0);
126 ctx->paint();
127 }
128 });
129
130 auto& change = get_widget<Gtk::Button>(_builder, "change-img");
131 change.signal_clicked().connect([this](){
132 if (_update.pending()) return;
133 auto window = dynamic_cast<Gtk::Window*>(get_root());
134 link_image(window, _image);
135 });
136
137 auto& extract = get_widget<Gtk::Button>(_builder, "export");
138 extract.signal_clicked().connect([this](){
139 if (_update.pending()) return;
140 auto window = dynamic_cast<Gtk::Window*>(get_root());
141 extract_image(window, _image);
142 });
143
144 _embed.signal_clicked().connect([this](){
145 if (_update.pending() || !_image) return;
146 // embed image in the current document
148 sp_embed_image(_image->getRepr(), &copy);
149 DocumentUndo::done(_image->document, _("Embed image"), INKSCAPE_ICON("selection-make-bitmap-copy"));
150 });
151
152 _rendering.signal_changed().connect([this](){
153 if (_update.pending()) return;
154 auto index = _rendering.get_active_row_number();
155 set_rendering_mode(_image, index);
156 });
157
158 _aspect.signal_toggled().connect([this](){
159 if (_update.pending()) return;
160 set_aspect_ratio(_image, _aspect.get_active());
161 });
162 _stretch.signal_toggled().connect([this](){
163 if (_update.pending()) return;
164 set_aspect_ratio(_image, !_stretch.get_active());
165 });
166}
167
169
171 if (!image && !_image) return; // nothing to do
172
173 _image = image;
174
175 auto scoped(_update.block());
176
177 auto small = [](const char* str) { return "<small>" + Glib::Markup::escape_text(str ? str : "") + "</small>"; };
178 auto& name = get_widget<Gtk::Label>(_builder, "name");
179 auto& info = get_widget<Gtk::Label>(_builder, "info");
180 auto& url = get_widget<Gtk::Entry>(_builder, "href");
181
182 if (!image) {
183 name.set_markup(small("-"));
184 info.set_markup(small("-"));
185 }
186 else {
187 Glib::ustring id(image->getId() ? image->getId() : "");
188 name.set_markup(small(id.empty() ? "-" : ("#" + id).c_str()));
189
190 bool embedded = false;
191 bool linked = false;
192 auto href = Inkscape::getHrefAttribute(*image->getRepr()).second;
193 if (href && std::strncmp(href, "data:", 5) == 0) {
194 embedded = true;
195 }
196 else if (href && *href) {
197 linked = true;
198 }
199
200 if (image->pixbuf) {
201 std::ostringstream ost;
202 if (!image->missing) {
203 auto times = "\u00d7"; // multiplication sign
204 // dimensions
205 ost << image->pixbuf->width() << times << image->pixbuf->height() << " px\n";
206
207 if (embedded) {
208 ost << _("Embedded");
209 ost << " (" << Util::format_file_size(std::strlen(href)) << ")\n";
210 }
211 if (linked) {
212 ost << _("Linked");
213 ost << '\n';
214 }
215 // color space
216 if (image->color_profile && *image->color_profile) {
217 ost << _("Color profile:") << ' ' << image->color_profile << '\n';
218 }
219 }
220 else {
221 ost << _("Missing image") << '\n';
222 }
223 info.set_markup(small(ost.str().c_str()));
224 }
225 else {
226 info.set_markup(small("-"));
227 }
228
229 url.set_text(linked ? href : "");
230 url.set_sensitive(linked);
231 _embed.set_sensitive(linked && image->pixbuf);
232
233 // aspect ratio
234 bool aspect_none = false;
235 if (image->aspect_set) {
236 aspect_none = image->aspect_align == SP_ASPECT_NONE;
237 }
238 if (aspect_none) {
239 _stretch.set_active();
240 }
241 else {
242 _aspect.set_active();
243 }
244
245 // rendering
246 _rendering.set_active(image->style ? image->style->image_rendering.value : -1);
247 }
248
251 if (image && image->pixbuf) {
252 double sw = image->pixbuf->width();
253 double sh = image->pixbuf->height();
254 double sx = sw / width;
255 double sy = sh / height;
256 auto scale = 1.0 / std::max(sx, sy);
257 width = std::max(1, int(sw * scale + 0.5));
258 height = std::max(1, int(sh * scale + 0.5));
259 }
260 // expand size to account for a frame around the image
261 int frame = 2;
262 width += frame;
263 height += frame;
264 _preview.set_size_request(width, height);
265 _preview.queue_draw();
266
267 // prepare preview
268 auto device_scale = get_scale_factor();
269 auto const fg = get_color();
270 auto foreground = conv_gdk_color_to_rgba(fg, 0.30);
272 _preview_image = draw_preview(_image, width, height, device_scale, foreground, _background_color);
273}
274
276 if (auto wnd = dynamic_cast<Gtk::Window*>(get_root())) {
277 auto const color = get_color_with_class(*wnd, "theme_bg_color");
279 }
280 else {
281 _background_color = 0x808080ff;
282 }
283}
284
285void ImageProperties::css_changed(GtkCssStyleChange * /*change*/)
286{
288 update(_image);
289}
290
291} // namespace Inkscape::UI::Widget
292
293/*
294 Local Variables:
295 mode:c++
296 c-file-style:"stroustrup"
297 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
298 indent-tabs-mode:nil
299 fill-column:99
300 End:
301*/
302// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
double scale
Definition aa.cpp:228
int main()
Gtk builder utilities.
Cairo integration helpers.
static void done(SPDocument *document, Glib::ustring const &event_description, Glib::ustring const &undo_icon, unsigned int object_modified_tag=0)
Class to hold image data for raster images.
Definition cairo-utils.h:31
Cairo::RefPtr< Cairo::Surface > _preview_image
Glib::RefPtr< Gtk::Builder > _builder
void css_changed(GtkCssStyleChange *change) final
Called after gtk_widget_css_changed(): when a CSS widget node is validated & style changed.
A class you can inherit to access GTK4ʼs Widget.css_changed & .focus vfuncs, missing in gtkmm4.
scoped_block block()
std::shared_ptr< Inkscape::Pixbuf const > pixbuf
Definition sp-image.h:50
SPDocument * document
Definition sp-object.h:188
Inkscape::XML::Node * getRepr()
Returns the XML representation of tree.
std::shared_ptr< Css const > css
TODO: insert short description here.
@ SP_ASPECT_NONE
Definition enums.h:42
Macro for icon names used in Inkscape.
std::unique_ptr< Magick::Image > image
Definition desktop.h:50
Custom widgets.
Definition desktop.h:126
static constexpr int height
W & get_widget(const Glib::RefPtr< Gtk::Builder > &builder, const char *id)
void pack_start(Gtk::Box &box, Gtk::Widget &child, bool const expand, bool const fill, unsigned const padding)
Adds child to box, packed with reference to the start of box.
Definition pack.cpp:141
Glib::RefPtr< Gtk::Builder > create_builder(const char *filename)
Glib::ustring format_file_size(std::size_t value)
void setHrefAttribute(XML::Node &node, Util::const_char_ptr value)
If the 'href' attribute already exists for the given node, then set a new value for it.
std::pair< char const *, char const * > getHrefAttribute(XML::Node const &node)
Get the 'href' or 'xlink:href' (fallback) attribute from an XML node.
bool extract_image(Gtk::Window *parent, SPImage *image)
Glib::RefPtr< Gio::File > choose_file_open(Glib::ustring const &title, Gtk::Window *parent, Glib::RefPtr< Gio::ListStore< Gtk::FileFilter > > const &filters_model, std::string &current_folder, Glib::ustring const &accept)
Synchronously run a Gtk::FileDialog to open a single file for reading data.
Helpers for using Gtk::Boxes, encapsulating large changes between GTK3 & GTK4.
SPCSSAttr * sp_repr_css_attr_new()
Creates an empty SPCSSAttr (a class for manipulating CSS style properties).
Definition repr-css.cpp:67
void sp_repr_css_change(Node *repr, SPCSSAttr *css, gchar const *attr)
Creates a new SPCSAttr with the values filled from a repr, merges in properties from the given SPCSAt...
Definition repr-css.cpp:358
void sp_repr_css_attr_unref(SPCSSAttr *css)
Unreferences an SPCSSAttr (will be garbage collected if no references remain).
Definition repr-css.cpp:76
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:191
void sp_embed_image(Inkscape::XML::Node *image_node, Inkscape::Pixbuf *pb)
Definition sp-image.cpp:716
SVG <image> implementation.
int index
double width
Glib::ustring name
Definition toolbars.cpp:55
uint32_t conv_gdk_color_to_rgba(const Gdk::RGBA &color, double replace_alpha)
Definition util.cpp:423
Gdk::RGBA get_color_with_class(Gtk::Widget &widget, Glib::ustring const &css_class)
Definition util.cpp:303