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