/src/graphicsmagick/Magick++/lib/Geometry.cpp
Line | Count | Source |
1 | | // This may look like C code, but it is really -*- C++ -*- |
2 | | // |
3 | | // Copyright Bob Friesenhahn, 1999-2026 |
4 | | // |
5 | | // Geometry implementation |
6 | | // |
7 | | |
8 | | #define MAGICK_IMPLEMENTATION |
9 | | #define MAGICK_PLUSPLUS_IMPLEMENTATION |
10 | | |
11 | | #include "Magick++/Include.h" |
12 | | #include <string> |
13 | | #include <ctype.h> // for isdigit |
14 | | #include <iostream> |
15 | | #include <sstream> // for ostringstream |
16 | | #include <string.h> // for strcpy |
17 | | #include <cassert> |
18 | | |
19 | | using namespace std; |
20 | | |
21 | | #include "Magick++/Geometry.h" |
22 | | #include "Magick++/Exception.h" |
23 | | |
24 | 0 | #define AbsoluteValue(x) ((x) < 0 ? -(x) : (x)) |
25 | | |
26 | | // Does object contain valid geometry? |
27 | | void Magick::Geometry::isValid ( bool isValid_ ) |
28 | 0 | { |
29 | 0 | _flags._b._isValid = isValid_; |
30 | 0 | } |
31 | | |
32 | | bool Magick::Geometry::isValid ( void ) const |
33 | 0 | { |
34 | 0 | return _flags._b._isValid; |
35 | 0 | } |
36 | | |
37 | | // Width |
38 | | void Magick::Geometry::width ( unsigned int width_ ) |
39 | 0 | { |
40 | 0 | _width = width_; |
41 | 0 | isValid( true ); |
42 | 0 | } |
43 | | unsigned int Magick::Geometry::width ( void ) const |
44 | 0 | { |
45 | 0 | return _width; |
46 | 0 | } |
47 | | |
48 | | // Height |
49 | | void Magick::Geometry::height ( unsigned int height_ ) |
50 | 0 | { |
51 | 0 | _height = height_; |
52 | 0 | } |
53 | | unsigned int Magick::Geometry::height ( void ) const |
54 | 0 | { |
55 | 0 | return _height; |
56 | 0 | } |
57 | | |
58 | | // X offset from origin |
59 | | void Magick::Geometry::xOff ( unsigned int xOff_ ) |
60 | 0 | { |
61 | 0 | _xOff = xOff_; |
62 | 0 | } |
63 | | unsigned int Magick::Geometry::xOff ( void ) const |
64 | 0 | { |
65 | 0 | return _xOff; |
66 | 0 | } |
67 | | |
68 | | // Y offset from origin |
69 | | void Magick::Geometry::yOff ( unsigned int yOff_ ) |
70 | 0 | { |
71 | 0 | _yOff = yOff_; |
72 | 0 | } |
73 | | unsigned int Magick::Geometry::yOff ( void ) const |
74 | 0 | { |
75 | 0 | return _yOff; |
76 | 0 | } |
77 | | |
78 | | // Sign of X offset negative? (X origin at right) |
79 | | void Magick::Geometry::xNegative ( bool xNegative_ ) |
80 | 0 | { |
81 | 0 | _flags._b._xNegative = xNegative_; |
82 | 0 | } |
83 | | bool Magick::Geometry::xNegative ( void ) const |
84 | 0 | { |
85 | 0 | return _flags._b._xNegative; |
86 | 0 | } |
87 | | |
88 | | // Sign of Y offset negative? (Y origin at bottom) |
89 | | void Magick::Geometry::yNegative ( bool yNegative_ ) |
90 | 0 | { |
91 | 0 | _flags._b._yNegative = yNegative_; |
92 | 0 | } |
93 | | bool Magick::Geometry::yNegative ( void ) const |
94 | 0 | { |
95 | 0 | return _flags._b._yNegative; |
96 | 0 | } |
97 | | |
98 | | // Interpret width & height as percentages (%) |
99 | | void Magick::Geometry::percent ( bool percent_ ) |
100 | 0 | { |
101 | 0 | _flags._b._percent = percent_; |
102 | 0 | } |
103 | | bool Magick::Geometry::percent ( void ) const |
104 | 0 | { |
105 | 0 | return _flags._b._percent; |
106 | 0 | } |
107 | | |
108 | | // Resize without preserving aspect ratio (!) |
109 | | void Magick::Geometry::aspect ( bool aspect_ ) |
110 | 0 | { |
111 | 0 | _flags._b._aspect = aspect_; |
112 | 0 | } |
113 | | bool Magick::Geometry::aspect ( void ) const |
114 | 0 | { |
115 | 0 | return _flags._b._aspect; |
116 | 0 | } |
117 | | |
118 | | // Resize if image is greater than size (>) |
119 | | void Magick::Geometry::greater ( bool greater_ ) |
120 | 0 | { |
121 | 0 | _flags._b._greater = greater_; |
122 | 0 | } |
123 | | bool Magick::Geometry::greater ( void ) const |
124 | 0 | { |
125 | 0 | return _flags._b._greater; |
126 | 0 | } |
127 | | |
128 | | // Resize if image is less than size (<) |
129 | | void Magick::Geometry::less ( bool less_ ) |
130 | 0 | { |
131 | 0 | _flags._b._less = less_; |
132 | 0 | } |
133 | | bool Magick::Geometry::less ( void ) const |
134 | 0 | { |
135 | 0 | return _flags._b._less; |
136 | 0 | } |
137 | | |
138 | | // Resize image to fit total pixel area specified by dimensions (@). |
139 | | void Magick::Geometry::limitPixels ( bool limitPixels_ ) |
140 | 0 | { |
141 | 0 | _flags._b._limitPixels = limitPixels_; |
142 | 0 | } |
143 | | bool Magick::Geometry::limitPixels ( void ) const |
144 | 0 | { |
145 | 0 | return _flags._b._limitPixels; |
146 | 0 | } |
147 | | |
148 | | // Resize image to fit a set of dimensions |
149 | | void Magick::Geometry::fillArea ( bool fillArea_ ) |
150 | 0 | { |
151 | 0 | _flags._b._fillArea = fillArea_; |
152 | 0 | } |
153 | | bool Magick::Geometry::fillArea ( void ) const |
154 | 0 | { |
155 | 0 | return _flags._b._fillArea; |
156 | 0 | } |
157 | | |
158 | | |
159 | | int Magick::operator == ( const Magick::Geometry& left_, |
160 | | const Magick::Geometry& right_ ) |
161 | 0 | { |
162 | 0 | return ( |
163 | 0 | ( left_.isValid() == right_.isValid() ) && |
164 | 0 | ( left_.width() == right_.width() ) && |
165 | 0 | ( left_.height() == right_.height() ) && |
166 | 0 | ( left_.xOff() == right_.xOff() ) && |
167 | 0 | ( left_.yOff() == right_.yOff() ) && |
168 | 0 | ( left_.xNegative() == right_.xNegative() ) && |
169 | 0 | ( left_.yNegative() == right_.yNegative() ) && |
170 | 0 | ( left_.percent() == right_.percent() ) && |
171 | 0 | ( left_.aspect() == right_.aspect() ) && |
172 | 0 | ( left_.greater() == right_.greater() ) && |
173 | 0 | ( left_.less() == right_.less() ) && |
174 | 0 | ( left_.limitPixels() == right_.limitPixels() ) && |
175 | 0 | ( left_.fillArea() == right_.fillArea() ) |
176 | 0 | ); |
177 | 0 | } |
178 | | int Magick::operator != ( const Magick::Geometry& left_, |
179 | | const Magick::Geometry& right_ ) |
180 | 0 | { |
181 | 0 | return ( ! (left_ == right_) ); |
182 | 0 | } |
183 | | int Magick::operator > ( const Magick::Geometry& left_, |
184 | | const Magick::Geometry& right_ ) |
185 | 0 | { |
186 | 0 | return ( !( left_ < right_ ) && ( left_ != right_ ) ); |
187 | 0 | } |
188 | | int Magick::operator < ( const Magick::Geometry& left_, |
189 | | const Magick::Geometry& right_ ) |
190 | 0 | { |
191 | 0 | return ( |
192 | 0 | ( left_.width() * left_.height() ) |
193 | 0 | < |
194 | 0 | ( right_.width() * right_.height() ) |
195 | 0 | ); |
196 | 0 | } |
197 | | int Magick::operator >= ( const Magick::Geometry& left_, |
198 | | const Magick::Geometry& right_ ) |
199 | 0 | { |
200 | 0 | return ( ( left_ > right_ ) || ( left_ == right_ ) ); |
201 | 0 | } |
202 | | int Magick::operator <= ( const Magick::Geometry& left_, |
203 | | const Magick::Geometry& right_ ) |
204 | 0 | { |
205 | 0 | return ( ( left_ < right_ ) || ( left_ == right_ ) ); |
206 | 0 | } |
207 | | |
208 | | // Construct using parameterized arguments |
209 | | Magick::Geometry::Geometry ( unsigned int width_, |
210 | | unsigned int height_, |
211 | | unsigned int xOff_, |
212 | | unsigned int yOff_, |
213 | | bool xNegative_, |
214 | | bool yNegative_ ) |
215 | 0 | : _width( width_ ), |
216 | 0 | _height( height_ ), |
217 | 0 | _xOff( xOff_ ), |
218 | 0 | _yOff( yOff_ ) |
219 | 0 | { |
220 | 0 | xNegative( xNegative_ ); |
221 | 0 | yNegative( yNegative_ ); |
222 | 0 | isValid( true ); |
223 | 0 | percent( false ); |
224 | 0 | aspect( false ); |
225 | 0 | greater( false ); |
226 | 0 | less( false ); |
227 | 0 | limitPixels (false ); |
228 | 0 | fillArea( false ); |
229 | 0 | } |
230 | | |
231 | | // Assignment from C++ string |
232 | | Magick::Geometry::Geometry ( const std::string &geometry_ ) |
233 | 0 | : _width( 0 ), |
234 | 0 | _height( 0 ), |
235 | 0 | _xOff( 0 ), |
236 | 0 | _yOff( 0 ) |
237 | 0 | { |
238 | 0 | assert(sizeof(_flags._padding) >= sizeof(_flags._b)); |
239 | 0 | xNegative( false ); |
240 | 0 | yNegative( false ); |
241 | 0 | isValid( false ); |
242 | 0 | percent( false ); |
243 | 0 | aspect( false ); |
244 | 0 | greater( false ); |
245 | 0 | less( false ); |
246 | 0 | limitPixels (false ); |
247 | 0 | fillArea( false ); |
248 | 0 | *this = geometry_; // Use assignment operator |
249 | 0 | } |
250 | | |
251 | | |
252 | | // Assignment from C character string |
253 | | Magick::Geometry::Geometry ( const char *geometry_ ) |
254 | 0 | : _width( 0 ), |
255 | 0 | _height( 0 ), |
256 | 0 | _xOff( 0 ), |
257 | 0 | _yOff( 0 ) |
258 | 0 | { |
259 | 0 | xNegative( false ); |
260 | 0 | yNegative( false ); |
261 | 0 | isValid( false ); |
262 | 0 | percent( false ); |
263 | 0 | aspect( false ); |
264 | 0 | greater( false ); |
265 | 0 | less( false ); |
266 | 0 | limitPixels ( false ); |
267 | 0 | fillArea( false ); |
268 | 0 | *this = geometry_; // Use assignment operator |
269 | 0 | } |
270 | | |
271 | | // Copy constructor |
272 | | Magick::Geometry::Geometry ( const Geometry &geometry_ ) |
273 | 0 | : _width( geometry_._width ), |
274 | 0 | _height( geometry_._height ), |
275 | 0 | _xOff( geometry_._xOff ), |
276 | 0 | _yOff( geometry_._yOff ) |
277 | 0 | { |
278 | 0 | xNegative( geometry_.xNegative() ); |
279 | 0 | yNegative( geometry_.yNegative() ); |
280 | 0 | isValid ( geometry_.isValid() ); |
281 | 0 | percent( geometry_.percent() ); |
282 | 0 | aspect( geometry_.aspect() ); |
283 | 0 | greater( geometry_.greater() ); |
284 | 0 | less( geometry_.less() ); |
285 | 0 | limitPixels( geometry_.limitPixels() ); |
286 | 0 | fillArea( geometry_.fillArea() ); |
287 | 0 | } |
288 | | |
289 | | // Default constructor |
290 | | Magick::Geometry::Geometry ( void ) |
291 | 0 | : _width( 0 ), |
292 | 0 | _height( 0 ), |
293 | 0 | _xOff( 0 ), |
294 | 0 | _yOff( 0 ) |
295 | 0 | { |
296 | 0 | assert(sizeof(_flags._padding) >= sizeof(_flags._b)); |
297 | 0 | xNegative( false ); |
298 | 0 | yNegative( false ); |
299 | 0 | isValid ( false ); |
300 | 0 | percent( false ); |
301 | 0 | aspect( false ); |
302 | 0 | greater( false ); |
303 | 0 | less( false ); |
304 | 0 | limitPixels( false ); |
305 | 0 | fillArea( false ); |
306 | 0 | } |
307 | | |
308 | | /* virtual */ Magick::Geometry::~Geometry ( void ) |
309 | 0 | { |
310 | | // Nothing to do |
311 | 0 | } |
312 | | |
313 | | Magick::Geometry& Magick::Geometry::operator = ( const Geometry& geometry_ ) |
314 | 0 | { |
315 | | // If not being set to ourself |
316 | 0 | if ( this != &geometry_ ) |
317 | 0 | { |
318 | 0 | _width = geometry_._width; |
319 | 0 | _height = geometry_._height; |
320 | 0 | _xOff = geometry_._xOff; |
321 | 0 | _yOff = geometry_._yOff; |
322 | |
|
323 | 0 | xNegative( geometry_.xNegative() ); |
324 | 0 | yNegative( geometry_.yNegative() ); |
325 | 0 | isValid ( geometry_.isValid() ); |
326 | 0 | percent( geometry_.percent() ); |
327 | 0 | aspect( geometry_.aspect() ); |
328 | 0 | greater( geometry_.greater() ); |
329 | 0 | less( geometry_.less() ); |
330 | 0 | limitPixels( geometry_.limitPixels() ); |
331 | 0 | fillArea( geometry_.fillArea() ); |
332 | 0 | } |
333 | 0 | return *this; |
334 | 0 | } |
335 | | |
336 | | // Set value via geometry string |
337 | | /* virtual */ const Magick::Geometry& |
338 | | Magick::Geometry::operator = ( const std::string &geometry_ ) |
339 | 0 | { |
340 | | // If argument does not start with digit, presume that it is a |
341 | | // page-size specification that needs to be converted to an |
342 | | // equivalent geometry specification using PostscriptGeometry() |
343 | 0 | std::string geom = geometry_; |
344 | 0 | if (geom[0] != '-' && |
345 | 0 | geom[0] != '+' && |
346 | 0 | geom[0] != 'x' && |
347 | 0 | !std::isdigit(static_cast<int>(geom[0]))) |
348 | 0 | { |
349 | 0 | char *pageptr = GetPageGeometry( geom.c_str() ); |
350 | 0 | if ( pageptr != 0 ) |
351 | 0 | { |
352 | 0 | geom = pageptr; |
353 | 0 | MagickFreeMemory(pageptr); |
354 | 0 | } |
355 | 0 | } |
356 | |
|
357 | 0 | long x = 0; |
358 | 0 | long y = 0; |
359 | 0 | unsigned long width_val = 0; |
360 | 0 | unsigned long height_val = 0; |
361 | 0 | int flags = GetGeometry (geom.c_str(), &x, &y, &width_val, &height_val ); |
362 | |
|
363 | 0 | if (flags == NoValue) |
364 | 0 | { |
365 | | // Total failure! |
366 | 0 | *this=Geometry(); |
367 | 0 | isValid( false ); |
368 | 0 | return *this; |
369 | 0 | } |
370 | | |
371 | 0 | if ( ( flags & WidthValue ) != 0 ) |
372 | 0 | { |
373 | 0 | width( width_val ); |
374 | 0 | isValid( true ); |
375 | 0 | } |
376 | |
|
377 | 0 | if ( ( flags & HeightValue ) != 0 ) |
378 | 0 | height( height_val ); |
379 | |
|
380 | 0 | if ( ( flags & XValue ) != 0 ) |
381 | 0 | { |
382 | 0 | xOff( static_cast<unsigned int>(AbsoluteValue(x)) ); |
383 | 0 | isValid( true ); |
384 | 0 | } |
385 | |
|
386 | 0 | if ( ( flags & YValue ) != 0 ) |
387 | 0 | { |
388 | 0 | yOff( static_cast<unsigned int>(AbsoluteValue(y)) ); |
389 | 0 | isValid( true ); |
390 | 0 | } |
391 | |
|
392 | 0 | if ( ( flags & XNegative ) != 0 ) |
393 | 0 | xNegative( true ); |
394 | |
|
395 | 0 | if ( ( flags & YNegative ) != 0 ) |
396 | 0 | yNegative( true ); |
397 | |
|
398 | 0 | if ( ( flags & PercentValue ) != 0 ) // '%' |
399 | 0 | percent( true ); |
400 | |
|
401 | 0 | if ( ( flags & AspectValue ) != 0 ) // '!' |
402 | 0 | aspect( true ); |
403 | |
|
404 | 0 | if ( ( flags & LessValue ) != 0 ) // '<' |
405 | 0 | less( true ); |
406 | |
|
407 | 0 | if ( ( flags & GreaterValue ) != 0 ) // '>' |
408 | 0 | greater( true ); |
409 | |
|
410 | 0 | if ( ( flags & AreaValue ) != 0 ) // '@' |
411 | 0 | limitPixels( true ); |
412 | |
|
413 | 0 | if ( ( flags & MinimumValue ) != 0 ) // '^' |
414 | 0 | fillArea( true ); |
415 | |
|
416 | 0 | return *this; |
417 | 0 | } |
418 | | |
419 | | |
420 | | // Set value via geometry C string |
421 | | /* virtual */ const Magick::Geometry& Magick::Geometry::operator = ( const char * geometry_ ) |
422 | 0 | { |
423 | 0 | *this = std::string(geometry_); |
424 | 0 | return *this; |
425 | 0 | } |
426 | | |
427 | | // Return geometry string |
428 | | Magick::Geometry::operator std::string() const |
429 | 0 | { |
430 | 0 | if (!isValid()) |
431 | 0 | { |
432 | 0 | throwExceptionExplicit( OptionError, "Invalid geometry argument" ); |
433 | 0 | } |
434 | |
|
435 | 0 | string geometry; |
436 | 0 | std::ostringstream oss; |
437 | |
|
438 | 0 | if ( _width ) |
439 | 0 | { |
440 | 0 | oss << _width; |
441 | 0 | } |
442 | |
|
443 | 0 | if ( _width && _height ) |
444 | 0 | { |
445 | 0 | oss << 'x' |
446 | 0 | << _height; |
447 | 0 | } |
448 | |
|
449 | 0 | if ( _xOff || _yOff ) |
450 | 0 | { |
451 | 0 | if ( xNegative() ) |
452 | 0 | oss << '-'; |
453 | 0 | else |
454 | 0 | oss << '+'; |
455 | |
|
456 | 0 | oss << _xOff; |
457 | |
|
458 | 0 | if ( yNegative() ) |
459 | 0 | oss << '-'; |
460 | 0 | else |
461 | 0 | oss << '+'; |
462 | |
|
463 | 0 | oss << _yOff; |
464 | 0 | } |
465 | |
|
466 | 0 | if ( percent() ) |
467 | 0 | oss << '%'; |
468 | |
|
469 | 0 | if ( aspect() ) |
470 | 0 | oss << '!'; |
471 | |
|
472 | 0 | if ( greater() ) |
473 | 0 | oss << '>'; |
474 | |
|
475 | 0 | if ( less() ) |
476 | 0 | oss << '<'; |
477 | |
|
478 | 0 | if ( limitPixels() ) |
479 | 0 | oss << '@'; |
480 | |
|
481 | 0 | if ( fillArea() ) |
482 | 0 | oss << '^'; |
483 | |
|
484 | 0 | geometry = oss.str(); |
485 | |
|
486 | 0 | return geometry; |
487 | 0 | } |
488 | | |
489 | | // Construct from RectangleInfo |
490 | | Magick::Geometry::Geometry ( const MagickLib::RectangleInfo &rectangle_ ) |
491 | 0 | : _width(static_cast<unsigned int>(rectangle_.width)), |
492 | 0 | _height(static_cast<unsigned int>(rectangle_.height)), |
493 | 0 | _xOff(static_cast<unsigned int>(AbsoluteValue(rectangle_.x))), |
494 | 0 | _yOff(static_cast<unsigned int>(AbsoluteValue(rectangle_.y))) |
495 | 0 | { |
496 | 0 | xNegative(rectangle_.x < 0 ? true : false); |
497 | 0 | yNegative(rectangle_.y < 0 ? true : false); |
498 | 0 | isValid(true); |
499 | 0 | percent(false); |
500 | 0 | aspect(false); |
501 | 0 | greater(false); |
502 | 0 | less(false); |
503 | 0 | limitPixels(false); |
504 | 0 | fillArea(false); |
505 | 0 | } |
506 | | |
507 | | // Return an ImageMagick RectangleInfo struct |
508 | | Magick::Geometry::operator MagickLib::RectangleInfo() const |
509 | 0 | { |
510 | 0 | RectangleInfo rectangle; |
511 | 0 | rectangle.width = _width; |
512 | 0 | rectangle.height = _height; |
513 | 0 | xNegative() ? rectangle.x = static_cast<long>(0-_xOff) : rectangle.x = static_cast<long>(_xOff); |
514 | 0 | yNegative() ? rectangle.y = static_cast<long>(0-_yOff) : rectangle.y = static_cast<long>(_yOff); |
515 | 0 | return rectangle; |
516 | 0 | } |