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