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