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