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