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