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