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((double) width/2.0-region->width/2.0+
568 region->x);
569 break;
570 }
571 case ForgetGravity:
572 case NorthWestGravity:
573 case WestGravity:
574 case SouthWestGravity:
575 default:
576 break;
577 }
578 switch (gravity)
579 {
580 case SouthWestGravity:
581 case SouthGravity:
582 case SouthEastGravity:
583 {
584 region->y=CastDoubleToLong((double) height-region->height-region->y);
585 break;
586 }
587 case EastGravity:
588 case WestGravity:
589 case CenterGravity:
590 {
591 region->y=CastDoubleToLong((double) height/2.0-region->height/2.0+
592 region->y);
593 break;
594 }
595 case ForgetGravity:
596 case NorthWestGravity:
597 case NorthGravity:
598 case NorthEastGravity:
599 default:
600 break;
601 }
602 return;
603}
604
605/*
606%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
607% %
608% %
609% %
610+ I s G e o m e t r y %
611% %
612% %
613% %
614%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
615%
616% IsGeometry() returns MagickTrue if the geometry specification is valid.
617% Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
618%
619% The format of the IsGeometry method is:
620%
621% MagickBooleanType IsGeometry(const char *geometry)
622%
623% A description of each parameter follows:
624%
625% o geometry: This string is the geometry specification.
626%
627*/
628MagickExport MagickBooleanType IsGeometry(const char *geometry)
629{
631 geometry_info;
632
633 MagickStatusType
634 flags;
635
636 if (geometry == (const char *) NULL)
637 return(MagickFalse);
638 flags=ParseGeometry(geometry,&geometry_info);
639 return(flags != NoValue ? MagickTrue : MagickFalse);
640}
641
642/*
643%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
644% %
645% %
646% %
647+ I s S c e n e G e o m e t r y %
648% %
649% %
650% %
651%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
652%
653% IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
654% specification (e.g. [1], [1-9], [1,7,4]).
655%
656% The format of the IsSceneGeometry method is:
657%
658% MagickBooleanType IsSceneGeometry(const char *geometry,
659% const MagickBooleanType pedantic)
660%
661% A description of each parameter follows:
662%
663% o geometry: This string is the geometry specification.
664%
665% o pedantic: A value other than 0 invokes a more restrictive set of
666% conditions for a valid specification (e.g. [1], [1-4], [4-1]).
667%
668*/
669MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
670 const MagickBooleanType pedantic)
671{
672 char
673 *p;
674
675 double
676 value;
677
678 if (geometry == (const char *) NULL)
679 return(MagickFalse);
680 p=(char *) geometry;
681 value=StringToDouble(geometry,&p);
682 if (IsNaN(value) != 0)
683 return(MagickFalse);
684 if (value > (double) MAGICK_SSIZE_MAX)
685 return(MagickFalse);
686 if (value < (double) MAGICK_SSIZE_MIN)
687 return(MagickFalse);
688 if (p == geometry)
689 return(MagickFalse);
690 if (strspn(geometry,"0123456789-, ") != strlen(geometry))
691 return(MagickFalse);
692 if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
693 return(MagickFalse);
694 return(MagickTrue);
695}
696
697/*
698%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
699% %
700% %
701% %
702+ L i s t P a g e s i z e s %
703% %
704% %
705% %
706%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
707%
708% ListPagesizes() lists the pagesizes and their associated geometry.
709%
710% The format of the ListPagesizes method is:
711%
712% MagickBooleanType ListPagesizes(FILE *file,ExceptionInfo *exception)
713%
714% A description of each parameter follows.
715%
716% o file: An pointer to the output FILE.
717%
718% o exception: return any errors or warnings in this structure.
719%
720*/
721MagickExport MagickBooleanType ListPagesizes(FILE *file,
722 ExceptionInfo *magick_unused(exception))
723{
724#define MaxMagickSpaces ((int) sizeof(Pagesizes[0].name))
725
726 const char
727 *spacer = " ";
728
729 ssize_t
730 i;
731
732 magick_unreferenced(exception);
733 if (file == (FILE *) NULL)
734 file=stdout;
735 (void) FormatLocaleFile(file,"\nPagesize Geometry \n");
736 (void) FormatLocaleFile(file,"---------------------\n");
737 for (i=0; *Pagesizes[i].name != '\0'; i++)
738 (void) FormatLocaleFile(file,"%s%.*s%s\n",Pagesizes[i].name,
739 MaxMagickSpaces-(int) Pagesizes[i].extent,spacer,Pagesizes[i].geometry);
740 return(MagickTrue);
741}
742
743/*
744%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
745% %
746% %
747% %
748% P a r s e A b s o l u t e G e o m e t r y %
749% %
750% %
751% %
752%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
753%
754% ParseAbsoluteGeometry() returns a region as defined by the geometry string,
755% without any modification by percentages or gravity.
756%
757% It currently just a wrapper around GetGeometry(), but may be expanded in
758% the future to handle other positioning information.
759%
760% The format of the ParseAbsoluteGeometry method is:
761%
762% MagickStatusType ParseAbsoluteGeometry(const char *geometry,
763% RectangleInfo *region_info)
764%
765% A description of each parameter follows:
766%
767% o geometry: The geometry string (e.g. "100x100+10+10").
768%
769% o region_info: the region as defined by the geometry string.
770%
771*/
772MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
773 RectangleInfo *region_info)
774{
775 MagickStatusType
776 flags;
777
778 flags=GetGeometry(geometry,&region_info->x,&region_info->y,
779 &region_info->width,&region_info->height);
780 return(flags);
781}
782
783/*
784%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
785% %
786% %
787% %
788% P a r s e A f f i n e G e o m e t r y %
789% %
790% %
791% %
792%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
793%
794% ParseAffineGeometry() returns an affine matrix as defined by a string of 4
795% to 6 comma/space separated floating point values.
796%
797% The affine matrix determinant is checked for validity of the values.
798%
799% The format of the ParseAffineGeometry method is:
800%
801% MagickStatusType ParseAffineGeometry(const char *geometry,
802% AffineMatrix *affine_matrix,ExceptionInfo *exception)
803%
804% A description of each parameter follows:
805%
806% o geometry: The geometry string (e.g. "1.0,0.0,0.0,1.0,3.2,1.2").
807%
808% o affine_matrix: the affine matrix as defined by the geometry string.
809%
810% o exception: return any errors or warnings in this structure.
811%
812*/
813MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
814 AffineMatrix *affine_matrix,ExceptionInfo *exception)
815{
816 char
817 token[MagickPathExtent];
818
819 const char
820 *p;
821
822 double
823 determinant;
824
825 MagickStatusType
826 flags;
827
828 ssize_t
829 i;
830
831 GetAffineMatrix(affine_matrix);
832 flags=NoValue;
833 p=(char *) geometry;
834 for (i=0; (*p != '\0') && (i < 6); i++)
835 {
836 (void) GetNextToken(p,&p,MagickPathExtent,token);
837 if (*token == ',')
838 (void) GetNextToken(p,&p,MagickPathExtent,token);
839 switch (i)
840 {
841 case 0:
842 {
843 affine_matrix->sx=StringToDouble(token,(char **) NULL);
844 break;
845 }
846 case 1:
847 {
848 affine_matrix->rx=StringToDouble(token,(char **) NULL);
849 break;
850 }
851 case 2:
852 {
853 affine_matrix->ry=StringToDouble(token,(char **) NULL);
854 break;
855 }
856 case 3:
857 {
858 affine_matrix->sy=StringToDouble(token,(char **) NULL);
859 break;
860 }
861 case 4:
862 {
863 affine_matrix->tx=StringToDouble(token,(char **) NULL);
864 flags|=XValue;
865 break;
866 }
867 case 5:
868 {
869 affine_matrix->ty=StringToDouble(token,(char **) NULL);
870 flags|=YValue;
871 break;
872 }
873 }
874 }
875 determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
876 affine_matrix->ry);
877 if (fabs(determinant) < MagickEpsilon)
878 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
879 "InvalidArgument","'%s' : 'Indeterminate Matrix'",geometry);
880 return(flags);
881}
882
883/*
884%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
885% %
886% %
887% %
888% P a r s e G e o m e t r y %
889% %
890% %
891% %
892%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
893%
894% ParseGeometry() parses a geometry specification and returns the sigma,
895% rho, xi, and psi values. It also returns flags that indicates which
896% of the four values (sigma, rho, xi, psi) were located in the string, and
897% whether the xi or pi values are negative.
898%
899% In addition, it reports if there are any of meta characters (%, !, <, >, @,
900% and ^) flags present. It does not report the location of the percentage
901% relative to the values.
902%
903% Values may also be separated by commas, colons, or slashes, and offsets.
904% Chroma subsampling definitions have to be in the form of a:b:c. Offsets may
905% be prefixed by multiple signs to make offset string substitutions easier to
906% handle from shell scripts. For example: "-10-10", "-+10-+10", or "+-10+-10"
907% will generate negative offsets, while "+10+10", "++10++10", or "--10--10"
908% will generate positive offsets.
909%
910% The format of the ParseGeometry method is:
911%
912% MagickStatusType ParseGeometry(const char *geometry,
913% GeometryInfo *geometry_info)
914%
915% A description of each parameter follows:
916%
917% o geometry: The geometry string (e.g. "100x100+10+10").
918%
919% o geometry_info: returns the parsed width/height/x/y in this structure.
920%
921*/
922MagickExport MagickStatusType ParseGeometry(const char *geometry,
923 GeometryInfo *geometry_info)
924{
925 char
926 *p,
927 pedantic_geometry[MagickPathExtent],
928 *q;
929
930 double
931 value;
932
934 coordinate;
935
936 int
937 c;
938
939 MagickStatusType
940 flags;
941
942 /*
943 Remove whitespaces meta characters from geometry specification.
944 */
945 assert(geometry_info != (GeometryInfo *) NULL);
946 (void) memset(geometry_info,0,sizeof(*geometry_info));
947 flags=NoValue;
948 if ((geometry == (char *) NULL) || (*geometry == '\0'))
949 return(flags);
950 if (strlen(geometry) >= (MagickPathExtent-1))
951 return(flags);
952 c=sscanf(geometry,"%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf",&coordinate.rho,
953 &coordinate.sigma,&coordinate.xi,&coordinate.psi);
954 if (c == 4)
955 {
956 /*
957 Special case: coordinate (e.g. 0,0 255,255).
958 */
959 geometry_info->rho=coordinate.rho;
960 geometry_info->sigma=coordinate.sigma;
961 geometry_info->xi=coordinate.xi;
962 geometry_info->psi=coordinate.psi;
963 flags|=RhoValue | SigmaValue | XiValue | PsiValue;
964 return(flags);
965 }
966 (void) CopyMagickString(pedantic_geometry,geometry,MagickPathExtent);
967 for (p=pedantic_geometry; *p != '\0'; )
968 {
969 c=(int) ((unsigned char) *p);
970 if (isspace((int) ((unsigned char) c)) != 0)
971 {
972 (void) CopyMagickString(p,p+1,MagickPathExtent);
973 continue;
974 }
975 switch (c)
976 {
977 case '%':
978 {
979 flags|=PercentValue;
980 (void) CopyMagickString(p,p+1,MagickPathExtent);
981 break;
982 }
983 case '!':
984 {
985 flags|=AspectValue;
986 (void) CopyMagickString(p,p+1,MagickPathExtent);
987 break;
988 }
989 case '<':
990 {
991 flags|=LessValue;
992 (void) CopyMagickString(p,p+1,MagickPathExtent);
993 break;
994 }
995 case '>':
996 {
997 flags|=GreaterValue;
998 (void) CopyMagickString(p,p+1,MagickPathExtent);
999 break;
1000 }
1001 case '#':
1002 {
1003 flags|=MaximumValue;
1004 (void) CopyMagickString(p,p+1,MagickPathExtent);
1005 break;
1006 }
1007 case '^':
1008 {
1009 flags|=MinimumValue;
1010 (void) CopyMagickString(p,p+1,MagickPathExtent);
1011 break;
1012 }
1013 case '@':
1014 {
1015 flags|=AreaValue;
1016 (void) CopyMagickString(p,p+1,MagickPathExtent);
1017 break;
1018 }
1019 case '(':
1020 {
1021 if (*(p+1) == ')')
1022 return(flags);
1023 (void) CopyMagickString(p,p+1,MagickPathExtent);
1024 break;
1025 }
1026 case ')':
1027 {
1028 (void) CopyMagickString(p,p+1,MagickPathExtent);
1029 break;
1030 }
1031 case 'x':
1032 case 'X':
1033 {
1034 flags|=SeparatorValue;
1035 p++;
1036 break;
1037 }
1038 case '-':
1039 case '+':
1040 case ',':
1041 case '0':
1042 case '1':
1043 case '2':
1044 case '3':
1045 case '4':
1046 case '5':
1047 case '6':
1048 case '7':
1049 case '8':
1050 case '9':
1051 case '/':
1052 case 215:
1053 case 'e':
1054 case 'E':
1055 {
1056 p++;
1057 break;
1058 }
1059 case '.':
1060 {
1061 p++;
1062 flags|=DecimalValue;
1063 break;
1064 }
1065 case ':':
1066 {
1067 p++;
1068 flags|=AspectRatioValue;
1069 break;
1070 }
1071 default:
1072 return(NoValue);
1073 }
1074 }
1075 /*
1076 Parse rho, sigma, xi, psi, and optionally chi.
1077 */
1078 p=pedantic_geometry;
1079 if (*p == '\0')
1080 return(flags);
1081 q=p;
1082 value=StringToDouble(p,&q);
1083 if (LocaleNCompare(p,"0x",2) == 0)
1084 (void) strtol(p,&q,10);
1085 c=(int) ((unsigned char) *q);
1086 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ':') ||
1087 (*q == ',') || (*q == '/') || (*q =='\0'))
1088 {
1089 /*
1090 Parse rho.
1091 */
1092 q=p;
1093 if (LocaleNCompare(p,"0x",2) == 0)
1094 value=(double) strtol(p,&p,10);
1095 else
1096 value=StringToDouble(p,&p);
1097 if (p != q)
1098 {
1099 flags|=RhoValue;
1100 geometry_info->rho=value;
1101 }
1102 }
1103 q=p;
1104 c=(int) ((unsigned char) *p);
1105 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ':') || (*p == ',') ||
1106 (*p == '/'))
1107 {
1108 /*
1109 Parse sigma.
1110 */
1111 p++;
1112 while (isspace((int) ((unsigned char) *p)) != 0)
1113 p++;
1114 c=(int) ((unsigned char) *q);
1115 if (((c != 215) && (*q != 'x') && (*q != 'X') && (*q != ':')) ||
1116 ((*p != '+') && (*p != '-')))
1117 {
1118 q=p;
1119 value=StringToDouble(p,&p);
1120 if (p != q)
1121 {
1122 flags|=SigmaValue;
1123 geometry_info->sigma=value;
1124 }
1125 }
1126 }
1127 while (isspace((int) ((unsigned char) *p)) != 0)
1128 p++;
1129 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
1130 {
1131 /*
1132 Parse xi value.
1133 */
1134 if ((*p == ',') || (*p == '/') || (*p == ':') )
1135 p++;
1136 while ((*p == '+') || (*p == '-'))
1137 {
1138 if (*p == '-')
1139 flags^=XiNegative; /* negate sign */
1140 p++;
1141 }
1142 q=p;
1143 value=StringToDouble(p,&p);
1144 if (p != q)
1145 {
1146 flags|=XiValue;
1147 if ((flags & XiNegative) != 0)
1148 value=(-value);
1149 geometry_info->xi=value;
1150 }
1151 while (isspace((int) ((unsigned char) *p)) != 0)
1152 p++;
1153 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1154 (*p == ':'))
1155 {
1156 /*
1157 Parse psi value.
1158 */
1159 if ((*p == ',') || (*p == '/') || (*p == ':'))
1160 p++;
1161 while ((*p == '+') || (*p == '-'))
1162 {
1163 if (*p == '-')
1164 flags^=PsiNegative; /* negate sign */
1165 p++;
1166 }
1167 q=p;
1168 value=StringToDouble(p,&p);
1169 if (p != q)
1170 {
1171 flags|=PsiValue;
1172 if ((flags & PsiNegative) != 0)
1173 value=(-value);
1174 geometry_info->psi=value;
1175 }
1176 }
1177 while (isspace((int) ((unsigned char) *p)) != 0)
1178 p++;
1179 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1180 (*p == ':'))
1181 {
1182 /*
1183 Parse chi value.
1184 */
1185 if ((*p == ',') || (*p == '/') || (*p == ':'))
1186 p++;
1187 while ((*p == '+') || (*p == '-'))
1188 {
1189 if (*p == '-')
1190 flags^=ChiNegative; /* negate sign */
1191 p++;
1192 }
1193 q=p;
1194 value=StringToDouble(p,&p);
1195 if (p != q)
1196 {
1197 flags|=ChiValue;
1198 if ((flags & ChiNegative) != 0)
1199 value=(-value);
1200 geometry_info->chi=value;
1201 }
1202 }
1203 }
1204 if (strchr(pedantic_geometry,':') != (char *) NULL)
1205 {
1206 /*
1207 Normalize sampling factor (e.g. 4:2:2 => 2x1).
1208 */
1209 if ((flags & SigmaValue) != 0)
1210 geometry_info->rho*=PerceptibleReciprocal(geometry_info->sigma);
1211 geometry_info->sigma=1.0;
1212 if (((flags & XiValue) != 0) && (geometry_info->xi == 0.0))
1213 geometry_info->sigma=2.0;
1214 }
1215 if (((flags & RhoValue) != 0) && ((flags & SigmaValue) == 0) &&
1216 ((flags & XiValue) != 0) && ((flags & XiNegative) != 0))
1217 {
1218 if ((flags & PsiValue) == 0)
1219 {
1220 /*
1221 Support negative height values (e.g. 30x-20).
1222 */
1223 geometry_info->sigma=geometry_info->xi;
1224 geometry_info->xi=0.0;
1225 flags|=SigmaValue;
1226 flags&=(unsigned int) (~XiValue);
1227 }
1228 else
1229 if ((flags & ChiValue) == 0)
1230 {
1231 /*
1232 Support negative height values (e.g. 30x-20+10).
1233 */
1234 geometry_info->sigma=geometry_info->xi;
1235 geometry_info->xi=geometry_info->psi;
1236 flags|=SigmaValue;
1237 flags|=XiValue;
1238 flags&=(unsigned int) (~PsiValue);
1239 }
1240 else
1241 {
1242 /*
1243 Support negative height values (e.g. 30x-20+10+10).
1244 */
1245 geometry_info->sigma=geometry_info->xi;
1246 geometry_info->xi=geometry_info->psi;
1247 geometry_info->psi=geometry_info->chi;
1248 flags|=SigmaValue;
1249 flags|=XiValue;
1250 flags|=PsiValue;
1251 flags&=(unsigned int) (~ChiValue);
1252 }
1253 }
1254 if ((flags & PercentValue) != 0)
1255 {
1256 if (((flags & SeparatorValue) == 0) && ((flags & SigmaValue) == 0))
1257 geometry_info->sigma=geometry_info->rho;
1258 if (((flags & SeparatorValue) != 0) && ((flags & RhoValue) == 0))
1259 geometry_info->rho=geometry_info->sigma;
1260 }
1261#if 0
1262 /* Debugging Geometry */
1263 (void) fprintf(stderr,"ParseGeometry...\n");
1264 (void) fprintf(stderr,"Flags: %c %c %s %s %s\n",
1265 (flags & RhoValue) ? 'W' : ' ',(flags & SigmaValue) ? 'H' : ' ',
1266 (flags & XiValue) ? ((flags & XiNegative) ? "-X" : "+X") : " ",
1267 (flags & PsiValue) ? ((flags & PsiNegative) ? "-Y" : "+Y") : " ",
1268 (flags & ChiValue) ? ((flags & ChiNegative) ? "-Z" : "+Z") : " ");
1269 (void) fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",geometry_info->rho,
1270 geometry_info->sigma,geometry_info->xi,geometry_info->psi,
1271 geometry_info->chi);
1272#endif
1273 return(flags);
1274}
1275
1276/*
1277%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1278% %
1279% %
1280% %
1281% P a r s e G r a v i t y G e o m e t r y %
1282% %
1283% %
1284% %
1285%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1286%
1287% ParseGravityGeometry() returns a region as defined by the geometry string
1288% with respect to the given image page (canvas) dimensions and the images
1289% gravity setting.
1290%
1291% This is typically used for specifying a area within a given image for
1292% cropping images to a smaller size, chopping out rows and or columns, or
1293% resizing and positioning overlay images.
1294%
1295% Percentages are relative to image size and not page size, and are set to
1296% nearest integer (pixel) size.
1297%
1298% The format of the ParseGravityGeometry method is:
1299%
1300% MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1301% RectangleInfo *region_info,ExceptionInfo *exception)
1302%
1303% A description of each parameter follows:
1304%
1305% o geometry: The geometry string (e.g. "100x100+10+10").
1306%
1307% o region_info: the region as defined by the geometry string with respect
1308% to the image dimensions and its gravity.
1309%
1310% o exception: return any errors or warnings in this structure.
1311%
1312*/
1313MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1314 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1315{
1316 MagickStatusType
1317 flags;
1318
1319 size_t
1320 height,
1321 width;
1322
1323 if (IsEventLogging() != MagickFalse)
1324 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1325 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1326 return(NoValue);
1327 SetGeometry(image,region_info);
1328 if (image->page.width != 0)
1329 region_info->width=image->page.width;
1330 if (image->page.height != 0)
1331 region_info->height=image->page.height;
1332 flags=ParseAbsoluteGeometry(geometry,region_info);
1333 if (flags == NoValue)
1334 {
1335 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1336 "InvalidGeometry","`%s'",geometry);
1337 return(flags);
1338 }
1339 if ((flags & PercentValue) != 0)
1340 {
1342 geometry_info;
1343
1344 MagickStatusType
1345 status;
1346
1347 PointInfo
1348 scale;
1349
1350 /*
1351 Geometry is a percentage of the image size, not canvas size
1352 */
1353 if (image->gravity != UndefinedGravity)
1354 flags|=XValue | YValue;
1355 status=ParseGeometry(geometry,&geometry_info);
1356 scale.x=geometry_info.rho;
1357 if ((status & RhoValue) == 0)
1358 scale.x=100.0;
1359 scale.y=geometry_info.sigma;
1360 if ((status & SigmaValue) == 0)
1361 scale.y=scale.x;
1362 region_info->width=CastDoubleToUnsigned(scale.x*image->columns/100.0+0.5);
1363 region_info->height=CastDoubleToUnsigned(scale.y*image->rows/100.0+0.5);
1364 }
1365 if ((flags & AspectRatioValue) != 0)
1366 {
1367 double
1368 geometry_ratio,
1369 image_ratio;
1370
1372 geometry_info;
1373
1374 /*
1375 Geometry is a relative to image size and aspect ratio.
1376 */
1377 if (image->gravity != UndefinedGravity)
1378 flags|=XValue | YValue;
1379 (void) ParseGeometry(geometry,&geometry_info);
1380 geometry_ratio=geometry_info.rho;
1381 image_ratio=(double) image->columns/image->rows;
1382 region_info->width=image->columns;
1383 region_info->height=image->rows;
1384 if ((flags & MaximumValue) != 0)
1385 {
1386 if (geometry_ratio < image_ratio)
1387 region_info->height=CastDoubleToUnsigned((double) image->rows*
1388 image_ratio/geometry_ratio+0.5);
1389 else
1390 region_info->width=CastDoubleToUnsigned((double) image->columns*
1391 geometry_ratio/image_ratio+0.5);
1392 }
1393 else
1394 if (geometry_ratio >= image_ratio)
1395 region_info->height=CastDoubleToUnsigned((double) image->rows*
1396 image_ratio/geometry_ratio+0.5);
1397 else
1398 region_info->width=CastDoubleToUnsigned((double) image->columns*
1399 geometry_ratio/image_ratio+0.5);
1400 }
1401 /*
1402 Adjust offset according to gravity setting.
1403 */
1404 width=region_info->width;
1405 height=region_info->height;
1406 if (width == 0)
1407 region_info->width=image->page.width | image->columns;
1408 if (height == 0)
1409 region_info->height=image->page.height | image->rows;
1410 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1411 if ((flags & LessValue) != 0)
1412 if ((region_info->width < image->columns) &&
1413 (region_info->height < image->rows))
1414 {
1415 SetGeometry(image,region_info);
1416 return(NoValue);
1417 }
1418 if ((flags & GreaterValue) != 0)
1419 if ((region_info->width > image->columns) &&
1420 (region_info->height > image->rows))
1421 {
1422 SetGeometry(image,region_info);
1423 return(NoValue);
1424 }
1425 region_info->width=width;
1426 region_info->height=height;
1427 return(flags);
1428}
1429
1430/*
1431%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1432% %
1433% %
1434% %
1435+ P a r s e M e t a G e o m e t r y %
1436% %
1437% %
1438% %
1439%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1440%
1441% ParseMetaGeometry() is similar to GetGeometry() except the returned
1442% geometry is modified as determined by the meta characters: %, !, <, >, @,
1443% :, and ^ in relation to image resizing.
1444%
1445% Final image dimensions are adjusted so as to preserve the aspect ratio as
1446% much as possible, while generating a integer (pixel) size, and fitting the
1447% image within the specified geometry width and height.
1448%
1449% Flags are interpreted...
1450% % geometry size is given percentage of original width and height given
1451% ! do not try to preserve aspect ratio
1452% < only enlarge images smaller that geometry
1453% > only shrink images larger than geometry
1454% @ fit image to contain at most this many pixels
1455% : width and height denotes an aspect ratio
1456% ^ contain the given geometry given, (minimal dimensions given)
1457%
1458% The format of the ParseMetaGeometry method is:
1459%
1460% MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1461% ssize_t *y, size_t *width,size_t *height)
1462%
1463% A description of each parameter follows:
1464%
1465% o geometry: The geometry string (e.g. "100x100+10+10").
1466%
1467% o x,y: The x and y offset, set according to the geometry specification.
1468%
1469% o width,height: The width and height of original image, modified by
1470% the given geometry specification.
1471%
1472*/
1473MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1474 ssize_t *y,size_t *width,size_t *height)
1475{
1477 geometry_info;
1478
1479 MagickStatusType
1480 flags;
1481
1482 size_t
1483 stasis_height,
1484 stasis_width;
1485
1486 /*
1487 Ensure the image geometry is valid.
1488 */
1489 assert(x != (ssize_t *) NULL);
1490 assert(y != (ssize_t *) NULL);
1491 assert(width != (size_t *) NULL);
1492 assert(height != (size_t *) NULL);
1493 if (IsEventLogging() != MagickFalse)
1494 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1495 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1496 return(NoValue);
1497 /*
1498 Parse geometry using GetGeometry.
1499 */
1500 stasis_width=(*width);
1501 stasis_height=(*height);
1502 SetGeometryInfo(&geometry_info);
1503 flags=GetGeometry(geometry,x,y,width,height);
1504 if ((flags & PercentValue) != 0)
1505 {
1506 MagickStatusType
1507 percent_flags;
1508
1509 PointInfo
1510 scale;
1511
1512 /*
1513 Geometry is a percentage of the image size.
1514 */
1515 percent_flags=ParseGeometry(geometry,&geometry_info);
1516 scale.x=geometry_info.rho;
1517 if ((percent_flags & RhoValue) == 0)
1518 scale.x=100.0;
1519 scale.y=geometry_info.sigma;
1520 if ((percent_flags & SigmaValue) == 0)
1521 scale.y=scale.x;
1522 *width=CastDoubleToUnsigned(scale.x*stasis_width/100.0+0.5);
1523 *height=CastDoubleToUnsigned(scale.y*stasis_height/100.0+0.5);
1524 stasis_width=(*width);
1525 stasis_height=(*height);
1526 }
1527 if ((flags & AspectRatioValue) != 0)
1528 {
1529 double
1530 geometry_ratio,
1531 image_ratio;
1532
1533 /*
1534 Geometry is a relative to image size and aspect ratio.
1535 */
1536 (void) ParseGeometry(geometry,&geometry_info);
1537 geometry_ratio=geometry_info.rho;
1538 image_ratio=(double) stasis_width*PerceptibleReciprocal((double)
1539 stasis_height);
1540 if (geometry_ratio >= image_ratio)
1541 {
1542 *width=stasis_width;
1543 *height=CastDoubleToUnsigned((double) (PerceptibleReciprocal(
1544 geometry_ratio)*stasis_height*image_ratio)+0.5);
1545 }
1546 else
1547 {
1548 *width=CastDoubleToUnsigned(PerceptibleReciprocal(image_ratio)*
1549 stasis_width*geometry_ratio+0.5);
1550 *height=stasis_height;
1551 }
1552 stasis_width=(*width);
1553 stasis_height=(*height);
1554 }
1555 if (((flags & AspectValue) != 0) || ((*width == stasis_width) &&
1556 (*height == stasis_height)))
1557 {
1558 if ((flags & RhoValue) == 0)
1559 *width=stasis_width;
1560 if ((flags & SigmaValue) == 0)
1561 *height=stasis_height;
1562 }
1563 else
1564 {
1565 double
1566 scale_factor;
1567
1568 /*
1569 Respect aspect ratio of the image.
1570 */
1571 if ((stasis_width == 0) || (stasis_height == 0))
1572 scale_factor=1.0;
1573 else
1574 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1575 {
1576 scale_factor=(double) *width/(double) stasis_width;
1577 if ((flags & MinimumValue) == 0)
1578 {
1579 if (scale_factor > ((double) *height/(double) stasis_height))
1580 scale_factor=(double) *height/(double) stasis_height;
1581 }
1582 else
1583 if (scale_factor < ((double) *height/(double) stasis_height))
1584 scale_factor=(double) *height/(double) stasis_height;
1585 }
1586 else
1587 if ((flags & RhoValue) != 0)
1588 {
1589 scale_factor=(double) *width/(double) stasis_width;
1590 if (((flags & MinimumValue) != 0) &&
1591 (scale_factor < ((double) *width/(double) stasis_height)))
1592 scale_factor=(double) *width/(double) stasis_height;
1593 }
1594 else
1595 {
1596 scale_factor=(double) *height/(double) stasis_height;
1597 if (((flags & MinimumValue) != 0) &&
1598 (scale_factor < ((double) *height/(double) stasis_width)))
1599 scale_factor=(double) *height/(double) stasis_width;
1600 }
1601 *width=CastDoubleToUnsigned(MagickMax(floor(scale_factor*stasis_width+
1602 0.5),1.0));
1603 *height=CastDoubleToUnsigned(MagickMax(floor(scale_factor*stasis_height+
1604 0.5),1.0));
1605 }
1606 if ((flags & GreaterValue) != 0)
1607 {
1608 if (stasis_width < *width)
1609 *width=stasis_width;
1610 if (stasis_height < *height)
1611 *height=stasis_height;
1612 }
1613 if ((flags & LessValue) != 0)
1614 {
1615 if (stasis_width > *width)
1616 *width=stasis_width;
1617 if (stasis_height > *height)
1618 *height=stasis_height;
1619 }
1620 if ((flags & AreaValue) != 0)
1621 {
1622 double
1623 area,
1624 distance;
1625
1626 PointInfo
1627 scale;
1628
1629 /*
1630 Geometry is a maximum area in pixels.
1631 */
1632 (void) ParseGeometry(geometry,&geometry_info);
1633 area=geometry_info.rho+sqrt(MagickEpsilon);
1634 distance=sqrt((double) stasis_width*stasis_height);
1635 scale.x=(double) stasis_width*PerceptibleReciprocal(distance*
1636 PerceptibleReciprocal(sqrt(area)));
1637 scale.y=(double) stasis_height*PerceptibleReciprocal(distance*
1638 PerceptibleReciprocal(sqrt(area)));
1639 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1640 {
1641 *width=CastDoubleToUnsigned((double) stasis_width*PerceptibleReciprocal(
1642 distance*PerceptibleReciprocal(sqrt(area)))+0.5);
1643 *height=CastDoubleToUnsigned((double) stasis_height*PerceptibleReciprocal(
1644 distance*PerceptibleReciprocal(sqrt(area)))+0.5);
1645 }
1646 }
1647 return(flags);
1648}
1649
1650/*
1651%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1652% %
1653% %
1654% %
1655% P a r s e P a g e G e o m e t r y %
1656% %
1657% %
1658% %
1659%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1660%
1661% ParsePageGeometry() returns a region as defined by the geometry string with
1662% respect to the image page (canvas) dimensions.
1663%
1664% WARNING: Percentage dimensions remain relative to the actual image
1665% dimensions, and not canvas dimensions.
1666%
1667% The format of the ParsePageGeometry method is:
1668%
1669% MagickStatusType ParsePageGeometry(const Image *image,
1670% const char *geometry,RectangleInfo *region_info,
1671% ExceptionInfo *exception)
1672%
1673% A description of each parameter follows:
1674%
1675% o geometry: The geometry string (e.g. "100x100+10+10").
1676%
1677% o region_info: the region as defined by the geometry string with
1678% respect to the image and its gravity.
1679%
1680% o exception: return any errors or warnings in this structure.
1681%
1682*/
1683MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1684 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1685{
1686 MagickStatusType
1687 flags;
1688
1689 SetGeometry(image,region_info);
1690 if (image->page.width != 0)
1691 region_info->width=image->page.width;
1692 if (image->page.height != 0)
1693 region_info->height=image->page.height;
1694 flags=ParseAbsoluteGeometry(geometry,region_info);
1695 if (flags == NoValue)
1696 {
1697 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1698 "InvalidGeometry","`%s'",geometry);
1699 return(flags);
1700 }
1701 if ((flags & PercentValue) != 0)
1702 {
1703 region_info->width=image->columns;
1704 region_info->height=image->rows;
1705 }
1706 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1707 &region_info->width,&region_info->height);
1708 if ((((flags & WidthValue) != 0) || ((flags & HeightValue) != 0)) &&
1709 (((flags & PercentValue) != 0) || ((flags & SeparatorValue) == 0)))
1710 {
1711 if ((flags & WidthValue) == 0)
1712 region_info->width=region_info->height;
1713 if ((flags & HeightValue) == 0)
1714 region_info->height=region_info->width;
1715 }
1716 return(flags);
1717}
1718
1719/*
1720%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1721% %
1722% %
1723% %
1724% P a r s e R e g i o n G e o m e t r y %
1725% %
1726% %
1727% %
1728%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1729%
1730% ParseRegionGeometry() returns a region as defined by the geometry string
1731% with respect to the image dimensions and aspect ratio.
1732%
1733% This is basically a wrapper around ParseMetaGeometry. This is typically
1734% used to parse a geometry string to work out the final integer dimensions
1735% for image resizing.
1736%
1737% The format of the ParseRegionGeometry method is:
1738%
1739% MagickStatusType ParseRegionGeometry(const Image *image,
1740% const char *geometry,RectangleInfo *region_info,
1741% ExceptionInfo *exception)
1742%
1743% A description of each parameter follows:
1744%
1745% o geometry: The geometry string (e.g. "100x100+10+10").
1746%
1747% o region_info: the region as defined by the geometry string.
1748%
1749% o exception: return any errors or warnings in this structure.
1750%
1751*/
1752MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1753 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1754{
1755 MagickStatusType
1756 flags;
1757
1758 SetGeometry(image,region_info);
1759 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1760 &region_info->width,&region_info->height);
1761 if (flags == NoValue)
1762 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1763 "InvalidGeometry","`%s'",geometry);
1764 return(flags);
1765}
1766
1767/*
1768%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1769% %
1770% %
1771% %
1772% S e t G e o m e t r y %
1773% %
1774% %
1775% %
1776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1777%
1778% SetGeometry() sets the geometry to its default values.
1779%
1780% The format of the SetGeometry method is:
1781%
1782% SetGeometry(const Image *image,RectangleInfo *geometry)
1783%
1784% A description of each parameter follows:
1785%
1786% o image: the image.
1787%
1788% o geometry: the geometry.
1789%
1790*/
1791MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1792{
1793 assert(image != (Image *) NULL);
1794 assert(image->signature == MagickCoreSignature);
1795 if (IsEventLogging() != MagickFalse)
1796 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1797 assert(geometry != (RectangleInfo *) NULL);
1798 (void) memset(geometry,0,sizeof(*geometry));
1799 geometry->width=image->columns;
1800 geometry->height=image->rows;
1801}
1802
1803/*
1804%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1805% %
1806% %
1807% %
1808% S e t G e o m e t r y I n f o %
1809% %
1810% %
1811% %
1812%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1813%
1814% SetGeometryInfo sets the GeometryInfo structure to its default values.
1815%
1816% The format of the SetGeometryInfo method is:
1817%
1818% SetGeometryInfo(GeometryInfo *geometry_info)
1819%
1820% A description of each parameter follows:
1821%
1822% o geometry_info: the geometry info structure.
1823%
1824*/
1825MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1826{
1827 assert(geometry_info != (GeometryInfo *) NULL);
1828 if (IsEventLogging() != MagickFalse)
1829 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1830 (void) memset(geometry_info,0,sizeof(*geometry_info));
1831}