1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % GGGG EEEEE OOO M M EEEEE TTTTT RRRR Y Y %
7 % G E O O MM MM E T R R Y Y %
8 % G GG EEE O O M M M EEE T RRRR Y %
9 % G G E O O M M E T R R Y %
10 % GGGG EEEEE OOO M M EEEEE T R R Y %
11 % %
12 % %
13 % MagickCore Geometry Methods %
14 % %
15 % Software Design %
16 % Cristy %
17 % January 2003 %
18 % %
19 % %
20 % Copyright 1999-2021 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
22 % %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
25 % %
26 % https://imagemagick.org/script/license.php %
27 % %
28 % Unless required by applicable law or agreed to in writing, software %
29 % distributed under the License is distributed on an "AS IS" BASIS, %
30 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31 % See the License for the specific language governing permissions and %
32 % limitations under the License. %
33 % %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35 %
36 %
37 */
38
39 /*
40 Include declarations.
41 */
42 #include "MagickCore/studio.h"
43 #include "MagickCore/constitute.h"
44 #include "MagickCore/draw.h"
45 #include "MagickCore/exception.h"
46 #include "MagickCore/exception-private.h"
47 #include "MagickCore/geometry.h"
48 #include "MagickCore/image-private.h"
49 #include "MagickCore/memory_.h"
50 #include "MagickCore/pixel-accessor.h"
51 #include "MagickCore/string_.h"
52 #include "MagickCore/string-private.h"
53 #include "MagickCore/token.h"
54
55 /*
56 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
57 % %
58 % %
59 % %
60 % G e t G e o m e t r y %
61 % %
62 % %
63 % %
64 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
65 %
66 % GetGeometry() parses a geometry specification and returns the width,
67 % height, x, and y values. It also returns flags that indicates which
68 % of the four values (width, height, x, y) were located in the string, and
69 % whether the x or y values are negative. In addition, there are flags to
70 % report any meta characters (%, !, <, or >).
71 %
72 % The value must form a proper geometry style specification of WxH+X+Y
73 % of integers only, and values can not be separated by comma, colon, or
74 % slash charcaters. See ParseGeometry() below.
75 %
76 % Offsets may be prefixed by multiple signs to make offset string
77 % substitutions easier to handle from shell scripts.
78 % For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
79 % offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
80 % offsets.
81 %
82 % The format of the GetGeometry method is:
83 %
84 % MagickStatusType GetGeometry(const char *geometry,ssize_t *x,ssize_t *y,
85 % size_t *width,size_t *height)
86 %
87 % A description of each parameter follows:
88 %
89 % o geometry: The geometry.
90 %
91 % o x,y: The x and y offset as determined by the geometry specification.
92 %
93 % o width,height: The width and height as determined by the geometry
94 % specification.
95 %
96 */
GetGeometry(const char * geometry,ssize_t * x,ssize_t * y,size_t * width,size_t * height)97 MagickExport MagickStatusType GetGeometry(const char *geometry,ssize_t *x,
98 ssize_t *y,size_t *width,size_t *height)
99 {
100 char
101 *p,
102 pedantic_geometry[MagickPathExtent],
103 *q;
104
105 double
106 value;
107
108 int
109 c;
110
111 MagickStatusType
112 flags;
113
114 /*
115 Remove whitespace and meta characters from geometry specification.
116 */
117 flags=NoValue;
118 if ((geometry == (char *) NULL) || (*geometry == '\0'))
119 return(flags);
120 if (strlen(geometry) >= (MagickPathExtent-1))
121 return(flags);
122 (void) CopyMagickString(pedantic_geometry,geometry,MagickPathExtent);
123 for (p=pedantic_geometry; *p != '\0'; )
124 {
125 if (isspace((int) ((unsigned char) *p)) != 0)
126 {
127 (void) CopyMagickString(p,p+1,MagickPathExtent);
128 continue;
129 }
130 c=(int) *p;
131 switch (c)
132 {
133 case '%':
134 {
135 flags|=PercentValue;
136 (void) CopyMagickString(p,p+1,MagickPathExtent);
137 break;
138 }
139 case '!':
140 {
141 flags|=AspectValue;
142 (void) CopyMagickString(p,p+1,MagickPathExtent);
143 break;
144 }
145 case '<':
146 {
147 flags|=LessValue;
148 (void) CopyMagickString(p,p+1,MagickPathExtent);
149 break;
150 }
151 case '>':
152 {
153 flags|=GreaterValue;
154 (void) CopyMagickString(p,p+1,MagickPathExtent);
155 break;
156 }
157 case '^':
158 {
159 flags|=MinimumValue;
160 (void) CopyMagickString(p,p+1,MagickPathExtent);
161 break;
162 }
163 case '@':
164 {
165 flags|=AreaValue;
166 (void) CopyMagickString(p,p+1,MagickPathExtent);
167 break;
168 }
169 case '(':
170 case ')':
171 {
172 (void) CopyMagickString(p,p+1,MagickPathExtent);
173 break;
174 }
175 case 'x':
176 case 'X':
177 {
178 flags|=SeparatorValue;
179 p++;
180 break;
181 }
182 case '-':
183 case ',':
184 case '+':
185 case '0':
186 case '1':
187 case '2':
188 case '3':
189 case '4':
190 case '5':
191 case '6':
192 case '7':
193 case '8':
194 case '9':
195 case 215:
196 case 'e':
197 case 'E':
198 {
199 p++;
200 break;
201 }
202 case '.':
203 {
204 p++;
205 flags|=DecimalValue;
206 break;
207 }
208 case ':':
209 {
210 p++;
211 flags|=AspectRatioValue;
212 break;
213 }
214 default:
215 return(flags);
216 }
217 }
218 /*
219 Parse width, height, x, and y.
220 */
221 p=pedantic_geometry;
222 if (*p == '\0')
223 return(flags);
224 q=p;
225 value=StringToDouble(p,&q);
226 (void) value;
227 if (LocaleNCompare(p,"0x",2) == 0)
228 value=(double) strtol(p,&q,10);
229 if ((*p != '+') && (*p != '-'))
230 {
231 c=(int) ((unsigned char) *q);
232 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ':') ||
233 (*q == '\0'))
234 {
235 /*
236 Parse width.
237 */
238 q=p;
239 if (width != (size_t *) NULL)
240 {
241 if (LocaleNCompare(p,"0x",2) == 0)
242 *width=(size_t) strtol(p,&p,10);
243 else
244 *width=((size_t) floor(StringToDouble(p,&p)+0.5)) & 0x7fffffff;
245 }
246 if (p != q)
247 flags|=WidthValue;
248 }
249 }
250 if ((*p != '+') && (*p != '-'))
251 {
252 c=(int) ((unsigned char) *p);
253 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ':'))
254 {
255 p++;
256 if ((*p != '+') && (*p != '-'))
257 {
258 /*
259 Parse height.
260 */
261 q=p;
262 if (height != (size_t *) NULL)
263 *height=((size_t) floor(StringToDouble(p,&p)+0.5)) & 0x7fffffff;
264 if (p != q)
265 flags|=HeightValue;
266 }
267 }
268 }
269 if ((*p == '+') || (*p == '-'))
270 {
271 /*
272 Parse x value.
273 */
274 while ((*p == '+') || (*p == '-'))
275 {
276 if (*p == '-')
277 flags^=XNegative; /* negate sign */
278 p++;
279 }
280 q=p;
281 if (x != (ssize_t *) NULL)
282 *x=((ssize_t) ceil(StringToDouble(p,&p)-0.5)) & 0x7fffffff;
283 if (p != q)
284 {
285 flags|=XValue;
286 if (((flags & XNegative) != 0) && (x != (ssize_t *) NULL))
287 *x=(-*x);
288 }
289 }
290 if ((*p == '+') || (*p == '-'))
291 {
292 /*
293 Parse y value.
294 */
295 while ((*p == '+') || (*p == '-'))
296 {
297 if (*p == '-')
298 flags^=YNegative; /* negate sign */
299 p++;
300 }
301 q=p;
302 if (y != (ssize_t *) NULL)
303 *y=((ssize_t) ceil(StringToDouble(p,&p)-0.5)) & 0x7fffffff;
304 if (p != q)
305 {
306 flags|=YValue;
307 if (((flags & YNegative) != 0) && (y != (ssize_t *) NULL))
308 *y=(-*y);
309 }
310 }
311 if ((flags & PercentValue) != 0)
312 {
313 if (((flags & SeparatorValue) == 0) && ((flags & HeightValue) == 0))
314 {
315 if ((height != (size_t *) NULL) && (width != (size_t *) NULL))
316 *height=(*width);
317 flags|=HeightValue;
318 }
319 if (((flags & SeparatorValue) != 0) && ((flags & WidthValue) == 0) &&
320 (height != (size_t *) NULL) && (width != (size_t *) NULL))
321 *width=(*height);
322 }
323 #if 0
324 /* Debugging Geometry */
325 (void) fprintf(stderr,"GetGeometry...\n");
326 (void) fprintf(stderr,"Input: %s\n",geometry);
327 (void) fprintf(stderr,"Flags: %c %c %s %s\n",
328 (flags & WidthValue) ? 'W' : ' ',(flags & HeightValue) ? 'H' : ' ',
329 (flags & XValue) ? ((flags & XNegative) ? "-X" : "+X") : " ",
330 (flags & YValue) ? ((flags & YNegative) ? "-Y" : "+Y") : " ");
331 (void) fprintf(stderr,"Geometry: %ldx%ld%+ld%+ld\n",(long) *width,(long)
332 *height,(long) *x,(long) *y);
333 #endif
334 return(flags);
335 }
336
337 /*
338 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
339 % %
340 % %
341 % %
342 % G e t P a g e G e o m e t r y %
343 % %
344 % %
345 % %
346 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
347 %
348 % GetPageGeometry() replaces any page mneumonic with the equivalent size in
349 % picas.
350 %
351 % The format of the GetPageGeometry method is:
352 %
353 % char *GetPageGeometry(const char *page_geometry)
354 %
355 % A description of each parameter follows.
356 %
357 % o page_geometry: Specifies a pointer to an array of characters. The
358 % string is either a Postscript page name (e.g. A4) or a postscript page
359 % geometry (e.g. 612x792+36+36).
360 %
361 */
GetPageGeometry(const char * page_geometry)362 MagickExport char *GetPageGeometry(const char *page_geometry)
363 {
364 #define MagickPageSize(name,geometry) { (name), sizeof(name)-1, (geometry) }
365
366 typedef struct _PageInfo
367 {
368 const char
369 name[12];
370
371 size_t
372 extent;
373
374 const char
375 geometry[10];
376 } PageInfo;
377
378 static const PageInfo
379 PageSizes[] =
380 {
381 MagickPageSize("4x6", "288x432"),
382 MagickPageSize("5x7", "360x504"),
383 MagickPageSize("7x9", "504x648"),
384 MagickPageSize("8x10", "576x720"),
385 MagickPageSize("9x11", "648x792"),
386 MagickPageSize("9x12", "648x864"),
387 MagickPageSize("10x13", "720x936"),
388 MagickPageSize("10x14", "720x1008"),
389 MagickPageSize("11x17", "792x1224"),
390 MagickPageSize("4A0", "4768x6741"),
391 MagickPageSize("2A0", "3370x4768"),
392 MagickPageSize("a0", "2384x3370"),
393 MagickPageSize("a1", "1684x2384"),
394 MagickPageSize("a2", "1191x1684"),
395 MagickPageSize("a3", "842x1191"),
396 MagickPageSize("a4", "595x842"),
397 MagickPageSize("a4small", "595x842"),
398 MagickPageSize("a5", "420x595"),
399 MagickPageSize("a6", "298x420"),
400 MagickPageSize("a7", "210x298"),
401 MagickPageSize("a8", "147x210"),
402 MagickPageSize("a9", "105x147"),
403 MagickPageSize("a10", "74x105"),
404 MagickPageSize("archa", "648x864"),
405 MagickPageSize("archb", "864x1296"),
406 MagickPageSize("archC", "1296x1728"),
407 MagickPageSize("archd", "1728x2592"),
408 MagickPageSize("arche", "2592x3456"),
409 MagickPageSize("b0", "2920x4127"),
410 MagickPageSize("b1", "2064x2920"),
411 MagickPageSize("b10", "91x127"),
412 MagickPageSize("b2", "1460x2064"),
413 MagickPageSize("b3", "1032x1460"),
414 MagickPageSize("b4", "729x1032"),
415 MagickPageSize("b5", "516x729"),
416 MagickPageSize("b6", "363x516"),
417 MagickPageSize("b7", "258x363"),
418 MagickPageSize("b8", "181x258"),
419 MagickPageSize("b9", "127x181"),
420 MagickPageSize("c0", "2599x3676"),
421 MagickPageSize("c1", "1837x2599"),
422 MagickPageSize("c2", "1298x1837"),
423 MagickPageSize("c3", "918x1296"),
424 MagickPageSize("c4", "649x918"),
425 MagickPageSize("c5", "459x649"),
426 MagickPageSize("c6", "323x459"),
427 MagickPageSize("c7", "230x323"),
428 MagickPageSize("csheet", "1224x1584"),
429 MagickPageSize("dsheet", "1584x2448"),
430 MagickPageSize("esheet", "2448x3168"),
431 MagickPageSize("executive", "540x720"),
432 MagickPageSize("flsa", "612x936"),
433 MagickPageSize("flse", "612x936"),
434 MagickPageSize("folio", "612x936"),
435 MagickPageSize("halfletter", "396x612"),
436 MagickPageSize("isob0", "2835x4008"),
437 MagickPageSize("isob1", "2004x2835"),
438 MagickPageSize("isob10", "88x125"),
439 MagickPageSize("isob2", "1417x2004"),
440 MagickPageSize("isob3", "1001x1417"),
441 MagickPageSize("isob4", "709x1001"),
442 MagickPageSize("isob5", "499x709"),
443 MagickPageSize("isob6", "354x499"),
444 MagickPageSize("isob7", "249x354"),
445 MagickPageSize("isob8", "176x249"),
446 MagickPageSize("isob9", "125x176"),
447 MagickPageSize("jisb0", "1030x1456"),
448 MagickPageSize("jisb1", "728x1030"),
449 MagickPageSize("jisb2", "515x728"),
450 MagickPageSize("jisb3", "364x515"),
451 MagickPageSize("jisb4", "257x364"),
452 MagickPageSize("jisb5", "182x257"),
453 MagickPageSize("jisb6", "128x182"),
454 MagickPageSize("ledger", "1224x792"),
455 MagickPageSize("legal", "612x1008"),
456 MagickPageSize("letter", "612x792"),
457 MagickPageSize("lettersmall", "612x792"),
458 MagickPageSize("monarch", "279x540"),
459 MagickPageSize("quarto", "610x780"),
460 MagickPageSize("statement", "396x612"),
461 MagickPageSize("tabloid", "792x1224")
462 };
463
464 char
465 page[MagickPathExtent];
466
467 ssize_t
468 i;
469
470 assert(page_geometry != (char *) NULL);
471 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
472 (void) CopyMagickString(page,page_geometry,MagickPathExtent);
473 for (i=0; i < (ssize_t) (sizeof(PageSizes)/sizeof(PageSizes[0])); i++)
474 {
475 int
476 status;
477
478 status=LocaleNCompare(PageSizes[i].name,page_geometry,PageSizes[i].extent);
479 if (status == 0)
480 {
481 MagickStatusType
482 flags;
483
484 RectangleInfo
485 geometry;
486
487 /*
488 Replace mneumonic with the equivalent size in dots-per-inch.
489 */
490 (void) FormatLocaleString(page,MagickPathExtent,"%s%.80s",
491 PageSizes[i].geometry,page_geometry+PageSizes[i].extent);
492 flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
493 &geometry.height);
494 if ((flags & GreaterValue) == 0)
495 (void) ConcatenateMagickString(page,">",MagickPathExtent);
496 break;
497 }
498 }
499 return(AcquireString(page));
500 }
501
502 /*
503 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
504 % %
505 % %
506 % %
507 % G r a v i t y A d j u s t G e o m e t r y %
508 % %
509 % %
510 % %
511 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
512 %
513 % GravityAdjustGeometry() adjusts the offset of a region with regard to the
514 % given: width, height and gravity; against which it is positioned.
515 %
516 % The region should also have an appropriate width and height to correctly
517 % set the right offset of the top left corner of the region.
518 %
519 % The format of the GravityAdjustGeometry method is:
520 %
521 % void GravityAdjustGeometry(const size_t width, const size_t height,
522 % const GravityType gravity,RectangleInfo *region);
523 %
524 % A description of each parameter follows:
525 %
526 % o width, height: the larger area the region is relative to
527 %
528 % o gravity: the edge/corner the current offset is relative to
529 %
530 % o region: The region requiring a offset adjustment relative to gravity
531 %
532 */
GravityAdjustGeometry(const size_t width,const size_t height,const GravityType gravity,RectangleInfo * region)533 MagickExport void GravityAdjustGeometry(const size_t width,
534 const size_t height,const GravityType gravity,RectangleInfo *region)
535 {
536 if (region->height == 0)
537 region->height=height;
538 if (region->width == 0)
539 region->width=width;
540 switch (gravity)
541 {
542 case NorthEastGravity:
543 case EastGravity:
544 case SouthEastGravity:
545 {
546 region->x=(ssize_t) (width-region->width-region->x);
547 break;
548 }
549 case NorthGravity:
550 case SouthGravity:
551 case CenterGravity:
552 {
553 region->x+=(ssize_t) (width/2-region->width/2);
554 break;
555 }
556 case ForgetGravity:
557 case NorthWestGravity:
558 case WestGravity:
559 case SouthWestGravity:
560 default:
561 break;
562 }
563 switch (gravity)
564 {
565 case SouthWestGravity:
566 case SouthGravity:
567 case SouthEastGravity:
568 {
569 region->y=(ssize_t) (height-region->height-region->y);
570 break;
571 }
572 case EastGravity:
573 case WestGravity:
574 case CenterGravity:
575 {
576 region->y+=(ssize_t) (height/2-region->height/2);
577 break;
578 }
579 case ForgetGravity:
580 case NorthWestGravity:
581 case NorthGravity:
582 case NorthEastGravity:
583 default:
584 break;
585 }
586 return;
587 }
588
589 /*
590 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
591 % %
592 % %
593 % %
594 + I s G e o m e t r y %
595 % %
596 % %
597 % %
598 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
599 %
600 % IsGeometry() returns MagickTrue if the geometry specification is valid.
601 % Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
602 %
603 % The format of the IsGeometry method is:
604 %
605 % MagickBooleanType IsGeometry(const char *geometry)
606 %
607 % A description of each parameter follows:
608 %
609 % o geometry: This string is the geometry specification.
610 %
611 */
IsGeometry(const char * geometry)612 MagickExport MagickBooleanType IsGeometry(const char *geometry)
613 {
614 GeometryInfo
615 geometry_info;
616
617 MagickStatusType
618 flags;
619
620 if (geometry == (const char *) NULL)
621 return(MagickFalse);
622 flags=ParseGeometry(geometry,&geometry_info);
623 return(flags != NoValue ? MagickTrue : MagickFalse);
624 }
625
626 /*
627 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
628 % %
629 % %
630 % %
631 + I s S c e n e G e o m e t r y %
632 % %
633 % %
634 % %
635 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
636 %
637 % IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
638 % specification (e.g. [1], [1-9], [1,7,4]).
639 %
640 % The format of the IsSceneGeometry method is:
641 %
642 % MagickBooleanType IsSceneGeometry(const char *geometry,
643 % const MagickBooleanType pedantic)
644 %
645 % A description of each parameter follows:
646 %
647 % o geometry: This string is the geometry specification.
648 %
649 % o pedantic: A value other than 0 invokes a more restrictive set of
650 % conditions for a valid specification (e.g. [1], [1-4], [4-1]).
651 %
652 */
IsSceneGeometry(const char * geometry,const MagickBooleanType pedantic)653 MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
654 const MagickBooleanType pedantic)
655 {
656 char
657 *p;
658
659 double
660 value;
661
662 if (geometry == (const char *) NULL)
663 return(MagickFalse);
664 p=(char *) geometry;
665 value=StringToDouble(geometry,&p);
666 if (IsNaN(value) != 0)
667 return(MagickFalse);
668 if (value > (double) MAGICK_SSIZE_MAX)
669 return(MagickFalse);
670 if (value < (double) MAGICK_SSIZE_MIN)
671 return(MagickFalse);
672 if (p == geometry)
673 return(MagickFalse);
674 if (strspn(geometry,"0123456789-, ") != strlen(geometry))
675 return(MagickFalse);
676 if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
677 return(MagickFalse);
678 return(MagickTrue);
679 }
680
681 /*
682 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
683 % %
684 % %
685 % %
686 % P a r s e A b s o l u t e G e o m e t r y %
687 % %
688 % %
689 % %
690 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
691 %
692 % ParseAbsoluteGeometry() returns a region as defined by the geometry string,
693 % without any modification by percentages or gravity.
694 %
695 % It currently just a wrapper around GetGeometry(), but may be expanded in
696 % the future to handle other positioning information.
697 %
698 % The format of the ParseAbsoluteGeometry method is:
699 %
700 % MagickStatusType ParseAbsoluteGeometry(const char *geometry,
701 % RectangleInfo *region_info)
702 %
703 % A description of each parameter follows:
704 %
705 % o geometry: The geometry string (e.g. "100x100+10+10").
706 %
707 % o region_info: the region as defined by the geometry string.
708 %
709 */
ParseAbsoluteGeometry(const char * geometry,RectangleInfo * region_info)710 MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
711 RectangleInfo *region_info)
712 {
713 MagickStatusType
714 flags;
715
716 flags=GetGeometry(geometry,®ion_info->x,®ion_info->y,
717 ®ion_info->width,®ion_info->height);
718 return(flags);
719 }
720
721 /*
722 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
723 % %
724 % %
725 % %
726 % P a r s e A f f i n e G e o m e t r y %
727 % %
728 % %
729 % %
730 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
731 %
732 % ParseAffineGeometry() returns an affine matrix as defined by a string of 4
733 % to 6 comma/space separated floating point values.
734 %
735 % The affine matrix determinant is checked for validity of the values.
736 %
737 % The format of the ParseAffineGeometry method is:
738 %
739 % MagickStatusType ParseAffineGeometry(const char *geometry,
740 % AffineMatrix *affine_matrix,ExceptionInfo *exception)
741 %
742 % A description of each parameter follows:
743 %
744 % o geometry: The geometry string (e.g. "1.0,0.0,0.0,1.0,3.2,1.2").
745 %
746 % o affine_matrix: the affine matrix as defined by the geometry string.
747 %
748 % o exception: return any errors or warnings in this structure.
749 %
750 */
ParseAffineGeometry(const char * geometry,AffineMatrix * affine_matrix,ExceptionInfo * exception)751 MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
752 AffineMatrix *affine_matrix,ExceptionInfo *exception)
753 {
754 char
755 token[MagickPathExtent];
756
757 const char
758 *p;
759
760 double
761 determinant;
762
763 MagickStatusType
764 flags;
765
766 ssize_t
767 i;
768
769 GetAffineMatrix(affine_matrix);
770 flags=NoValue;
771 p=(char *) geometry;
772 for (i=0; (*p != '\0') && (i < 6); i++)
773 {
774 (void) GetNextToken(p,&p,MagickPathExtent,token);
775 if (*token == ',')
776 (void) GetNextToken(p,&p,MagickPathExtent,token);
777 switch (i)
778 {
779 case 0:
780 {
781 affine_matrix->sx=StringToDouble(token,(char **) NULL);
782 break;
783 }
784 case 1:
785 {
786 affine_matrix->rx=StringToDouble(token,(char **) NULL);
787 break;
788 }
789 case 2:
790 {
791 affine_matrix->ry=StringToDouble(token,(char **) NULL);
792 break;
793 }
794 case 3:
795 {
796 affine_matrix->sy=StringToDouble(token,(char **) NULL);
797 break;
798 }
799 case 4:
800 {
801 affine_matrix->tx=StringToDouble(token,(char **) NULL);
802 flags|=XValue;
803 break;
804 }
805 case 5:
806 {
807 affine_matrix->ty=StringToDouble(token,(char **) NULL);
808 flags|=YValue;
809 break;
810 }
811 }
812 }
813 determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
814 affine_matrix->ry);
815 if (fabs(determinant) < MagickEpsilon)
816 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
817 "InvalidArgument","'%s' : 'Indeterminate Matrix'",geometry);
818 return(flags);
819 }
820
821 /*
822 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
823 % %
824 % %
825 % %
826 % P a r s e G e o m e t r y %
827 % %
828 % %
829 % %
830 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
831 %
832 % ParseGeometry() parses a geometry specification and returns the sigma,
833 % rho, xi, and psi values. It also returns flags that indicates which
834 % of the four values (sigma, rho, xi, psi) were located in the string, and
835 % whether the xi or pi values are negative.
836 %
837 % In addition, it reports if there are any of meta characters (%, !, <, >, @,
838 % and ^) flags present. It does not report the location of the percentage
839 % relative to the values.
840 %
841 % Values may also be separated by commas, colons, or slashes, and offsets.
842 % Offsets may be prefixed by multiple signs to make offset string
843 % substitutions easier to handle from shell scripts.
844 % For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
845 % offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
846 % offsets.
847 %
848 % The format of the ParseGeometry method is:
849 %
850 % MagickStatusType ParseGeometry(const char *geometry,
851 % GeometryInfo *geometry_info)
852 %
853 % A description of each parameter follows:
854 %
855 % o geometry: The geometry string (e.g. "100x100+10+10").
856 %
857 % o geometry_info: returns the parsed width/height/x/y in this structure.
858 %
859 */
ParseGeometry(const char * geometry,GeometryInfo * geometry_info)860 MagickExport MagickStatusType ParseGeometry(const char *geometry,
861 GeometryInfo *geometry_info)
862 {
863 char
864 *p,
865 pedantic_geometry[MagickPathExtent],
866 *q;
867
868 double
869 value;
870
871 GeometryInfo
872 coordinate;
873
874 int
875 c;
876
877 MagickStatusType
878 flags;
879
880 /*
881 Remove whitespaces meta characters from geometry specification.
882 */
883 assert(geometry_info != (GeometryInfo *) NULL);
884 (void) memset(geometry_info,0,sizeof(*geometry_info));
885 flags=NoValue;
886 if ((geometry == (char *) NULL) || (*geometry == '\0'))
887 return(flags);
888 if (strlen(geometry) >= (MagickPathExtent-1))
889 return(flags);
890 c=sscanf(geometry,"%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf",&coordinate.rho,
891 &coordinate.sigma,&coordinate.xi,&coordinate.psi);
892 if (c == 4)
893 {
894 /*
895 Special case: coordinate (e.g. 0,0 255,255).
896 */
897 geometry_info->rho=coordinate.rho;
898 geometry_info->sigma=coordinate.sigma;
899 geometry_info->xi=coordinate.xi;
900 geometry_info->psi=coordinate.psi;
901 flags|=RhoValue | SigmaValue | XiValue | PsiValue;
902 return(flags);
903 }
904 (void) CopyMagickString(pedantic_geometry,geometry,MagickPathExtent);
905 for (p=pedantic_geometry; *p != '\0'; )
906 {
907 c=(int) ((unsigned char) *p);
908 if (isspace((int) ((unsigned char) c)) != 0)
909 {
910 (void) CopyMagickString(p,p+1,MagickPathExtent);
911 continue;
912 }
913 switch (c)
914 {
915 case '%':
916 {
917 flags|=PercentValue;
918 (void) CopyMagickString(p,p+1,MagickPathExtent);
919 break;
920 }
921 case '!':
922 {
923 flags|=AspectValue;
924 (void) CopyMagickString(p,p+1,MagickPathExtent);
925 break;
926 }
927 case '<':
928 {
929 flags|=LessValue;
930 (void) CopyMagickString(p,p+1,MagickPathExtent);
931 break;
932 }
933 case '>':
934 {
935 flags|=GreaterValue;
936 (void) CopyMagickString(p,p+1,MagickPathExtent);
937 break;
938 }
939 case '^':
940 {
941 flags|=MinimumValue;
942 (void) CopyMagickString(p,p+1,MagickPathExtent);
943 break;
944 }
945 case '@':
946 {
947 flags|=AreaValue;
948 (void) CopyMagickString(p,p+1,MagickPathExtent);
949 break;
950 }
951 case '(':
952 {
953 if (*(p+1) == ')')
954 return(flags);
955 }
956 case ')':
957 {
958 (void) CopyMagickString(p,p+1,MagickPathExtent);
959 break;
960 }
961 case 'x':
962 case 'X':
963 {
964 flags|=SeparatorValue;
965 p++;
966 break;
967 }
968 case '-':
969 case '+':
970 case ',':
971 case '0':
972 case '1':
973 case '2':
974 case '3':
975 case '4':
976 case '5':
977 case '6':
978 case '7':
979 case '8':
980 case '9':
981 case '/':
982 case 215:
983 case 'e':
984 case 'E':
985 {
986 p++;
987 break;
988 }
989 case '.':
990 {
991 p++;
992 flags|=DecimalValue;
993 break;
994 }
995 case ':':
996 {
997 p++;
998 flags|=AspectRatioValue;
999 break;
1000 }
1001 default:
1002 return(NoValue);
1003 }
1004 }
1005 /*
1006 Parse rho, sigma, xi, psi, and optionally chi.
1007 */
1008 p=pedantic_geometry;
1009 if (*p == '\0')
1010 return(flags);
1011 q=p;
1012 value=StringToDouble(p,&q);
1013 if (LocaleNCompare(p,"0x",2) == 0)
1014 (void) strtol(p,&q,10);
1015 c=(int) ((unsigned char) *q);
1016 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ':') ||
1017 (*q == ',') || (*q == '/') || (*q =='\0'))
1018 {
1019 /*
1020 Parse rho.
1021 */
1022 q=p;
1023 if (LocaleNCompare(p,"0x",2) == 0)
1024 value=(double) strtol(p,&p,10);
1025 else
1026 value=StringToDouble(p,&p);
1027 if (p != q)
1028 {
1029 flags|=RhoValue;
1030 geometry_info->rho=value;
1031 }
1032 }
1033 q=p;
1034 c=(int) ((unsigned char) *p);
1035 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ':') || (*p == ',') ||
1036 (*p == '/'))
1037 {
1038 /*
1039 Parse sigma.
1040 */
1041 p++;
1042 while (isspace((int) ((unsigned char) *p)) != 0)
1043 p++;
1044 c=(int) ((unsigned char) *q);
1045 if (((c != 215) && (*q != 'x') && (*q != 'X') && (*q != ':')) ||
1046 ((*p != '+') && (*p != '-')))
1047 {
1048 q=p;
1049 value=StringToDouble(p,&p);
1050 if (p != q)
1051 {
1052 flags|=SigmaValue;
1053 geometry_info->sigma=value;
1054 }
1055 }
1056 }
1057 while (isspace((int) ((unsigned char) *p)) != 0)
1058 p++;
1059 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
1060 {
1061 /*
1062 Parse xi value.
1063 */
1064 if ((*p == ',') || (*p == '/') || (*p == ':') )
1065 p++;
1066 while ((*p == '+') || (*p == '-'))
1067 {
1068 if (*p == '-')
1069 flags^=XiNegative; /* negate sign */
1070 p++;
1071 }
1072 q=p;
1073 value=StringToDouble(p,&p);
1074 if (p != q)
1075 {
1076 flags|=XiValue;
1077 if ((flags & XiNegative) != 0)
1078 value=(-value);
1079 geometry_info->xi=value;
1080 }
1081 while (isspace((int) ((unsigned char) *p)) != 0)
1082 p++;
1083 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1084 (*p == ':'))
1085 {
1086 /*
1087 Parse psi value.
1088 */
1089 if ((*p == ',') || (*p == '/') || (*p == ':'))
1090 p++;
1091 while ((*p == '+') || (*p == '-'))
1092 {
1093 if (*p == '-')
1094 flags^=PsiNegative; /* negate sign */
1095 p++;
1096 }
1097 q=p;
1098 value=StringToDouble(p,&p);
1099 if (p != q)
1100 {
1101 flags|=PsiValue;
1102 if ((flags & PsiNegative) != 0)
1103 value=(-value);
1104 geometry_info->psi=value;
1105 }
1106 }
1107 while (isspace((int) ((unsigned char) *p)) != 0)
1108 p++;
1109 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1110 (*p == ':'))
1111 {
1112 /*
1113 Parse chi value.
1114 */
1115 if ((*p == ',') || (*p == '/') || (*p == ':'))
1116 p++;
1117 while ((*p == '+') || (*p == '-'))
1118 {
1119 if (*p == '-')
1120 flags^=ChiNegative; /* negate sign */
1121 p++;
1122 }
1123 q=p;
1124 value=StringToDouble(p,&p);
1125 if (p != q)
1126 {
1127 flags|=ChiValue;
1128 if ((flags & ChiNegative) != 0)
1129 value=(-value);
1130 geometry_info->chi=value;
1131 }
1132 }
1133 }
1134 if (strchr(pedantic_geometry,':') != (char *) NULL)
1135 {
1136 /*
1137 Normalize sampling factor (e.g. 4:2:2 => 2x1).
1138 */
1139 if ((flags & SigmaValue) != 0)
1140 geometry_info->rho*=PerceptibleReciprocal(geometry_info->sigma);
1141 geometry_info->sigma=1.0;
1142 if (((flags & XiValue) != 0) && (geometry_info->xi == 0.0))
1143 geometry_info->sigma=2.0;
1144 }
1145 if (((flags & RhoValue) != 0) && ((flags & SigmaValue) == 0) &&
1146 ((flags & XiValue) != 0) && ((flags & XiNegative) != 0))
1147 {
1148 if ((flags & PsiValue) == 0)
1149 {
1150 /*
1151 Support negative height values (e.g. 30x-20).
1152 */
1153 geometry_info->sigma=geometry_info->xi;
1154 geometry_info->xi=0.0;
1155 flags|=SigmaValue;
1156 flags&=(~XiValue);
1157 }
1158 else
1159 if ((flags & ChiValue) == 0)
1160 {
1161 /*
1162 Support negative height values (e.g. 30x-20+10).
1163 */
1164 geometry_info->sigma=geometry_info->xi;
1165 geometry_info->xi=geometry_info->psi;
1166 flags|=SigmaValue;
1167 flags|=XiValue;
1168 flags&=(~PsiValue);
1169 }
1170 else
1171 {
1172 /*
1173 Support negative height values (e.g. 30x-20+10+10).
1174 */
1175 geometry_info->sigma=geometry_info->xi;
1176 geometry_info->xi=geometry_info->psi;
1177 geometry_info->psi=geometry_info->chi;
1178 flags|=SigmaValue;
1179 flags|=XiValue;
1180 flags|=PsiValue;
1181 flags&=(~ChiValue);
1182 }
1183 }
1184 if ((flags & PercentValue) != 0)
1185 {
1186 if (((flags & SeparatorValue) == 0) && ((flags & SigmaValue) == 0))
1187 geometry_info->sigma=geometry_info->rho;
1188 if (((flags & SeparatorValue) != 0) && ((flags & RhoValue) == 0))
1189 geometry_info->rho=geometry_info->sigma;
1190 }
1191 #if 0
1192 /* Debugging Geometry */
1193 (void) fprintf(stderr,"ParseGeometry...\n");
1194 (void) fprintf(stderr,"Flags: %c %c %s %s %s\n",
1195 (flags & RhoValue) ? 'W' : ' ',(flags & SigmaValue) ? 'H' : ' ',
1196 (flags & XiValue) ? ((flags & XiNegative) ? "-X" : "+X") : " ",
1197 (flags & PsiValue) ? ((flags & PsiNegative) ? "-Y" : "+Y") : " ",
1198 (flags & ChiValue) ? ((flags & ChiNegative) ? "-Z" : "+Z") : " ");
1199 (void) fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",geometry_info->rho,
1200 geometry_info->sigma,geometry_info->xi,geometry_info->psi,
1201 geometry_info->chi);
1202 #endif
1203 return(flags);
1204 }
1205
1206 /*
1207 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1208 % %
1209 % %
1210 % %
1211 % P a r s e G r a v i t y G e o m e t r y %
1212 % %
1213 % %
1214 % %
1215 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1216 %
1217 % ParseGravityGeometry() returns a region as defined by the geometry string
1218 % with respect to the given image page (canvas) dimensions and the images
1219 % gravity setting.
1220 %
1221 % This is typically used for specifing a area within a given image for
1222 % cropping images to a smaller size, chopping out rows and or columns, or
1223 % resizing and positioning overlay images.
1224 %
1225 % Percentages are relative to image size and not page size, and are set to
1226 % nearest integer (pixel) size.
1227 %
1228 % The format of the ParseGravityGeometry method is:
1229 %
1230 % MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1231 % RectangeInfo *region_info,ExceptionInfo *exception)
1232 %
1233 % A description of each parameter follows:
1234 %
1235 % o geometry: The geometry string (e.g. "100x100+10+10").
1236 %
1237 % o region_info: the region as defined by the geometry string with respect
1238 % to the image dimensions and its gravity.
1239 %
1240 % o exception: return any errors or warnings in this structure.
1241 %
1242 */
ParseGravityGeometry(const Image * image,const char * geometry,RectangleInfo * region_info,ExceptionInfo * exception)1243 MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1244 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1245 {
1246 MagickStatusType
1247 flags;
1248
1249 size_t
1250 height,
1251 width;
1252
1253 SetGeometry(image,region_info);
1254 if (image->page.width != 0)
1255 region_info->width=image->page.width;
1256 if (image->page.height != 0)
1257 region_info->height=image->page.height;
1258 flags=ParseAbsoluteGeometry(geometry,region_info);
1259 if (flags == NoValue)
1260 {
1261 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1262 "InvalidGeometry","`%s'",geometry);
1263 return(flags);
1264 }
1265 if ((flags & PercentValue) != 0)
1266 {
1267 GeometryInfo
1268 geometry_info;
1269
1270 MagickStatusType
1271 status;
1272
1273 PointInfo
1274 scale;
1275
1276 /*
1277 Geometry is a percentage of the image size, not canvas size
1278 */
1279 if (image->gravity != UndefinedGravity)
1280 flags|=XValue | YValue;
1281 status=ParseGeometry(geometry,&geometry_info);
1282 scale.x=geometry_info.rho;
1283 if ((status & RhoValue) == 0)
1284 scale.x=100.0;
1285 scale.y=geometry_info.sigma;
1286 if ((status & SigmaValue) == 0)
1287 scale.y=scale.x;
1288 region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1289 region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
1290 }
1291 if ((flags & AspectRatioValue) != 0)
1292 {
1293 double
1294 geometry_ratio,
1295 image_ratio;
1296
1297 GeometryInfo
1298 geometry_info;
1299
1300 /*
1301 Geometry is a relative to image size and aspect ratio.
1302 */
1303 if (image->gravity != UndefinedGravity)
1304 flags|=XValue | YValue;
1305 (void) ParseGeometry(geometry,&geometry_info);
1306 geometry_ratio=geometry_info.rho;
1307 image_ratio=(double) image->columns/image->rows;
1308 if (geometry_ratio >= image_ratio)
1309 {
1310 region_info->width=image->columns;
1311 region_info->height=(size_t) floor((double) (image->rows*image_ratio/
1312 geometry_ratio)+0.5);
1313 }
1314 else
1315 {
1316 region_info->width=(size_t) floor((double) (image->columns*
1317 geometry_ratio/image_ratio)+0.5);
1318 region_info->height=image->rows;
1319 }
1320 }
1321 /*
1322 Adjust offset according to gravity setting.
1323 */
1324 width=region_info->width;
1325 height=region_info->height;
1326 if (width == 0)
1327 region_info->width=image->page.width | image->columns;
1328 if (height == 0)
1329 region_info->height=image->page.height | image->rows;
1330 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1331 region_info->width=width;
1332 region_info->height=height;
1333 return(flags);
1334 }
1335
1336 /*
1337 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1338 % %
1339 % %
1340 % %
1341 + P a r s e M e t a G e o m e t r y %
1342 % %
1343 % %
1344 % %
1345 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1346 %
1347 % ParseMetaGeometry() is similar to GetGeometry() except the returned
1348 % geometry is modified as determined by the meta characters: %, !, <, >, @,
1349 % :, and ^ in relation to image resizing.
1350 %
1351 % Final image dimensions are adjusted so as to preserve the aspect ratio as
1352 % much as possible, while generating a integer (pixel) size, and fitting the
1353 % image within the specified geometry width and height.
1354 %
1355 % Flags are interpreted...
1356 % % geometry size is given percentage of original width and height given
1357 % ! do not try to preserve aspect ratio
1358 % < only enlarge images smaller that geometry
1359 % > only shrink images larger than geometry
1360 % @ fit image to contain at most this many pixels
1361 % : width and height denotes an aspect ratio
1362 % ^ contain the given geometry given, (minimal dimensions given)
1363 %
1364 % The format of the ParseMetaGeometry method is:
1365 %
1366 % MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1367 % ssize_t *y, size_t *width,size_t *height)
1368 %
1369 % A description of each parameter follows:
1370 %
1371 % o geometry: The geometry string (e.g. "100x100+10+10").
1372 %
1373 % o x,y: The x and y offset, set according to the geometry specification.
1374 %
1375 % o width,height: The width and height of original image, modified by
1376 % the given geometry specification.
1377 %
1378 */
ParseMetaGeometry(const char * geometry,ssize_t * x,ssize_t * y,size_t * width,size_t * height)1379 MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1380 ssize_t *y,size_t *width,size_t *height)
1381 {
1382 GeometryInfo
1383 geometry_info;
1384
1385 MagickStatusType
1386 flags;
1387
1388 size_t
1389 former_height,
1390 former_width;
1391
1392 /*
1393 Ensure the image geometry is valid.
1394 */
1395 assert(x != (ssize_t *) NULL);
1396 assert(y != (ssize_t *) NULL);
1397 assert(width != (size_t *) NULL);
1398 assert(height != (size_t *) NULL);
1399 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1400 return(NoValue);
1401 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1402 /*
1403 Parse geometry using GetGeometry.
1404 */
1405 SetGeometryInfo(&geometry_info);
1406 former_width=(*width);
1407 former_height=(*height);
1408 flags=GetGeometry(geometry,x,y,width,height);
1409 if ((flags & PercentValue) != 0)
1410 {
1411 MagickStatusType
1412 percent_flags;
1413
1414 PointInfo
1415 scale;
1416
1417 /*
1418 Geometry is a percentage of the image size.
1419 */
1420 percent_flags=ParseGeometry(geometry,&geometry_info);
1421 scale.x=geometry_info.rho;
1422 if ((percent_flags & RhoValue) == 0)
1423 scale.x=100.0;
1424 scale.y=geometry_info.sigma;
1425 if ((percent_flags & SigmaValue) == 0)
1426 scale.y=scale.x;
1427 *width=(size_t) floor(scale.x*former_width/100.0+0.5);
1428 *height=(size_t) floor(scale.y*former_height/100.0+0.5);
1429 former_width=(*width);
1430 former_height=(*height);
1431 }
1432 if ((flags & AspectRatioValue) != 0)
1433 {
1434 double
1435 geometry_ratio,
1436 image_ratio;
1437
1438 /*
1439 Geometry is a relative to image size and aspect ratio.
1440 */
1441 (void) ParseGeometry(geometry,&geometry_info);
1442 geometry_ratio=geometry_info.rho;
1443 image_ratio=(double) former_width*
1444 PerceptibleReciprocal((double) former_height);
1445 if (geometry_ratio >= image_ratio)
1446 {
1447 *width=former_width;
1448 *height=(size_t) floor((double) (PerceptibleReciprocal(
1449 geometry_ratio)*former_height*image_ratio)+0.5);
1450 }
1451 else
1452 {
1453 *width=(size_t) floor((double) (PerceptibleReciprocal(
1454 image_ratio)*former_width*geometry_ratio)+0.5);
1455 *height=former_height;
1456 }
1457 former_width=(*width);
1458 former_height=(*height);
1459 }
1460 if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1461 (*height == former_height)))
1462 {
1463 if ((flags & RhoValue) == 0)
1464 *width=former_width;
1465 if ((flags & SigmaValue) == 0)
1466 *height=former_height;
1467 }
1468 else
1469 {
1470 double
1471 scale_factor;
1472
1473 /*
1474 Respect aspect ratio of the image.
1475 */
1476 if ((former_width == 0) || (former_height == 0))
1477 scale_factor=1.0;
1478 else
1479 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1480 {
1481 scale_factor=(double) *width/(double) former_width;
1482 if ((flags & MinimumValue) == 0)
1483 {
1484 if (scale_factor > ((double) *height/(double) former_height))
1485 scale_factor=(double) *height/(double) former_height;
1486 }
1487 else
1488 if (scale_factor < ((double) *height/(double) former_height))
1489 scale_factor=(double) *height/(double) former_height;
1490 }
1491 else
1492 if ((flags & RhoValue) != 0)
1493 {
1494 scale_factor=(double) *width/(double) former_width;
1495 if (((flags & MinimumValue) != 0) &&
1496 (scale_factor < ((double) *width/(double) former_height)))
1497 scale_factor=(double) *width/(double) former_height;
1498 }
1499 else
1500 {
1501 scale_factor=(double) *height/(double) former_height;
1502 if (((flags & MinimumValue) != 0) &&
1503 (scale_factor < ((double) *height/(double) former_width)))
1504 scale_factor=(double) *height/(double) former_width;
1505 }
1506 *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1507 *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
1508 }
1509 if ((flags & GreaterValue) != 0)
1510 {
1511 if (former_width < *width)
1512 *width=former_width;
1513 if (former_height < *height)
1514 *height=former_height;
1515 }
1516 if ((flags & LessValue) != 0)
1517 {
1518 if (former_width > *width)
1519 *width=former_width;
1520 if (former_height > *height)
1521 *height=former_height;
1522 }
1523 if ((flags & AreaValue) != 0)
1524 {
1525 double
1526 area,
1527 distance;
1528
1529 PointInfo
1530 scale;
1531
1532 /*
1533 Geometry is a maximum area in pixels.
1534 */
1535 (void) ParseGeometry(geometry,&geometry_info);
1536 area=geometry_info.rho+sqrt(MagickEpsilon);
1537 distance=sqrt((double) former_width*former_height);
1538 scale.x=(double) former_width*PerceptibleReciprocal(distance*
1539 PerceptibleReciprocal(sqrt(area)));
1540 scale.y=(double) former_height*PerceptibleReciprocal(distance*
1541 PerceptibleReciprocal(sqrt(area)));
1542 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1543 {
1544 *width=(unsigned long) (former_width*PerceptibleReciprocal(
1545 distance*PerceptibleReciprocal(sqrt(area))));
1546 *height=(unsigned long) (former_height*PerceptibleReciprocal(
1547 distance*PerceptibleReciprocal(sqrt(area))));
1548 }
1549 former_width=(*width);
1550 former_height=(*height);
1551 }
1552 return(flags);
1553 }
1554
1555 /*
1556 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1557 % %
1558 % %
1559 % %
1560 % P a r s e P a g e G e o m e t r y %
1561 % %
1562 % %
1563 % %
1564 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1565 %
1566 % ParsePageGeometry() returns a region as defined by the geometry string with
1567 % respect to the image page (canvas) dimensions.
1568 %
1569 % WARNING: Percentage dimensions remain relative to the actual image
1570 % dimensions, and not canvas dimensions.
1571 %
1572 % The format of the ParsePageGeometry method is:
1573 %
1574 % MagickStatusType ParsePageGeometry(const Image *image,
1575 % const char *geometry,RectangeInfo *region_info,
1576 % ExceptionInfo *exception)
1577 %
1578 % A description of each parameter follows:
1579 %
1580 % o geometry: The geometry string (e.g. "100x100+10+10").
1581 %
1582 % o region_info: the region as defined by the geometry string with
1583 % respect to the image and its gravity.
1584 %
1585 % o exception: return any errors or warnings in this structure.
1586 %
1587 */
ParsePageGeometry(const Image * image,const char * geometry,RectangleInfo * region_info,ExceptionInfo * exception)1588 MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1589 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1590 {
1591 MagickStatusType
1592 flags;
1593
1594 SetGeometry(image,region_info);
1595 if (image->page.width != 0)
1596 region_info->width=image->page.width;
1597 if (image->page.height != 0)
1598 region_info->height=image->page.height;
1599 flags=ParseAbsoluteGeometry(geometry,region_info);
1600 if (flags == NoValue)
1601 {
1602 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1603 "InvalidGeometry","`%s'",geometry);
1604 return(flags);
1605 }
1606 if ((flags & PercentValue) != 0)
1607 {
1608 region_info->width=image->columns;
1609 region_info->height=image->rows;
1610 }
1611 flags=ParseMetaGeometry(geometry,®ion_info->x,®ion_info->y,
1612 ®ion_info->width,®ion_info->height);
1613 if ((((flags & WidthValue) != 0) || ((flags & HeightValue) != 0)) &&
1614 (((flags & PercentValue) != 0) || ((flags & SeparatorValue) == 0)))
1615 {
1616 if ((flags & WidthValue) == 0)
1617 region_info->width=region_info->height;
1618 if ((flags & HeightValue) == 0)
1619 region_info->height=region_info->width;
1620 }
1621 return(flags);
1622 }
1623
1624 /*
1625 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1626 % %
1627 % %
1628 % %
1629 % P a r s e R e g i o n G e o m e t r y %
1630 % %
1631 % %
1632 % %
1633 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1634 %
1635 % ParseRegionGeometry() returns a region as defined by the geometry string
1636 % with respect to the image dimensions and aspect ratio.
1637 %
1638 % This is basically a wrapper around ParseMetaGeometry. This is typically
1639 % used to parse a geometry string to work out the final integer dimensions
1640 % for image resizing.
1641 %
1642 % The format of the ParseRegionGeometry method is:
1643 %
1644 % MagickStatusType ParseRegionGeometry(const Image *image,
1645 % const char *geometry,RectangeInfo *region_info,
1646 % ExceptionInfo *exception)
1647 %
1648 % A description of each parameter follows:
1649 %
1650 % o geometry: The geometry string (e.g. "100x100+10+10").
1651 %
1652 % o region_info: the region as defined by the geometry string.
1653 %
1654 % o exception: return any errors or warnings in this structure.
1655 %
1656 */
ParseRegionGeometry(const Image * image,const char * geometry,RectangleInfo * region_info,ExceptionInfo * exception)1657 MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1658 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1659 {
1660 MagickStatusType
1661 flags;
1662
1663 SetGeometry(image,region_info);
1664 flags=ParseMetaGeometry(geometry,®ion_info->x,®ion_info->y,
1665 ®ion_info->width,®ion_info->height);
1666 if (flags == NoValue)
1667 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1668 "InvalidGeometry","`%s'",geometry);
1669 return(flags);
1670 }
1671
1672 /*
1673 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1674 % %
1675 % %
1676 % %
1677 % S e t G e o m e t r y %
1678 % %
1679 % %
1680 % %
1681 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1682 %
1683 % SetGeometry() sets the geometry to its default values.
1684 %
1685 % The format of the SetGeometry method is:
1686 %
1687 % SetGeometry(const Image *image,RectangleInfo *geometry)
1688 %
1689 % A description of each parameter follows:
1690 %
1691 % o image: the image.
1692 %
1693 % o geometry: the geometry.
1694 %
1695 */
SetGeometry(const Image * image,RectangleInfo * geometry)1696 MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1697 {
1698 assert(image != (Image *) NULL);
1699 assert(image->signature == MagickCoreSignature);
1700 if (image->debug != MagickFalse)
1701 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1702 assert(geometry != (RectangleInfo *) NULL);
1703 (void) memset(geometry,0,sizeof(*geometry));
1704 geometry->width=image->columns;
1705 geometry->height=image->rows;
1706 }
1707
1708 /*
1709 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1710 % %
1711 % %
1712 % %
1713 % S e t G e o m e t r y I n f o %
1714 % %
1715 % %
1716 % %
1717 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1718 %
1719 % SetGeometryInfo sets the GeometryInfo structure to its default values.
1720 %
1721 % The format of the SetGeometryInfo method is:
1722 %
1723 % SetGeometryInfo(GeometryInfo *geometry_info)
1724 %
1725 % A description of each parameter follows:
1726 %
1727 % o geometry_info: the geometry info structure.
1728 %
1729 */
SetGeometryInfo(GeometryInfo * geometry_info)1730 MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1731 {
1732 assert(geometry_info != (GeometryInfo *) NULL);
1733 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1734 (void) memset(geometry_info,0,sizeof(*geometry_info));
1735 }
1736