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