blob: 3f8a41dfc1834407717cde03e3b406dcc956ce3f [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
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% John Cristy %
17% January 2003 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/constitute.h"
44#include "MagickCore/draw.h"
45#include "MagickCore/exception.h"
46#include "MagickCore/exception-private.h"
47#include "MagickCore/geometry.h"
48#include "MagickCore/memory_.h"
49#include "MagickCore/string_.h"
50#include "MagickCore/string-private.h"
51#include "MagickCore/token.h"
cristy3ed852e2009-09-05 21:47:34 +000052
53/*
54%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
55% %
56% %
57% %
58% G e t G e o m e t r y %
59% %
60% %
61% %
62%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
63%
64% GetGeometry() parses a geometry specification and returns the width,
65% height, x, and y values. It also returns flags that indicates which
66% of the four values (width, height, x, y) were located in the string, and
67% whether the x or y values are negative. In addition, there are flags to
68% report any meta characters (%, !, <, or >).
69%
anthony6634c532012-06-15 06:56:39 +000070% The value must form a proper geometry style specification of WxH+X+Y
71% of integers only, and values can not be separated by comma, colon, or
72% slash charcaters. See ParseGeometry() below.
73%
74% Offsets may be prefixed by multiple signs to make offset string
75% substitutions easier to handle from shell scripts.
76% For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
77% offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
78% offsets.
79%
cristy3ed852e2009-09-05 21:47:34 +000080% The format of the GetGeometry method is:
81%
cristybb503372010-05-27 20:51:26 +000082% MagickStatusType GetGeometry(const char *geometry,ssize_t *x,ssize_t *y,
83% size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +000084%
85% A description of each parameter follows:
86%
87% o geometry: The geometry.
88%
89% o x,y: The x and y offset as determined by the geometry specification.
90%
91% o width,height: The width and height as determined by the geometry
92% specification.
93%
94*/
cristy272f23a2011-03-30 00:37:11 +000095MagickExport MagickStatusType GetGeometry(const char *geometry,ssize_t *x,
96 ssize_t *y,size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +000097{
98 char
99 *p,
100 pedantic_geometry[MaxTextExtent],
101 *q;
102
cristy00976d82011-02-20 20:31:28 +0000103 double
104 value;
105
cristyf30f47d2011-09-06 14:40:59 +0000106 int
107 c;
108
cristy3ed852e2009-09-05 21:47:34 +0000109 MagickStatusType
110 flags;
111
112 /*
113 Remove whitespace and meta characters from geometry specification.
114 */
115 flags=NoValue;
116 if ((geometry == (char *) NULL) || (*geometry == '\0'))
117 return(flags);
cristy37e0b382011-06-07 13:31:21 +0000118 if (strlen(geometry) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000119 return(flags);
120 (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
121 for (p=pedantic_geometry; *p != '\0'; )
122 {
123 if (isspace((int) ((unsigned char) *p)) != 0)
124 {
125 (void) CopyMagickString(p,p+1,MaxTextExtent);
126 continue;
127 }
anthonyc330e152012-04-19 14:40:36 +0000128 c=(int)*p;
cristy35f75ac2011-09-06 20:33:35 +0000129 switch (c)
cristy3ed852e2009-09-05 21:47:34 +0000130 {
131 case '%':
132 {
133 flags|=PercentValue;
134 (void) CopyMagickString(p,p+1,MaxTextExtent);
135 break;
136 }
137 case '!':
138 {
139 flags|=AspectValue;
140 (void) CopyMagickString(p,p+1,MaxTextExtent);
141 break;
142 }
143 case '<':
144 {
145 flags|=LessValue;
146 (void) CopyMagickString(p,p+1,MaxTextExtent);
147 break;
148 }
149 case '>':
150 {
151 flags|=GreaterValue;
152 (void) CopyMagickString(p,p+1,MaxTextExtent);
153 break;
154 }
155 case '^':
156 {
157 flags|=MinimumValue;
158 (void) CopyMagickString(p,p+1,MaxTextExtent);
159 break;
160 }
161 case '@':
162 {
163 flags|=AreaValue;
164 (void) CopyMagickString(p,p+1,MaxTextExtent);
165 break;
166 }
167 case '(':
168 case ')':
169 {
170 (void) CopyMagickString(p,p+1,MaxTextExtent);
171 break;
172 }
173 case '-':
174 case '.':
175 case ',':
176 case '+':
177 case '0':
178 case '1':
179 case '2':
180 case '3':
181 case '4':
182 case '5':
183 case '6':
184 case '7':
185 case '8':
186 case '9':
cristy3ed852e2009-09-05 21:47:34 +0000187 case 'x':
188 case 'X':
cristy35f75ac2011-09-06 20:33:35 +0000189 case 215:
cristy3ed852e2009-09-05 21:47:34 +0000190 {
191 p++;
192 break;
193 }
194 default:
195 return(flags);
196 }
197 }
198 /*
199 Parse width, height, x, and y.
200 */
201 p=pedantic_geometry;
202 if (*p == '\0')
203 return(flags);
204 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000205 value=StringToDouble(p,&q);
cristy00976d82011-02-20 20:31:28 +0000206 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000207 if (LocaleNCompare(p,"0x",2) == 0)
cristyf53f26f2011-05-16 00:56:05 +0000208 value=(double) strtol(p,&q,10);
cristy57936e02012-07-03 00:33:28 +0000209 if ((*p != '+') && (*p != '-'))
cristy3ed852e2009-09-05 21:47:34 +0000210 {
cristy57936e02012-07-03 00:33:28 +0000211 c=(int) ((unsigned char) *q);
212 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == '\0'))
cristy3ed852e2009-09-05 21:47:34 +0000213 {
214 /*
cristy57936e02012-07-03 00:33:28 +0000215 Parse width.
cristy3ed852e2009-09-05 21:47:34 +0000216 */
217 q=p;
cristy57936e02012-07-03 00:33:28 +0000218 if (LocaleNCompare(p,"0x",2) == 0)
219 *width=(size_t) strtol(p,&p,10);
220 else
221 *width=(size_t) floor(StringToDouble(p,&p)+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000222 if (p != q)
cristy57936e02012-07-03 00:33:28 +0000223 flags|=WidthValue;
224 }
225 }
226 if ((*p != '+') && (*p != '-'))
227 {
228 c=(int) ((unsigned char) *p);
229 if ((c == 215) || (*p == 'x') || (*p == 'X'))
230 {
231 p++;
232 if ((*p != '+') && (*p != '-'))
233 {
234 /*
235 Parse height.
236 */
237 q=p;
238 *height=(size_t) floor(StringToDouble(p,&p)+0.5);
239 if (p != q)
240 flags|=HeightValue;
241 }
cristy3ed852e2009-09-05 21:47:34 +0000242 }
243 }
244 if ((*p == '+') || (*p == '-'))
245 {
246 /*
247 Parse x value.
248 */
anthony6634c532012-06-15 06:56:39 +0000249 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +0000250 {
251 if (*p == '-')
252 flags^=XNegative; /* negate sign */
253 p++;
254 }
cristy3ed852e2009-09-05 21:47:34 +0000255 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000256 *x=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
cristy3ed852e2009-09-05 21:47:34 +0000257 if (p != q)
cristy3ed852e2009-09-05 21:47:34 +0000258 {
anthony6634c532012-06-15 06:56:39 +0000259 flags|=XValue;
cristy57936e02012-07-03 00:33:28 +0000260 if ((flags & XNegative) != 0)
261 *x=(-*x);
cristy3ed852e2009-09-05 21:47:34 +0000262 }
263 }
anthony6634c532012-06-15 06:56:39 +0000264 if ((*p == '+') || (*p == '-'))
265 {
266 /*
267 Parse y value.
268 */
269 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +0000270 {
271 if (*p == '-')
272 flags^=YNegative; /* negate sign */
273 p++;
274 }
anthony6634c532012-06-15 06:56:39 +0000275 q=p;
276 *y=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
277 if (p != q)
278 {
279 flags|=YValue;
cristy57936e02012-07-03 00:33:28 +0000280 if ((flags & YNegative) != 0)
281 *y=(-*y);
anthony6634c532012-06-15 06:56:39 +0000282 }
283 }
284#if 0
285 /* Debugging Geometry */
cristy57936e02012-07-03 00:33:28 +0000286 (void) fprintf(stderr,"GetGeometry...\n");
287 (void) fprintf(stderr,"Input: %s\n",geometry);
288 (void) fprintf(stderr,"Flags: %c %c %s %s\n",
289 (flags & WidthValue) ? 'W' : ' ',(flags & HeightValue) ? 'H' : ' ',
290 (flags & XValue) ? ((flags & XNegative) ? "-X" : "+X") : " ",
291 (flags & YValue) ? ((flags & YNegative) ? "-Y" : "+Y") : " ");
292 (void) fprintf(stderr,"Geometry: %ldx%ld%+ld%+ld\n",(long) *width,(long)
293 *height,(long) *x,(long) *y);
anthony6634c532012-06-15 06:56:39 +0000294#endif
cristy3ed852e2009-09-05 21:47:34 +0000295 return(flags);
296}
297
298/*
299%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
300% %
301% %
302% %
303% G e t P a g e G e o m e t r y %
304% %
305% %
306% %
307%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
308%
309% GetPageGeometry() replaces any page mneumonic with the equivalent size in
310% picas.
311%
312% The format of the GetPageGeometry method is:
313%
314% char *GetPageGeometry(const char *page_geometry)
315%
316% A description of each parameter follows.
317%
cristy272f23a2011-03-30 00:37:11 +0000318% o page_geometry: Specifies a pointer to an array of characters. The
319% string is either a Postscript page name (e.g. A4) or a postscript page
320% geometry (e.g. 612x792+36+36).
cristy3ed852e2009-09-05 21:47:34 +0000321%
322*/
323MagickExport char *GetPageGeometry(const char *page_geometry)
324{
325 static const char
326 *PageSizes[][2]=
327 {
328 { "4x6", "288x432" },
329 { "5x7", "360x504" },
330 { "7x9", "504x648" },
331 { "8x10", "576x720" },
332 { "9x11", "648x792" },
333 { "9x12", "648x864" },
334 { "10x13", "720x936" },
335 { "10x14", "720x1008" },
336 { "11x17", "792x1224" },
337 { "a0", "2384x3370" },
338 { "a1", "1684x2384" },
339 { "a10", "73x105" },
340 { "a2", "1191x1684" },
341 { "a3", "842x1191" },
342 { "a4", "595x842" },
343 { "a4smaLL", "595x842" },
344 { "a5", "420x595" },
345 { "a6", "297x420" },
346 { "a7", "210x297" },
347 { "a8", "148x210" },
348 { "a9", "105x148" },
349 { "archa", "648x864" },
350 { "archb", "864x1296" },
351 { "archC", "1296x1728" },
352 { "archd", "1728x2592" },
353 { "arche", "2592x3456" },
354 { "b0", "2920x4127" },
355 { "b1", "2064x2920" },
356 { "b10", "91x127" },
357 { "b2", "1460x2064" },
358 { "b3", "1032x1460" },
359 { "b4", "729x1032" },
360 { "b5", "516x729" },
361 { "b6", "363x516" },
362 { "b7", "258x363" },
363 { "b8", "181x258" },
364 { "b9", "127x181" },
365 { "c0", "2599x3676" },
366 { "c1", "1837x2599" },
367 { "c2", "1298x1837" },
368 { "c3", "918x1296" },
369 { "c4", "649x918" },
370 { "c5", "459x649" },
371 { "c6", "323x459" },
372 { "c7", "230x323" },
373 { "executive", "540x720" },
374 { "flsa", "612x936" },
375 { "flse", "612x936" },
376 { "folio", "612x936" },
377 { "halfletter", "396x612" },
378 { "isob0", "2835x4008" },
379 { "isob1", "2004x2835" },
380 { "isob10", "88x125" },
381 { "isob2", "1417x2004" },
382 { "isob3", "1001x1417" },
383 { "isob4", "709x1001" },
384 { "isob5", "499x709" },
385 { "isob6", "354x499" },
386 { "isob7", "249x354" },
387 { "isob8", "176x249" },
388 { "isob9", "125x176" },
389 { "jisb0", "1030x1456" },
390 { "jisb1", "728x1030" },
391 { "jisb2", "515x728" },
392 { "jisb3", "364x515" },
393 { "jisb4", "257x364" },
394 { "jisb5", "182x257" },
395 { "jisb6", "128x182" },
396 { "ledger", "1224x792" },
397 { "legal", "612x1008" },
398 { "letter", "612x792" },
399 { "lettersmaLL", "612x792" },
400 { "quarto", "610x780" },
401 { "statement", "396x612" },
402 { "tabloid", "792x1224" },
403 { (char *) NULL, (char *) NULL }
404 };
405
406 char
407 *page;
408
cristybb503372010-05-27 20:51:26 +0000409 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000410 i;
411
412 assert(page_geometry != (char *) NULL);
413 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
414 page=AcquireString(page_geometry);
415 for (i=0; *PageSizes[i] != (char *) NULL; i++)
416 if (LocaleNCompare(PageSizes[i][0],page,strlen(PageSizes[i][0])) == 0)
417 {
418 RectangleInfo
419 geometry;
420
421 MagickStatusType
422 flags;
423
424 /*
425 Replace mneumonic with the equivalent size in dots-per-inch.
426 */
427 (void) CopyMagickString(page,PageSizes[i][1],MaxTextExtent);
428 (void) ConcatenateMagickString(page,page_geometry+
429 strlen(PageSizes[i][0]),MaxTextExtent);
430 flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
431 &geometry.height);
432 if ((flags & GreaterValue) == 0)
433 (void) ConcatenateMagickString(page,">",MaxTextExtent);
434 break;
435 }
436 return(page);
437}
438
439/*
440%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
441% %
442% %
443% %
444% G r a v i t y A d j u s t G e o m e t r y %
445% %
cristy272f23a2011-03-30 00:37:11 +0000446% % % %
cristy3ed852e2009-09-05 21:47:34 +0000447%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
448%
449% GravityAdjustGeometry() adjusts the offset of a region with regard to the
450% given: width, height and gravity; against which it is positioned.
451%
452% The region should also have an appropriate width and height to correctly
453% set the right offset of the top left corner of the region.
454%
455% The format of the GravityAdjustGeometry method is:
456%
cristy272f23a2011-03-30 00:37:11 +0000457% void GravityAdjustGeometry(const size_t width, const size_t height,
458% const GravityType gravity,RectangleInfo *region);
cristy3ed852e2009-09-05 21:47:34 +0000459%
460% A description of each parameter follows:
461%
462% o width, height: the larger area the region is relative to
463%
464% o gravity: the edge/corner the current offset is relative to
465%
466% o region: The region requiring a offset adjustment relative to gravity
467%
468*/
cristybb503372010-05-27 20:51:26 +0000469MagickExport void GravityAdjustGeometry(const size_t width,
470 const size_t height,const GravityType gravity,RectangleInfo *region)
cristy3ed852e2009-09-05 21:47:34 +0000471{
472 if (region->height == 0)
473 region->height=height;
474 if (region->width == 0)
475 region->width=width;
476 switch (gravity)
477 {
478 case NorthEastGravity:
479 case EastGravity:
480 case SouthEastGravity:
481 {
cristybb503372010-05-27 20:51:26 +0000482 region->x=(ssize_t) (width-region->width-region->x);
cristy3ed852e2009-09-05 21:47:34 +0000483 break;
484 }
485 case NorthGravity:
486 case SouthGravity:
487 case CenterGravity:
cristy3ed852e2009-09-05 21:47:34 +0000488 {
cristybb503372010-05-27 20:51:26 +0000489 region->x+=(ssize_t) (width/2-region->width/2);
cristy3ed852e2009-09-05 21:47:34 +0000490 break;
491 }
492 case ForgetGravity:
493 case NorthWestGravity:
494 case WestGravity:
495 case SouthWestGravity:
496 default:
497 break;
498 }
499 switch (gravity)
500 {
501 case SouthWestGravity:
502 case SouthGravity:
503 case SouthEastGravity:
504 {
cristybb503372010-05-27 20:51:26 +0000505 region->y=(ssize_t) (height-region->height-region->y);
cristy3ed852e2009-09-05 21:47:34 +0000506 break;
507 }
508 case EastGravity:
509 case WestGravity:
510 case CenterGravity:
cristy3ed852e2009-09-05 21:47:34 +0000511 {
cristybb503372010-05-27 20:51:26 +0000512 region->y+=(ssize_t) (height/2-region->height/2);
cristy3ed852e2009-09-05 21:47:34 +0000513 break;
514 }
515 case ForgetGravity:
516 case NorthWestGravity:
517 case NorthGravity:
518 case NorthEastGravity:
519 default:
520 break;
521 }
522 return;
523}
524
525/*
526%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
527% %
528% %
529% %
530+ I s G e o m e t r y %
531% %
532% %
533% %
534%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
535%
536% IsGeometry() returns MagickTrue if the geometry specification is valid.
537% Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
538%
539% The format of the IsGeometry method is:
540%
541% MagickBooleanType IsGeometry(const char *geometry)
542%
543% A description of each parameter follows:
544%
545% o geometry: This string is the geometry specification.
546%
547*/
548MagickExport MagickBooleanType IsGeometry(const char *geometry)
549{
550 GeometryInfo
551 geometry_info;
552
553 MagickStatusType
554 flags;
555
556 if (geometry == (const char *) NULL)
557 return(MagickFalse);
558 flags=ParseGeometry(geometry,&geometry_info);
cristy3ed852e2009-09-05 21:47:34 +0000559 return(flags != NoValue ? MagickTrue : MagickFalse);
560}
561
562/*
563%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
564% %
565% %
566% %
567+ I s S c e n e G e o m e t r y %
568% %
569% %
570% %
571%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
572%
573% IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
574% specification (e.g. [1], [1-9], [1,7,4]).
575%
576% The format of the IsSceneGeometry method is:
577%
578% MagickBooleanType IsSceneGeometry(const char *geometry,
579% const MagickBooleanType pedantic)
580%
581% A description of each parameter follows:
582%
583% o geometry: This string is the geometry specification.
584%
585% o pedantic: A value other than 0 invokes a more restrictive set of
586% conditions for a valid specification (e.g. [1], [1-4], [4-1]).
587%
588*/
589MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
590 const MagickBooleanType pedantic)
591{
592 char
593 *p;
594
cristy00976d82011-02-20 20:31:28 +0000595 double
596 value;
597
cristy3ed852e2009-09-05 21:47:34 +0000598 if (geometry == (const char *) NULL)
599 return(MagickFalse);
600 p=(char *) geometry;
cristydbdd0e32011-11-04 23:29:40 +0000601 value=StringToDouble(geometry,&p);
cristy00976d82011-02-20 20:31:28 +0000602 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000603 if (p == geometry)
604 return(MagickFalse);
605 if (strspn(geometry,"0123456789-, ") != strlen(geometry))
606 return(MagickFalse);
607 if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
608 return(MagickFalse);
609 return(MagickTrue);
610}
611
612/*
613%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
614% %
615% %
616% %
617% P a r s e A b s o l u t e G e o m e t r y %
618% %
619% %
620% %
621%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
622%
anthonyd58e60c2011-03-30 00:24:34 +0000623% ParseAbsoluteGeometry() returns a region as defined by the geometry string,
624% without any modification by percentages or gravity.
625%
626% It currently just a wrapper around GetGeometry(), but may be expanded in
627% the future to handle other positioning information.
cristy3ed852e2009-09-05 21:47:34 +0000628%
629% The format of the ParseAbsoluteGeometry method is:
630%
631% MagickStatusType ParseAbsoluteGeometry(const char *geometry,
632% RectangleInfo *region_info)
633%
634% A description of each parameter follows:
635%
anthonyd58e60c2011-03-30 00:24:34 +0000636% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000637%
638% o region_info: the region as defined by the geometry string.
639%
640*/
641MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
642 RectangleInfo *region_info)
643{
644 MagickStatusType
645 flags;
646
647 flags=GetGeometry(geometry,&region_info->x,&region_info->y,
648 &region_info->width,&region_info->height);
649 return(flags);
650}
651
652/*
653%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
654% %
655% %
656% %
657% P a r s e A f f i n e G e o m e t r y %
658% %
659% %
660% %
661%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
662%
cristy272f23a2011-03-30 00:37:11 +0000663% ParseAffineGeometry() returns an affine matrix as defined by a string of 4
664% to 6 comma/space separated floating point values.
anthonyd58e60c2011-03-30 00:24:34 +0000665%
666% The affine matrix determinant is checked for validity of the values.
cristy3ed852e2009-09-05 21:47:34 +0000667%
668% The format of the ParseAffineGeometry method is:
669%
670% MagickStatusType ParseAffineGeometry(const char *geometry,
671% AffineMatrix *affine_matrix,ExceptionInfo *exception)
672%
673% A description of each parameter follows:
674%
anthonyd58e60c2011-03-30 00:24:34 +0000675% o geometry: The geometry string (e.g. "1.0,0.0,0.0,1.0,3.2,1.2").
cristy3ed852e2009-09-05 21:47:34 +0000676%
677% o affine_matrix: the affine matrix as defined by the geometry string.
678%
679% o exception: return any errors or warnings in this structure.
680%
681*/
682MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
683 AffineMatrix *affine_matrix,ExceptionInfo *exception)
684{
685 char
686 token[MaxTextExtent];
687
688 const char
689 *p;
690
691 double
692 determinant;
693
694 MagickStatusType
695 flags;
696
cristybb503372010-05-27 20:51:26 +0000697 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000698 i;
699
700 GetAffineMatrix(affine_matrix);
701 flags=NoValue;
702 p=(char *) geometry;
703 for (i=0; (*p != '\0') && (i < 6); i++)
704 {
705 GetMagickToken(p,&p,token);
706 if (*token == ',')
707 GetMagickToken(p,&p,token);
708 switch (i)
709 {
cristyc1acd842011-05-19 23:05:47 +0000710 case 0:
711 {
cristydbdd0e32011-11-04 23:29:40 +0000712 affine_matrix->sx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000713 break;
714 }
715 case 1:
716 {
cristydbdd0e32011-11-04 23:29:40 +0000717 affine_matrix->rx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000718 break;
719 }
720 case 2:
721 {
cristydbdd0e32011-11-04 23:29:40 +0000722 affine_matrix->ry=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000723 break;
724 }
725 case 3:
726 {
cristydbdd0e32011-11-04 23:29:40 +0000727 affine_matrix->sy=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000728 break;
729 }
730 case 4:
731 {
cristydbdd0e32011-11-04 23:29:40 +0000732 affine_matrix->tx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000733 flags|=XValue;
734 break;
735 }
736 case 5:
737 {
cristydbdd0e32011-11-04 23:29:40 +0000738 affine_matrix->ty=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000739 flags|=YValue;
740 break;
741 }
cristy3ed852e2009-09-05 21:47:34 +0000742 }
743 }
cristy57936e02012-07-03 00:33:28 +0000744 determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
745 affine_matrix->ry);
cristy3ed852e2009-09-05 21:47:34 +0000746 if (fabs(determinant) < MagickEpsilon)
747 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony92c93bd2012-03-19 14:02:47 +0000748 "InvalidArgument","'%s' : 'Indeterminate Matrix'",geometry);
cristy3ed852e2009-09-05 21:47:34 +0000749 return(flags);
750}
751
752/*
753%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
754% %
755% %
756% %
757% P a r s e G e o m e t r y %
758% %
759% %
760% %
761%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
762%
763% ParseGeometry() parses a geometry specification and returns the sigma,
764% rho, xi, and psi values. It also returns flags that indicates which
765% of the four values (sigma, rho, xi, psi) were located in the string, and
anthonyd58e60c2011-03-30 00:24:34 +0000766% whether the xi or pi values are negative.
767%
768% In addition, it reports if there are any of meta characters (%, !, <, >, @,
769% and ^) flags present. It does not report the location of the percentage
770% relative to the values.
cristy3ed852e2009-09-05 21:47:34 +0000771%
anthony6634c532012-06-15 06:56:39 +0000772% Values may also be seperated by commas, colons, or slashes, and offsets.
773% Offsets may be prefixed by multiple signs to make offset string
774% substitutions easier to handle from shell scripts.
775% For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
776% offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
777% offsets.
778%
cristy3ed852e2009-09-05 21:47:34 +0000779% The format of the ParseGeometry method is:
780%
781% MagickStatusType ParseGeometry(const char *geometry,
782% GeometryInfo *geometry_info)
783%
784% A description of each parameter follows:
785%
anthonyd58e60c2011-03-30 00:24:34 +0000786% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000787%
788% o geometry_info: returns the parsed width/height/x/y in this structure.
789%
790*/
791MagickExport MagickStatusType ParseGeometry(const char *geometry,
792 GeometryInfo *geometry_info)
793{
794 char
795 *p,
796 pedantic_geometry[MaxTextExtent],
797 *q;
798
799 double
800 value;
801
cristyf30f47d2011-09-06 14:40:59 +0000802 int
803 c;
804
cristy3ed852e2009-09-05 21:47:34 +0000805 MagickStatusType
806 flags;
807
808 /*
809 Remove whitespaces meta characters from geometry specification.
810 */
811 assert(geometry_info != (GeometryInfo *) NULL);
812 flags=NoValue;
813 if ((geometry == (char *) NULL) || (*geometry == '\0'))
814 return(flags);
cristy37e0b382011-06-07 13:31:21 +0000815 if (strlen(geometry) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000816 return(flags);
817 (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
818 for (p=pedantic_geometry; *p != '\0'; )
819 {
820 if (isspace((int) ((unsigned char) *p)) != 0)
821 {
822 (void) CopyMagickString(p,p+1,MaxTextExtent);
823 continue;
824 }
cristy35abcb72011-09-06 20:36:15 +0000825 c=(int) ((unsigned char) *p);
826 switch (c)
cristy3ed852e2009-09-05 21:47:34 +0000827 {
828 case '%':
829 {
830 flags|=PercentValue;
831 (void) CopyMagickString(p,p+1,MaxTextExtent);
832 break;
833 }
834 case '!':
835 {
836 flags|=AspectValue;
837 (void) CopyMagickString(p,p+1,MaxTextExtent);
838 break;
839 }
840 case '<':
841 {
842 flags|=LessValue;
843 (void) CopyMagickString(p,p+1,MaxTextExtent);
844 break;
845 }
846 case '>':
847 {
848 flags|=GreaterValue;
849 (void) CopyMagickString(p,p+1,MaxTextExtent);
850 break;
851 }
852 case '^':
853 {
854 flags|=MinimumValue;
855 (void) CopyMagickString(p,p+1,MaxTextExtent);
856 break;
857 }
858 case '@':
859 {
860 flags|=AreaValue;
861 (void) CopyMagickString(p,p+1,MaxTextExtent);
862 break;
863 }
864 case '(':
865 case ')':
866 {
867 (void) CopyMagickString(p,p+1,MaxTextExtent);
868 break;
869 }
870 case '-':
871 case '+':
872 case ',':
873 case '0':
874 case '1':
875 case '2':
876 case '3':
877 case '4':
878 case '5':
879 case '6':
880 case '7':
881 case '8':
882 case '9':
cristy3ed852e2009-09-05 21:47:34 +0000883 case 'x':
884 case 'X':
885 case '/':
886 case ':':
cristy35f75ac2011-09-06 20:33:35 +0000887 case 215:
cristy3ed852e2009-09-05 21:47:34 +0000888 {
889 p++;
890 break;
891 }
892 case '.':
893 {
894 p++;
895 flags|=DecimalValue;
896 break;
897 }
898 default:
899 return(flags);
900 }
901 }
902 /*
903 Parse rho, sigma, xi, psi, and optionally chi.
904 */
905 p=pedantic_geometry;
906 if (*p == '\0')
907 return(flags);
908 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000909 value=StringToDouble(p,&q);
cristy3ed852e2009-09-05 21:47:34 +0000910 if (LocaleNCompare(p,"0x",2) == 0)
911 value=(double) strtol(p,&q,10);
cristy35f75ac2011-09-06 20:33:35 +0000912 c=(int) ((unsigned char) *q);
913 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
anthonyd1303502010-05-11 12:00:43 +0000914 (*q == '/') || (*q == ':') || (*q =='\0'))
cristy3ed852e2009-09-05 21:47:34 +0000915 {
916 /*
917 Parse rho.
918 */
919 q=p;
920 if (LocaleNCompare(p,"0x",2) == 0)
921 value=(double) strtol(p,&p,10);
922 else
cristydbdd0e32011-11-04 23:29:40 +0000923 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000924 if (p != q)
925 {
926 flags|=RhoValue;
927 geometry_info->rho=value;
928 }
929 }
930 q=p;
cristy35f75ac2011-09-06 20:33:35 +0000931 c=(int) ((unsigned char) *p);
932 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ',') || (*p == '/') ||
cristyf30f47d2011-09-06 14:40:59 +0000933 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000934 {
935 /*
936 Parse sigma.
937 */
938 p++;
939 while (isspace((int) ((unsigned char) *p)) != 0)
940 p++;
cristy35f75ac2011-09-06 20:33:35 +0000941 c=(int) ((unsigned char) *q);
942 if (((c != 215) && (*q != 'x') && (*q != 'X')) || ((*p != '+') &&
cristyf30f47d2011-09-06 14:40:59 +0000943 (*p != '-')))
cristy3ed852e2009-09-05 21:47:34 +0000944 {
945 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000946 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000947 if (p != q)
948 {
949 flags|=SigmaValue;
950 geometry_info->sigma=value;
951 }
952 }
953 }
954 while (isspace((int) ((unsigned char) *p)) != 0)
955 p++;
anthonyd1303502010-05-11 12:00:43 +0000956 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000957 {
958 /*
959 Parse xi value.
960 */
anthony6634c532012-06-15 06:56:39 +0000961 if ((*p == ',') || (*p == '/') || (*p == ':') )
cristy3ed852e2009-09-05 21:47:34 +0000962 p++;
anthony6634c532012-06-15 06:56:39 +0000963 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +0000964 {
965 if (*p == '-')
966 flags^=XiNegative; /* negate sign */
967 p++;
968 }
cristy3ed852e2009-09-05 21:47:34 +0000969 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000970 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000971 if (p != q)
972 {
973 flags|=XiValue;
cristy57936e02012-07-03 00:33:28 +0000974 if ((flags & XiNegative) != 0)
975 value=(-value);
cristy3ed852e2009-09-05 21:47:34 +0000976 geometry_info->xi=value;
977 }
978 while (isspace((int) ((unsigned char) *p)) != 0)
979 p++;
980 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +0000981 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000982 {
983 /*
984 Parse psi value.
985 */
anthony6634c532012-06-15 06:56:39 +0000986 if ((*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000987 p++;
anthony6634c532012-06-15 06:56:39 +0000988 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +0000989 {
990 if (*p == '-')
991 flags^=PsiNegative; /* negate sign */
992 p++;
993 }
cristy3ed852e2009-09-05 21:47:34 +0000994 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000995 value=StringToDouble(p,&p);
cristy57936e02012-07-03 00:33:28 +0000996 if (p != q)
997 {
998 flags|=PsiValue;
999 if ((flags & PsiNegative) != 0)
1000 value=(-value);
1001 geometry_info->psi=value;
1002 }
1003 }
cristy3ed852e2009-09-05 21:47:34 +00001004 while (isspace((int) ((unsigned char) *p)) != 0)
1005 p++;
1006 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +00001007 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +00001008 {
1009 /*
1010 Parse chi value.
1011 */
anthony6634c532012-06-15 06:56:39 +00001012 if ((*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +00001013 p++;
anthony6634c532012-06-15 06:56:39 +00001014 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +00001015 {
1016 if (*p == '-')
1017 flags^=ChiNegative; /* negate sign */
1018 p++;
1019 }
cristy3ed852e2009-09-05 21:47:34 +00001020 q=p;
cristydbdd0e32011-11-04 23:29:40 +00001021 value=StringToDouble(p,&p);
cristy57936e02012-07-03 00:33:28 +00001022 if (p != q)
1023 {
1024 flags|=ChiValue;
1025 if ((flags & ChiNegative) != 0)
1026 value=(-value);
1027 geometry_info->chi=value;
1028 }
cristy3ed852e2009-09-05 21:47:34 +00001029 }
1030 }
1031 if (strchr(pedantic_geometry,':') != (char *) NULL)
1032 {
1033 /*
1034 Normalize sampling factor (e.g. 4:2:2 => 2x1).
1035 */
1036 geometry_info->rho/=geometry_info->sigma;
1037 geometry_info->sigma=1.0;
1038 if (geometry_info->xi == 0.0)
1039 geometry_info->sigma=2.0;
1040 }
1041 if (((flags & SigmaValue) == 0) && ((flags & XiValue) != 0) &&
1042 ((flags & PsiValue) == 0))
1043 {
1044 /*
1045 Support negative height values (e.g. 30x-20).
1046 */
1047 geometry_info->sigma=geometry_info->xi;
1048 geometry_info->xi=0.0;
1049 flags|=SigmaValue;
1050 flags&=(~XiValue);
1051 }
anthony6634c532012-06-15 06:56:39 +00001052#if 0
1053 /* Debugging Geometry */
cristy57936e02012-07-03 00:33:28 +00001054 (void) fprintf(stderr,"ParseGeometry...\n");
1055 (void) fprintf(stderr,"Flags: %c %c %s %s %s\n",
1056 (flags & RhoValue) ? 'W' : ' ',(flags & SigmaValue) ? 'H' : ' ',
1057 (flags & XiValue) ? ((flags & XiNegative) ? "-X" : "+X") : " ",
1058 (flags & PsiValue) ? ((flags & PsiNegative) ? "-Y" : "+Y") : " ",
1059 (flags & ChiValue) ? ((flags & ChiNegative) ? "-Z" : "+Z") : " ");
1060 (void) fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",geometry_info->rho,
1061 geometry_info->sigma,geometry_info->xi,geometry_info->psi,
1062 geometry_info->chi);
anthony6634c532012-06-15 06:56:39 +00001063#endif
cristy3ed852e2009-09-05 21:47:34 +00001064 return(flags);
1065}
1066
1067/*
1068%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1069% %
1070% %
1071% %
1072% P a r s e G r a v i t y G e o m e t r y %
1073% %
1074% %
1075% %
1076%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1077%
1078% ParseGravityGeometry() returns a region as defined by the geometry string
anthonyd58e60c2011-03-30 00:24:34 +00001079% with respect to the given image page (canvas) dimensions and the images
1080% gravity setting.
1081%
1082% This is typically used for specifing a area within a given image for
1083% cropping images to a smaller size, chopping out rows and or columns, or
1084% resizing and positioning overlay images.
1085%
1086% Percentages are relative to image size and not page size, and are set to
1087% nearest integer (pixel) size.
cristy3ed852e2009-09-05 21:47:34 +00001088%
1089% The format of the ParseGravityGeometry method is:
1090%
1091% MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1092% RectangeInfo *region_info,ExceptionInfo *exception)
1093%
1094% A description of each parameter follows:
1095%
anthonyd58e60c2011-03-30 00:24:34 +00001096% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001097%
cristy272f23a2011-03-30 00:37:11 +00001098% o region_info: the region as defined by the geometry string with respect
1099% to the image dimensions and its gravity.
cristy3ed852e2009-09-05 21:47:34 +00001100%
1101% o exception: return any errors or warnings in this structure.
1102%
1103*/
1104MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1105 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1106{
1107 MagickStatusType
1108 flags;
1109
cristybb503372010-05-27 20:51:26 +00001110 size_t
cristy3ed852e2009-09-05 21:47:34 +00001111 height,
1112 width;
1113
1114 SetGeometry(image,region_info);
1115 if (image->page.width != 0)
1116 region_info->width=image->page.width;
1117 if (image->page.height != 0)
1118 region_info->height=image->page.height;
1119 flags=ParseAbsoluteGeometry(geometry,region_info);
1120 if (flags == NoValue)
1121 {
1122 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001123 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001124 return(flags);
1125 }
1126 if ((flags & PercentValue) != 0)
1127 {
1128 GeometryInfo
1129 geometry_info;
1130
1131 MagickStatusType
1132 status;
1133
1134 PointInfo
1135 scale;
1136
1137 /*
anthonyd58e60c2011-03-30 00:24:34 +00001138 Geometry is a percentage of the image size, not canvas size
cristy3ed852e2009-09-05 21:47:34 +00001139 */
1140 if (image->gravity != UndefinedGravity)
1141 flags|=XValue | YValue;
1142 status=ParseGeometry(geometry,&geometry_info);
1143 scale.x=geometry_info.rho;
1144 if ((status & RhoValue) == 0)
1145 scale.x=100.0;
1146 scale.y=geometry_info.sigma;
1147 if ((status & SigmaValue) == 0)
1148 scale.y=scale.x;
cristy272f23a2011-03-30 00:37:11 +00001149 region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1150 region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001151 }
1152 /*
1153 Adjust offset according to gravity setting.
1154 */
1155 width=region_info->width;
1156 height=region_info->height;
1157 if (width == 0)
1158 region_info->width=image->page.width | image->columns;
1159 if (height == 0)
1160 region_info->height=image->page.height | image->rows;
1161 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1162 region_info->width=width;
1163 region_info->height=height;
1164 return(flags);
1165}
1166
1167/*
1168%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1169% %
1170% %
1171% %
1172+ P a r s e M e t a G e o m e t r y %
1173% %
1174% %
1175% %
1176%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1177%
1178% ParseMetaGeometry() is similar to GetGeometry() except the returned
anthonyd58e60c2011-03-30 00:24:34 +00001179% geometry is modified as determined by the meta characters: %, !, <, >, @,
1180% and ^ in relation to image resizing.
1181%
1182% Final image dimensions are adjusted so as to preserve the aspect ratio as
glennrpdcabf6d2011-06-25 03:26:14 +00001183% much as possible, while generating a integer (pixel) size, and fitting the
anthonyd58e60c2011-03-30 00:24:34 +00001184% image within the specified geometry width and height.
1185%
1186% Flags are interpreted...
anthony6634c532012-06-15 06:56:39 +00001187% % geometry size is given percentage of original width and height given
cristy272f23a2011-03-30 00:37:11 +00001188% ! do not try to preserve aspect ratio
1189% < only enlarge images smaller that geometry
1190% > only shrink images larger than geometry
1191% @ Fit image to contain at most this many pixels
1192% ^ Contain the given geometry given, (minimal dimensions given)
cristy3ed852e2009-09-05 21:47:34 +00001193%
1194% The format of the ParseMetaGeometry method is:
1195%
anthonyd58e60c2011-03-30 00:24:34 +00001196% MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
cristy272f23a2011-03-30 00:37:11 +00001197% ssize_t *y, size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001198%
1199% A description of each parameter follows:
1200%
anthonyd58e60c2011-03-30 00:24:34 +00001201% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001202%
anthonyd58e60c2011-03-30 00:24:34 +00001203% o x,y: The x and y offset, set according to the geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001204%
cristy272f23a2011-03-30 00:37:11 +00001205% o width,height: The width and height of original image, modified by
1206% the given geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001207%
1208*/
1209
cristybb503372010-05-27 20:51:26 +00001210static inline size_t MagickMax(const size_t x,
1211 const size_t y)
cristy3ed852e2009-09-05 21:47:34 +00001212{
1213 if (x > y)
1214 return(x);
1215 return(y);
1216}
1217
cristybb503372010-05-27 20:51:26 +00001218MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1219 ssize_t *y,size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001220{
1221 GeometryInfo
1222 geometry_info;
1223
1224 MagickStatusType
1225 flags;
1226
cristybb503372010-05-27 20:51:26 +00001227 size_t
cristy3ed852e2009-09-05 21:47:34 +00001228 former_height,
1229 former_width;
1230
1231 /*
1232 Ensure the image geometry is valid.
1233 */
cristybb503372010-05-27 20:51:26 +00001234 assert(x != (ssize_t *) NULL);
1235 assert(y != (ssize_t *) NULL);
1236 assert(width != (size_t *) NULL);
1237 assert(height != (size_t *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001238 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1239 return(NoValue);
1240 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1241 /*
1242 Parse geometry using GetGeometry.
1243 */
1244 SetGeometryInfo(&geometry_info);
1245 former_width=(*width);
1246 former_height=(*height);
1247 flags=GetGeometry(geometry,x,y,width,height);
1248 if ((flags & PercentValue) != 0)
1249 {
1250 MagickStatusType
1251 flags;
1252
1253 PointInfo
1254 scale;
1255
1256 /*
1257 Geometry is a percentage of the image size.
1258 */
1259 flags=ParseGeometry(geometry,&geometry_info);
1260 scale.x=geometry_info.rho;
1261 if ((flags & RhoValue) == 0)
1262 scale.x=100.0;
1263 scale.y=geometry_info.sigma;
1264 if ((flags & SigmaValue) == 0)
1265 scale.y=scale.x;
cristybb503372010-05-27 20:51:26 +00001266 *width=(size_t) floor(scale.x*former_width/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001267 if (*width == 0)
1268 *width=1;
cristybb503372010-05-27 20:51:26 +00001269 *height=(size_t) floor(scale.y*former_height/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001270 if (*height == 0)
1271 *height=1;
1272 former_width=(*width);
1273 former_height=(*height);
1274 }
cristy07623352011-12-07 13:04:03 +00001275 if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1276 (*height == former_height)))
cristy3ed852e2009-09-05 21:47:34 +00001277 {
cristy07623352011-12-07 13:04:03 +00001278 if ((flags & RhoValue) == 0)
1279 *width=former_width;
1280 if ((flags & SigmaValue) == 0)
1281 *height=former_height;
1282 }
1283 else
1284 {
1285 double
cristy3ed852e2009-09-05 21:47:34 +00001286 scale_factor;
1287
1288 /*
1289 Respect aspect ratio of the image.
1290 */
1291 if ((former_width == 0) || (former_height == 0))
1292 scale_factor=1.0;
1293 else
1294 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1295 {
cristy07623352011-12-07 13:04:03 +00001296 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001297 if ((flags & MinimumValue) == 0)
1298 {
cristy07623352011-12-07 13:04:03 +00001299 if (scale_factor > ((double) *height/(double) former_height))
1300 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001301 }
1302 else
cristy07623352011-12-07 13:04:03 +00001303 if (scale_factor < ((double) *height/(double) former_height))
1304 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001305 }
1306 else
1307 if ((flags & RhoValue) != 0)
1308 {
cristy07623352011-12-07 13:04:03 +00001309 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001310 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001311 (scale_factor < ((double) *width/(double) former_height)))
1312 scale_factor=(double) *width/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001313 }
1314 else
1315 {
cristy07623352011-12-07 13:04:03 +00001316 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001317 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001318 (scale_factor < ((double) *height/(double) former_width)))
1319 scale_factor=(double) *height/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001320 }
cristy272f23a2011-03-30 00:37:11 +00001321 *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1322 *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
cristy3ed852e2009-09-05 21:47:34 +00001323 }
1324 if ((flags & GreaterValue) != 0)
1325 {
1326 if (former_width < *width)
1327 *width=former_width;
1328 if (former_height < *height)
1329 *height=former_height;
1330 }
1331 if ((flags & LessValue) != 0)
1332 {
1333 if (former_width > *width)
1334 *width=former_width;
1335 if (former_height > *height)
1336 *height=former_height;
1337 }
1338 if ((flags & AreaValue) != 0)
1339 {
cristy07623352011-12-07 13:04:03 +00001340 double
cristy3ed852e2009-09-05 21:47:34 +00001341 area,
1342 distance;
1343
1344 PointInfo
1345 scale;
1346
1347 /*
1348 Geometry is a maximum area in pixels.
1349 */
1350 (void) ParseGeometry(geometry,&geometry_info);
1351 area=geometry_info.rho;
1352 distance=sqrt((double) former_width*former_height);
cristye42f6582012-02-11 17:59:50 +00001353 scale.x=(double) former_width/(distance/sqrt((double) area));
1354 scale.y=(double) former_height/(distance/sqrt((double) area));
cristy3ed852e2009-09-05 21:47:34 +00001355 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1356 {
cristy11e0a872012-01-10 16:10:21 +00001357 *width=(size_t) (former_width/(distance/sqrt(area))+0.5);
1358 *height=(size_t) (former_height/(distance/sqrt(area))+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001359 }
1360 former_width=(*width);
1361 former_height=(*height);
1362 }
1363 return(flags);
1364}
1365
1366/*
1367%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1368% %
1369% %
1370% %
1371% P a r s e P a g e G e o m e t r y %
1372% %
1373% %
1374% %
1375%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1376%
1377% ParsePageGeometry() returns a region as defined by the geometry string with
anthonyd58e60c2011-03-30 00:24:34 +00001378% respect to the image page (canvas) dimensions.
1379%
1380% WARNING: Percentage dimensions remain relative to the actual image
1381% dimensions, and not canvas dimensions.
cristy3ed852e2009-09-05 21:47:34 +00001382%
1383% The format of the ParsePageGeometry method is:
1384%
1385% MagickStatusType ParsePageGeometry(const Image *image,
1386% const char *geometry,RectangeInfo *region_info,
1387% ExceptionInfo *exception)
1388%
1389% A description of each parameter follows:
1390%
anthonyd58e60c2011-03-30 00:24:34 +00001391% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001392%
1393% o region_info: the region as defined by the geometry string with
1394% respect to the image and its gravity.
1395%
1396% o exception: return any errors or warnings in this structure.
1397%
1398*/
1399MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1400 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1401{
1402 MagickStatusType
1403 flags;
1404
1405 SetGeometry(image,region_info);
1406 if (image->page.width != 0)
1407 region_info->width=image->page.width;
1408 if (image->page.height != 0)
1409 region_info->height=image->page.height;
1410 flags=ParseAbsoluteGeometry(geometry,region_info);
1411 if (flags == NoValue)
1412 {
1413 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001414 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001415 return(flags);
1416 }
1417 if ((flags & PercentValue) != 0)
1418 {
1419 region_info->width=image->columns;
1420 region_info->height=image->rows;
1421 }
1422 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1423 &region_info->width,&region_info->height);
1424 return(flags);
1425}
1426
1427/*
1428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1429% %
1430% %
1431% %
1432% P a r s e R e g i o n G e o m e t r y %
1433% %
1434% %
1435% %
1436%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1437%
1438% ParseRegionGeometry() returns a region as defined by the geometry string
1439% with respect to the image dimensions and aspect ratio.
1440%
anthonyd58e60c2011-03-30 00:24:34 +00001441% This is basically a wrapper around ParseMetaGeometry. This is typically
1442% used to parse a geometry string to work out the final integer dimensions
1443% for image resizing.
1444%
cristy3ed852e2009-09-05 21:47:34 +00001445% The format of the ParseRegionGeometry method is:
1446%
1447% MagickStatusType ParseRegionGeometry(const Image *image,
1448% const char *geometry,RectangeInfo *region_info,
1449% ExceptionInfo *exception)
1450%
1451% A description of each parameter follows:
1452%
anthonyd58e60c2011-03-30 00:24:34 +00001453% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001454%
1455% o region_info: the region as defined by the geometry string.
1456%
1457% o exception: return any errors or warnings in this structure.
1458%
1459*/
1460MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1461 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1462{
1463 MagickStatusType
1464 flags;
1465
1466 SetGeometry(image,region_info);
1467 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1468 &region_info->width,&region_info->height);
1469 if (flags == NoValue)
1470 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001471 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001472 return(flags);
1473}
1474
1475/*
1476%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1477% %
1478% %
1479% %
1480% S e t G e o m e t r y %
1481% %
1482% %
1483% %
1484%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1485%
1486% SetGeometry() sets the geometry to its default values.
1487%
1488% The format of the SetGeometry method is:
1489%
1490% SetGeometry(const Image *image,RectangleInfo *geometry)
1491%
1492% A description of each parameter follows:
1493%
1494% o image: the image.
1495%
1496% o geometry: the geometry.
1497%
1498*/
1499MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1500{
1501 assert(image != (Image *) NULL);
1502 assert(image->signature == MagickSignature);
1503 if (image->debug != MagickFalse)
1504 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1505 assert(geometry != (RectangleInfo *) NULL);
1506 (void) ResetMagickMemory(geometry,0,sizeof(*geometry));
1507 geometry->width=image->columns;
1508 geometry->height=image->rows;
1509}
1510
1511/*
1512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1513% %
1514% %
1515% %
1516% S e t G e o m e t r y I n f o %
1517% %
1518% %
1519% %
1520%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1521%
1522% SetGeometryInfo sets the GeometryInfo structure to its default values.
1523%
1524% The format of the SetGeometryInfo method is:
1525%
1526% SetGeometryInfo(GeometryInfo *geometry_info)
1527%
1528% A description of each parameter follows:
1529%
1530% o geometry_info: the geometry info structure.
1531%
1532*/
1533MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1534{
1535 assert(geometry_info != (GeometryInfo *) NULL);
1536 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1537 (void) ResetMagickMemory(geometry_info,0,sizeof(*geometry_info));
1538}