Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
drawing-image.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
/*
6 * Authors:
7 * Krzysztof KosiƄski <tweenk.pl@gmail.com>
8 *
9 * Copyright (C) 2011 Authors
10 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
11 */
12
13#include <2geom/bezier-curve.h>
14
15#include "drawing.h"
16#include "drawing-context.h"
17#include "drawing-image.h"
18#include "cairo-utils.h"
19#include "cairo-templates.h"
20
21namespace Inkscape {
22
24 : DrawingItem(drawing)
25 , style_image_rendering(SP_CSS_IMAGE_RENDERING_AUTO)
26{
27}
28
29void DrawingImage::setPixbuf(std::shared_ptr<Inkscape::Pixbuf const> pixbuf)
30{
31 defer([this, pixbuf = std::move(pixbuf)] () mutable {
32 _pixbuf = std::move(pixbuf);
34 });
35}
36
37void DrawingImage::setScale(double sx, double sy)
38{
39 defer([=, this] {
40 _scale = Geom::Scale(sx, sy);
42 });
43}
44
46{
47 defer([=, this] {
50 });
51}
52
54{
55 defer([=, this] {
56 _clipbox = box;
58 });
59}
60
62{
63 if (!_pixbuf) return _clipbox;
64
65 double pw = _pixbuf->width();
66 double ph = _pixbuf->height();
67 double vw = pw * _scale[Geom::X];
68 double vh = ph * _scale[Geom::Y];
69 Geom::Point wh(vw, vh);
70 Geom::Rect view(_origin, _origin+wh);
71 Geom::OptRect res = _clipbox & view;
72 Geom::Rect ret = res ? *res : _clipbox;
73
74 return ret;
75}
76
77void DrawingImage::setStyle(SPStyle const *style, SPStyle const *context_style)
78{
79 DrawingItem::setStyle(style, context_style);
80
81 auto image_rendering = SP_CSS_IMAGE_RENDERING_AUTO;
82 if (_style) {
83 image_rendering = _style->image_rendering.computed;
84 }
85
86 defer([=, this] {
87 style_image_rendering = image_rendering;
88 });
89}
90
91unsigned DrawingImage::_updateItem(Geom::IntRect const &, UpdateContext const &, unsigned, unsigned)
92{
93 // Calculate bbox
94 if (_pixbuf) {
95 Geom::Rect r = bounds() * _ctm;
96 _bbox = r.roundOutwards();
97 } else {
99 }
100
101 return STATE_ALL;
102}
103
104unsigned DrawingImage::_renderItem(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &/*area*/, unsigned flags, DrawingItem const */*stop_at*/) const
105{
106 bool const outline = (flags & RENDER_OUTLINE) && !_drawing.imageOutlineMode();
107
108 if (!outline) {
109 if (!_pixbuf) return RENDER_OK;
110 if (_scale.vector().x() * _scale.vector().y() == 0.0) return RENDER_OK;
111
113 dc.transform(_ctm);
114 dc.newPath();
116 dc.clip();
117
118 dc.translate(_origin);
119 dc.scale(_scale);
120 // const_cast required since Cairo needs to modify the internal refcount variable, but we do not want to give up the
121 // benefits of const for the rest of our code. The underlying object is guaranteed to be non-const, so this is well-defined.
122 // It is also thread-safe to modify the refcount in this way, since Cairo uses atomics internally.
123 dc.setSource(const_cast<cairo_surface_t*>(_pixbuf->getSurfaceRaw()), 0, 0);
124 dc.patternSetExtend(CAIRO_EXTEND_PAD);
125
126 // See: http://www.w3.org/TR/SVG/painting.html#ImageRenderingProperty
127 // https://drafts.csswg.org/css-images-3/#the-image-rendering
128 // style.h/style.cpp, cairo-render-context.cpp
129 //
130 // CSS 3 defines:
131 // 'optimizeSpeed' as alias for "pixelated"
132 // 'optimizeQuality' as alias for "smooth"
133 switch (style_image_rendering) {
136 // we don't have an implementation for crisp-edges, but it should *not* smooth or blur
138 dc.patternSetFilter( CAIRO_FILTER_NEAREST );
139 break;
142 default:
143 // In recent Cairo, BEST used Lanczos3, which is prohibitively slow
144 dc.patternSetFilter( CAIRO_FILTER_GOOD );
145 break;
146 }
147
148 // Handle an exceptional case where the greyscale color mode needs to be applied per-image.
149 bool const greyscale_exception = (flags & RENDER_OUTLINE) && _drawing.colorMode() == ColorMode::GRAYSCALE;
150 if (greyscale_exception) {
151 dc.pushGroup();
152 }
153
154 dc.paint();
155
156 if (greyscale_exception) {
158 dc.popGroupToSource();
159 dc.paint();
160 }
161
162 } else { // outline; draw a rect instead
163
164 auto rgba = _drawing.imageOutlineColor();
165
167 dc.transform(_ctm);
168 dc.newPath();
169
170 Geom::Rect r = bounds();
171 Geom::Point c00 = r.corner(0);
172 Geom::Point c01 = r.corner(3);
173 Geom::Point c11 = r.corner(2);
174 Geom::Point c10 = r.corner(1);
175
176 dc.moveTo(c00);
177 // the box
178 dc.lineTo(c10);
179 dc.lineTo(c11);
180 dc.lineTo(c01);
181 dc.lineTo(c00);
182 // the diagonals
183 dc.lineTo(c11);
184 dc.moveTo(c10);
185 dc.lineTo(c01);
186 }
187
188 dc.setLineWidth(0.5);
189 dc.setSource(rgba);
190 dc.stroke();
191 }
192 return RENDER_OK;
193}
194
196static double distance_to_segment(Geom::Point const &p, Geom::Point const &a1, Geom::Point const &a2)
197{
198 Geom::LineSegment l(a1, a2);
199 Geom::Point np = l.pointAt(l.nearestTime(p));
200 return Geom::distance(np, p);
201}
202
203DrawingItem *DrawingImage::_pickItem(Geom::Point const &p, double delta, unsigned flags)
204{
205 if (!_pixbuf) return nullptr;
206
207 bool outline = (flags & PICK_OUTLINE) && !_drawing.imageOutlineMode();
208
209 if (outline) {
210 Geom::Rect r = bounds();
212
213 // find whether any side or diagonal is within delta
214 // to do so, iterate over all pairs of corners
215 for (unsigned i = 0; i < 3; ++i) { // for i=3, there is nothing to do
216 for (unsigned j = i+1; j < 4; ++j) {
217 if (distance_to_segment(pick, r.corner(i), r.corner(j)) < delta) {
218 return this;
219 }
220 }
221 }
222 return nullptr;
223
224 } else {
225 auto pixels = _pixbuf->pixels();
226 int width = _pixbuf->width();
227 int height = _pixbuf->height();
228 size_t rowstride = _pixbuf->rowstride();
229
230 Geom::Point tp = p * _ctm.inverse();
231 Geom::Rect r = bounds();
232
233 if (!r.contains(tp))
234 return nullptr;
235
236 double vw = width * _scale[Geom::X];
237 double vh = height * _scale[Geom::Y];
238 int ix = floor((tp[Geom::X] - _origin[Geom::X]) / vw * width);
239 int iy = floor((tp[Geom::Y] - _origin[Geom::Y]) / vh * height);
240
241 if ((ix < 0) || (iy < 0) || (ix >= width) || (iy >= height))
242 return nullptr;
243
244 auto pix_ptr = pixels + iy * rowstride + ix * 4;
245 // pick if the image is less than 99% transparent
246 guint32 alpha = 0;
247 if (_pixbuf->pixelFormat() == Inkscape::Pixbuf::PF_CAIRO) {
248 guint32 px = *reinterpret_cast<guint32 const *>(pix_ptr);
249 alpha = (px & 0xff000000) >> 24;
250 } else if (_pixbuf->pixelFormat() == Inkscape::Pixbuf::PF_GDK) {
251 alpha = pix_ptr[3];
252 } else {
253 throw std::runtime_error("Unrecognized pixel format");
254 }
255 float alpha_f = (alpha / 255.0f) * _opacity;
256 return alpha_f > 0.01 ? this : nullptr;
257 }
258}
259
260} // namespace Inkscape
261
262/*
263 Local Variables:
264 mode:c++
265 c-file-style:"stroustrup"
266 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
267 indent-tabs-mode:nil
268 fill-column:99
269 End:
270*/
271// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Point origin
Definition aa.cpp:227
Bezier curve.
Cairo software blending templates.
void ink_cairo_surface_filter(cairo_surface_t *in, cairo_surface_t *out, Filter &&filter)
Cairo integration helpers.
Affine inverse() const
Compute the inverse matrix.
Definition affine.cpp:388
Coord nearestTime(Point const &p, Coord from=0, Coord to=1) const override
Compute a time value at which the curve comes closest to a specified point.
Point pointAt(Coord t) const override
Evaluate the curve at a specified time value.
Axis aligned, non-empty, generic rectangle.
bool contains(GenericRect< C > const &r) const
Check whether the rectangle includes all points in the given rectangle.
C width() const
Get the horizontal extent of the rectangle.
CPoint corner(unsigned i) const
Return the n-th corner of the rectangle.
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Two-dimensional point that doubles as a vector.
Definition point.h:66
constexpr Coord y() const noexcept
Definition point.h:106
constexpr Coord x() const noexcept
Definition point.h:104
Axis aligned, non-empty rectangle.
Definition rect.h:92
IntRect roundOutwards() const
Return the smallest integer rectangle which contains this one.
Definition rect.h:141
Scaling from the origin.
Definition transforms.h:150
Point vector() const
Definition transforms.h:171
RAII idiom for saving the state of DrawingContext.
Minimal wrapper over Cairo.
void setSource(cairo_pattern_t *source)
cairo_surface_t * rawTarget()
void scale(Geom::Scale const &s)
void transform(Geom::Affine const &trans)
void paint(double alpha=1.0)
void rectangle(Geom::Rect const &r)
void patternSetExtend(cairo_extend_t extend)
void lineTo(Geom::Point const &p)
void moveTo(Geom::Point const &p)
void translate(Geom::Point const &t)
void patternSetFilter(cairo_filter_t filter)
void setClipbox(Geom::Rect const &box)
void setScale(double sx, double sy)
Geom::Rect _clipbox
for preserveAspectRatio
DrawingImage(Drawing &drawing)
SPImageRendering style_image_rendering
unsigned _updateItem(Geom::IntRect const &area, UpdateContext const &ctx, unsigned flags, unsigned reset) override
unsigned _renderItem(DrawingContext &dc, RenderContext &rc, Geom::IntRect const &area, unsigned flags, DrawingItem const *stop_at) const override
void setPixbuf(std::shared_ptr< Inkscape::Pixbuf const > pb)
void setOrigin(Geom::Point const &o)
std::shared_ptr< Inkscape::Pixbuf const > _pixbuf
Geom::Rect bounds() const
void setStyle(SPStyle const *style, SPStyle const *context_style=nullptr) override
Process information related to the new style.
DrawingItem * _pickItem(Geom::Point const &p, double delta, unsigned flags) override
SVG drawing item for display.
DrawingItem * pick(Geom::Point const &p, double delta, unsigned flags=0)
Get the item under the specified point.
Geom::OptIntRect _bbox
Bounding box in display (pixel) coords including stroke.
virtual void setStyle(SPStyle const *style, SPStyle const *context_style=nullptr)
Process information related to the new style.
SPStyle const * _style
void _markForUpdate(unsigned state, bool propagate)
Marks the item as needing a recomputation of internal data.
Geom::Affine _ctm
Total transform from item coords to display coords.
uint32_t imageOutlineColor() const
Definition drawing.h:73
ColorMode colorMode() const
Definition drawing.h:68
auto & grayscaleMatrix() const
Definition drawing.h:70
bool imageOutlineMode() const
Definition drawing.h:74
An SVG style object.
Definition style.h:45
T< SPAttr::IMAGE_RENDERING, SPIEnum< SPImageRendering > > image_rendering
Definition style.h:292
RectangularCluster rc
Cairo drawing context with Inkscape extensions.
unsigned int guint32
Bitmap image belonging to an SVG drawing.
struct _cairo_surface cairo_surface_t
SVG drawing for display.
@ Y
Definition coord.h:48
@ X
Definition coord.h:48
auto floor(Geom::Rect const &rect)
Definition geom.h:130
Angle distance(Angle const &a, Angle const &b)
Definition angle.h:163
GenericOptRect< IntCoord > OptIntRect
Definition forward.h:58
Helper class to stream background task notifications as a series of messages.
Geom::PathVector outline(Geom::Path const &input, double width, double miter, LineJoinType join, LineCapType butt, double tolerance)
Strokes the path given by input.
static double distance_to_segment(Geom::Point const &p, Geom::Point const &a1, Geom::Point const &a2)
Calculates the closest distance from p to the segment a1-a2.
@ SP_CSS_IMAGE_RENDERING_PIXELATED
@ SP_CSS_IMAGE_RENDERING_OPTIMIZEQUALITY
@ SP_CSS_IMAGE_RENDERING_AUTO
@ SP_CSS_IMAGE_RENDERING_OPTIMIZESPEED
@ SP_CSS_IMAGE_RENDERING_CRISPEDGES
int delta
double height
double width