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-2011 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 % http://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/memory_.h"
49 #include "MagickCore/string_.h"
50 #include "MagickCore/string-private.h"
51 #include "MagickCore/token.h"
54 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
58 % G e t G e o m e t r y %
62 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
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 >).
70 % The format of the GetGeometry method is:
72 % MagickStatusType GetGeometry(const char *geometry,ssize_t *x,ssize_t *y,
73 % size_t *width,size_t *height)
75 % A description of each parameter follows:
77 % o geometry: The geometry.
79 % o x,y: The x and y offset as determined by the geometry specification.
81 % o width,height: The width and height as determined by the geometry
85 MagickExport MagickStatusType GetGeometry(const char *geometry,ssize_t *x,
86 ssize_t *y,size_t *width,size_t *height)
90 pedantic_geometry[MaxTextExtent],
103 Remove whitespace and meta characters from geometry specification.
106 if ((geometry == (char *) NULL) || (*geometry == '\0'))
108 if (strlen(geometry) >= (MaxTextExtent-1))
110 (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
111 for (p=pedantic_geometry; *p != '\0'; )
113 if (isspace((int) ((unsigned char) *p)) != 0)
115 (void) CopyMagickString(p,p+1,MaxTextExtent);
118 c=(int) ((unsigned int) *p);
124 (void) CopyMagickString(p,p+1,MaxTextExtent);
130 (void) CopyMagickString(p,p+1,MaxTextExtent);
136 (void) CopyMagickString(p,p+1,MaxTextExtent);
142 (void) CopyMagickString(p,p+1,MaxTextExtent);
148 (void) CopyMagickString(p,p+1,MaxTextExtent);
154 (void) CopyMagickString(p,p+1,MaxTextExtent);
160 (void) CopyMagickString(p,p+1,MaxTextExtent);
189 Parse width, height, x, and y.
195 value=StringToDouble(p,&q);
197 if (LocaleNCompare(p,"0x",2) == 0)
198 value=(double) strtol(p,&q,10);
199 c=(int) ((unsigned char) *q);
200 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == '\0'))
206 if (LocaleNCompare(p,"0x",2) == 0)
207 *width=(size_t) strtol(p,&p,10);
209 *width=(size_t) floor(StringToDouble(p,&p)+0.5);
214 c=(int) ((unsigned char) *p);
215 if ((c == 215) || (*p == 'x') || (*p == 'X'))
218 if ((*p != '+') && (*p != '-'))
224 *height=(size_t) floor(StringToDouble(p,&p)+0.5);
229 if ((*p == '+') || (*p == '-'))
237 *x=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
240 if ((*p == '+') || (*p == '-'))
248 *y=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
257 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
261 % G e t P a g e G e o m e t r y %
265 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
267 % GetPageGeometry() replaces any page mneumonic with the equivalent size in
270 % The format of the GetPageGeometry method is:
272 % char *GetPageGeometry(const char *page_geometry)
274 % A description of each parameter follows.
276 % o page_geometry: Specifies a pointer to an array of characters. The
277 % string is either a Postscript page name (e.g. A4) or a postscript page
278 % geometry (e.g. 612x792+36+36).
281 MagickExport char *GetPageGeometry(const char *page_geometry)
286 { "4x6", "288x432" },
287 { "5x7", "360x504" },
288 { "7x9", "504x648" },
289 { "8x10", "576x720" },
290 { "9x11", "648x792" },
291 { "9x12", "648x864" },
292 { "10x13", "720x936" },
293 { "10x14", "720x1008" },
294 { "11x17", "792x1224" },
295 { "a0", "2384x3370" },
296 { "a1", "1684x2384" },
298 { "a2", "1191x1684" },
299 { "a3", "842x1191" },
301 { "a4smaLL", "595x842" },
307 { "archa", "648x864" },
308 { "archb", "864x1296" },
309 { "archC", "1296x1728" },
310 { "archd", "1728x2592" },
311 { "arche", "2592x3456" },
312 { "b0", "2920x4127" },
313 { "b1", "2064x2920" },
315 { "b2", "1460x2064" },
316 { "b3", "1032x1460" },
317 { "b4", "729x1032" },
323 { "c0", "2599x3676" },
324 { "c1", "1837x2599" },
325 { "c2", "1298x1837" },
326 { "c3", "918x1296" },
331 { "executive", "540x720" },
332 { "flsa", "612x936" },
333 { "flse", "612x936" },
334 { "folio", "612x936" },
335 { "halfletter", "396x612" },
336 { "isob0", "2835x4008" },
337 { "isob1", "2004x2835" },
338 { "isob10", "88x125" },
339 { "isob2", "1417x2004" },
340 { "isob3", "1001x1417" },
341 { "isob4", "709x1001" },
342 { "isob5", "499x709" },
343 { "isob6", "354x499" },
344 { "isob7", "249x354" },
345 { "isob8", "176x249" },
346 { "isob9", "125x176" },
347 { "jisb0", "1030x1456" },
348 { "jisb1", "728x1030" },
349 { "jisb2", "515x728" },
350 { "jisb3", "364x515" },
351 { "jisb4", "257x364" },
352 { "jisb5", "182x257" },
353 { "jisb6", "128x182" },
354 { "ledger", "1224x792" },
355 { "legal", "612x1008" },
356 { "letter", "612x792" },
357 { "lettersmaLL", "612x792" },
358 { "quarto", "610x780" },
359 { "statement", "396x612" },
360 { "tabloid", "792x1224" },
361 { (char *) NULL, (char *) NULL }
370 assert(page_geometry != (char *) NULL);
371 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
372 page=AcquireString(page_geometry);
373 for (i=0; *PageSizes[i] != (char *) NULL; i++)
374 if (LocaleNCompare(PageSizes[i][0],page,strlen(PageSizes[i][0])) == 0)
383 Replace mneumonic with the equivalent size in dots-per-inch.
385 (void) CopyMagickString(page,PageSizes[i][1],MaxTextExtent);
386 (void) ConcatenateMagickString(page,page_geometry+
387 strlen(PageSizes[i][0]),MaxTextExtent);
388 flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
390 if ((flags & GreaterValue) == 0)
391 (void) ConcatenateMagickString(page,">",MaxTextExtent);
398 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
402 % G r a v i t y A d j u s t G e o m e t r y %
405 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
407 % GravityAdjustGeometry() adjusts the offset of a region with regard to the
408 % given: width, height and gravity; against which it is positioned.
410 % The region should also have an appropriate width and height to correctly
411 % set the right offset of the top left corner of the region.
413 % The format of the GravityAdjustGeometry method is:
415 % void GravityAdjustGeometry(const size_t width, const size_t height,
416 % const GravityType gravity,RectangleInfo *region);
418 % A description of each parameter follows:
420 % o width, height: the larger area the region is relative to
422 % o gravity: the edge/corner the current offset is relative to
424 % o region: The region requiring a offset adjustment relative to gravity
427 MagickExport void GravityAdjustGeometry(const size_t width,
428 const size_t height,const GravityType gravity,RectangleInfo *region)
430 if (region->height == 0)
431 region->height=height;
432 if (region->width == 0)
436 case NorthEastGravity:
438 case SouthEastGravity:
440 region->x=(ssize_t) (width-region->width-region->x);
448 region->x+=(ssize_t) (width/2-region->width/2);
452 case NorthWestGravity:
454 case SouthWestGravity:
460 case SouthWestGravity:
462 case SouthEastGravity:
464 region->y=(ssize_t) (height-region->height-region->y);
472 region->y+=(ssize_t) (height/2-region->height/2);
476 case NorthWestGravity:
478 case NorthEastGravity:
486 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
490 + I s G e o m e t r y %
494 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
496 % IsGeometry() returns MagickTrue if the geometry specification is valid.
497 % Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
499 % The format of the IsGeometry method is:
501 % MagickBooleanType IsGeometry(const char *geometry)
503 % A description of each parameter follows:
505 % o geometry: This string is the geometry specification.
508 MagickExport MagickBooleanType IsGeometry(const char *geometry)
516 if (geometry == (const char *) NULL)
518 flags=ParseGeometry(geometry,&geometry_info);
519 if (flags == NoValue)
520 flags=ParseGeometry(geometry+1,&geometry_info); /* i.e. +-4+-4 */
521 return(flags != NoValue ? MagickTrue : MagickFalse);
525 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
529 + I s S c e n e G e o m e t r y %
533 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
535 % IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
536 % specification (e.g. [1], [1-9], [1,7,4]).
538 % The format of the IsSceneGeometry method is:
540 % MagickBooleanType IsSceneGeometry(const char *geometry,
541 % const MagickBooleanType pedantic)
543 % A description of each parameter follows:
545 % o geometry: This string is the geometry specification.
547 % o pedantic: A value other than 0 invokes a more restrictive set of
548 % conditions for a valid specification (e.g. [1], [1-4], [4-1]).
551 MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
552 const MagickBooleanType pedantic)
560 if (geometry == (const char *) NULL)
563 value=StringToDouble(geometry,&p);
567 if (strspn(geometry,"0123456789-, ") != strlen(geometry))
569 if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
575 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
579 % P a r s e A b s o l u t e G e o m e t r y %
583 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
585 % ParseAbsoluteGeometry() returns a region as defined by the geometry string,
586 % without any modification by percentages or gravity.
588 % It currently just a wrapper around GetGeometry(), but may be expanded in
589 % the future to handle other positioning information.
591 % The format of the ParseAbsoluteGeometry method is:
593 % MagickStatusType ParseAbsoluteGeometry(const char *geometry,
594 % RectangleInfo *region_info)
596 % A description of each parameter follows:
598 % o geometry: The geometry string (e.g. "100x100+10+10").
600 % o region_info: the region as defined by the geometry string.
603 MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
604 RectangleInfo *region_info)
609 flags=GetGeometry(geometry,®ion_info->x,®ion_info->y,
610 ®ion_info->width,®ion_info->height);
615 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
619 % P a r s e A f f i n e G e o m e t r y %
623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
625 % ParseAffineGeometry() returns an affine matrix as defined by a string of 4
626 % to 6 comma/space separated floating point values.
628 % The affine matrix determinant is checked for validity of the values.
630 % The format of the ParseAffineGeometry method is:
632 % MagickStatusType ParseAffineGeometry(const char *geometry,
633 % AffineMatrix *affine_matrix,ExceptionInfo *exception)
635 % A description of each parameter follows:
637 % o geometry: The geometry string (e.g. "1.0,0.0,0.0,1.0,3.2,1.2").
639 % o affine_matrix: the affine matrix as defined by the geometry string.
641 % o exception: return any errors or warnings in this structure.
644 MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
645 AffineMatrix *affine_matrix,ExceptionInfo *exception)
648 token[MaxTextExtent];
662 GetAffineMatrix(affine_matrix);
665 for (i=0; (*p != '\0') && (i < 6); i++)
667 GetMagickToken(p,&p,token);
669 GetMagickToken(p,&p,token);
674 affine_matrix->sx=StringToDouble(token,(char **) NULL);
679 affine_matrix->rx=StringToDouble(token,(char **) NULL);
684 affine_matrix->ry=StringToDouble(token,(char **) NULL);
689 affine_matrix->sy=StringToDouble(token,(char **) NULL);
694 affine_matrix->tx=StringToDouble(token,(char **) NULL);
700 affine_matrix->ty=StringToDouble(token,(char **) NULL);
706 determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
708 if (fabs(determinant) < MagickEpsilon)
709 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
710 "InvalidGeometry","`%s'",geometry);
715 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719 % P a r s e G e o m e t r y %
723 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
725 % ParseGeometry() parses a geometry specification and returns the sigma,
726 % rho, xi, and psi values. It also returns flags that indicates which
727 % of the four values (sigma, rho, xi, psi) were located in the string, and
728 % whether the xi or pi values are negative.
730 % In addition, it reports if there are any of meta characters (%, !, <, >, @,
731 % and ^) flags present. It does not report the location of the percentage
732 % relative to the values.
734 % The format of the ParseGeometry method is:
736 % MagickStatusType ParseGeometry(const char *geometry,
737 % GeometryInfo *geometry_info)
739 % A description of each parameter follows:
741 % o geometry: The geometry string (e.g. "100x100+10+10").
743 % o geometry_info: returns the parsed width/height/x/y in this structure.
746 MagickExport MagickStatusType ParseGeometry(const char *geometry,
747 GeometryInfo *geometry_info)
751 pedantic_geometry[MaxTextExtent],
764 Remove whitespaces meta characters from geometry specification.
766 assert(geometry_info != (GeometryInfo *) NULL);
768 if ((geometry == (char *) NULL) || (*geometry == '\0'))
770 if (strlen(geometry) >= (MaxTextExtent-1))
772 (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
773 for (p=pedantic_geometry; *p != '\0'; )
775 if (isspace((int) ((unsigned char) *p)) != 0)
777 (void) CopyMagickString(p,p+1,MaxTextExtent);
780 c=(int) ((unsigned char) *p);
786 (void) CopyMagickString(p,p+1,MaxTextExtent);
792 (void) CopyMagickString(p,p+1,MaxTextExtent);
798 (void) CopyMagickString(p,p+1,MaxTextExtent);
804 (void) CopyMagickString(p,p+1,MaxTextExtent);
810 (void) CopyMagickString(p,p+1,MaxTextExtent);
816 (void) CopyMagickString(p,p+1,MaxTextExtent);
822 (void) CopyMagickString(p,p+1,MaxTextExtent);
858 Parse rho, sigma, xi, psi, and optionally chi.
864 value=StringToDouble(p,&q);
865 if (LocaleNCompare(p,"0x",2) == 0)
866 value=(double) strtol(p,&q,10);
867 c=(int) ((unsigned char) *q);
868 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
869 (*q == '/') || (*q == ':') || (*q =='\0'))
875 if (LocaleNCompare(p,"0x",2) == 0)
876 value=(double) strtol(p,&p,10);
878 value=StringToDouble(p,&p);
882 geometry_info->rho=value;
886 c=(int) ((unsigned char) *p);
887 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ',') || (*p == '/') ||
894 while (isspace((int) ((unsigned char) *p)) != 0)
896 c=(int) ((unsigned char) *q);
897 if (((c != 215) && (*q != 'x') && (*q != 'X')) || ((*p != '+') &&
901 value=StringToDouble(p,&p);
905 geometry_info->sigma=value;
909 while (isspace((int) ((unsigned char) *p)) != 0)
911 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
916 if ((*p == ',') || (*p == '/') || (*p == ':'))
919 value=StringToDouble(p,&p);
925 geometry_info->xi=value;
927 while (isspace((int) ((unsigned char) *p)) != 0)
929 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
935 if ((*p == ',') || (*p == '/') || (*p == ':'))
938 value=StringToDouble(p,&p);
944 geometry_info->psi=value;
947 while (isspace((int) ((unsigned char) *p)) != 0)
949 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
955 if ((*p == ',') || (*p == '/') || (*p == ':'))
958 value=StringToDouble(p,&p);
964 geometry_info->chi=value;
968 if (strchr(pedantic_geometry,':') != (char *) NULL)
971 Normalize sampling factor (e.g. 4:2:2 => 2x1).
973 geometry_info->rho/=geometry_info->sigma;
974 geometry_info->sigma=1.0;
975 if (geometry_info->xi == 0.0)
976 geometry_info->sigma=2.0;
978 if (((flags & SigmaValue) == 0) && ((flags & XiValue) != 0) &&
979 ((flags & PsiValue) == 0))
982 Support negative height values (e.g. 30x-20).
984 geometry_info->sigma=geometry_info->xi;
985 geometry_info->xi=0.0;
993 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
997 % P a r s e G r a v i t y G e o m e t r y %
1001 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1003 % ParseGravityGeometry() returns a region as defined by the geometry string
1004 % with respect to the given image page (canvas) dimensions and the images
1007 % This is typically used for specifing a area within a given image for
1008 % cropping images to a smaller size, chopping out rows and or columns, or
1009 % resizing and positioning overlay images.
1011 % Percentages are relative to image size and not page size, and are set to
1012 % nearest integer (pixel) size.
1014 % The format of the ParseGravityGeometry method is:
1016 % MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1017 % RectangeInfo *region_info,ExceptionInfo *exception)
1019 % A description of each parameter follows:
1021 % o geometry: The geometry string (e.g. "100x100+10+10").
1023 % o region_info: the region as defined by the geometry string with respect
1024 % to the image dimensions and its gravity.
1026 % o exception: return any errors or warnings in this structure.
1029 MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1030 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1039 SetGeometry(image,region_info);
1040 if (image->page.width != 0)
1041 region_info->width=image->page.width;
1042 if (image->page.height != 0)
1043 region_info->height=image->page.height;
1044 flags=ParseAbsoluteGeometry(geometry,region_info);
1045 if (flags == NoValue)
1047 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1048 "InvalidGeometry","`%s'",geometry);
1051 if ((flags & PercentValue) != 0)
1063 Geometry is a percentage of the image size, not canvas size
1065 if (image->gravity != UndefinedGravity)
1066 flags|=XValue | YValue;
1067 status=ParseGeometry(geometry,&geometry_info);
1068 scale.x=geometry_info.rho;
1069 if ((status & RhoValue) == 0)
1071 scale.y=geometry_info.sigma;
1072 if ((status & SigmaValue) == 0)
1074 region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1075 region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
1078 Adjust offset according to gravity setting.
1080 width=region_info->width;
1081 height=region_info->height;
1083 region_info->width=image->page.width | image->columns;
1085 region_info->height=image->page.height | image->rows;
1086 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1087 region_info->width=width;
1088 region_info->height=height;
1093 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1097 + P a r s e M e t a G e o m e t r y %
1101 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1103 % ParseMetaGeometry() is similar to GetGeometry() except the returned
1104 % geometry is modified as determined by the meta characters: %, !, <, >, @,
1105 % and ^ in relation to image resizing.
1107 % Final image dimensions are adjusted so as to preserve the aspect ratio as
1108 % much as possible, while generating a integer (pixel) size, and fitting the
1109 % image within the specified geometry width and height.
1111 % Flags are interpreted...
1112 % % geometry size is given percentage of original image size
1113 % ! do not try to preserve aspect ratio
1114 % < only enlarge images smaller that geometry
1115 % > only shrink images larger than geometry
1116 % @ Fit image to contain at most this many pixels
1117 % ^ Contain the given geometry given, (minimal dimensions given)
1119 % The format of the ParseMetaGeometry method is:
1121 % MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1122 % ssize_t *y, size_t *width,size_t *height)
1124 % A description of each parameter follows:
1126 % o geometry: The geometry string (e.g. "100x100+10+10").
1128 % o x,y: The x and y offset, set according to the geometry specification.
1130 % o width,height: The width and height of original image, modified by
1131 % the given geometry specification.
1135 static inline size_t MagickMax(const size_t x,
1143 MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1144 ssize_t *y,size_t *width,size_t *height)
1157 Ensure the image geometry is valid.
1159 assert(x != (ssize_t *) NULL);
1160 assert(y != (ssize_t *) NULL);
1161 assert(width != (size_t *) NULL);
1162 assert(height != (size_t *) NULL);
1163 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1165 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1167 Parse geometry using GetGeometry.
1169 SetGeometryInfo(&geometry_info);
1170 former_width=(*width);
1171 former_height=(*height);
1172 flags=GetGeometry(geometry,x,y,width,height);
1173 if ((flags & PercentValue) != 0)
1182 Geometry is a percentage of the image size.
1184 flags=ParseGeometry(geometry,&geometry_info);
1185 scale.x=geometry_info.rho;
1186 if ((flags & RhoValue) == 0)
1188 scale.y=geometry_info.sigma;
1189 if ((flags & SigmaValue) == 0)
1191 *width=(size_t) floor(scale.x*former_width/100.0+0.5);
1194 *height=(size_t) floor(scale.y*former_height/100.0+0.5);
1197 former_width=(*width);
1198 former_height=(*height);
1200 if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1201 (*height == former_height)))
1203 if ((flags & RhoValue) == 0)
1204 *width=former_width;
1205 if ((flags & SigmaValue) == 0)
1206 *height=former_height;
1214 Respect aspect ratio of the image.
1216 if ((former_width == 0) || (former_height == 0))
1219 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1221 scale_factor=(double) *width/(double) former_width;
1222 if ((flags & MinimumValue) == 0)
1224 if (scale_factor > ((double) *height/(double) former_height))
1225 scale_factor=(double) *height/(double) former_height;
1228 if (scale_factor < ((double) *height/(double) former_height))
1229 scale_factor=(double) *height/(double) former_height;
1232 if ((flags & RhoValue) != 0)
1234 scale_factor=(double) *width/(double) former_width;
1235 if (((flags & MinimumValue) != 0) &&
1236 (scale_factor < ((double) *width/(double) former_height)))
1237 scale_factor=(double) *width/(double) former_height;
1241 scale_factor=(double) *height/(double) former_height;
1242 if (((flags & MinimumValue) != 0) &&
1243 (scale_factor < ((double) *height/(double) former_width)))
1244 scale_factor=(double) *height/(double) former_width;
1246 *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1247 *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
1249 if ((flags & GreaterValue) != 0)
1251 if (former_width < *width)
1252 *width=former_width;
1253 if (former_height < *height)
1254 *height=former_height;
1256 if ((flags & LessValue) != 0)
1258 if (former_width > *width)
1259 *width=former_width;
1260 if (former_height > *height)
1261 *height=former_height;
1263 if ((flags & AreaValue) != 0)
1273 Geometry is a maximum area in pixels.
1275 (void) ParseGeometry(geometry,&geometry_info);
1276 area=geometry_info.rho;
1277 distance=sqrt((double) former_width*former_height);
1278 scale.x=former_width/(distance/sqrt((double) area));
1279 scale.y=former_height/(distance/sqrt((double) area));
1280 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1282 *width=(size_t) (former_width/(distance/sqrt(area)));
1283 *height=(size_t) (former_height/(distance/sqrt(area)));
1285 former_width=(*width);
1286 former_height=(*height);
1292 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1296 % P a r s e P a g e G e o m e t r y %
1300 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1302 % ParsePageGeometry() returns a region as defined by the geometry string with
1303 % respect to the image page (canvas) dimensions.
1305 % WARNING: Percentage dimensions remain relative to the actual image
1306 % dimensions, and not canvas dimensions.
1308 % The format of the ParsePageGeometry method is:
1310 % MagickStatusType ParsePageGeometry(const Image *image,
1311 % const char *geometry,RectangeInfo *region_info,
1312 % ExceptionInfo *exception)
1314 % A description of each parameter follows:
1316 % o geometry: The geometry string (e.g. "100x100+10+10").
1318 % o region_info: the region as defined by the geometry string with
1319 % respect to the image and its gravity.
1321 % o exception: return any errors or warnings in this structure.
1324 MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1325 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1330 SetGeometry(image,region_info);
1331 if (image->page.width != 0)
1332 region_info->width=image->page.width;
1333 if (image->page.height != 0)
1334 region_info->height=image->page.height;
1335 flags=ParseAbsoluteGeometry(geometry,region_info);
1336 if (flags == NoValue)
1338 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1339 "InvalidGeometry","`%s'",geometry);
1342 if ((flags & PercentValue) != 0)
1344 region_info->width=image->columns;
1345 region_info->height=image->rows;
1347 flags=ParseMetaGeometry(geometry,®ion_info->x,®ion_info->y,
1348 ®ion_info->width,®ion_info->height);
1353 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1357 % P a r s e R e g i o n G e o m e t r y %
1361 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1363 % ParseRegionGeometry() returns a region as defined by the geometry string
1364 % with respect to the image dimensions and aspect ratio.
1366 % This is basically a wrapper around ParseMetaGeometry. This is typically
1367 % used to parse a geometry string to work out the final integer dimensions
1368 % for image resizing.
1370 % The format of the ParseRegionGeometry method is:
1372 % MagickStatusType ParseRegionGeometry(const Image *image,
1373 % const char *geometry,RectangeInfo *region_info,
1374 % ExceptionInfo *exception)
1376 % A description of each parameter follows:
1378 % o geometry: The geometry string (e.g. "100x100+10+10").
1380 % o region_info: the region as defined by the geometry string.
1382 % o exception: return any errors or warnings in this structure.
1385 MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1386 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1391 SetGeometry(image,region_info);
1392 flags=ParseMetaGeometry(geometry,®ion_info->x,®ion_info->y,
1393 ®ion_info->width,®ion_info->height);
1394 if (flags == NoValue)
1395 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1396 "InvalidGeometry","`%s'",geometry);
1401 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1405 % S e t G e o m e t r y %
1409 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1411 % SetGeometry() sets the geometry to its default values.
1413 % The format of the SetGeometry method is:
1415 % SetGeometry(const Image *image,RectangleInfo *geometry)
1417 % A description of each parameter follows:
1419 % o image: the image.
1421 % o geometry: the geometry.
1424 MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1426 assert(image != (Image *) NULL);
1427 assert(image->signature == MagickSignature);
1428 if (image->debug != MagickFalse)
1429 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1430 assert(geometry != (RectangleInfo *) NULL);
1431 (void) ResetMagickMemory(geometry,0,sizeof(*geometry));
1432 geometry->width=image->columns;
1433 geometry->height=image->rows;
1437 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1441 % S e t G e o m e t r y I n f o %
1445 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1447 % SetGeometryInfo sets the GeometryInfo structure to its default values.
1449 % The format of the SetGeometryInfo method is:
1451 % SetGeometryInfo(GeometryInfo *geometry_info)
1453 % A description of each parameter follows:
1455 % o geometry_info: the geometry info structure.
1458 MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1460 assert(geometry_info != (GeometryInfo *) NULL);
1461 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1462 (void) ResetMagickMemory(geometry_info,0,sizeof(*geometry_info));