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-2019 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;
370
371 size_t
372 extent;
373
374 const char
375 *geometry;
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 GetNextToken(p,&p,MagickPathExtent,token);
768 if (*token == ',')
769 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 case ')':
946 {
947 (void) CopyMagickString(p,p+1,MagickPathExtent);
948 break;
949 }
950 case 'x':
951 case 'X':
952 {
953 flags|=SeparatorValue;
954 p++;
955 break;
956 }
957 case '-':
958 case '+':
959 case ',':
960 case '0':
961 case '1':
962 case '2':
963 case '3':
964 case '4':
965 case '5':
966 case '6':
967 case '7':
968 case '8':
969 case '9':
970 case '/':
971 case 215:
972 case 'e':
973 case 'E':
974 {
975 p++;
976 break;
977 }
978 case '.':
979 {
980 p++;
981 flags|=DecimalValue;
982 break;
983 }
984 case ':':
985 {
986 p++;
987 flags|=AspectRatioValue;
988 break;
989 }
990 default:
991 return(NoValue);
992 }
993 }
994 /*
995 Parse rho, sigma, xi, psi, and optionally chi.
996 */
997 p=pedantic_geometry;
998 if (*p == '\0')
999 return(flags);
1000 q=p;
1001 value=StringToDouble(p,&q);
1002 if (LocaleNCompare(p,"0x",2) == 0)
1003 (void) strtol(p,&q,10);
1004 c=(int) ((unsigned char) *q);
1005 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ':') ||
1006 (*q == ',') || (*q == '/') || (*q =='\0'))
1007 {
1008 /*
1009 Parse rho.
1010 */
1011 q=p;
1012 if (LocaleNCompare(p,"0x",2) == 0)
1013 value=(double) strtol(p,&p,10);
1014 else
1015 value=StringToDouble(p,&p);
1016 if (p != q)
1017 {
1018 flags|=RhoValue;
1019 geometry_info->rho=value;
1020 }
1021 }
1022 q=p;
1023 c=(int) ((unsigned char) *p);
1024 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ':') || (*p == ',') ||
1025 (*p == '/'))
1026 {
1027 /*
1028 Parse sigma.
1029 */
1030 p++;
1031 while (isspace((int) ((unsigned char) *p)) != 0)
1032 p++;
1033 c=(int) ((unsigned char) *q);
1034 if (((c != 215) && (*q != 'x') && (*q != 'X') && (*q != ':')) ||
1035 ((*p != '+') && (*p != '-')))
1036 {
1037 q=p;
1038 value=StringToDouble(p,&p);
1039 if (p != q)
1040 {
1041 flags|=SigmaValue;
1042 geometry_info->sigma=value;
1043 }
1044 }
1045 }
1046 while (isspace((int) ((unsigned char) *p)) != 0)
1047 p++;
1048 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
1049 {
1050 /*
1051 Parse xi value.
1052 */
1053 if ((*p == ',') || (*p == '/') || (*p == ':') )
1054 p++;
1055 while ((*p == '+') || (*p == '-'))
1056 {
1057 if (*p == '-')
1058 flags^=XiNegative; /* negate sign */
1059 p++;
1060 }
1061 q=p;
1062 value=StringToDouble(p,&p);
1063 if (p != q)
1064 {
1065 flags|=XiValue;
1066 if ((flags & XiNegative) != 0)
1067 value=(-value);
1068 geometry_info->xi=value;
1069 }
1070 while (isspace((int) ((unsigned char) *p)) != 0)
1071 p++;
1072 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1073 (*p == ':'))
1074 {
1075 /*
1076 Parse psi value.
1077 */
1078 if ((*p == ',') || (*p == '/') || (*p == ':'))
1079 p++;
1080 while ((*p == '+') || (*p == '-'))
1081 {
1082 if (*p == '-')
1083 flags^=PsiNegative; /* negate sign */
1084 p++;
1085 }
1086 q=p;
1087 value=StringToDouble(p,&p);
1088 if (p != q)
1089 {
1090 flags|=PsiValue;
1091 if ((flags & PsiNegative) != 0)
1092 value=(-value);
1093 geometry_info->psi=value;
1094 }
1095 }
1096 while (isspace((int) ((unsigned char) *p)) != 0)
1097 p++;
1098 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1099 (*p == ':'))
1100 {
1101 /*
1102 Parse chi value.
1103 */
1104 if ((*p == ',') || (*p == '/') || (*p == ':'))
1105 p++;
1106 while ((*p == '+') || (*p == '-'))
1107 {
1108 if (*p == '-')
1109 flags^=ChiNegative; /* negate sign */
1110 p++;
1111 }
1112 q=p;
1113 value=StringToDouble(p,&p);
1114 if (p != q)
1115 {
1116 flags|=ChiValue;
1117 if ((flags & ChiNegative) != 0)
1118 value=(-value);
1119 geometry_info->chi=value;
1120 }
1121 }
1122 }
1123 if (strchr(pedantic_geometry,':') != (char *) NULL)
1124 {
1125 /*
1126 Normalize sampling factor (e.g. 4:2:2 => 2x1).
1127 */
1128 if ((flags & SigmaValue) != 0)
1129 geometry_info->rho*=PerceptibleReciprocal(geometry_info->sigma);
1130 geometry_info->sigma=1.0;
1131 if (((flags & XiValue) != 0) && (geometry_info->xi == 0.0))
1132 geometry_info->sigma=2.0;
1133 }
1134 if (((flags & RhoValue) != 0) && ((flags & SigmaValue) == 0) &&
1135 ((flags & XiValue) != 0) && ((flags & XiNegative) != 0))
1136 {
1137 if ((flags & PsiValue) == 0)
1138 {
1139 /*
1140 Support negative height values (e.g. 30x-20).
1141 */
1142 geometry_info->sigma=geometry_info->xi;
1143 geometry_info->xi=0.0;
1144 flags|=SigmaValue;
1145 flags&=(~XiValue);
1146 }
1147 else
1148 if ((flags & ChiValue) == 0)
1149 {
1150 /*
1151 Support negative height values (e.g. 30x-20+10).
1152 */
1153 geometry_info->sigma=geometry_info->xi;
1154 geometry_info->xi=geometry_info->psi;
1155 flags|=SigmaValue;
1156 flags|=XiValue;
1157 flags&=(~PsiValue);
1158 }
1159 else
1160 {
1161 /*
1162 Support negative height values (e.g. 30x-20+10+10).
1163 */
1164 geometry_info->sigma=geometry_info->xi;
1165 geometry_info->xi=geometry_info->psi;
1166 geometry_info->psi=geometry_info->chi;
1167 flags|=SigmaValue;
1168 flags|=XiValue;
1169 flags|=PsiValue;
1170 flags&=(~ChiValue);
1171 }
1172 }
1173 if ((flags & PercentValue) != 0)
1174 {
1175 if (((flags & SeparatorValue) == 0) && ((flags & SigmaValue) == 0))
1176 geometry_info->sigma=geometry_info->rho;
1177 if (((flags & SeparatorValue) != 0) && ((flags & RhoValue) == 0))
1178 geometry_info->rho=geometry_info->sigma;
1179 }
1180 #if 0
1181 /* Debugging Geometry */
1182 (void) fprintf(stderr,"ParseGeometry...\n");
1183 (void) fprintf(stderr,"Flags: %c %c %s %s %s\n",
1184 (flags & RhoValue) ? 'W' : ' ',(flags & SigmaValue) ? 'H' : ' ',
1185 (flags & XiValue) ? ((flags & XiNegative) ? "-X" : "+X") : " ",
1186 (flags & PsiValue) ? ((flags & PsiNegative) ? "-Y" : "+Y") : " ",
1187 (flags & ChiValue) ? ((flags & ChiNegative) ? "-Z" : "+Z") : " ");
1188 (void) fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",geometry_info->rho,
1189 geometry_info->sigma,geometry_info->xi,geometry_info->psi,
1190 geometry_info->chi);
1191 #endif
1192 return(flags);
1193 }
1194
1195 /*
1196 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1197 % %
1198 % %
1199 % %
1200 % P a r s e G r a v i t y G e o m e t r y %
1201 % %
1202 % %
1203 % %
1204 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1205 %
1206 % ParseGravityGeometry() returns a region as defined by the geometry string
1207 % with respect to the given image page (canvas) dimensions and the images
1208 % gravity setting.
1209 %
1210 % This is typically used for specifing a area within a given image for
1211 % cropping images to a smaller size, chopping out rows and or columns, or
1212 % resizing and positioning overlay images.
1213 %
1214 % Percentages are relative to image size and not page size, and are set to
1215 % nearest integer (pixel) size.
1216 %
1217 % The format of the ParseGravityGeometry method is:
1218 %
1219 % MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1220 % RectangeInfo *region_info,ExceptionInfo *exception)
1221 %
1222 % A description of each parameter follows:
1223 %
1224 % o geometry: The geometry string (e.g. "100x100+10+10").
1225 %
1226 % o region_info: the region as defined by the geometry string with respect
1227 % to the image dimensions and its gravity.
1228 %
1229 % o exception: return any errors or warnings in this structure.
1230 %
1231 */
ParseGravityGeometry(const Image * image,const char * geometry,RectangleInfo * region_info,ExceptionInfo * exception)1232 MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1233 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1234 {
1235 MagickStatusType
1236 flags;
1237
1238 size_t
1239 height,
1240 width;
1241
1242 SetGeometry(image,region_info);
1243 if (image->page.width != 0)
1244 region_info->width=image->page.width;
1245 if (image->page.height != 0)
1246 region_info->height=image->page.height;
1247 flags=ParseAbsoluteGeometry(geometry,region_info);
1248 if (flags == NoValue)
1249 {
1250 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1251 "InvalidGeometry","`%s'",geometry);
1252 return(flags);
1253 }
1254 if ((flags & PercentValue) != 0)
1255 {
1256 GeometryInfo
1257 geometry_info;
1258
1259 MagickStatusType
1260 status;
1261
1262 PointInfo
1263 scale;
1264
1265 /*
1266 Geometry is a percentage of the image size, not canvas size
1267 */
1268 if (image->gravity != UndefinedGravity)
1269 flags|=XValue | YValue;
1270 status=ParseGeometry(geometry,&geometry_info);
1271 scale.x=geometry_info.rho;
1272 if ((status & RhoValue) == 0)
1273 scale.x=100.0;
1274 scale.y=geometry_info.sigma;
1275 if ((status & SigmaValue) == 0)
1276 scale.y=scale.x;
1277 region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1278 region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
1279 }
1280 if ((flags & AspectRatioValue) != 0)
1281 {
1282 double
1283 geometry_ratio,
1284 image_ratio;
1285
1286 GeometryInfo
1287 geometry_info;
1288
1289 /*
1290 Geometry is a relative to image size and aspect ratio.
1291 */
1292 if (image->gravity != UndefinedGravity)
1293 flags|=XValue | YValue;
1294 (void) ParseGeometry(geometry,&geometry_info);
1295 geometry_ratio=geometry_info.rho;
1296 image_ratio=(double) image->columns/image->rows;
1297 if (geometry_ratio >= image_ratio)
1298 {
1299 region_info->width=image->columns;
1300 region_info->height=(size_t) floor((double) (image->rows*image_ratio/
1301 geometry_ratio)+0.5);
1302 }
1303 else
1304 {
1305 region_info->width=(size_t) floor((double) (image->columns*
1306 geometry_ratio/image_ratio)+0.5);
1307 region_info->height=image->rows;
1308 }
1309 }
1310 /*
1311 Adjust offset according to gravity setting.
1312 */
1313 width=region_info->width;
1314 height=region_info->height;
1315 if (width == 0)
1316 region_info->width=image->page.width | image->columns;
1317 if (height == 0)
1318 region_info->height=image->page.height | image->rows;
1319 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1320 region_info->width=width;
1321 region_info->height=height;
1322 return(flags);
1323 }
1324
1325 /*
1326 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1327 % %
1328 % %
1329 % %
1330 + P a r s e M e t a G e o m e t r y %
1331 % %
1332 % %
1333 % %
1334 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1335 %
1336 % ParseMetaGeometry() is similar to GetGeometry() except the returned
1337 % geometry is modified as determined by the meta characters: %, !, <, >, @,
1338 % :, and ^ in relation to image resizing.
1339 %
1340 % Final image dimensions are adjusted so as to preserve the aspect ratio as
1341 % much as possible, while generating a integer (pixel) size, and fitting the
1342 % image within the specified geometry width and height.
1343 %
1344 % Flags are interpreted...
1345 % % geometry size is given percentage of original width and height given
1346 % ! do not try to preserve aspect ratio
1347 % < only enlarge images smaller that geometry
1348 % > only shrink images larger than geometry
1349 % @ fit image to contain at most this many pixels
1350 % : width and height denotes an aspect ratio
1351 % ^ contain the given geometry given, (minimal dimensions given)
1352 %
1353 % The format of the ParseMetaGeometry method is:
1354 %
1355 % MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1356 % ssize_t *y, size_t *width,size_t *height)
1357 %
1358 % A description of each parameter follows:
1359 %
1360 % o geometry: The geometry string (e.g. "100x100+10+10").
1361 %
1362 % o x,y: The x and y offset, set according to the geometry specification.
1363 %
1364 % o width,height: The width and height of original image, modified by
1365 % the given geometry specification.
1366 %
1367 */
ParseMetaGeometry(const char * geometry,ssize_t * x,ssize_t * y,size_t * width,size_t * height)1368 MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1369 ssize_t *y,size_t *width,size_t *height)
1370 {
1371 GeometryInfo
1372 geometry_info;
1373
1374 MagickStatusType
1375 flags;
1376
1377 size_t
1378 former_height,
1379 former_width;
1380
1381 /*
1382 Ensure the image geometry is valid.
1383 */
1384 assert(x != (ssize_t *) NULL);
1385 assert(y != (ssize_t *) NULL);
1386 assert(width != (size_t *) NULL);
1387 assert(height != (size_t *) NULL);
1388 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1389 return(NoValue);
1390 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1391 /*
1392 Parse geometry using GetGeometry.
1393 */
1394 SetGeometryInfo(&geometry_info);
1395 former_width=(*width);
1396 former_height=(*height);
1397 flags=GetGeometry(geometry,x,y,width,height);
1398 if ((flags & PercentValue) != 0)
1399 {
1400 MagickStatusType
1401 percent_flags;
1402
1403 PointInfo
1404 scale;
1405
1406 /*
1407 Geometry is a percentage of the image size.
1408 */
1409 percent_flags=ParseGeometry(geometry,&geometry_info);
1410 scale.x=geometry_info.rho;
1411 if ((percent_flags & RhoValue) == 0)
1412 scale.x=100.0;
1413 scale.y=geometry_info.sigma;
1414 if ((percent_flags & SigmaValue) == 0)
1415 scale.y=scale.x;
1416 *width=(size_t) MagickMax(floor(scale.x*former_width/100.0+0.5),1.0);
1417 *height=(size_t) MagickMax(floor(scale.y*former_height/100.0+0.5),1.0);
1418 former_width=(*width);
1419 former_height=(*height);
1420 }
1421 if ((flags & AspectRatioValue) != 0)
1422 {
1423 double
1424 geometry_ratio,
1425 image_ratio;
1426
1427 GeometryInfo
1428 geometry_info;
1429
1430 /*
1431 Geometry is a relative to image size and aspect ratio.
1432 */
1433 (void) ParseGeometry(geometry,&geometry_info);
1434 geometry_ratio=geometry_info.rho;
1435 image_ratio=(double) former_width*
1436 PerceptibleReciprocal((double) former_height);
1437 if (geometry_ratio >= image_ratio)
1438 {
1439 *width=former_width;
1440 *height=(size_t) floor((double) (former_height*image_ratio/
1441 geometry_ratio)+0.5);
1442 }
1443 else
1444 {
1445 *width=(size_t) floor((double) (former_width*geometry_ratio/
1446 image_ratio)+0.5);
1447 *height=former_height;
1448 }
1449 former_width=(*width);
1450 former_height=(*height);
1451 }
1452 if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1453 (*height == former_height)))
1454 {
1455 if ((flags & RhoValue) == 0)
1456 *width=former_width;
1457 if ((flags & SigmaValue) == 0)
1458 *height=former_height;
1459 }
1460 else
1461 {
1462 double
1463 scale_factor;
1464
1465 /*
1466 Respect aspect ratio of the image.
1467 */
1468 if ((former_width == 0) || (former_height == 0))
1469 scale_factor=1.0;
1470 else
1471 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1472 {
1473 scale_factor=(double) *width/(double) former_width;
1474 if ((flags & MinimumValue) == 0)
1475 {
1476 if (scale_factor > ((double) *height/(double) former_height))
1477 scale_factor=(double) *height/(double) former_height;
1478 }
1479 else
1480 if (scale_factor < ((double) *height/(double) former_height))
1481 scale_factor=(double) *height/(double) former_height;
1482 }
1483 else
1484 if ((flags & RhoValue) != 0)
1485 {
1486 scale_factor=(double) *width/(double) former_width;
1487 if (((flags & MinimumValue) != 0) &&
1488 (scale_factor < ((double) *width/(double) former_height)))
1489 scale_factor=(double) *width/(double) former_height;
1490 }
1491 else
1492 {
1493 scale_factor=(double) *height/(double) former_height;
1494 if (((flags & MinimumValue) != 0) &&
1495 (scale_factor < ((double) *height/(double) former_width)))
1496 scale_factor=(double) *height/(double) former_width;
1497 }
1498 *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1499 *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
1500 }
1501 if ((flags & GreaterValue) != 0)
1502 {
1503 if (former_width < *width)
1504 *width=former_width;
1505 if (former_height < *height)
1506 *height=former_height;
1507 }
1508 if ((flags & LessValue) != 0)
1509 {
1510 if (former_width > *width)
1511 *width=former_width;
1512 if (former_height > *height)
1513 *height=former_height;
1514 }
1515 if ((flags & AreaValue) != 0)
1516 {
1517 double
1518 area,
1519 distance;
1520
1521 PointInfo
1522 scale;
1523
1524 /*
1525 Geometry is a maximum area in pixels.
1526 */
1527 (void) ParseGeometry(geometry,&geometry_info);
1528 area=geometry_info.rho+sqrt(MagickEpsilon);
1529 distance=sqrt((double) former_width*former_height);
1530 scale.x=(double) former_width*PerceptibleReciprocal(distance/sqrt(area));
1531 scale.y=(double) former_height*PerceptibleReciprocal(distance/sqrt(area));
1532 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1533 {
1534 *width=(unsigned long) (former_width*PerceptibleReciprocal(
1535 distance/sqrt(area)));
1536 *height=(unsigned long) (former_height*PerceptibleReciprocal(
1537 distance/sqrt(area)));
1538 }
1539 former_width=(*width);
1540 former_height=(*height);
1541 }
1542 return(flags);
1543 }
1544
1545 /*
1546 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1547 % %
1548 % %
1549 % %
1550 % P a r s e P a g e G e o m e t r y %
1551 % %
1552 % %
1553 % %
1554 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1555 %
1556 % ParsePageGeometry() returns a region as defined by the geometry string with
1557 % respect to the image page (canvas) dimensions.
1558 %
1559 % WARNING: Percentage dimensions remain relative to the actual image
1560 % dimensions, and not canvas dimensions.
1561 %
1562 % The format of the ParsePageGeometry method is:
1563 %
1564 % MagickStatusType ParsePageGeometry(const Image *image,
1565 % const char *geometry,RectangeInfo *region_info,
1566 % ExceptionInfo *exception)
1567 %
1568 % A description of each parameter follows:
1569 %
1570 % o geometry: The geometry string (e.g. "100x100+10+10").
1571 %
1572 % o region_info: the region as defined by the geometry string with
1573 % respect to the image and its gravity.
1574 %
1575 % o exception: return any errors or warnings in this structure.
1576 %
1577 */
ParsePageGeometry(const Image * image,const char * geometry,RectangleInfo * region_info,ExceptionInfo * exception)1578 MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1579 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1580 {
1581 MagickStatusType
1582 flags;
1583
1584 SetGeometry(image,region_info);
1585 if (image->page.width != 0)
1586 region_info->width=image->page.width;
1587 if (image->page.height != 0)
1588 region_info->height=image->page.height;
1589 flags=ParseAbsoluteGeometry(geometry,region_info);
1590 if (flags == NoValue)
1591 {
1592 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1593 "InvalidGeometry","`%s'",geometry);
1594 return(flags);
1595 }
1596 if ((flags & PercentValue) != 0)
1597 {
1598 region_info->width=image->columns;
1599 region_info->height=image->rows;
1600 }
1601 flags=ParseMetaGeometry(geometry,®ion_info->x,®ion_info->y,
1602 ®ion_info->width,®ion_info->height);
1603 if ((((flags & WidthValue) != 0) || ((flags & HeightValue) != 0)) &&
1604 (((flags & PercentValue) != 0) || ((flags & SeparatorValue) == 0)))
1605 {
1606 if ((flags & WidthValue) == 0)
1607 region_info->width=region_info->height;
1608 if ((flags & HeightValue) == 0)
1609 region_info->height=region_info->width;
1610 }
1611 return(flags);
1612 }
1613
1614 /*
1615 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1616 % %
1617 % %
1618 % %
1619 % P a r s e R e g i o n G e o m e t r y %
1620 % %
1621 % %
1622 % %
1623 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1624 %
1625 % ParseRegionGeometry() returns a region as defined by the geometry string
1626 % with respect to the image dimensions and aspect ratio.
1627 %
1628 % This is basically a wrapper around ParseMetaGeometry. This is typically
1629 % used to parse a geometry string to work out the final integer dimensions
1630 % for image resizing.
1631 %
1632 % The format of the ParseRegionGeometry method is:
1633 %
1634 % MagickStatusType ParseRegionGeometry(const Image *image,
1635 % const char *geometry,RectangeInfo *region_info,
1636 % ExceptionInfo *exception)
1637 %
1638 % A description of each parameter follows:
1639 %
1640 % o geometry: The geometry string (e.g. "100x100+10+10").
1641 %
1642 % o region_info: the region as defined by the geometry string.
1643 %
1644 % o exception: return any errors or warnings in this structure.
1645 %
1646 */
ParseRegionGeometry(const Image * image,const char * geometry,RectangleInfo * region_info,ExceptionInfo * exception)1647 MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1648 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1649 {
1650 MagickStatusType
1651 flags;
1652
1653 SetGeometry(image,region_info);
1654 flags=ParseMetaGeometry(geometry,®ion_info->x,®ion_info->y,
1655 ®ion_info->width,®ion_info->height);
1656 if (flags == NoValue)
1657 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1658 "InvalidGeometry","`%s'",geometry);
1659 return(flags);
1660 }
1661
1662 /*
1663 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1664 % %
1665 % %
1666 % %
1667 % S e t G e o m e t r y %
1668 % %
1669 % %
1670 % %
1671 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1672 %
1673 % SetGeometry() sets the geometry to its default values.
1674 %
1675 % The format of the SetGeometry method is:
1676 %
1677 % SetGeometry(const Image *image,RectangleInfo *geometry)
1678 %
1679 % A description of each parameter follows:
1680 %
1681 % o image: the image.
1682 %
1683 % o geometry: the geometry.
1684 %
1685 */
SetGeometry(const Image * image,RectangleInfo * geometry)1686 MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1687 {
1688 assert(image != (Image *) NULL);
1689 assert(image->signature == MagickCoreSignature);
1690 if (image->debug != MagickFalse)
1691 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1692 assert(geometry != (RectangleInfo *) NULL);
1693 (void) memset(geometry,0,sizeof(*geometry));
1694 geometry->width=image->columns;
1695 geometry->height=image->rows;
1696 }
1697
1698 /*
1699 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1700 % %
1701 % %
1702 % %
1703 % S e t G e o m e t r y I n f o %
1704 % %
1705 % %
1706 % %
1707 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1708 %
1709 % SetGeometryInfo sets the GeometryInfo structure to its default values.
1710 %
1711 % The format of the SetGeometryInfo method is:
1712 %
1713 % SetGeometryInfo(GeometryInfo *geometry_info)
1714 %
1715 % A description of each parameter follows:
1716 %
1717 % o geometry_info: the geometry info structure.
1718 %
1719 */
SetGeometryInfo(GeometryInfo * geometry_info)1720 MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1721 {
1722 assert(geometry_info != (GeometryInfo *) NULL);
1723 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1724 (void) memset(geometry_info,0,sizeof(*geometry_info));
1725 }
1726