Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
drawing-surface.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
15#include "display/cairo-utils.h"
16#include "ui/util.h"
17
18namespace Inkscape {
19
41DrawingSurface::DrawingSurface(Geom::IntRect const &area, int device_scale)
42 : _surface(nullptr)
43 , _origin(area.min())
44 , _scale(1, 1)
45 , _pixels(area.dimensions())
46 , _device_scale(device_scale)
47{
48 assert(_device_scale > 0);
49}
50
59DrawingSurface::DrawingSurface(Geom::Rect const &logbox, Geom::IntPoint const &pixdims, int device_scale)
60 : _surface(nullptr)
61 , _origin(logbox.min())
62 , _scale(pixdims / logbox.dimensions())
63 , _pixels(pixdims)
64 , _device_scale(device_scale)
65{
66 assert(_device_scale > 0);
67}
68
75 : _surface(surface)
76 , _origin(origin)
77 , _scale(1, 1)
78{
79 cairo_surface_reference(surface);
80
81 double x_scale = 0;
82 double y_scale = 0;
83 cairo_surface_get_device_scale( surface, &x_scale, &y_scale);
84 if (x_scale != y_scale) {
85 std::cerr << "DrawingSurface::DrawingSurface: non-uniform device scale!" << std::endl;
86 }
87 _device_scale = x_scale;
88 assert(_device_scale > 0);
89
90 _pixels = Geom::IntPoint(cairo_image_surface_get_width(surface) / _device_scale, cairo_image_surface_get_height(surface) / _device_scale);
91}
92
94{
95 if (_surface) {
96 cairo_surface_destroy(_surface);
97 }
98}
99
102{
103 if (_surface) {
104 cairo_surface_destroy(_surface);
105 _surface = nullptr;
106 }
107}
108
114{
115 // deferred allocation
116 if (!_surface) {
117 _surface = cairo_image_surface_create(CAIRO_FORMAT_ARGB32,
120 cairo_surface_set_device_scale(_surface, _device_scale, _device_scale);
121 }
122 cairo_t *ct = cairo_create(_surface);
123 if (_scale != Geom::Scale::identity()) {
124 cairo_scale(ct, _scale.vector().x(), _scale.vector().y());
125 }
126 cairo_translate(ct, -_origin.x(), -_origin.y());
127 return ct;
128}
129
134
136
137DrawingCache::DrawingCache(Geom::IntRect const &area, int device_scale)
138 : DrawingSurface(area, device_scale)
139 , _clean_region(cairo_region_create())
140 , _pending_area(area)
141{
142}
143
145{
146 cairo_region_destroy(_clean_region);
147}
148
150{
151 auto const dirty = geom_to_cairo(area);
152 cairo_region_subtract_rectangle(_clean_region, &dirty);
153}
154
156{
157 auto const r = area & pixelArea();
158 if (!r) return;
159 auto const clean = geom_to_cairo(*r);
160 cairo_region_union_rectangle(_clean_region, &clean);
161}
162
165{
166 _pending_area = new_area;
167 _pending_transform *= trans;
168}
169
173{
174 Geom::IntRect old_area = pixelArea();
175 bool is_identity = _pending_transform.isIdentity();
176 if (is_identity && _pending_area == old_area) return; // no change
177
178 bool is_integer_translation = is_identity;
179 if (!is_identity && _pending_transform.isTranslation()) {
182 is_integer_translation = true;
183 cairo_region_translate(_clean_region, t.x(), t.y());
184 if (old_area + t == _pending_area) {
185 // if the areas match, the only thing to do
186 // is to ensure that the clean area is not too large
187 // we can exit early
188 auto const limit = geom_to_cairo(_pending_area);
189 cairo_region_intersect_rectangle(_clean_region, &limit);
190 _origin += t;
192 return;
193 }
194 }
195 }
196
197 // the area has changed, so the cache content needs to be copied
198 Geom::IntPoint old_origin = old_area.min();
199 cairo_surface_t *old_surface = _surface;
200 _surface = nullptr;
203
204 if (is_integer_translation) {
205 // transform the cache only for integer translations and identities
207 if (!is_identity) {
209 }
210 cairo_set_source_surface(ct, old_surface, old_origin.x(), old_origin.y());
211 cairo_set_operator(ct, CAIRO_OPERATOR_SOURCE);
212 cairo_pattern_set_filter(cairo_get_source(ct), CAIRO_FILTER_NEAREST);
213 cairo_paint(ct);
214 cairo_destroy(ct);
215
216 auto const limit = geom_to_cairo(_pending_area);
217 cairo_region_intersect_rectangle(_clean_region, &limit);
218 } else {
219 // dirty everything
220 cairo_region_destroy(_clean_region);
221 _clean_region = cairo_region_create();
222 }
223
224 //std::cout << _pending_transform << old_area << _pending_area << std::endl;
225 cairo_surface_destroy(old_surface);
227}
228
234{
235 if (!area) return;
236
237 // We subtract the clean region from the area, then get the bounds
238 // of the resulting region. This is the area that needs to be repainted
239 // by the item.
240 // Then we subtract the area that needs to be repainted from the
241 // original area and paint the resulting region from cache.
242 auto const area_c = geom_to_cairo(*area);
243 cairo_region_t *dirty_region = cairo_region_create_rectangle(&area_c);
244 cairo_region_t *cache_region = cairo_region_copy(dirty_region);
245 cairo_region_subtract(dirty_region, _clean_region);
246
247 if (is_filter && !cairo_region_is_empty(dirty_region)) { // To allow fast panning on high zoom on filters
248 cairo_region_destroy(cache_region);
249 cairo_region_destroy(dirty_region);
250 cairo_region_destroy(_clean_region);
251 _clean_region = cairo_region_create();
252 return;
253 }
254
255 if (cairo_region_is_empty(dirty_region)) {
257 } else {
258 cairo_rectangle_int_t to_repaint;
259 cairo_region_get_extents(dirty_region, &to_repaint);
260 area = cairo_to_geom(to_repaint);
261 cairo_region_subtract_rectangle(cache_region, &to_repaint);
262 }
263 cairo_region_destroy(dirty_region);
264
265 if (!cairo_region_is_empty(cache_region)) {
266 int nr = cairo_region_num_rectangles(cache_region);
267 for (int i = 0; i < nr; ++i) {
268 cairo_rectangle_int_t tmp;
269 cairo_region_get_rectangle(cache_region, i, &tmp);
270 dc.rectangle(cairo_to_geom(tmp));
271 }
272 dc.setSource(this);
273 dc.fill();
274 }
275 cairo_region_destroy(cache_region);
276}
277
278// debugging utility
280{
281 static int dumpnr = 0;
284 if (!cairo_region_is_empty(_clean_region)) {
286 int nr = cairo_region_num_rectangles(_clean_region);
287 cairo_rectangle_int_t tmp;
288 for (int i = 0; i < nr; ++i) {
289 cairo_region_get_rectangle(_clean_region, i, &tmp);
290 dc.rectangle(cairo_to_geom(tmp));
291 }
292 dc.setSource(0,1,0,0.1);
293 dc.fill();
294 }
295 dc.rectangle(*area);
296 dc.setSource(1,0,0,0.1);
297 dc.fill();
298 char *fn = g_strdup_printf("dump%d.png", dumpnr++);
299 cairo_surface_write_to_png(surface, fn);
300 cairo_surface_destroy(surface);
301 g_free(fn);
302}
303
304} // namespace Inkscape
305
306/*
307 Local Variables:
308 mode:c++
309 c-file-style:"stroustrup"
310 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
311 indent-tabs-mode:nil
312 fill-column:99
313 End:
314*/
315// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:fileencoding=utf-8:textwidth=99 :
Point origin
Definition aa.cpp:227
void ink_cairo_transform(cairo_t *ct, Geom::Affine const &m)
cairo_surface_t * ink_cairo_surface_copy(cairo_surface_t *s)
Create an exact copy of a surface.
Cairo integration helpers.
Cairo::RefPtr< Cairo::ImageSurface > surface
Definition canvas.cpp:137
Cairo::RefPtr< Cairo::Region > clean
Definition canvas.cpp:183
3x3 matrix representing an affine transformation.
Definition affine.h:70
Point translation() const
Gets the translation imparted by the Affine.
Definition affine.cpp:41
bool isIdentity(Coord eps=EPSILON) const
Check whether this matrix is an identity matrix.
Definition affine.cpp:109
void setIdentity()
Sets this matrix to be the Identity Affine.
Definition affine.cpp:96
bool isTranslation(Coord eps=EPSILON) const
Check whether this matrix represents a pure translation.
Definition affine.cpp:123
Axis-aligned generic rectangle that can be empty.
Axis aligned, non-empty, generic rectangle.
static CRect from_xywh(C x, C y, C w, C h)
Create rectangle from origin and dimensions.
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
CPoint dimensions() const
Get rectangle's width and height as a point.
Two-dimensional point with integer coordinates.
Definition int-point.h:57
constexpr IntCoord x() const noexcept
Definition int-point.h:77
constexpr IntCoord y() const noexcept
Definition int-point.h:79
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
IntPoint round() const
Round to nearest integer coordinates.
Definition point.h:202
Axis aligned, non-empty rectangle.
Definition rect.h:92
static Scale identity()
Definition transforms.h:173
Point vector() const
Definition transforms.h:171
DrawingCache(Geom::IntRect const &area, int device_scale=1)
void markDirty(Geom::IntRect const &area=Geom::IntRect::infinite())
void prepare()
Transforms the cache according to the transform specified during the update phase.
void paintFromCache(DrawingContext &dc, Geom::OptIntRect &area, bool is_filter)
Paints the clean area from cache and modifies the area parameter to the bounds of the region that mus...
void scheduleTransform(Geom::IntRect const &new_area, Geom::Affine const &trans)
Call this during the update phase to schedule a transformation of the cache.
Geom::Affine _pending_transform
void _dumpCache(Geom::OptIntRect const &area)
void markClean(Geom::IntRect const &area=Geom::IntRect::infinite())
cairo_region_t * _clean_region
Geom::IntRect _pending_area
RAII idiom for saving the state of DrawingContext.
Minimal wrapper over Cairo.
void setSource(cairo_pattern_t *source)
void rectangle(Geom::Rect const &r)
Drawing surface that remembers its origin.
void dropContents()
Drop contents of the surface and release the underlying Cairo object.
Geom::IntRect pixelArea() const
cairo_t * createRawContext()
Create a drawing context for this surface.
DrawingSurface(Geom::IntRect const &area, int device_scale=1)
Creates a surface with the given physical extents.
Geom::Rect area() const
Get the logical extents of the surface.
cairo_surface_t * _surface
Cairo drawing context with Inkscape extensions.
Cairo surface that remembers its origin.
struct _cairo_surface cairo_surface_t
struct _cairo_region cairo_region_t
GenericOptRect< IntCoord > OptIntRect
Definition forward.h:58
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
Helper class to stream background task notifications as a series of messages.
struct _cairo cairo_t
Definition path-cairo.h:16
Cairo::RectangleInt geom_to_cairo(const Geom::IntRect &rect)
Definition util.cpp:352
Geom::IntRect cairo_to_geom(const Cairo::RectangleInt &rect)
Definition util.cpp:357
Geom::IntPoint dimensions(const Cairo::RefPtr< Cairo::ImageSurface > &surface)
Definition util.cpp:367