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