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