Inkscape
Vector Graphics Editor
Loading...
Searching...
No Matches
viewbox.cpp
Go to the documentation of this file.
1// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * viewBox helper class, common code used by root, symbol, marker, pattern, image, view
4 *
5 * Authors:
6 * Lauris Kaplinski <lauris@kaplinski.com> (code extracted from symbol.cpp)
7 * Tavmjong Bah <tavmjong@free.fr>
8 * Johan Engelen
9 *
10 * Copyright (C) 2013-2014 Tavmjong Bah, authors
11 *
12 * Released under GNU GPL v2+, read the file 'COPYING' for more information.
13 *
14 */
15
16#include <2geom/transforms.h>
17
18#include "viewbox.h"
19#include "enums.h"
20#include "sp-item.h"
21#include "svg/stringstream.h"
22
23#include <map>
24
25namespace {
26std::map<unsigned int, char const *> const ASPECT_ALIGN_STRINGS{
27 {SP_ASPECT_NONE, "none"}, //
28 {SP_ASPECT_XMIN_YMIN, "xMinYMin"}, //
29 {SP_ASPECT_XMID_YMIN, "xMidYMin"}, //
30 {SP_ASPECT_XMAX_YMIN, "xMaxYMin"}, //
31 {SP_ASPECT_XMIN_YMID, "xMinYMid"}, //
32 {SP_ASPECT_XMID_YMID, "xMidYMid"}, //
33 {SP_ASPECT_XMAX_YMID, "xMaxYMid"}, //
34 {SP_ASPECT_XMIN_YMAX, "xMinYMax"}, //
35 {SP_ASPECT_XMID_YMAX, "xMidYMax"}, //
36 {SP_ASPECT_XMAX_YMAX, "xMaxYMax"}, //
37};
38}
39
41 : viewBox_set(false)
42 , viewBox()
43 , aspect_set(false)
44 , aspect_align(SP_ASPECT_XMID_YMID) // Default per spec
45 , aspect_clip(SP_ASPECT_MEET)
46 , c2p(Geom::identity())
47{
48}
49
50void SPViewBox::set_viewBox(const gchar* value) {
51
52 if (value) {
53 gchar *eptr = const_cast<gchar*>(value); // const-cast necessary because of const-incorrect interface definition of g_ascii_strtod
54
55 double x = g_ascii_strtod (eptr, &eptr);
56
57 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) {
58 eptr++;
59 }
60
61 double y = g_ascii_strtod (eptr, &eptr);
62
63 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) {
64 eptr++;
65 }
66
67 double width = g_ascii_strtod (eptr, &eptr);
68
69 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) {
70 eptr++;
71 }
72
73 double height = g_ascii_strtod (eptr, &eptr);
74
75 while (*eptr && ((*eptr == ',') || (*eptr == ' '))) {
76 eptr++;
77 }
78
79 if ((width > 0) && (height > 0)) {
80 /* Set viewbox */
82 this->viewBox_set = true;
83 } else {
84 this->viewBox_set = false;
85 }
86 } else {
87 this->viewBox_set = false;
88 }
89
90 // The C++ way? -- not necessarily using iostreams
91 // std::string sv( value );
92 // std::replace( sv.begin(), sv.end(), ',', ' ');
93 // std::stringstream ss( sv );
94 // double x, y, width, height;
95 // ss >> x >> y >> width >> height;
96}
97
98void SPViewBox::set_preserveAspectRatio(const gchar* value) {
99
100 /* Do setup before, so we can use break to escape */
101 this->aspect_set = false;
102 this->aspect_align = SP_ASPECT_XMID_YMID; // Default per spec
104
105 if (value) {
106 const gchar *p = value;
107
108 while (*p && (*p == 32)) {
109 p += 1;
110 }
111
112 if (!*p) {
113 return;
114 }
115
116 const gchar *e = p;
117
118 while (*e && (*e != 32)) {
119 e += 1;
120 }
121
122 int len = e - p;
123
124 if (len > 8) { // Can't have buffer overflow as 8 < 256
125 return;
126 }
127
128 gchar c[256];
129 memcpy (c, value, len);
130
131 c[len] = 0;
132
133 /* Now the actual part */
134 unsigned int align = SP_ASPECT_NONE;
135 if (!strcmp (c, "none")) {
136 align = SP_ASPECT_NONE;
137 } else if (!strcmp (c, "xMinYMin")) {
138 align = SP_ASPECT_XMIN_YMIN;
139 } else if (!strcmp (c, "xMidYMin")) {
140 align = SP_ASPECT_XMID_YMIN;
141 } else if (!strcmp (c, "xMaxYMin")) {
142 align = SP_ASPECT_XMAX_YMIN;
143 } else if (!strcmp (c, "xMinYMid")) {
144 align = SP_ASPECT_XMIN_YMID;
145 } else if (!strcmp (c, "xMidYMid")) {
146 align = SP_ASPECT_XMID_YMID;
147 } else if (!strcmp (c, "xMaxYMid")) {
148 align = SP_ASPECT_XMAX_YMID;
149 } else if (!strcmp (c, "xMinYMax")) {
150 align = SP_ASPECT_XMIN_YMAX;
151 } else if (!strcmp (c, "xMidYMax")) {
152 align = SP_ASPECT_XMID_YMAX;
153 } else if (!strcmp (c, "xMaxYMax")) {
154 align = SP_ASPECT_XMAX_YMAX;
155 } else {
156 return;
157 }
158
159 unsigned int clip = SP_ASPECT_MEET;
160
161 while (*e && (*e == 32)) {
162 e += 1;
163 }
164
165 if (*e) {
166 if (!strcmp (e, "meet")) {
168 } else if (!strcmp (e, "slice")) {
170 } else {
171 return;
172 }
173 }
174
175 this->aspect_set = true;
176 this->aspect_align = align;
177 this->aspect_clip = clip;
178 }
179}
180
181// Apply scaling from viewbox
182void SPViewBox::apply_viewbox(const Geom::Rect& in, double scale_none) {
183
184 /* Determine actual viewbox in viewport coordinates */
185 // scale_none is the scale that would apply if the viewbox and page size are same size
186 // it is passed here because it is a double-precision variable, while 'in' is originally float
187 double x = 0.0;
188 double y = 0.0;
189 double scale_x = in.width() / this->viewBox.width();
190 double scale_y = in.height() / this->viewBox.height();
191 double scale_uniform = 1.0; // used only if scaling is uniform
192
193 if (Geom::are_near(scale_x / scale_y, 1.0, Geom::EPSILON)) {
194 // scaling is already uniform, reduce numerical error
195 scale_uniform = (scale_x + scale_y)/2.0;
196 if (Geom::are_near(scale_uniform / scale_none, 1.0, Geom::EPSILON))
197 scale_uniform = scale_none; // objects are same size, reduce numerical error
198 scale_x = scale_uniform;
199 scale_y = scale_uniform;
200 } else if (this->aspect_align != SP_ASPECT_NONE) {
201 // scaling is not uniform, but force it to be
202 scale_uniform = (this->aspect_clip == SP_ASPECT_MEET) ? MIN (scale_x, scale_y) : MAX (scale_x, scale_y);
203 scale_x = scale_uniform;
204 scale_y = scale_uniform;
205 double width = this->viewBox.width() * scale_uniform;
206 double height = this->viewBox.height() * scale_uniform;
207
208 /* Now place viewbox to requested position */
209 switch (this->aspect_align) {
211 break;
213 x = 0.5 * (in.width() - width);
214 break;
216 x = 1.0 * (in.width() - width);
217 break;
219 y = 0.5 * (in.height() - height);
220 break;
222 x = 0.5 * (in.width() - width);
223 y = 0.5 * (in.height() - height);
224 break;
226 x = 1.0 * (in.width() - width);
227 y = 0.5 * (in.height() - height);
228 break;
230 y = 1.0 * (in.height() - height);
231 break;
233 x = 0.5 * (in.width() - width);
234 y = 1.0 * (in.height() - height);
235 break;
237 x = 1.0 * (in.width() - width);
238 y = 1.0 * (in.height() - height);
239 break;
240 default:
241 break;
242 }
243 }
244
245 /* Viewbox transform from scale and position */
246 Geom::Affine q;
247 q[0] = scale_x;
248 q[1] = 0.0;
249 q[2] = 0.0;
250 q[3] = scale_y;
251 q[4] = x - scale_x * this->viewBox.left();
252 q[5] = y - scale_y * this->viewBox.top();
253
254 // std::cout << " q\n" << q << std::endl;
255
256 /* Append viewbox transformation */
257 this->c2p = q * this->c2p;
258}
259
260SPItemCtx SPViewBox::get_rctx(const SPItemCtx* ictx, double scale_none) {
261
262 /* Create copy of item context */
263 SPItemCtx rctx = *ictx;
264
265 /* Calculate child to parent transformation */
266 /* Apply parent translation (set up as viewport) */
267 this->c2p = Geom::Translate(rctx.viewport.min());
268
269 if (this->viewBox_set) {
270 // Adjusts c2p for viewbox
271 apply_viewbox( rctx.viewport, scale_none );
272 }
273
274 rctx.i2doc = this->c2p * rctx.i2doc;
275
276 /* If viewBox is set initialize child viewport */
277 /* Otherwise it is already correct */
278 if (this->viewBox_set) {
279 rctx.viewport = this->viewBox;
280 rctx.i2vp = Geom::identity();
281 }
282
283 return rctx;
284}
285
290{
291 if (viewBox_set) {
293 os << viewBox.left() << " " //
294 << viewBox.top() << " " //
295 << viewBox.width() << " " //
296 << viewBox.height();
297
298 repr->setAttribute("viewBox", os.str());
299 }
300}
301
306{
307 if (aspect_set) {
308 std::string aspect = ASPECT_ALIGN_STRINGS.at(aspect_align);
309
311 aspect += " slice";
312 }
313
314 repr->setAttribute("preserveAspectRatio", aspect);
315 }
316}
317
318/*
319 * Get the real paintable area given the original size and the requested size and position rect.
320 *
321 * @arg width - The original width, for example the pixbuf's pixel width
322 * @arg height - The original height
323 * @arg size - The requested computed size. see SPImage::bbox.
324 *
325 * @returns the real box that the image would be painted to.
326 */
328{
330 return size;
331
332 double scalex = size->width() / width;
333 double scaley = size->height() / height;
334 double scale = (aspect_clip == SP_ASPECT_MEET) ? std::min(scalex, scaley) : std::max(scalex, scaley);
335 auto pbox = Geom::Rect::from_xywh(size->left(), size->top(), width * scale, height * scale);
336 double x, y = 0.0;
337
338 /* Now place viewbox to requested position */
339 switch (aspect_align) {
343 x -= 0.5;
347 x -= 0.5;
348 default:
349 break;
350 }
351 switch (aspect_align) {
355 y -= 0.5;
359 y -= 0.5;
360 default:
361 break;
362 }
363 return pbox * Geom::Translate(x * (pbox.width() - size->width()),
364 y * (pbox.height() - size->height()));
365}
366
367/*
368 Local Variables:
369 mode:c++
370 c-file-style:"stroustrup"
371 c-basic-offset:2
372 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
373 indent-tabs-mode:nil
374 fill-column:99
375 End:
376*/
377// vim: filetype=cpp:expandtab:shiftwidth=2:tabstop=8:softtabstop=2:fileencoding=utf-8:textwidth=99 :
double scale
Definition aa.cpp:228
3x3 matrix representing an affine transformation.
Definition affine.h:70
static CRect from_xywh(Coord x, Coord y, Coord w, Coord h)
Create rectangle from origin and dimensions.
C top() const
Return top coordinate of the rectangle (+Y is downwards).
C left() const
Return leftmost coordinate of the rectangle (+X is to the right).
C height() const
Get the vertical extent of the rectangle.
C width() const
Get the horizontal extent of the rectangle.
CPoint min() const
Get the corner of the rectangle with smallest coordinate values.
Axis-aligned rectangle that can be empty.
Definition rect.h:203
Axis aligned, non-empty rectangle.
Definition rect.h:92
Translation by a vector.
Definition transforms.h:115
std::string str() const
Interface for refcounted XML nodes.
Definition node.h:80
void setAttribute(Util::const_char_ptr key, Util::const_char_ptr value)
Change an attribute of this node.
Definition node.cpp:25
void set_viewBox(const gchar *value)
Definition viewbox.cpp:50
unsigned int aspect_align
Definition viewbox.h:39
Geom::Rect viewBox
Definition viewbox.h:35
bool aspect_set
Definition viewbox.h:38
Geom::Affine c2p
Definition viewbox.h:43
void set_preserveAspectRatio(const gchar *value)
Definition viewbox.cpp:98
unsigned int aspect_clip
Definition viewbox.h:40
void write_preserveAspectRatio(Inkscape::XML::Node *repr) const
Write preserveAspectRatio attribute to XML, if set.
Definition viewbox.cpp:305
Geom::OptRect get_paintbox(double width, double height, Geom::OptRect const &size) const
Definition viewbox.cpp:327
SPItemCtx get_rctx(const SPItemCtx *ictx, double scale_none=1.0)
Definition viewbox.cpp:260
void write_viewBox(Inkscape::XML::Node *repr) const
Write viewBox attribute to XML, if set.
Definition viewbox.cpp:289
void apply_viewbox(const Geom::Rect &in, double scale_none=1.0)
Definition viewbox.cpp:182
bool viewBox_set
Definition viewbox.h:34
Geom::IntPoint size
double c[8][4]
@ SP_ASPECT_XMAX_YMIN
Definition enums.h:45
@ SP_ASPECT_XMID_YMIN
Definition enums.h:44
@ SP_ASPECT_XMIN_YMID
Definition enums.h:46
@ SP_ASPECT_XMIN_YMAX
Definition enums.h:49
@ SP_ASPECT_XMAX_YMID
Definition enums.h:48
@ SP_ASPECT_XMID_YMAX
Definition enums.h:50
@ SP_ASPECT_NONE
Definition enums.h:42
@ SP_ASPECT_XMIN_YMIN
Definition enums.h:43
@ SP_ASPECT_XMAX_YMAX
Definition enums.h:51
@ SP_ASPECT_XMID_YMID
Definition enums.h:47
@ SP_ASPECT_MEET
Definition enums.h:55
@ SP_ASPECT_SLICE
Definition enums.h:56
constexpr Coord EPSILON
Default "acceptably small" value.
Definition coord.h:84
Various utility functions.
Definition affine.h:22
Affine identity()
Create an identity matrix.
Definition affine.h:210
bool are_near(Affine const &a1, Affine const &a2, Coord eps=EPSILON)
static T clip(T const &v, T const &a, T const &b)
auto len
Definition safe-printf.h:21
Some things pertinent to all visible shapes: SPItem, SPItemView, SPItemCtx.
Contains transformations to document/viewport and the viewport size.
Definition sp-item.h:92
Geom::Affine i2doc
Item to document transformation.
Definition sp-item.h:94
Geom::Affine i2vp
Item to viewport transformation.
Definition sp-item.h:100
Geom::Rect viewport
Viewport size.
Definition sp-item.h:97
TODO: insert short description here.
double height
double width
Affine transformation classes.