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