2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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 %
13 % MagickCore Geometry Methods %
20 % Copyright 1999-2018 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % https://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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/image-private.h"
49 #include "MagickCore/memory_.h"
50 #include "MagickCore/pixel-accessor.h"
51 #include "MagickCore/string_.h"
52 #include "MagickCore/string-private.h"
53 #include "MagickCore/token.h"
56 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
60 % G e t G e o m e t r y %
64 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
66 % GetGeometry() parses a geometry specification and returns the width,
67 % height, x, and y values. It also returns flags that indicates which
68 % of the four values (width, height, x, y) were located in the string, and
69 % whether the x or y values are negative. In addition, there are flags to
70 % report any meta characters (%, !, <, or >).
72 % The value must form a proper geometry style specification of WxH+X+Y
73 % of integers only, and values can not be separated by comma, colon, or
74 % slash charcaters. See ParseGeometry() below.
76 % Offsets may be prefixed by multiple signs to make offset string
77 % substitutions easier to handle from shell scripts.
78 % For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
79 % offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
82 % The format of the GetGeometry method is:
84 % MagickStatusType GetGeometry(const char *geometry,ssize_t *x,ssize_t *y,
85 % size_t *width,size_t *height)
87 % A description of each parameter follows:
89 % o geometry: The geometry.
91 % o x,y: The x and y offset as determined by the geometry specification.
93 % o width,height: The width and height as determined by the geometry
97 MagickExport MagickStatusType GetGeometry(const char *geometry,ssize_t *x,
98 ssize_t *y,size_t *width,size_t *height)
102 pedantic_geometry[MagickPathExtent],
115 Remove whitespace and meta characters from geometry specification.
118 if ((geometry == (char *) NULL) || (*geometry == '\0'))
120 if (strlen(geometry) >= (MagickPathExtent-1))
122 (void) CopyMagickString(pedantic_geometry,geometry,MagickPathExtent);
123 for (p=pedantic_geometry; *p != '\0'; )
125 if (isspace((int) ((unsigned char) *p)) != 0)
127 (void) CopyMagickString(p,p+1,MagickPathExtent);
136 (void) CopyMagickString(p,p+1,MagickPathExtent);
142 (void) CopyMagickString(p,p+1,MagickPathExtent);
148 (void) CopyMagickString(p,p+1,MagickPathExtent);
154 (void) CopyMagickString(p,p+1,MagickPathExtent);
160 (void) CopyMagickString(p,p+1,MagickPathExtent);
166 (void) CopyMagickString(p,p+1,MagickPathExtent);
172 (void) CopyMagickString(p,p+1,MagickPathExtent);
178 flags|=SeparatorValue;
211 flags|=AspectRatioValue;
219 Parse width, height, x, and y.
225 value=StringToDouble(p,&q);
227 if (LocaleNCompare(p,"0x",2) == 0)
228 value=(double) strtol(p,&q,10);
229 if ((*p != '+') && (*p != '-'))
231 c=(int) ((unsigned char) *q);
232 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == '\0'))
238 if (width != (size_t *) NULL)
240 if (LocaleNCompare(p,"0x",2) == 0)
241 *width=(size_t) strtol(p,&p,10);
243 *width=((size_t) floor(StringToDouble(p,&p)+0.5)) & 0x7fffffff;
249 if ((*p != '+') && (*p != '-'))
251 c=(int) ((unsigned char) *p);
252 if ((c == 215) || (*p == 'x') || (*p == 'X'))
255 if ((*p != '+') && (*p != '-'))
261 if (height != (size_t *) NULL)
262 *height=((size_t) floor(StringToDouble(p,&p)+0.5)) & 0x7fffffff;
268 if ((*p == '+') || (*p == '-'))
273 while ((*p == '+') || (*p == '-'))
276 flags^=XNegative; /* negate sign */
280 if (x != (ssize_t *) NULL)
281 *x=((ssize_t) ceil(StringToDouble(p,&p)-0.5)) & 0x7fffffff;
285 if (((flags & XNegative) != 0) && (x != (ssize_t *) NULL))
289 if ((*p == '+') || (*p == '-'))
294 while ((*p == '+') || (*p == '-'))
297 flags^=YNegative; /* negate sign */
301 if (y != (ssize_t *) NULL)
302 *y=((ssize_t) ceil(StringToDouble(p,&p)-0.5)) & 0x7fffffff;
306 if (((flags & YNegative) != 0) && (y != (ssize_t *) NULL))
310 if ((flags & PercentValue) != 0)
312 if (((flags & SeparatorValue) == 0) && ((flags & HeightValue) == 0))
314 if ((height != (size_t *) NULL) && (width != (size_t *) NULL))
318 if (((flags & SeparatorValue) != 0) && ((flags & WidthValue) == 0) &&
319 (height != (size_t *) NULL) && (width != (size_t *) NULL))
323 /* Debugging Geometry */
324 (void) fprintf(stderr,"GetGeometry...\n");
325 (void) fprintf(stderr,"Input: %s\n",geometry);
326 (void) fprintf(stderr,"Flags: %c %c %s %s\n",
327 (flags & WidthValue) ? 'W' : ' ',(flags & HeightValue) ? 'H' : ' ',
328 (flags & XValue) ? ((flags & XNegative) ? "-X" : "+X") : " ",
329 (flags & YValue) ? ((flags & YNegative) ? "-Y" : "+Y") : " ");
330 (void) fprintf(stderr,"Geometry: %ldx%ld%+ld%+ld\n",(long) *width,(long)
331 *height,(long) *x,(long) *y);
337 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
341 % G e t P a g e G e o m e t r y %
345 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
347 % GetPageGeometry() replaces any page mneumonic with the equivalent size in
350 % The format of the GetPageGeometry method is:
352 % char *GetPageGeometry(const char *page_geometry)
354 % A description of each parameter follows.
356 % o page_geometry: Specifies a pointer to an array of characters. The
357 % string is either a Postscript page name (e.g. A4) or a postscript page
358 % geometry (e.g. 612x792+36+36).
361 MagickExport char *GetPageGeometry(const char *page_geometry)
363 #define MagickPageSize(name,geometry) { (name), sizeof(name)-1, (geometry) }
365 typedef struct _PageInfo
377 static const PageInfo
380 MagickPageSize("4x6", "288x432"),
381 MagickPageSize("5x7", "360x504"),
382 MagickPageSize("7x9", "504x648"),
383 MagickPageSize("8x10", "576x720"),
384 MagickPageSize("9x11", "648x792"),
385 MagickPageSize("9x12", "648x864"),
386 MagickPageSize("10x13", "720x936"),
387 MagickPageSize("10x14", "720x1008"),
388 MagickPageSize("11x17", "792x1224"),
389 MagickPageSize("a0", "2384x3370"),
390 MagickPageSize("a1", "1684x2384"),
391 MagickPageSize("a10", "73x105"),
392 MagickPageSize("a2", "1191x1684"),
393 MagickPageSize("a3", "842x1191"),
394 MagickPageSize("a4", "595x842"),
395 MagickPageSize("a4small", "595x842"),
396 MagickPageSize("a5", "420x595"),
397 MagickPageSize("a6", "297x420"),
398 MagickPageSize("a7", "210x297"),
399 MagickPageSize("a8", "148x210"),
400 MagickPageSize("a9", "105x148"),
401 MagickPageSize("archa", "648x864"),
402 MagickPageSize("archb", "864x1296"),
403 MagickPageSize("archC", "1296x1728"),
404 MagickPageSize("archd", "1728x2592"),
405 MagickPageSize("arche", "2592x3456"),
406 MagickPageSize("b0", "2920x4127"),
407 MagickPageSize("b1", "2064x2920"),
408 MagickPageSize("b10", "91x127"),
409 MagickPageSize("b2", "1460x2064"),
410 MagickPageSize("b3", "1032x1460"),
411 MagickPageSize("b4", "729x1032"),
412 MagickPageSize("b5", "516x729"),
413 MagickPageSize("b6", "363x516"),
414 MagickPageSize("b7", "258x363"),
415 MagickPageSize("b8", "181x258"),
416 MagickPageSize("b9", "127x181"),
417 MagickPageSize("c0", "2599x3676"),
418 MagickPageSize("c1", "1837x2599"),
419 MagickPageSize("c2", "1298x1837"),
420 MagickPageSize("c3", "918x1296"),
421 MagickPageSize("c4", "649x918"),
422 MagickPageSize("c5", "459x649"),
423 MagickPageSize("c6", "323x459"),
424 MagickPageSize("c7", "230x323"),
425 MagickPageSize("csheet", "1224x1584"),
426 MagickPageSize("dsheet", "1584x2448"),
427 MagickPageSize("esheet", "2448x3168"),
428 MagickPageSize("executive", "540x720"),
429 MagickPageSize("flsa", "612x936"),
430 MagickPageSize("flse", "612x936"),
431 MagickPageSize("folio", "612x936"),
432 MagickPageSize("halfletter", "396x612"),
433 MagickPageSize("isob0", "2835x4008"),
434 MagickPageSize("isob1", "2004x2835"),
435 MagickPageSize("isob10", "88x125"),
436 MagickPageSize("isob2", "1417x2004"),
437 MagickPageSize("isob3", "1001x1417"),
438 MagickPageSize("isob4", "709x1001"),
439 MagickPageSize("isob5", "499x709"),
440 MagickPageSize("isob6", "354x499"),
441 MagickPageSize("isob7", "249x354"),
442 MagickPageSize("isob8", "176x249"),
443 MagickPageSize("isob9", "125x176"),
444 MagickPageSize("jisb0", "1030x1456"),
445 MagickPageSize("jisb1", "728x1030"),
446 MagickPageSize("jisb2", "515x728"),
447 MagickPageSize("jisb3", "364x515"),
448 MagickPageSize("jisb4", "257x364"),
449 MagickPageSize("jisb5", "182x257"),
450 MagickPageSize("jisb6", "128x182"),
451 MagickPageSize("ledger", "1224x792"),
452 MagickPageSize("legal", "612x1008"),
453 MagickPageSize("letter", "612x792"),
454 MagickPageSize("lettersmall", "612x792"),
455 MagickPageSize("monarch", "279x540"),
456 MagickPageSize("quarto", "610x780"),
457 MagickPageSize("statement", "396x612"),
458 MagickPageSize("tabloid", "792x1224")
467 assert(page_geometry != (char *) NULL);
468 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
469 (void) CopyMagickString(page,page_geometry,MaxTextExtent);
470 for (i=0; i < (ssize_t) (sizeof(PageSizes)/sizeof(PageSizes[0])); i++)
475 status=LocaleNCompare(PageSizes[i].name,page_geometry,PageSizes[i].extent);
485 Replace mneumonic with the equivalent size in dots-per-inch.
487 (void) FormatLocaleString(page,MaxTextExtent,"%s%.80s",
488 PageSizes[i].geometry,page_geometry+PageSizes[i].extent);
489 flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
491 if ((flags & GreaterValue) == 0)
492 (void) ConcatenateMagickString(page,">",MaxTextExtent);
496 return(AcquireString(page));
500 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
504 % G r a v i t y A d j u s t G e o m e t r y %
508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
510 % GravityAdjustGeometry() adjusts the offset of a region with regard to the
511 % given: width, height and gravity; against which it is positioned.
513 % The region should also have an appropriate width and height to correctly
514 % set the right offset of the top left corner of the region.
516 % The format of the GravityAdjustGeometry method is:
518 % void GravityAdjustGeometry(const size_t width, const size_t height,
519 % const GravityType gravity,RectangleInfo *region);
521 % A description of each parameter follows:
523 % o width, height: the larger area the region is relative to
525 % o gravity: the edge/corner the current offset is relative to
527 % o region: The region requiring a offset adjustment relative to gravity
530 MagickExport void GravityAdjustGeometry(const size_t width,
531 const size_t height,const GravityType gravity,RectangleInfo *region)
533 if (region->height == 0)
534 region->height=height;
535 if (region->width == 0)
539 case NorthEastGravity:
541 case SouthEastGravity:
543 region->x=(ssize_t) (width-region->width-region->x);
550 region->x+=(ssize_t) (width/2-region->width/2);
554 case NorthWestGravity:
556 case SouthWestGravity:
562 case SouthWestGravity:
564 case SouthEastGravity:
566 region->y=(ssize_t) (height-region->height-region->y);
573 region->y+=(ssize_t) (height/2-region->height/2);
577 case NorthWestGravity:
579 case NorthEastGravity:
587 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
591 + I s G e o m e t r y %
595 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
597 % IsGeometry() returns MagickTrue if the geometry specification is valid.
598 % Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
600 % The format of the IsGeometry method is:
602 % MagickBooleanType IsGeometry(const char *geometry)
604 % A description of each parameter follows:
606 % o geometry: This string is the geometry specification.
609 MagickExport MagickBooleanType IsGeometry(const char *geometry)
617 if (geometry == (const char *) NULL)
619 flags=ParseGeometry(geometry,&geometry_info);
620 return(flags != NoValue ? MagickTrue : MagickFalse);
624 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
628 + I s S c e n e G e o m e t r y %
632 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
634 % IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
635 % specification (e.g. [1], [1-9], [1,7,4]).
637 % The format of the IsSceneGeometry method is:
639 % MagickBooleanType IsSceneGeometry(const char *geometry,
640 % const MagickBooleanType pedantic)
642 % A description of each parameter follows:
644 % o geometry: This string is the geometry specification.
646 % o pedantic: A value other than 0 invokes a more restrictive set of
647 % conditions for a valid specification (e.g. [1], [1-4], [4-1]).
650 MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
651 const MagickBooleanType pedantic)
659 if (geometry == (const char *) NULL)
662 value=StringToDouble(geometry,&p);
666 if (strspn(geometry,"0123456789-, ") != strlen(geometry))
668 if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
674 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
678 % P a r s e A b s o l u t e G e o m e t r y %
682 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
684 % ParseAbsoluteGeometry() returns a region as defined by the geometry string,
685 % without any modification by percentages or gravity.
687 % It currently just a wrapper around GetGeometry(), but may be expanded in
688 % the future to handle other positioning information.
690 % The format of the ParseAbsoluteGeometry method is:
692 % MagickStatusType ParseAbsoluteGeometry(const char *geometry,
693 % RectangleInfo *region_info)
695 % A description of each parameter follows:
697 % o geometry: The geometry string (e.g. "100x100+10+10").
699 % o region_info: the region as defined by the geometry string.
702 MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
703 RectangleInfo *region_info)
708 flags=GetGeometry(geometry,®ion_info->x,®ion_info->y,
709 ®ion_info->width,®ion_info->height);
714 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
718 % P a r s e A f f i n e G e o m e t r y %
722 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
724 % ParseAffineGeometry() returns an affine matrix as defined by a string of 4
725 % to 6 comma/space separated floating point values.
727 % The affine matrix determinant is checked for validity of the values.
729 % The format of the ParseAffineGeometry method is:
731 % MagickStatusType ParseAffineGeometry(const char *geometry,
732 % AffineMatrix *affine_matrix,ExceptionInfo *exception)
734 % A description of each parameter follows:
736 % o geometry: The geometry string (e.g. "1.0,0.0,0.0,1.0,3.2,1.2").
738 % o affine_matrix: the affine matrix as defined by the geometry string.
740 % o exception: return any errors or warnings in this structure.
743 MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
744 AffineMatrix *affine_matrix,ExceptionInfo *exception)
747 token[MagickPathExtent];
761 GetAffineMatrix(affine_matrix);
764 for (i=0; (*p != '\0') && (i < 6); i++)
766 GetNextToken(p,&p,MagickPathExtent,token);
768 GetNextToken(p,&p,MagickPathExtent,token);
773 affine_matrix->sx=StringToDouble(token,(char **) NULL);
778 affine_matrix->rx=StringToDouble(token,(char **) NULL);
783 affine_matrix->ry=StringToDouble(token,(char **) NULL);
788 affine_matrix->sy=StringToDouble(token,(char **) NULL);
793 affine_matrix->tx=StringToDouble(token,(char **) NULL);
799 affine_matrix->ty=StringToDouble(token,(char **) NULL);
805 determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
807 if (fabs(determinant) < MagickEpsilon)
808 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
809 "InvalidArgument","'%s' : 'Indeterminate Matrix'",geometry);
814 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
818 % P a r s e G e o m e t r y %
822 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
824 % ParseGeometry() parses a geometry specification and returns the sigma,
825 % rho, xi, and psi values. It also returns flags that indicates which
826 % of the four values (sigma, rho, xi, psi) were located in the string, and
827 % whether the xi or pi values are negative.
829 % In addition, it reports if there are any of meta characters (%, !, <, >, @,
830 % and ^) flags present. It does not report the location of the percentage
831 % relative to the values.
833 % Values may also be separated by commas, colons, or slashes, and offsets.
834 % Offsets may be prefixed by multiple signs to make offset string
835 % substitutions easier to handle from shell scripts.
836 % For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
837 % offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
840 % The format of the ParseGeometry method is:
842 % MagickStatusType ParseGeometry(const char *geometry,
843 % GeometryInfo *geometry_info)
845 % A description of each parameter follows:
847 % o geometry: The geometry string (e.g. "100x100+10+10").
849 % o geometry_info: returns the parsed width/height/x/y in this structure.
852 MagickExport MagickStatusType ParseGeometry(const char *geometry,
853 GeometryInfo *geometry_info)
857 pedantic_geometry[MagickPathExtent],
873 Remove whitespaces meta characters from geometry specification.
875 assert(geometry_info != (GeometryInfo *) NULL);
876 (void) memset(geometry_info,0,sizeof(*geometry_info));
878 if ((geometry == (char *) NULL) || (*geometry == '\0'))
880 if (strlen(geometry) >= (MagickPathExtent-1))
882 c=sscanf(geometry,"%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf",&coordinate.rho,
883 &coordinate.sigma,&coordinate.xi,&coordinate.psi);
887 Special case: coordinate (e.g. 0,0 255,255).
889 geometry_info->rho=coordinate.rho;
890 geometry_info->sigma=coordinate.sigma;
891 geometry_info->xi=coordinate.xi;
892 geometry_info->psi=coordinate.psi;
893 flags|=RhoValue | SigmaValue | XiValue | PsiValue;
896 (void) CopyMagickString(pedantic_geometry,geometry,MagickPathExtent);
897 for (p=pedantic_geometry; *p != '\0'; )
899 c=(int) ((unsigned char) *p);
902 (void) CopyMagickString(p,p+1,MagickPathExtent);
910 (void) CopyMagickString(p,p+1,MagickPathExtent);
916 (void) CopyMagickString(p,p+1,MagickPathExtent);
922 (void) CopyMagickString(p,p+1,MagickPathExtent);
928 (void) CopyMagickString(p,p+1,MagickPathExtent);
934 (void) CopyMagickString(p,p+1,MagickPathExtent);
940 (void) CopyMagickString(p,p+1,MagickPathExtent);
946 (void) CopyMagickString(p,p+1,MagickPathExtent);
952 flags|=SeparatorValue;
986 flags|=AspectRatioValue;
994 Parse rho, sigma, xi, psi, and optionally chi.
1000 value=StringToDouble(p,&q);
1001 if (LocaleNCompare(p,"0x",2) == 0)
1002 (void) strtol(p,&q,10);
1003 c=(int) ((unsigned char) *q);
1004 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
1005 (*q == '/') || (*q == ':') || (*q =='\0'))
1011 if (LocaleNCompare(p,"0x",2) == 0)
1012 value=(double) strtol(p,&p,10);
1014 value=StringToDouble(p,&p);
1018 geometry_info->rho=value;
1022 c=(int) ((unsigned char) *p);
1023 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ',') || (*p == '/') ||
1030 while (isspace((int) ((unsigned char) *p)) != 0)
1032 c=(int) ((unsigned char) *q);
1033 if (((c != 215) && (*q != 'x') && (*q != 'X')) || ((*p != '+') &&
1037 value=StringToDouble(p,&p);
1041 geometry_info->sigma=value;
1045 while (isspace((int) ((unsigned char) *p)) != 0)
1047 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
1052 if ((*p == ',') || (*p == '/') || (*p == ':') )
1054 while ((*p == '+') || (*p == '-'))
1057 flags^=XiNegative; /* negate sign */
1061 value=StringToDouble(p,&p);
1065 if ((flags & XiNegative) != 0)
1067 geometry_info->xi=value;
1069 while (isspace((int) ((unsigned char) *p)) != 0)
1071 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1077 if ((*p == ',') || (*p == '/') || (*p == ':'))
1079 while ((*p == '+') || (*p == '-'))
1082 flags^=PsiNegative; /* negate sign */
1086 value=StringToDouble(p,&p);
1090 if ((flags & PsiNegative) != 0)
1092 geometry_info->psi=value;
1095 while (isspace((int) ((unsigned char) *p)) != 0)
1097 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1103 if ((*p == ',') || (*p == '/') || (*p == ':'))
1105 while ((*p == '+') || (*p == '-'))
1108 flags^=ChiNegative; /* negate sign */
1112 value=StringToDouble(p,&p);
1116 if ((flags & ChiNegative) != 0)
1118 geometry_info->chi=value;
1122 if (strchr(pedantic_geometry,':') != (char *) NULL)
1125 Normalize sampling factor (e.g. 4:2:2 => 2x1).
1127 if ((flags & SigmaValue) != 0)
1128 geometry_info->rho*=PerceptibleReciprocal(geometry_info->sigma);
1129 geometry_info->sigma=1.0;
1130 if (((flags & XiValue) != 0) && (geometry_info->xi == 0.0))
1131 geometry_info->sigma=2.0;
1133 if (((flags & SigmaValue) == 0) && ((flags & XiValue) != 0) &&
1134 ((flags & PsiValue) == 0))
1137 Support negative height values (e.g. 30x-20).
1139 geometry_info->sigma=geometry_info->xi;
1140 geometry_info->xi=0.0;
1144 if ((flags & PercentValue) != 0)
1146 if (((flags & SeparatorValue) == 0) && ((flags & SigmaValue) == 0))
1147 geometry_info->sigma=geometry_info->rho;
1148 if (((flags & SeparatorValue) != 0) && ((flags & RhoValue) == 0))
1149 geometry_info->rho=geometry_info->sigma;
1152 /* Debugging Geometry */
1153 (void) fprintf(stderr,"ParseGeometry...\n");
1154 (void) fprintf(stderr,"Flags: %c %c %s %s %s\n",
1155 (flags & RhoValue) ? 'W' : ' ',(flags & SigmaValue) ? 'H' : ' ',
1156 (flags & XiValue) ? ((flags & XiNegative) ? "-X" : "+X") : " ",
1157 (flags & PsiValue) ? ((flags & PsiNegative) ? "-Y" : "+Y") : " ",
1158 (flags & ChiValue) ? ((flags & ChiNegative) ? "-Z" : "+Z") : " ");
1159 (void) fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",geometry_info->rho,
1160 geometry_info->sigma,geometry_info->xi,geometry_info->psi,
1161 geometry_info->chi);
1167 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1171 % P a r s e G r a v i t y G e o m e t r y %
1175 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1177 % ParseGravityGeometry() returns a region as defined by the geometry string
1178 % with respect to the given image page (canvas) dimensions and the images
1181 % This is typically used for specifing a area within a given image for
1182 % cropping images to a smaller size, chopping out rows and or columns, or
1183 % resizing and positioning overlay images.
1185 % Percentages are relative to image size and not page size, and are set to
1186 % nearest integer (pixel) size.
1188 % The format of the ParseGravityGeometry method is:
1190 % MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1191 % RectangeInfo *region_info,ExceptionInfo *exception)
1193 % A description of each parameter follows:
1195 % o geometry: The geometry string (e.g. "100x100+10+10").
1197 % o region_info: the region as defined by the geometry string with respect
1198 % to the image dimensions and its gravity.
1200 % o exception: return any errors or warnings in this structure.
1203 MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1204 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1213 SetGeometry(image,region_info);
1214 if (image->page.width != 0)
1215 region_info->width=image->page.width;
1216 if (image->page.height != 0)
1217 region_info->height=image->page.height;
1218 flags=ParseAbsoluteGeometry(geometry,region_info);
1219 if (flags == NoValue)
1221 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1222 "InvalidGeometry","`%s'",geometry);
1225 if ((flags & PercentValue) != 0)
1237 Geometry is a percentage of the image size, not canvas size
1239 if (image->gravity != UndefinedGravity)
1240 flags|=XValue | YValue;
1241 status=ParseGeometry(geometry,&geometry_info);
1242 scale.x=geometry_info.rho;
1243 if ((status & RhoValue) == 0)
1245 scale.y=geometry_info.sigma;
1246 if ((status & SigmaValue) == 0)
1248 region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1249 region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
1251 if ((flags & AspectRatioValue) != 0)
1261 Geometry is a relative to image size and aspect ratio.
1263 if (image->gravity != UndefinedGravity)
1264 flags|=XValue | YValue;
1265 (void) ParseGeometry(geometry,&geometry_info);
1266 geometry_ratio=geometry_info.rho;
1267 image_ratio=(double) image->columns/image->rows;
1268 if (geometry_ratio >= image_ratio)
1270 region_info->width=image->columns;
1271 region_info->height=(size_t) floor((double) (image->rows*image_ratio/
1272 geometry_ratio)+0.5);
1276 region_info->width=(size_t) floor((double) (image->columns*
1277 geometry_ratio/image_ratio)+0.5);
1278 region_info->height=image->rows;
1282 Adjust offset according to gravity setting.
1284 width=region_info->width;
1285 height=region_info->height;
1287 region_info->width=image->page.width | image->columns;
1289 region_info->height=image->page.height | image->rows;
1290 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1291 region_info->width=width;
1292 region_info->height=height;
1297 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1301 + P a r s e M e t a G e o m e t r y %
1305 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1307 % ParseMetaGeometry() is similar to GetGeometry() except the returned
1308 % geometry is modified as determined by the meta characters: %, !, <, >, @,
1309 % :, and ^ in relation to image resizing.
1311 % Final image dimensions are adjusted so as to preserve the aspect ratio as
1312 % much as possible, while generating a integer (pixel) size, and fitting the
1313 % image within the specified geometry width and height.
1315 % Flags are interpreted...
1316 % % geometry size is given percentage of original width and height given
1317 % ! do not try to preserve aspect ratio
1318 % < only enlarge images smaller that geometry
1319 % > only shrink images larger than geometry
1320 % @ fit image to contain at most this many pixels
1321 % : width and height denotes an aspect ratio
1322 % ^ contain the given geometry given, (minimal dimensions given)
1324 % The format of the ParseMetaGeometry method is:
1326 % MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1327 % ssize_t *y, size_t *width,size_t *height)
1329 % A description of each parameter follows:
1331 % o geometry: The geometry string (e.g. "100x100+10+10").
1333 % o x,y: The x and y offset, set according to the geometry specification.
1335 % o width,height: The width and height of original image, modified by
1336 % the given geometry specification.
1340 MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1341 ssize_t *y,size_t *width,size_t *height)
1354 Ensure the image geometry is valid.
1356 assert(x != (ssize_t *) NULL);
1357 assert(y != (ssize_t *) NULL);
1358 assert(width != (size_t *) NULL);
1359 assert(height != (size_t *) NULL);
1360 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1362 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1364 Parse geometry using GetGeometry.
1366 SetGeometryInfo(&geometry_info);
1367 former_width=(*width);
1368 former_height=(*height);
1369 flags=GetGeometry(geometry,x,y,width,height);
1370 if ((flags & PercentValue) != 0)
1379 Geometry is a percentage of the image size.
1381 percent_flags=ParseGeometry(geometry,&geometry_info);
1382 scale.x=geometry_info.rho;
1383 if ((percent_flags & RhoValue) == 0)
1385 scale.y=geometry_info.sigma;
1386 if ((percent_flags & SigmaValue) == 0)
1388 *width=(size_t) MagickMax(floor(scale.x*former_width/100.0+0.5),1.0);
1389 *height=(size_t) MagickMax(floor(scale.y*former_height/100.0+0.5),1.0);
1390 former_width=(*width);
1391 former_height=(*height);
1393 if ((flags & AspectRatioValue) != 0)
1403 Geometry is a relative to image size and aspect ratio.
1405 (void) ParseGeometry(geometry,&geometry_info);
1406 geometry_ratio=geometry_info.rho;
1407 image_ratio=(double) former_width*
1408 PerceptibleReciprocal((double) former_height);
1409 if (geometry_ratio >= image_ratio)
1411 *width=former_width;
1412 *height=(size_t) floor((double) (former_height*image_ratio/
1413 geometry_ratio)+0.5);
1417 *width=(size_t) floor((double) (former_width*geometry_ratio/
1419 *height=former_height;
1421 former_width=(*width);
1422 former_height=(*height);
1424 if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1425 (*height == former_height)))
1427 if ((flags & RhoValue) == 0)
1428 *width=former_width;
1429 if ((flags & SigmaValue) == 0)
1430 *height=former_height;
1438 Respect aspect ratio of the image.
1440 if ((former_width == 0) || (former_height == 0))
1443 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1445 scale_factor=(double) *width/(double) former_width;
1446 if ((flags & MinimumValue) == 0)
1448 if (scale_factor > ((double) *height/(double) former_height))
1449 scale_factor=(double) *height/(double) former_height;
1452 if (scale_factor < ((double) *height/(double) former_height))
1453 scale_factor=(double) *height/(double) former_height;
1456 if ((flags & RhoValue) != 0)
1458 scale_factor=(double) *width/(double) former_width;
1459 if (((flags & MinimumValue) != 0) &&
1460 (scale_factor < ((double) *width/(double) former_height)))
1461 scale_factor=(double) *width/(double) former_height;
1465 scale_factor=(double) *height/(double) former_height;
1466 if (((flags & MinimumValue) != 0) &&
1467 (scale_factor < ((double) *height/(double) former_width)))
1468 scale_factor=(double) *height/(double) former_width;
1470 *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1471 *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
1473 if ((flags & GreaterValue) != 0)
1475 if (former_width < *width)
1476 *width=former_width;
1477 if (former_height < *height)
1478 *height=former_height;
1480 if ((flags & LessValue) != 0)
1482 if (former_width > *width)
1483 *width=former_width;
1484 if (former_height > *height)
1485 *height=former_height;
1487 if ((flags & AreaValue) != 0)
1497 Geometry is a maximum area in pixels.
1499 (void) ParseGeometry(geometry,&geometry_info);
1500 area=geometry_info.rho+sqrt(MagickEpsilon);
1501 distance=sqrt((double) former_width*former_height);
1502 scale.x=(double) former_width*PerceptibleReciprocal(distance/sqrt(area));
1503 scale.y=(double) former_height*PerceptibleReciprocal(distance/sqrt(area));
1504 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1506 *width=(unsigned long) (former_width*PerceptibleReciprocal(
1507 distance/sqrt(area)));
1508 *height=(unsigned long) (former_height*PerceptibleReciprocal(
1509 distance/sqrt(area)));
1511 former_width=(*width);
1512 former_height=(*height);
1518 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1522 % P a r s e P a g e G e o m e t r y %
1526 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1528 % ParsePageGeometry() returns a region as defined by the geometry string with
1529 % respect to the image page (canvas) dimensions.
1531 % WARNING: Percentage dimensions remain relative to the actual image
1532 % dimensions, and not canvas dimensions.
1534 % The format of the ParsePageGeometry method is:
1536 % MagickStatusType ParsePageGeometry(const Image *image,
1537 % const char *geometry,RectangeInfo *region_info,
1538 % ExceptionInfo *exception)
1540 % A description of each parameter follows:
1542 % o geometry: The geometry string (e.g. "100x100+10+10").
1544 % o region_info: the region as defined by the geometry string with
1545 % respect to the image and its gravity.
1547 % o exception: return any errors or warnings in this structure.
1550 MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1551 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1556 SetGeometry(image,region_info);
1557 if (image->page.width != 0)
1558 region_info->width=image->page.width;
1559 if (image->page.height != 0)
1560 region_info->height=image->page.height;
1561 flags=ParseAbsoluteGeometry(geometry,region_info);
1562 if (flags == NoValue)
1564 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1565 "InvalidGeometry","`%s'",geometry);
1568 if ((flags & PercentValue) != 0)
1570 region_info->width=image->columns;
1571 region_info->height=image->rows;
1573 flags=ParseMetaGeometry(geometry,®ion_info->x,®ion_info->y,
1574 ®ion_info->width,®ion_info->height);
1575 if ((((flags & WidthValue) != 0) || ((flags & HeightValue) != 0)) &&
1576 (((flags & PercentValue) != 0) || ((flags & SeparatorValue) == 0)))
1578 if ((flags & WidthValue) == 0)
1579 region_info->width=region_info->height;
1580 if ((flags & HeightValue) == 0)
1581 region_info->height=region_info->width;
1587 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1591 % P a r s e R e g i o n G e o m e t r y %
1595 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1597 % ParseRegionGeometry() returns a region as defined by the geometry string
1598 % with respect to the image dimensions and aspect ratio.
1600 % This is basically a wrapper around ParseMetaGeometry. This is typically
1601 % used to parse a geometry string to work out the final integer dimensions
1602 % for image resizing.
1604 % The format of the ParseRegionGeometry method is:
1606 % MagickStatusType ParseRegionGeometry(const Image *image,
1607 % const char *geometry,RectangeInfo *region_info,
1608 % ExceptionInfo *exception)
1610 % A description of each parameter follows:
1612 % o geometry: The geometry string (e.g. "100x100+10+10").
1614 % o region_info: the region as defined by the geometry string.
1616 % o exception: return any errors or warnings in this structure.
1619 MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1620 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1625 SetGeometry(image,region_info);
1626 flags=ParseMetaGeometry(geometry,®ion_info->x,®ion_info->y,
1627 ®ion_info->width,®ion_info->height);
1628 if (flags == NoValue)
1629 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1630 "InvalidGeometry","`%s'",geometry);
1635 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1639 % S e t G e o m e t r y %
1643 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1645 % SetGeometry() sets the geometry to its default values.
1647 % The format of the SetGeometry method is:
1649 % SetGeometry(const Image *image,RectangleInfo *geometry)
1651 % A description of each parameter follows:
1653 % o image: the image.
1655 % o geometry: the geometry.
1658 MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1660 assert(image != (Image *) NULL);
1661 assert(image->signature == MagickCoreSignature);
1662 if (image->debug != MagickFalse)
1663 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1664 assert(geometry != (RectangleInfo *) NULL);
1665 (void) memset(geometry,0,sizeof(*geometry));
1666 geometry->width=image->columns;
1667 geometry->height=image->rows;
1671 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1675 % S e t G e o m e t r y I n f o %
1679 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1681 % SetGeometryInfo sets the GeometryInfo structure to its default values.
1683 % The format of the SetGeometryInfo method is:
1685 % SetGeometryInfo(GeometryInfo *geometry_info)
1687 % A description of each parameter follows:
1689 % o geometry_info: the geometry info structure.
1692 MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1694 assert(geometry_info != (GeometryInfo *) NULL);
1695 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1696 (void) memset(geometry_info,0,sizeof(*geometry_info));