MagickCore  6.9.12-92
Convert, Edit, Or Compose Bitmap Images
geometry.c
1 /*
2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3 % %
4 % %
5 % %
6 % GGGG EEEEE OOO M M EEEEE TTTTT RRRR Y Y %
7 % G E O O MM MM E T R R Y Y %
8 % G GG EEE O O M M M EEE T RRRR Y %
9 % G G E O O M M E T R R Y %
10 % GGGG EEEEE OOO M M EEEEE T R R Y %
11 % %
12 % %
13 % MagickCore Geometry Methods %
14 % %
15 % Software Design %
16 % Cristy %
17 % 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=CastDoubleToUnsigned(StringToDouble(p,&p)+0.5);
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=CastDoubleToUnsigned(StringToDouble(p,&p)+0.5);
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=CastDoubleToLong(StringToDouble(p,&p));
397  if (p != q)
398  {
399  flags|=XValue;
400  if (((flags & XNegative) != 0) && (x != (ssize_t *) NULL))
401  *x=CastDoubleToLong(-1.0**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=CastDoubleToLong(StringToDouble(p,&p));
418  if (p != q)
419  {
420  flags|=YValue;
421  if (((flags & YNegative) != 0) && (y != (ssize_t *) NULL))
422  *y=CastDoubleToLong(-1.0**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; *Pagesizes[i].name != '\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=CastDoubleToLong((double) width-region->width-region->x);
564  break;
565  }
566  case NorthGravity:
567  case SouthGravity:
568  case CenterGravity:
569  case StaticGravity:
570  {
571  region->x+=CastDoubleToLong(width/2.0-region->width/2.0);
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=CastDoubleToLong((double) height-region->height-region->y);
588  break;
589  }
590  case EastGravity:
591  case WestGravity:
592  case CenterGravity:
593  case StaticGravity:
594  {
595  region->y+=CastDoubleToLong(height/2.0-region->height/2.0);
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 *magick_unused(exception))
726 {
727 #define MaxMagickSpaces ((int) sizeof(Pagesizes[0].name))
728 
729  const char
730  *spacer = " ";
731 
732  ssize_t
733  i;
734 
735  magick_unreferenced(exception);
736  if (file == (FILE *) NULL)
737  file=stdout;
738  (void) FormatLocaleFile(file,"\nPagesize Geometry \n");
739  (void) FormatLocaleFile(file,"---------------------\n");
740  for (i=0; *Pagesizes[i].name != '\0'; i++)
741  (void) FormatLocaleFile(file,"%s%.*s%s\n",Pagesizes[i].name,
742  MaxMagickSpaces-(int) Pagesizes[i].extent,spacer,Pagesizes[i].geometry);
743  return(MagickTrue);
744 }
745 ␌
746 /*
747 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
748 % %
749 % %
750 % %
751 % P a r s e A b s o l u t e G e o m e t r y %
752 % %
753 % %
754 % %
755 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
756 %
757 % ParseAbsoluteGeometry() returns a region as defined by the geometry string,
758 % without any modification by percentages or gravity.
759 %
760 % It currently just a wrapper around GetGeometry(), but may be expanded in
761 % the future to handle other positioning information.
762 %
763 % The format of the ParseAbsoluteGeometry method is:
764 %
765 % MagickStatusType ParseAbsoluteGeometry(const char *geometry,
766 % RectangleInfo *region_info)
767 %
768 % A description of each parameter follows:
769 %
770 % o geometry: The geometry string (e.g. "100x100+10+10").
771 %
772 % o region_info: the region as defined by the geometry string.
773 %
774 */
775 MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
776  RectangleInfo *region_info)
777 {
778  MagickStatusType
779  flags;
780 
781  flags=GetGeometry(geometry,&region_info->x,&region_info->y,
782  &region_info->width,&region_info->height);
783  return(flags);
784 }
785 ␌
786 /*
787 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
788 % %
789 % %
790 % %
791 % P a r s e A f f i n e G e o m e t r y %
792 % %
793 % %
794 % %
795 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
796 %
797 % ParseAffineGeometry() returns an affine matrix as defined by a string of 4
798 % to 6 comma/space separated floating point values.
799 %
800 % The affine matrix determinant is checked for validity of the values.
801 %
802 % The format of the ParseAffineGeometry method is:
803 %
804 % MagickStatusType ParseAffineGeometry(const char *geometry,
805 % AffineMatrix *affine_matrix,ExceptionInfo *exception)
806 %
807 % A description of each parameter follows:
808 %
809 % o geometry: The geometry string (e.g. "1.0,0.0,0.0,1.0,3.2,1.2").
810 %
811 % o affine_matrix: the affine matrix as defined by the geometry string.
812 %
813 % o exception: return any errors or warnings in this structure.
814 %
815 */
816 MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
817  AffineMatrix *affine_matrix,ExceptionInfo *exception)
818 {
819  char
820  token[MaxTextExtent];
821 
822  const char
823  *p;
824 
825  double
826  determinant;
827 
828  MagickStatusType
829  flags;
830 
831  ssize_t
832  i;
833 
834  GetAffineMatrix(affine_matrix);
835  flags=NoValue;
836  p=(char *) geometry;
837  for (i=0; (*p != '\0') && (i < 6); i++)
838  {
839  (void) GetNextToken(p,&p,MaxTextExtent,token);
840  if (*token == ',')
841  (void) GetNextToken(p,&p,MaxTextExtent,token);
842  switch (i)
843  {
844  case 0:
845  {
846  affine_matrix->sx=StringToDouble(token,(char **) NULL);
847  break;
848  }
849  case 1:
850  {
851  affine_matrix->rx=StringToDouble(token,(char **) NULL);
852  break;
853  }
854  case 2:
855  {
856  affine_matrix->ry=StringToDouble(token,(char **) NULL);
857  break;
858  }
859  case 3:
860  {
861  affine_matrix->sy=StringToDouble(token,(char **) NULL);
862  break;
863  }
864  case 4:
865  {
866  affine_matrix->tx=StringToDouble(token,(char **) NULL);
867  flags|=XValue;
868  break;
869  }
870  case 5:
871  {
872  affine_matrix->ty=StringToDouble(token,(char **) NULL);
873  flags|=YValue;
874  break;
875  }
876  }
877  }
878  determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
879  affine_matrix->ry);
880  if (fabs(determinant) < MagickEpsilon)
881  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
882  "InvalidArgument","'%s' : 'Indeterminate Matrix'",geometry);
883  return(flags);
884 }
885 ␌
886 /*
887 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
888 % %
889 % %
890 % %
891 % P a r s e G e o m e t r y %
892 % %
893 % %
894 % %
895 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
896 %
897 % ParseGeometry() parses a geometry specification and returns the sigma,
898 % rho, xi, and psi values. It also returns flags that indicates which
899 % of the four values (sigma, rho, xi, psi) were located in the string, and
900 % whether the xi or pi values are negative.
901 %
902 % In addition, it reports if there are any of meta characters (%, !, <, >, @,
903 % and ^) flags present. It does not report the location of the percentage
904 % relative to the values.
905 %
906 % Values may also be separated by commas, colons, or slashes, and offsets.
907 $ Chroma subsampling definitions have to be in the form of a:b:c. Offsets may
908 % be prefixed by multiple signs to make offset string substitutions easier to
909 % handle from shell scripts. For example: "-10-10", "-+10-+10", or "+-10+-10"
910 % will generate negative offsets, while "+10+10", "++10++10", or "--10--10"
911 % will generate positive offsets.
912 %
913 % The format of the ParseGeometry method is:
914 %
915 % MagickStatusType ParseGeometry(const char *geometry,
916 % GeometryInfo *geometry_info)
917 %
918 % A description of each parameter follows:
919 %
920 % o geometry: The geometry string (e.g. "100x100+10+10").
921 %
922 % o geometry_info: returns the parsed width/height/x/y in this structure.
923 %
924 */
925 MagickExport MagickStatusType ParseGeometry(const char *geometry,
926  GeometryInfo *geometry_info)
927 {
928  char
929  *p,
930  pedantic_geometry[MaxTextExtent],
931  *q;
932 
933  double
934  value;
935 
936  int
937  c;
938 
939  MagickStatusType
940  flags;
941 
942  /*
943  Remove whitespaces meta characters from geometry specification.
944  */
945  assert(geometry_info != (GeometryInfo *) NULL);
946  (void) memset(geometry_info,0,sizeof(*geometry_info));
947  flags=NoValue;
948  if ((geometry == (char *) NULL) || (*geometry == '\0'))
949  return(flags);
950  if (strlen(geometry) >= (MaxTextExtent-1))
951  return(flags);
952  (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
953  for (p=pedantic_geometry; *p != '\0'; )
954  {
955  c=(int) ((unsigned char) *p);
956  if (isspace((int) ((unsigned char) c)) != 0)
957  {
958  (void) CopyMagickString(p,p+1,MaxTextExtent);
959  continue;
960  }
961  switch (c)
962  {
963  case '%':
964  {
965  flags|=PercentValue;
966  (void) CopyMagickString(p,p+1,MaxTextExtent);
967  break;
968  }
969  case '!':
970  {
971  flags|=AspectValue;
972  (void) CopyMagickString(p,p+1,MaxTextExtent);
973  break;
974  }
975  case '<':
976  {
977  flags|=LessValue;
978  (void) CopyMagickString(p,p+1,MaxTextExtent);
979  break;
980  }
981  case '>':
982  {
983  flags|=GreaterValue;
984  (void) CopyMagickString(p,p+1,MaxTextExtent);
985  break;
986  }
987  case '^':
988  {
989  flags|=MinimumValue;
990  (void) CopyMagickString(p,p+1,MaxTextExtent);
991  break;
992  }
993  case '@':
994  {
995  flags|=AreaValue;
996  (void) CopyMagickString(p,p+1,MaxTextExtent);
997  break;
998  }
999  case '(':
1000  {
1001  if (*(p+1) == ')')
1002  return(flags);
1003  (void) CopyMagickString(p,p+1,MaxTextExtent);
1004  break;
1005  }
1006  case ')':
1007  {
1008  (void) CopyMagickString(p,p+1,MaxTextExtent);
1009  break;
1010  }
1011  case 'x':
1012  case 'X':
1013  {
1014  flags|=SeparatorValue;
1015  p++;
1016  break;
1017  }
1018  case '-':
1019  case '+':
1020  case ',':
1021  case '0':
1022  case '1':
1023  case '2':
1024  case '3':
1025  case '4':
1026  case '5':
1027  case '6':
1028  case '7':
1029  case '8':
1030  case '9':
1031  case '/':
1032  case 215:
1033  case 'e':
1034  case 'E':
1035  {
1036  p++;
1037  break;
1038  }
1039  case '.':
1040  {
1041  p++;
1042  flags|=DecimalValue;
1043  break;
1044  }
1045  case ':':
1046  {
1047  p++;
1048  flags|=AspectRatioValue;
1049  break;
1050  }
1051  default:
1052  return(NoValue);
1053  }
1054  }
1055  /*
1056  Parse rho, sigma, xi, psi, and optionally chi.
1057  */
1058  p=pedantic_geometry;
1059  if (*p == '\0')
1060  return(flags);
1061  q=p;
1062  value=StringToDouble(p,&q);
1063  if (LocaleNCompare(p,"0x",2) == 0)
1064  (void) strtol(p,&q,10);
1065  c=(int) ((unsigned char) *q);
1066  if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ':') ||
1067  (*q == ',') || (*q == '/') || (*q =='\0'))
1068  {
1069  /*
1070  Parse rho.
1071  */
1072  q=p;
1073  if (LocaleNCompare(p,"0x",2) == 0)
1074  value=(double) strtol(p,&p,10);
1075  else
1076  value=StringToDouble(p,&p);
1077  if (p != q)
1078  {
1079  flags|=RhoValue;
1080  geometry_info->rho=value;
1081  }
1082  }
1083  q=p;
1084  c=(int) ((unsigned char) *p);
1085  if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ':') || (*p == ',') ||
1086  (*p == '/'))
1087  {
1088  /*
1089  Parse sigma.
1090  */
1091  p++;
1092  while (isspace((int) ((unsigned char) *p)) != 0)
1093  p++;
1094  c=(int) ((unsigned char) *q);
1095  if (((c != 215) && (*q != 'x') && (*q != 'X') && (*q != ':')) ||
1096  ((*p != '+') && (*p != '-')))
1097  {
1098  q=p;
1099  value=StringToDouble(p,&p);
1100  if (p != q)
1101  {
1102  flags|=SigmaValue;
1103  geometry_info->sigma=value;
1104  }
1105  }
1106  }
1107  while (isspace((int) ((unsigned char) *p)) != 0)
1108  p++;
1109  if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
1110  {
1111  /*
1112  Parse xi value.
1113  */
1114  if ((*p == ',') || (*p == '/') || (*p == ':') )
1115  p++;
1116  while ((*p == '+') || (*p == '-'))
1117  {
1118  if (*p == '-')
1119  flags^=XiNegative; /* negate sign */
1120  p++;
1121  }
1122  q=p;
1123  value=StringToDouble(p,&p);
1124  if (p != q)
1125  {
1126  flags|=XiValue;
1127  if ((flags & XiNegative) != 0)
1128  value=(-value);
1129  geometry_info->xi=value;
1130  }
1131  while (isspace((int) ((unsigned char) *p)) != 0)
1132  p++;
1133  if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1134  (*p == ':'))
1135  {
1136  /*
1137  Parse psi value.
1138  */
1139  if ((*p == ',') || (*p == '/') || (*p == ':'))
1140  p++;
1141  while ((*p == '+') || (*p == '-'))
1142  {
1143  if (*p == '-')
1144  flags^=PsiNegative; /* negate sign */
1145  p++;
1146  }
1147  q=p;
1148  value=StringToDouble(p,&p);
1149  if (p != q)
1150  {
1151  flags|=PsiValue;
1152  if ((flags & PsiNegative) != 0)
1153  value=(-value);
1154  geometry_info->psi=value;
1155  }
1156  }
1157  while (isspace((int) ((unsigned char) *p)) != 0)
1158  p++;
1159  if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1160  (*p == ':'))
1161  {
1162  /*
1163  Parse chi value.
1164  */
1165  if ((*p == ',') || (*p == '/') || (*p == ':'))
1166  p++;
1167  while ((*p == '+') || (*p == '-'))
1168  {
1169  if (*p == '-')
1170  flags^=ChiNegative; /* negate sign */
1171  p++;
1172  }
1173  q=p;
1174  value=StringToDouble(p,&p);
1175  if (p != q)
1176  {
1177  flags|=ChiValue;
1178  if ((flags & ChiNegative) != 0)
1179  value=(-value);
1180  geometry_info->chi=value;
1181  }
1182  }
1183  }
1184  if (strchr(pedantic_geometry,':') != (char *) NULL)
1185  {
1186  /*
1187  Normalize sampling factor (e.g. 4:2:2 => 2x1).
1188  */
1189  if ((flags & SigmaValue) != 0)
1190  geometry_info->rho*=PerceptibleReciprocal(geometry_info->sigma);
1191  geometry_info->sigma=1.0;
1192  if (((flags & XiValue) != 0) && (geometry_info->xi == 0.0))
1193  geometry_info->sigma=2.0;
1194  }
1195  if (((flags & RhoValue) != 0) && ((flags & SigmaValue) == 0) &&
1196  ((flags & XiValue) != 0) && ((flags & XiNegative) != 0))
1197  {
1198  if ((flags & PsiValue) == 0)
1199  {
1200  /*
1201  Support negative height values (e.g. 30x-20).
1202  */
1203  geometry_info->sigma=geometry_info->xi;
1204  geometry_info->xi=0.0;
1205  flags|=SigmaValue;
1206  flags&=(~XiValue);
1207  }
1208  else
1209  if ((flags & ChiValue) == 0)
1210  {
1211  /*
1212  Support negative height values (e.g. 30x-20+10).
1213  */
1214  geometry_info->sigma=geometry_info->xi;
1215  geometry_info->xi=geometry_info->psi;
1216  flags|=SigmaValue;
1217  flags|=XiValue;
1218  flags&=(~PsiValue);
1219  }
1220  else
1221  {
1222  /*
1223  Support negative height values (e.g. 30x-20+10+10).
1224  */
1225  geometry_info->sigma=geometry_info->xi;
1226  geometry_info->xi=geometry_info->psi;
1227  geometry_info->psi=geometry_info->chi;
1228  flags|=SigmaValue;
1229  flags|=XiValue;
1230  flags|=PsiValue;
1231  flags&=(~ChiValue);
1232  }
1233  }
1234  if ((flags & PercentValue) != 0)
1235  {
1236  if (((flags & SeparatorValue) == 0) && ((flags & SigmaValue) == 0))
1237  geometry_info->sigma=geometry_info->rho;
1238  if (((flags & SeparatorValue) != 0) && ((flags & RhoValue) == 0))
1239  geometry_info->rho=geometry_info->sigma;
1240  }
1241 #if 0
1242  /* Debugging Geometry */
1243  (void) fprintf(stderr,"ParseGeometry...\n");
1244  (void) fprintf(stderr,"Flags: %c %c %s %s %s\n",
1245  (flags & RhoValue) ? 'W' : ' ',(flags & SigmaValue) ? 'H' : ' ',
1246  (flags & XiValue) ? ((flags & XiNegative) ? "-X" : "+X") : " ",
1247  (flags & PsiValue) ? ((flags & PsiNegative) ? "-Y" : "+Y") : " ",
1248  (flags & ChiValue) ? ((flags & ChiNegative) ? "-Z" : "+Z") : " ");
1249  (void) fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",geometry_info->rho,
1250  geometry_info->sigma,geometry_info->xi,geometry_info->psi,
1251  geometry_info->chi);
1252 #endif
1253  return(flags);
1254 }
1255 ␌
1256 /*
1257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1258 % %
1259 % %
1260 % %
1261 % P a r s e G r a v i t y G e o m e t r y %
1262 % %
1263 % %
1264 % %
1265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1266 %
1267 % ParseGravityGeometry() returns a region as defined by the geometry string
1268 % with respect to the given image page (canvas) dimensions and the images
1269 % gravity setting.
1270 %
1271 % This is typically used for specifying a area within a given image for
1272 % cropping images to a smaller size, chopping out rows and or columns, or
1273 % resizing and positioning overlay images.
1274 %
1275 % Percentages are relative to image size and not page size, and are set to
1276 % nearest integer (pixel) size.
1277 %
1278 % The format of the ParseGravityGeometry method is:
1279 %
1280 % MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1281 % RectangleInfo *region_info,ExceptionInfo *exception)
1282 %
1283 % A description of each parameter follows:
1284 %
1285 % o geometry: The geometry string (e.g. "100x100+10+10").
1286 %
1287 % o region_info: the region as defined by the geometry string with respect
1288 % to the image dimensions and its gravity.
1289 %
1290 % o exception: return any errors or warnings in this structure.
1291 %
1292 */
1293 MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1294  const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1295 {
1296  MagickStatusType
1297  flags;
1298 
1299  size_t
1300  height,
1301  width;
1302 
1303  SetGeometry(image,region_info);
1304  if (image->page.width != 0)
1305  region_info->width=image->page.width;
1306  if (image->page.height != 0)
1307  region_info->height=image->page.height;
1308  flags=ParseAbsoluteGeometry(geometry,region_info);
1309  if (flags == NoValue)
1310  {
1311  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1312  "InvalidGeometry","`%s'",geometry);
1313  return(flags);
1314  }
1315  if ((flags & PercentValue) != 0)
1316  {
1317  GeometryInfo
1318  geometry_info;
1319 
1320  MagickStatusType
1321  status;
1322 
1323  PointInfo
1324  scale;
1325 
1326  /*
1327  Geometry is a percentage of the image size, not canvas size
1328  */
1329  if (image->gravity != UndefinedGravity)
1330  flags|=XValue | YValue;
1331  status=ParseGeometry(geometry,&geometry_info);
1332  scale.x=geometry_info.rho;
1333  if ((status & RhoValue) == 0)
1334  scale.x=100.0;
1335  scale.y=geometry_info.sigma;
1336  if ((status & SigmaValue) == 0)
1337  scale.y=scale.x;
1338  region_info->width=CastDoubleToUnsigned(scale.x*image->columns/100.0+0.5);
1339  region_info->height=CastDoubleToUnsigned(scale.y*image->rows/100.0+0.5);
1340  }
1341  if ((flags & AspectRatioValue) != 0)
1342  {
1343  double
1344  geometry_ratio,
1345  image_ratio;
1346 
1347  GeometryInfo
1348  geometry_info;
1349 
1350  /*
1351  Geometry is a relative to image size and aspect ratio.
1352  */
1353  if (image->gravity != UndefinedGravity)
1354  flags|=XValue | YValue;
1355  (void) ParseGeometry(geometry,&geometry_info);
1356  geometry_ratio=geometry_info.rho;
1357  image_ratio=(double) image->columns/image->rows;
1358  if (geometry_ratio >= image_ratio)
1359  {
1360  region_info->width=image->columns;
1361  region_info->height=CastDoubleToUnsigned((double) image->rows*
1362  image_ratio/geometry_ratio+0.5);
1363  }
1364  else
1365  {
1366  region_info->width=CastDoubleToUnsigned((double) image->columns*
1367  geometry_ratio/image_ratio+0.5);
1368  region_info->height=image->rows;
1369  }
1370  }
1371  /*
1372  Adjust offset according to gravity setting.
1373  */
1374  width=region_info->width;
1375  height=region_info->height;
1376  if (width == 0)
1377  region_info->width=image->page.width | image->columns;
1378  if (height == 0)
1379  region_info->height=image->page.height | image->rows;
1380  GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1381  region_info->width=width;
1382  region_info->height=height;
1383  return(flags);
1384 }
1385 ␌
1386 /*
1387 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1388 % %
1389 % %
1390 % %
1391 + P a r s e M e t a G e o m e t r y %
1392 % %
1393 % %
1394 % %
1395 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1396 %
1397 % ParseMetaGeometry() is similar to GetGeometry() except the returned
1398 % geometry is modified as determined by the meta characters: %, !, <, >, @,
1399 % and ^ in relation to image resizing.
1400 %
1401 % Final image dimensions are adjusted so as to preserve the aspect ratio as
1402 % much as possible, while generating a integer (pixel) size, and fitting the
1403 % image within the specified geometry width and height.
1404 %
1405 % Flags are interpreted...
1406 % % geometry size is given percentage of original width and height given
1407 % ! do not try to preserve aspect ratio
1408 % < only enlarge images smaller that geometry
1409 % > only shrink images larger than geometry
1410 % @ Fit image to contain at most this many pixels
1411 % ^ Contain the given geometry given, (minimal dimensions given)
1412 %
1413 % The format of the ParseMetaGeometry method is:
1414 %
1415 % MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1416 % ssize_t *y, size_t *width,size_t *height)
1417 %
1418 % A description of each parameter follows:
1419 %
1420 % o geometry: The geometry string (e.g. "100x100+10+10").
1421 %
1422 % o x,y: The x and y offset, set according to the geometry specification.
1423 %
1424 % o width,height: The width and height of original image, modified by
1425 % the given geometry specification.
1426 %
1427 */
1428 
1429 MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1430  ssize_t *y,size_t *width,size_t *height)
1431 {
1432  GeometryInfo
1433  geometry_info;
1434 
1435  MagickStatusType
1436  flags;
1437 
1438  size_t
1439  former_height,
1440  former_width;
1441 
1442  /*
1443  Ensure the image geometry is valid.
1444  */
1445  assert(x != (ssize_t *) NULL);
1446  assert(y != (ssize_t *) NULL);
1447  assert(width != (size_t *) NULL);
1448  assert(height != (size_t *) NULL);
1449  if ((geometry == (char *) NULL) || (*geometry == '\0'))
1450  return(NoValue);
1451  if (IsEventLogging() != MagickFalse)
1452  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1453  /*
1454  Parse geometry using GetGeometry.
1455  */
1456  SetGeometryInfo(&geometry_info);
1457  former_width=(*width);
1458  former_height=(*height);
1459  flags=GetGeometry(geometry,x,y,width,height);
1460  if ((flags & PercentValue) != 0)
1461  {
1462  MagickStatusType
1463  flags;
1464 
1465  PointInfo
1466  scale;
1467 
1468  /*
1469  Geometry is a percentage of the image size.
1470  */
1471  flags=ParseGeometry(geometry,&geometry_info);
1472  scale.x=geometry_info.rho;
1473  if ((flags & RhoValue) == 0)
1474  scale.x=100.0;
1475  scale.y=geometry_info.sigma;
1476  if ((flags & SigmaValue) == 0)
1477  scale.y=scale.x;
1478  *width=CastDoubleToUnsigned(scale.x*former_width/100.0+0.5);
1479  *height=CastDoubleToUnsigned(scale.y*former_height/100.0+0.5);
1480  former_width=(*width);
1481  former_height=(*height);
1482  }
1483  if ((flags & AspectRatioValue) != 0)
1484  {
1485  double
1486  geometry_ratio,
1487  image_ratio;
1488 
1489  GeometryInfo
1490  geometry_info;
1491 
1492  /*
1493  Geometry is a relative to image size and aspect ratio.
1494  */
1495  (void) ParseGeometry(geometry,&geometry_info);
1496  geometry_ratio=geometry_info.rho;
1497  image_ratio=(double) former_width*PerceptibleReciprocal(former_height);
1498  if (geometry_ratio >= image_ratio)
1499  {
1500  *width=former_width;
1501  *height=CastDoubleToUnsigned(PerceptibleReciprocal(geometry_ratio)*
1502  former_height*image_ratio+0.5);
1503  }
1504  else
1505  {
1506  *width=CastDoubleToUnsigned(PerceptibleReciprocal(image_ratio)*
1507  former_width*geometry_ratio+0.5);
1508  *height=former_height;
1509  }
1510  former_width=(*width);
1511  former_height=(*height);
1512  }
1513  if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1514  (*height == former_height)))
1515  {
1516  if ((flags & RhoValue) == 0)
1517  *width=former_width;
1518  if ((flags & SigmaValue) == 0)
1519  *height=former_height;
1520  }
1521  else
1522  {
1523  double
1524  scale_factor;
1525 
1526  /*
1527  Respect aspect ratio of the image.
1528  */
1529  if ((former_width == 0) || (former_height == 0))
1530  scale_factor=1.0;
1531  else
1532  if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1533  {
1534  scale_factor=(double) *width/(double) former_width;
1535  if ((flags & MinimumValue) == 0)
1536  {
1537  if (scale_factor > ((double) *height/(double) former_height))
1538  scale_factor=(double) *height/(double) former_height;
1539  }
1540  else
1541  if (scale_factor < ((double) *height/(double) former_height))
1542  scale_factor=(double) *height/(double) former_height;
1543  }
1544  else
1545  if ((flags & RhoValue) != 0)
1546  {
1547  scale_factor=(double) *width/(double) former_width;
1548  if (((flags & MinimumValue) != 0) &&
1549  (scale_factor < ((double) *width/(double) former_height)))
1550  scale_factor=(double) *width/(double) former_height;
1551  }
1552  else
1553  {
1554  scale_factor=(double) *height/(double) former_height;
1555  if (((flags & MinimumValue) != 0) &&
1556  (scale_factor < ((double) *height/(double) former_width)))
1557  scale_factor=(double) *height/(double) former_width;
1558  }
1559  *width=CastDoubleToUnsigned(MagickMax(floor(scale_factor*former_width+
1560  0.5),1.0));
1561  *height=CastDoubleToUnsigned(MagickMax(floor(scale_factor*former_height+
1562  0.5),1.0));
1563  }
1564  if ((flags & GreaterValue) != 0)
1565  {
1566  if (former_width < *width)
1567  *width=former_width;
1568  if (former_height < *height)
1569  *height=former_height;
1570  }
1571  if ((flags & LessValue) != 0)
1572  {
1573  if (former_width > *width)
1574  *width=former_width;
1575  if (former_height > *height)
1576  *height=former_height;
1577  }
1578  if ((flags & AreaValue) != 0)
1579  {
1580  double
1581  area,
1582  distance;
1583 
1584  PointInfo
1585  scale;
1586 
1587  /*
1588  Geometry is a maximum area in pixels.
1589  */
1590  (void) ParseGeometry(geometry,&geometry_info);
1591  area=geometry_info.rho+sqrt(MagickEpsilon);
1592  distance=sqrt((double) former_width*former_height);
1593  scale.x=(double) former_width*PerceptibleReciprocal(distance*
1594  PerceptibleReciprocal(sqrt(area)));
1595  scale.y=(double) former_height*PerceptibleReciprocal(distance*
1596  PerceptibleReciprocal(sqrt(area)));
1597  if ((scale.x < (double) *width) || (scale.y < (double) *height))
1598  {
1599  *width=CastDoubleToUnsigned(former_width*PerceptibleReciprocal(
1600  distance*PerceptibleReciprocal(sqrt(area)))+0.5);
1601  *height=CastDoubleToUnsigned(former_height*PerceptibleReciprocal(
1602  distance*PerceptibleReciprocal(sqrt(area)))+0.5);
1603  }
1604  former_width=(*width);
1605  former_height=(*height);
1606  }
1607  return(flags);
1608 }
1609 ␌
1610 /*
1611 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1612 % %
1613 % %
1614 % %
1615 % P a r s e P a g e G e o m e t r y %
1616 % %
1617 % %
1618 % %
1619 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1620 %
1621 % ParsePageGeometry() returns a region as defined by the geometry string with
1622 % respect to the image page (canvas) dimensions.
1623 %
1624 % WARNING: Percentage dimensions remain relative to the actual image
1625 % dimensions, and not canvas dimensions.
1626 %
1627 % The format of the ParsePageGeometry method is:
1628 %
1629 % MagickStatusType ParsePageGeometry(const Image *image,
1630 % const char *geometry,RectangleInfo *region_info,
1631 % ExceptionInfo *exception)
1632 %
1633 % A description of each parameter follows:
1634 %
1635 % o geometry: The geometry string (e.g. "100x100+10+10").
1636 %
1637 % o region_info: the region as defined by the geometry string with
1638 % respect to the image and its gravity.
1639 %
1640 % o exception: return any errors or warnings in this structure.
1641 %
1642 */
1643 MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1644  const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1645 {
1646  MagickStatusType
1647  flags;
1648 
1649  SetGeometry(image,region_info);
1650  if (image->page.width != 0)
1651  region_info->width=image->page.width;
1652  if (image->page.height != 0)
1653  region_info->height=image->page.height;
1654  flags=ParseAbsoluteGeometry(geometry,region_info);
1655  if (flags == NoValue)
1656  {
1657  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1658  "InvalidGeometry","`%s'",geometry);
1659  return(flags);
1660  }
1661  if ((flags & PercentValue) != 0)
1662  {
1663  region_info->width=image->columns;
1664  region_info->height=image->rows;
1665  }
1666  flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1667  &region_info->width,&region_info->height);
1668  if ((((flags & WidthValue) != 0) || ((flags & HeightValue) != 0)) &&
1669  (((flags & PercentValue) != 0) || ((flags & SeparatorValue) == 0)))
1670  {
1671  if ((flags & WidthValue) == 0)
1672  region_info->width=region_info->height;
1673  if ((flags & HeightValue) == 0)
1674  region_info->height=region_info->width;
1675  }
1676  return(flags);
1677 }
1678 ␌
1679 /*
1680 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1681 % %
1682 % %
1683 % %
1684 % P a r s e R e g i o n G e o m e t r y %
1685 % %
1686 % %
1687 % %
1688 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1689 %
1690 % ParseRegionGeometry() returns a region as defined by the geometry string
1691 % with respect to the image dimensions and aspect ratio.
1692 %
1693 % This is basically a wrapper around ParseMetaGeometry. This is typically
1694 % used to parse a geometry string to work out the final integer dimensions
1695 % for image resizing.
1696 %
1697 % The format of the ParseRegionGeometry method is:
1698 %
1699 % MagickStatusType ParseRegionGeometry(const Image *image,
1700 % const char *geometry,RectangleInfo *region_info,
1701 % ExceptionInfo *exception)
1702 %
1703 % A description of each parameter follows:
1704 %
1705 % o geometry: The geometry string (e.g. "100x100+10+10").
1706 %
1707 % o region_info: the region as defined by the geometry string.
1708 %
1709 % o exception: return any errors or warnings in this structure.
1710 %
1711 */
1712 MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1713  const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1714 {
1715  MagickStatusType
1716  flags;
1717 
1718  SetGeometry(image,region_info);
1719  flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1720  &region_info->width,&region_info->height);
1721  if (flags == NoValue)
1722  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1723  "InvalidGeometry","`%s'",geometry);
1724  return(flags);
1725 }
1726 ␌
1727 /*
1728 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1729 % %
1730 % %
1731 % %
1732 % S e t G e o m e t r y %
1733 % %
1734 % %
1735 % %
1736 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1737 %
1738 % SetGeometry() sets the geometry to its default values.
1739 %
1740 % The format of the SetGeometry method is:
1741 %
1742 % SetGeometry(const Image *image,RectangleInfo *geometry)
1743 %
1744 % A description of each parameter follows:
1745 %
1746 % o image: the image.
1747 %
1748 % o geometry: the geometry.
1749 %
1750 */
1751 MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1752 {
1753  assert(image != (Image *) NULL);
1754  assert(image->signature == MagickCoreSignature);
1755  if (IsEventLogging() != MagickFalse)
1756  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1757  assert(geometry != (RectangleInfo *) NULL);
1758  (void) memset(geometry,0,sizeof(*geometry));
1759  geometry->width=image->columns;
1760  geometry->height=image->rows;
1761 }
1762 ␌
1763 /*
1764 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1765 % %
1766 % %
1767 % %
1768 % S e t G e o m e t r y I n f o %
1769 % %
1770 % %
1771 % %
1772 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1773 %
1774 % SetGeometryInfo sets the GeometryInfo structure to its default values.
1775 %
1776 % The format of the SetGeometryInfo method is:
1777 %
1778 % SetGeometryInfo(GeometryInfo *geometry_info)
1779 %
1780 % A description of each parameter follows:
1781 %
1782 % o geometry_info: the geometry info structure.
1783 %
1784 */
1785 MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1786 {
1787  assert(geometry_info != (GeometryInfo *) NULL);
1788  if (IsEventLogging() != MagickFalse)
1789  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1790  (void) memset(geometry_info,0,sizeof(*geometry_info));
1791 }
Definition: image.h:153