blob: 6d7fa071022a9e8fde36dff9113218413480a850 [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 }
cristy1ad6c872012-08-02 11:54:05 +0000173 case 'x':
174 case 'X':
175 {
176 flags|=SeparatorValue;
177 p++;
178 break;
179 }
cristy3ed852e2009-09-05 21:47:34 +0000180 case '-':
181 case '.':
182 case ',':
183 case '+':
184 case '0':
185 case '1':
186 case '2':
187 case '3':
188 case '4':
189 case '5':
190 case '6':
191 case '7':
192 case '8':
193 case '9':
cristy35f75ac2011-09-06 20:33:35 +0000194 case 215:
cristy3ed852e2009-09-05 21:47:34 +0000195 {
196 p++;
197 break;
198 }
199 default:
200 return(flags);
201 }
202 }
203 /*
204 Parse width, height, x, and y.
205 */
206 p=pedantic_geometry;
207 if (*p == '\0')
208 return(flags);
209 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000210 value=StringToDouble(p,&q);
cristy00976d82011-02-20 20:31:28 +0000211 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000212 if (LocaleNCompare(p,"0x",2) == 0)
cristyf53f26f2011-05-16 00:56:05 +0000213 value=(double) strtol(p,&q,10);
cristy57936e02012-07-03 00:33:28 +0000214 if ((*p != '+') && (*p != '-'))
cristy3ed852e2009-09-05 21:47:34 +0000215 {
cristy57936e02012-07-03 00:33:28 +0000216 c=(int) ((unsigned char) *q);
217 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == '\0'))
cristy3ed852e2009-09-05 21:47:34 +0000218 {
219 /*
cristy57936e02012-07-03 00:33:28 +0000220 Parse width.
cristy3ed852e2009-09-05 21:47:34 +0000221 */
222 q=p;
cristy57936e02012-07-03 00:33:28 +0000223 if (LocaleNCompare(p,"0x",2) == 0)
224 *width=(size_t) strtol(p,&p,10);
225 else
226 *width=(size_t) floor(StringToDouble(p,&p)+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000227 if (p != q)
cristy57936e02012-07-03 00:33:28 +0000228 flags|=WidthValue;
229 }
230 }
231 if ((*p != '+') && (*p != '-'))
232 {
233 c=(int) ((unsigned char) *p);
234 if ((c == 215) || (*p == 'x') || (*p == 'X'))
235 {
236 p++;
237 if ((*p != '+') && (*p != '-'))
238 {
239 /*
240 Parse height.
241 */
242 q=p;
243 *height=(size_t) floor(StringToDouble(p,&p)+0.5);
244 if (p != q)
245 flags|=HeightValue;
246 }
cristy3ed852e2009-09-05 21:47:34 +0000247 }
248 }
249 if ((*p == '+') || (*p == '-'))
250 {
251 /*
252 Parse x value.
253 */
anthony6634c532012-06-15 06:56:39 +0000254 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +0000255 {
256 if (*p == '-')
257 flags^=XNegative; /* negate sign */
258 p++;
259 }
cristy3ed852e2009-09-05 21:47:34 +0000260 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000261 *x=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
cristy3ed852e2009-09-05 21:47:34 +0000262 if (p != q)
cristy3ed852e2009-09-05 21:47:34 +0000263 {
anthony6634c532012-06-15 06:56:39 +0000264 flags|=XValue;
cristy57936e02012-07-03 00:33:28 +0000265 if ((flags & XNegative) != 0)
266 *x=(-*x);
cristy3ed852e2009-09-05 21:47:34 +0000267 }
268 }
anthony6634c532012-06-15 06:56:39 +0000269 if ((*p == '+') || (*p == '-'))
270 {
271 /*
272 Parse y value.
273 */
274 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +0000275 {
276 if (*p == '-')
277 flags^=YNegative; /* negate sign */
278 p++;
279 }
anthony6634c532012-06-15 06:56:39 +0000280 q=p;
281 *y=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
282 if (p != q)
283 {
284 flags|=YValue;
cristy57936e02012-07-03 00:33:28 +0000285 if ((flags & YNegative) != 0)
286 *y=(-*y);
anthony6634c532012-06-15 06:56:39 +0000287 }
288 }
cristy64ad8f92012-08-03 00:58:24 +0000289 if ((flags & PercentValue) != 0)
cristy1ad6c872012-08-02 11:54:05 +0000290 {
cristy64ad8f92012-08-03 00:58:24 +0000291 if (((flags & SeparatorValue) == 0) && ((flags & HeightValue) == 0))
cristy1ad6c872012-08-02 11:54:05 +0000292 {
293 *height=(*width);
294 flags|=HeightValue;
295 }
cristy64ad8f92012-08-03 00:58:24 +0000296 if (((flags & SeparatorValue) != 0) && ((flags & WidthValue) == 0))
297 *width=(*height);
cristy1ad6c872012-08-02 11:54:05 +0000298 }
anthony6634c532012-06-15 06:56:39 +0000299#if 0
300 /* Debugging Geometry */
cristy57936e02012-07-03 00:33:28 +0000301 (void) fprintf(stderr,"GetGeometry...\n");
302 (void) fprintf(stderr,"Input: %s\n",geometry);
303 (void) fprintf(stderr,"Flags: %c %c %s %s\n",
304 (flags & WidthValue) ? 'W' : ' ',(flags & HeightValue) ? 'H' : ' ',
305 (flags & XValue) ? ((flags & XNegative) ? "-X" : "+X") : " ",
306 (flags & YValue) ? ((flags & YNegative) ? "-Y" : "+Y") : " ");
307 (void) fprintf(stderr,"Geometry: %ldx%ld%+ld%+ld\n",(long) *width,(long)
308 *height,(long) *x,(long) *y);
anthony6634c532012-06-15 06:56:39 +0000309#endif
cristy3ed852e2009-09-05 21:47:34 +0000310 return(flags);
311}
312
313/*
314%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
315% %
316% %
317% %
318% G e t P a g e G e o m e t r y %
319% %
320% %
321% %
322%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
323%
324% GetPageGeometry() replaces any page mneumonic with the equivalent size in
325% picas.
326%
327% The format of the GetPageGeometry method is:
328%
329% char *GetPageGeometry(const char *page_geometry)
330%
331% A description of each parameter follows.
332%
cristy272f23a2011-03-30 00:37:11 +0000333% o page_geometry: Specifies a pointer to an array of characters. The
334% string is either a Postscript page name (e.g. A4) or a postscript page
335% geometry (e.g. 612x792+36+36).
cristy3ed852e2009-09-05 21:47:34 +0000336%
337*/
338MagickExport char *GetPageGeometry(const char *page_geometry)
339{
340 static const char
341 *PageSizes[][2]=
342 {
343 { "4x6", "288x432" },
344 { "5x7", "360x504" },
345 { "7x9", "504x648" },
346 { "8x10", "576x720" },
347 { "9x11", "648x792" },
348 { "9x12", "648x864" },
349 { "10x13", "720x936" },
350 { "10x14", "720x1008" },
351 { "11x17", "792x1224" },
352 { "a0", "2384x3370" },
353 { "a1", "1684x2384" },
354 { "a10", "73x105" },
355 { "a2", "1191x1684" },
356 { "a3", "842x1191" },
357 { "a4", "595x842" },
358 { "a4smaLL", "595x842" },
359 { "a5", "420x595" },
360 { "a6", "297x420" },
361 { "a7", "210x297" },
362 { "a8", "148x210" },
363 { "a9", "105x148" },
364 { "archa", "648x864" },
365 { "archb", "864x1296" },
366 { "archC", "1296x1728" },
367 { "archd", "1728x2592" },
368 { "arche", "2592x3456" },
369 { "b0", "2920x4127" },
370 { "b1", "2064x2920" },
371 { "b10", "91x127" },
372 { "b2", "1460x2064" },
373 { "b3", "1032x1460" },
374 { "b4", "729x1032" },
375 { "b5", "516x729" },
376 { "b6", "363x516" },
377 { "b7", "258x363" },
378 { "b8", "181x258" },
379 { "b9", "127x181" },
380 { "c0", "2599x3676" },
381 { "c1", "1837x2599" },
382 { "c2", "1298x1837" },
383 { "c3", "918x1296" },
384 { "c4", "649x918" },
385 { "c5", "459x649" },
386 { "c6", "323x459" },
387 { "c7", "230x323" },
388 { "executive", "540x720" },
389 { "flsa", "612x936" },
390 { "flse", "612x936" },
391 { "folio", "612x936" },
392 { "halfletter", "396x612" },
393 { "isob0", "2835x4008" },
394 { "isob1", "2004x2835" },
395 { "isob10", "88x125" },
396 { "isob2", "1417x2004" },
397 { "isob3", "1001x1417" },
398 { "isob4", "709x1001" },
399 { "isob5", "499x709" },
400 { "isob6", "354x499" },
401 { "isob7", "249x354" },
402 { "isob8", "176x249" },
403 { "isob9", "125x176" },
404 { "jisb0", "1030x1456" },
405 { "jisb1", "728x1030" },
406 { "jisb2", "515x728" },
407 { "jisb3", "364x515" },
408 { "jisb4", "257x364" },
409 { "jisb5", "182x257" },
410 { "jisb6", "128x182" },
411 { "ledger", "1224x792" },
412 { "legal", "612x1008" },
413 { "letter", "612x792" },
414 { "lettersmaLL", "612x792" },
415 { "quarto", "610x780" },
416 { "statement", "396x612" },
417 { "tabloid", "792x1224" },
418 { (char *) NULL, (char *) NULL }
419 };
420
421 char
422 *page;
423
cristybb503372010-05-27 20:51:26 +0000424 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000425 i;
426
427 assert(page_geometry != (char *) NULL);
428 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
429 page=AcquireString(page_geometry);
430 for (i=0; *PageSizes[i] != (char *) NULL; i++)
431 if (LocaleNCompare(PageSizes[i][0],page,strlen(PageSizes[i][0])) == 0)
432 {
433 RectangleInfo
434 geometry;
435
436 MagickStatusType
437 flags;
438
439 /*
440 Replace mneumonic with the equivalent size in dots-per-inch.
441 */
442 (void) CopyMagickString(page,PageSizes[i][1],MaxTextExtent);
443 (void) ConcatenateMagickString(page,page_geometry+
444 strlen(PageSizes[i][0]),MaxTextExtent);
445 flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
446 &geometry.height);
447 if ((flags & GreaterValue) == 0)
448 (void) ConcatenateMagickString(page,">",MaxTextExtent);
449 break;
450 }
451 return(page);
452}
453
454/*
455%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
456% %
457% %
458% %
459% G r a v i t y A d j u s t G e o m e t r y %
460% %
cristy272f23a2011-03-30 00:37:11 +0000461% % % %
cristy3ed852e2009-09-05 21:47:34 +0000462%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
463%
464% GravityAdjustGeometry() adjusts the offset of a region with regard to the
465% given: width, height and gravity; against which it is positioned.
466%
467% The region should also have an appropriate width and height to correctly
468% set the right offset of the top left corner of the region.
469%
470% The format of the GravityAdjustGeometry method is:
471%
cristy272f23a2011-03-30 00:37:11 +0000472% void GravityAdjustGeometry(const size_t width, const size_t height,
473% const GravityType gravity,RectangleInfo *region);
cristy3ed852e2009-09-05 21:47:34 +0000474%
475% A description of each parameter follows:
476%
477% o width, height: the larger area the region is relative to
478%
479% o gravity: the edge/corner the current offset is relative to
480%
481% o region: The region requiring a offset adjustment relative to gravity
482%
483*/
cristybb503372010-05-27 20:51:26 +0000484MagickExport void GravityAdjustGeometry(const size_t width,
485 const size_t height,const GravityType gravity,RectangleInfo *region)
cristy3ed852e2009-09-05 21:47:34 +0000486{
487 if (region->height == 0)
488 region->height=height;
489 if (region->width == 0)
490 region->width=width;
491 switch (gravity)
492 {
493 case NorthEastGravity:
494 case EastGravity:
495 case SouthEastGravity:
496 {
cristybb503372010-05-27 20:51:26 +0000497 region->x=(ssize_t) (width-region->width-region->x);
cristy3ed852e2009-09-05 21:47:34 +0000498 break;
499 }
500 case NorthGravity:
501 case SouthGravity:
502 case CenterGravity:
cristy3ed852e2009-09-05 21:47:34 +0000503 {
cristybb503372010-05-27 20:51:26 +0000504 region->x+=(ssize_t) (width/2-region->width/2);
cristy3ed852e2009-09-05 21:47:34 +0000505 break;
506 }
507 case ForgetGravity:
508 case NorthWestGravity:
509 case WestGravity:
510 case SouthWestGravity:
511 default:
512 break;
513 }
514 switch (gravity)
515 {
516 case SouthWestGravity:
517 case SouthGravity:
518 case SouthEastGravity:
519 {
cristybb503372010-05-27 20:51:26 +0000520 region->y=(ssize_t) (height-region->height-region->y);
cristy3ed852e2009-09-05 21:47:34 +0000521 break;
522 }
523 case EastGravity:
524 case WestGravity:
525 case CenterGravity:
cristy3ed852e2009-09-05 21:47:34 +0000526 {
cristybb503372010-05-27 20:51:26 +0000527 region->y+=(ssize_t) (height/2-region->height/2);
cristy3ed852e2009-09-05 21:47:34 +0000528 break;
529 }
530 case ForgetGravity:
531 case NorthWestGravity:
532 case NorthGravity:
533 case NorthEastGravity:
534 default:
535 break;
536 }
537 return;
538}
539
540/*
541%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
542% %
543% %
544% %
545+ I s G e o m e t r y %
546% %
547% %
548% %
549%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
550%
551% IsGeometry() returns MagickTrue if the geometry specification is valid.
552% Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
553%
554% The format of the IsGeometry method is:
555%
556% MagickBooleanType IsGeometry(const char *geometry)
557%
558% A description of each parameter follows:
559%
560% o geometry: This string is the geometry specification.
561%
562*/
563MagickExport MagickBooleanType IsGeometry(const char *geometry)
564{
565 GeometryInfo
566 geometry_info;
567
568 MagickStatusType
569 flags;
570
571 if (geometry == (const char *) NULL)
572 return(MagickFalse);
573 flags=ParseGeometry(geometry,&geometry_info);
cristy3ed852e2009-09-05 21:47:34 +0000574 return(flags != NoValue ? MagickTrue : MagickFalse);
575}
576
577/*
578%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
579% %
580% %
581% %
582+ I s S c e n e G e o m e t r y %
583% %
584% %
585% %
586%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
587%
588% IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
589% specification (e.g. [1], [1-9], [1,7,4]).
590%
591% The format of the IsSceneGeometry method is:
592%
593% MagickBooleanType IsSceneGeometry(const char *geometry,
594% const MagickBooleanType pedantic)
595%
596% A description of each parameter follows:
597%
598% o geometry: This string is the geometry specification.
599%
600% o pedantic: A value other than 0 invokes a more restrictive set of
601% conditions for a valid specification (e.g. [1], [1-4], [4-1]).
602%
603*/
604MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
605 const MagickBooleanType pedantic)
606{
607 char
608 *p;
609
cristy00976d82011-02-20 20:31:28 +0000610 double
611 value;
612
cristy3ed852e2009-09-05 21:47:34 +0000613 if (geometry == (const char *) NULL)
614 return(MagickFalse);
615 p=(char *) geometry;
cristydbdd0e32011-11-04 23:29:40 +0000616 value=StringToDouble(geometry,&p);
cristy00976d82011-02-20 20:31:28 +0000617 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000618 if (p == geometry)
619 return(MagickFalse);
620 if (strspn(geometry,"0123456789-, ") != strlen(geometry))
621 return(MagickFalse);
622 if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
623 return(MagickFalse);
624 return(MagickTrue);
625}
626
627/*
628%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
629% %
630% %
631% %
632% P a r s e A b s o l u t e G e o m e t r y %
633% %
634% %
635% %
636%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
637%
anthonyd58e60c2011-03-30 00:24:34 +0000638% ParseAbsoluteGeometry() returns a region as defined by the geometry string,
639% without any modification by percentages or gravity.
640%
641% It currently just a wrapper around GetGeometry(), but may be expanded in
642% the future to handle other positioning information.
cristy3ed852e2009-09-05 21:47:34 +0000643%
644% The format of the ParseAbsoluteGeometry method is:
645%
646% MagickStatusType ParseAbsoluteGeometry(const char *geometry,
647% RectangleInfo *region_info)
648%
649% A description of each parameter follows:
650%
anthonyd58e60c2011-03-30 00:24:34 +0000651% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000652%
653% o region_info: the region as defined by the geometry string.
654%
655*/
656MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
657 RectangleInfo *region_info)
658{
659 MagickStatusType
660 flags;
661
662 flags=GetGeometry(geometry,&region_info->x,&region_info->y,
663 &region_info->width,&region_info->height);
664 return(flags);
665}
666
667/*
668%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
669% %
670% %
671% %
672% P a r s e A f f i n e G e o m e t r y %
673% %
674% %
675% %
676%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
677%
cristy272f23a2011-03-30 00:37:11 +0000678% ParseAffineGeometry() returns an affine matrix as defined by a string of 4
679% to 6 comma/space separated floating point values.
anthonyd58e60c2011-03-30 00:24:34 +0000680%
681% The affine matrix determinant is checked for validity of the values.
cristy3ed852e2009-09-05 21:47:34 +0000682%
683% The format of the ParseAffineGeometry method is:
684%
685% MagickStatusType ParseAffineGeometry(const char *geometry,
686% AffineMatrix *affine_matrix,ExceptionInfo *exception)
687%
688% A description of each parameter follows:
689%
anthonyd58e60c2011-03-30 00:24:34 +0000690% 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 +0000691%
692% o affine_matrix: the affine matrix as defined by the geometry string.
693%
694% o exception: return any errors or warnings in this structure.
695%
696*/
697MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
698 AffineMatrix *affine_matrix,ExceptionInfo *exception)
699{
700 char
701 token[MaxTextExtent];
702
703 const char
704 *p;
705
706 double
707 determinant;
708
709 MagickStatusType
710 flags;
711
cristybb503372010-05-27 20:51:26 +0000712 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000713 i;
714
715 GetAffineMatrix(affine_matrix);
716 flags=NoValue;
717 p=(char *) geometry;
718 for (i=0; (*p != '\0') && (i < 6); i++)
719 {
720 GetMagickToken(p,&p,token);
721 if (*token == ',')
722 GetMagickToken(p,&p,token);
723 switch (i)
724 {
cristyc1acd842011-05-19 23:05:47 +0000725 case 0:
726 {
cristydbdd0e32011-11-04 23:29:40 +0000727 affine_matrix->sx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000728 break;
729 }
730 case 1:
731 {
cristydbdd0e32011-11-04 23:29:40 +0000732 affine_matrix->rx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000733 break;
734 }
735 case 2:
736 {
cristydbdd0e32011-11-04 23:29:40 +0000737 affine_matrix->ry=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000738 break;
739 }
740 case 3:
741 {
cristydbdd0e32011-11-04 23:29:40 +0000742 affine_matrix->sy=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000743 break;
744 }
745 case 4:
746 {
cristydbdd0e32011-11-04 23:29:40 +0000747 affine_matrix->tx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000748 flags|=XValue;
749 break;
750 }
751 case 5:
752 {
cristydbdd0e32011-11-04 23:29:40 +0000753 affine_matrix->ty=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000754 flags|=YValue;
755 break;
756 }
cristy3ed852e2009-09-05 21:47:34 +0000757 }
758 }
cristy57936e02012-07-03 00:33:28 +0000759 determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
760 affine_matrix->ry);
cristy3ed852e2009-09-05 21:47:34 +0000761 if (fabs(determinant) < MagickEpsilon)
762 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony92c93bd2012-03-19 14:02:47 +0000763 "InvalidArgument","'%s' : 'Indeterminate Matrix'",geometry);
cristy3ed852e2009-09-05 21:47:34 +0000764 return(flags);
765}
766
767/*
768%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
769% %
770% %
771% %
772% P a r s e G e o m e t r y %
773% %
774% %
775% %
776%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
777%
778% ParseGeometry() parses a geometry specification and returns the sigma,
779% rho, xi, and psi values. It also returns flags that indicates which
780% of the four values (sigma, rho, xi, psi) were located in the string, and
anthonyd58e60c2011-03-30 00:24:34 +0000781% whether the xi or pi values are negative.
782%
783% In addition, it reports if there are any of meta characters (%, !, <, >, @,
784% and ^) flags present. It does not report the location of the percentage
785% relative to the values.
cristy3ed852e2009-09-05 21:47:34 +0000786%
anthony6634c532012-06-15 06:56:39 +0000787% Values may also be seperated by commas, colons, or slashes, and offsets.
788% Offsets may be prefixed by multiple signs to make offset string
789% substitutions easier to handle from shell scripts.
790% For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
791% offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
792% offsets.
793%
cristy3ed852e2009-09-05 21:47:34 +0000794% The format of the ParseGeometry method is:
795%
796% MagickStatusType ParseGeometry(const char *geometry,
797% GeometryInfo *geometry_info)
798%
799% A description of each parameter follows:
800%
anthonyd58e60c2011-03-30 00:24:34 +0000801% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000802%
803% o geometry_info: returns the parsed width/height/x/y in this structure.
804%
805*/
806MagickExport MagickStatusType ParseGeometry(const char *geometry,
807 GeometryInfo *geometry_info)
808{
809 char
810 *p,
811 pedantic_geometry[MaxTextExtent],
812 *q;
813
814 double
815 value;
816
cristyf30f47d2011-09-06 14:40:59 +0000817 int
818 c;
819
cristy3ed852e2009-09-05 21:47:34 +0000820 MagickStatusType
821 flags;
822
823 /*
824 Remove whitespaces meta characters from geometry specification.
825 */
826 assert(geometry_info != (GeometryInfo *) NULL);
827 flags=NoValue;
828 if ((geometry == (char *) NULL) || (*geometry == '\0'))
829 return(flags);
cristy37e0b382011-06-07 13:31:21 +0000830 if (strlen(geometry) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000831 return(flags);
832 (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
833 for (p=pedantic_geometry; *p != '\0'; )
834 {
835 if (isspace((int) ((unsigned char) *p)) != 0)
836 {
837 (void) CopyMagickString(p,p+1,MaxTextExtent);
838 continue;
839 }
cristy35abcb72011-09-06 20:36:15 +0000840 c=(int) ((unsigned char) *p);
841 switch (c)
cristy3ed852e2009-09-05 21:47:34 +0000842 {
843 case '%':
844 {
845 flags|=PercentValue;
846 (void) CopyMagickString(p,p+1,MaxTextExtent);
847 break;
848 }
849 case '!':
850 {
851 flags|=AspectValue;
852 (void) CopyMagickString(p,p+1,MaxTextExtent);
853 break;
854 }
855 case '<':
856 {
857 flags|=LessValue;
858 (void) CopyMagickString(p,p+1,MaxTextExtent);
859 break;
860 }
861 case '>':
862 {
863 flags|=GreaterValue;
864 (void) CopyMagickString(p,p+1,MaxTextExtent);
865 break;
866 }
867 case '^':
868 {
869 flags|=MinimumValue;
870 (void) CopyMagickString(p,p+1,MaxTextExtent);
871 break;
872 }
873 case '@':
874 {
875 flags|=AreaValue;
876 (void) CopyMagickString(p,p+1,MaxTextExtent);
877 break;
878 }
879 case '(':
880 case ')':
881 {
882 (void) CopyMagickString(p,p+1,MaxTextExtent);
883 break;
884 }
cristy1ad6c872012-08-02 11:54:05 +0000885 case 'x':
886 case 'X':
887 {
888 flags|=SeparatorValue;
889 p++;
890 break;
891 }
cristy3ed852e2009-09-05 21:47:34 +0000892 case '-':
893 case '+':
894 case ',':
895 case '0':
896 case '1':
897 case '2':
898 case '3':
899 case '4':
900 case '5':
901 case '6':
902 case '7':
903 case '8':
904 case '9':
cristy3ed852e2009-09-05 21:47:34 +0000905 case '/':
906 case ':':
cristy35f75ac2011-09-06 20:33:35 +0000907 case 215:
cristy3ed852e2009-09-05 21:47:34 +0000908 {
909 p++;
910 break;
911 }
912 case '.':
913 {
914 p++;
915 flags|=DecimalValue;
916 break;
917 }
918 default:
919 return(flags);
920 }
921 }
922 /*
923 Parse rho, sigma, xi, psi, and optionally chi.
924 */
925 p=pedantic_geometry;
926 if (*p == '\0')
927 return(flags);
928 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000929 value=StringToDouble(p,&q);
cristy3ed852e2009-09-05 21:47:34 +0000930 if (LocaleNCompare(p,"0x",2) == 0)
931 value=(double) strtol(p,&q,10);
cristy35f75ac2011-09-06 20:33:35 +0000932 c=(int) ((unsigned char) *q);
933 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
anthonyd1303502010-05-11 12:00:43 +0000934 (*q == '/') || (*q == ':') || (*q =='\0'))
cristy3ed852e2009-09-05 21:47:34 +0000935 {
936 /*
937 Parse rho.
938 */
939 q=p;
940 if (LocaleNCompare(p,"0x",2) == 0)
941 value=(double) strtol(p,&p,10);
942 else
cristydbdd0e32011-11-04 23:29:40 +0000943 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000944 if (p != q)
945 {
946 flags|=RhoValue;
947 geometry_info->rho=value;
948 }
949 }
950 q=p;
cristy35f75ac2011-09-06 20:33:35 +0000951 c=(int) ((unsigned char) *p);
952 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ',') || (*p == '/') ||
cristyf30f47d2011-09-06 14:40:59 +0000953 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000954 {
955 /*
956 Parse sigma.
957 */
958 p++;
959 while (isspace((int) ((unsigned char) *p)) != 0)
960 p++;
cristy35f75ac2011-09-06 20:33:35 +0000961 c=(int) ((unsigned char) *q);
962 if (((c != 215) && (*q != 'x') && (*q != 'X')) || ((*p != '+') &&
cristyf30f47d2011-09-06 14:40:59 +0000963 (*p != '-')))
cristy3ed852e2009-09-05 21:47:34 +0000964 {
965 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000966 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000967 if (p != q)
968 {
969 flags|=SigmaValue;
970 geometry_info->sigma=value;
971 }
972 }
973 }
974 while (isspace((int) ((unsigned char) *p)) != 0)
975 p++;
anthonyd1303502010-05-11 12:00:43 +0000976 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000977 {
978 /*
979 Parse xi value.
980 */
anthony6634c532012-06-15 06:56:39 +0000981 if ((*p == ',') || (*p == '/') || (*p == ':') )
cristy3ed852e2009-09-05 21:47:34 +0000982 p++;
anthony6634c532012-06-15 06:56:39 +0000983 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +0000984 {
985 if (*p == '-')
986 flags^=XiNegative; /* negate sign */
987 p++;
988 }
cristy3ed852e2009-09-05 21:47:34 +0000989 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000990 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000991 if (p != q)
992 {
993 flags|=XiValue;
cristy57936e02012-07-03 00:33:28 +0000994 if ((flags & XiNegative) != 0)
995 value=(-value);
cristy3ed852e2009-09-05 21:47:34 +0000996 geometry_info->xi=value;
997 }
998 while (isspace((int) ((unsigned char) *p)) != 0)
999 p++;
1000 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +00001001 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +00001002 {
1003 /*
1004 Parse psi value.
1005 */
anthony6634c532012-06-15 06:56:39 +00001006 if ((*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +00001007 p++;
anthony6634c532012-06-15 06:56:39 +00001008 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +00001009 {
1010 if (*p == '-')
1011 flags^=PsiNegative; /* negate sign */
1012 p++;
1013 }
cristy3ed852e2009-09-05 21:47:34 +00001014 q=p;
cristydbdd0e32011-11-04 23:29:40 +00001015 value=StringToDouble(p,&p);
cristy57936e02012-07-03 00:33:28 +00001016 if (p != q)
1017 {
1018 flags|=PsiValue;
1019 if ((flags & PsiNegative) != 0)
1020 value=(-value);
1021 geometry_info->psi=value;
1022 }
1023 }
cristy3ed852e2009-09-05 21:47:34 +00001024 while (isspace((int) ((unsigned char) *p)) != 0)
1025 p++;
1026 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +00001027 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +00001028 {
1029 /*
1030 Parse chi value.
1031 */
anthony6634c532012-06-15 06:56:39 +00001032 if ((*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +00001033 p++;
anthony6634c532012-06-15 06:56:39 +00001034 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +00001035 {
1036 if (*p == '-')
1037 flags^=ChiNegative; /* negate sign */
1038 p++;
1039 }
cristy3ed852e2009-09-05 21:47:34 +00001040 q=p;
cristydbdd0e32011-11-04 23:29:40 +00001041 value=StringToDouble(p,&p);
cristy57936e02012-07-03 00:33:28 +00001042 if (p != q)
1043 {
1044 flags|=ChiValue;
1045 if ((flags & ChiNegative) != 0)
1046 value=(-value);
1047 geometry_info->chi=value;
1048 }
cristy3ed852e2009-09-05 21:47:34 +00001049 }
1050 }
1051 if (strchr(pedantic_geometry,':') != (char *) NULL)
1052 {
1053 /*
1054 Normalize sampling factor (e.g. 4:2:2 => 2x1).
1055 */
1056 geometry_info->rho/=geometry_info->sigma;
1057 geometry_info->sigma=1.0;
1058 if (geometry_info->xi == 0.0)
1059 geometry_info->sigma=2.0;
1060 }
1061 if (((flags & SigmaValue) == 0) && ((flags & XiValue) != 0) &&
1062 ((flags & PsiValue) == 0))
1063 {
1064 /*
1065 Support negative height values (e.g. 30x-20).
1066 */
1067 geometry_info->sigma=geometry_info->xi;
1068 geometry_info->xi=0.0;
1069 flags|=SigmaValue;
1070 flags&=(~XiValue);
1071 }
cristy64ad8f92012-08-03 00:58:24 +00001072 if ((flags & PercentValue) != 0)
cristy1ad6c872012-08-02 11:54:05 +00001073 {
cristy64ad8f92012-08-03 00:58:24 +00001074 if (((flags & SeparatorValue) == 0) && ((flags & SigmaValue) == 0))
cristy1ad6c872012-08-02 11:54:05 +00001075 {
1076 geometry_info->sigma=geometry_info->rho;
1077 flags|=SigmaValue;
1078 }
cristy64ad8f92012-08-03 00:58:24 +00001079 if (((flags & SeparatorValue) != 0) && ((flags & RhoValue) == 0))
1080 geometry_info->rho=geometry_info->sigma;
cristy1ad6c872012-08-02 11:54:05 +00001081 }
anthony6634c532012-06-15 06:56:39 +00001082#if 0
1083 /* Debugging Geometry */
cristy57936e02012-07-03 00:33:28 +00001084 (void) fprintf(stderr,"ParseGeometry...\n");
1085 (void) fprintf(stderr,"Flags: %c %c %s %s %s\n",
1086 (flags & RhoValue) ? 'W' : ' ',(flags & SigmaValue) ? 'H' : ' ',
1087 (flags & XiValue) ? ((flags & XiNegative) ? "-X" : "+X") : " ",
1088 (flags & PsiValue) ? ((flags & PsiNegative) ? "-Y" : "+Y") : " ",
1089 (flags & ChiValue) ? ((flags & ChiNegative) ? "-Z" : "+Z") : " ");
1090 (void) fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",geometry_info->rho,
1091 geometry_info->sigma,geometry_info->xi,geometry_info->psi,
1092 geometry_info->chi);
anthony6634c532012-06-15 06:56:39 +00001093#endif
cristy3ed852e2009-09-05 21:47:34 +00001094 return(flags);
1095}
1096
1097/*
1098%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1099% %
1100% %
1101% %
1102% P a r s e G r a v i t y G e o m e t r y %
1103% %
1104% %
1105% %
1106%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1107%
1108% ParseGravityGeometry() returns a region as defined by the geometry string
anthonyd58e60c2011-03-30 00:24:34 +00001109% with respect to the given image page (canvas) dimensions and the images
1110% gravity setting.
1111%
1112% This is typically used for specifing a area within a given image for
1113% cropping images to a smaller size, chopping out rows and or columns, or
1114% resizing and positioning overlay images.
1115%
1116% Percentages are relative to image size and not page size, and are set to
1117% nearest integer (pixel) size.
cristy3ed852e2009-09-05 21:47:34 +00001118%
1119% The format of the ParseGravityGeometry method is:
1120%
1121% MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1122% RectangeInfo *region_info,ExceptionInfo *exception)
1123%
1124% A description of each parameter follows:
1125%
anthonyd58e60c2011-03-30 00:24:34 +00001126% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001127%
cristy272f23a2011-03-30 00:37:11 +00001128% o region_info: the region as defined by the geometry string with respect
1129% to the image dimensions and its gravity.
cristy3ed852e2009-09-05 21:47:34 +00001130%
1131% o exception: return any errors or warnings in this structure.
1132%
1133*/
1134MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1135 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1136{
1137 MagickStatusType
1138 flags;
1139
cristybb503372010-05-27 20:51:26 +00001140 size_t
cristy3ed852e2009-09-05 21:47:34 +00001141 height,
1142 width;
1143
1144 SetGeometry(image,region_info);
1145 if (image->page.width != 0)
1146 region_info->width=image->page.width;
1147 if (image->page.height != 0)
1148 region_info->height=image->page.height;
1149 flags=ParseAbsoluteGeometry(geometry,region_info);
1150 if (flags == NoValue)
1151 {
1152 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001153 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001154 return(flags);
1155 }
1156 if ((flags & PercentValue) != 0)
1157 {
1158 GeometryInfo
1159 geometry_info;
1160
1161 MagickStatusType
1162 status;
1163
1164 PointInfo
1165 scale;
1166
1167 /*
anthonyd58e60c2011-03-30 00:24:34 +00001168 Geometry is a percentage of the image size, not canvas size
cristy3ed852e2009-09-05 21:47:34 +00001169 */
1170 if (image->gravity != UndefinedGravity)
1171 flags|=XValue | YValue;
1172 status=ParseGeometry(geometry,&geometry_info);
1173 scale.x=geometry_info.rho;
1174 if ((status & RhoValue) == 0)
1175 scale.x=100.0;
1176 scale.y=geometry_info.sigma;
1177 if ((status & SigmaValue) == 0)
1178 scale.y=scale.x;
cristy272f23a2011-03-30 00:37:11 +00001179 region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1180 region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001181 }
1182 /*
1183 Adjust offset according to gravity setting.
1184 */
1185 width=region_info->width;
1186 height=region_info->height;
1187 if (width == 0)
1188 region_info->width=image->page.width | image->columns;
1189 if (height == 0)
1190 region_info->height=image->page.height | image->rows;
1191 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1192 region_info->width=width;
1193 region_info->height=height;
1194 return(flags);
1195}
1196
1197/*
1198%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1199% %
1200% %
1201% %
1202+ P a r s e M e t a G e o m e t r y %
1203% %
1204% %
1205% %
1206%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1207%
1208% ParseMetaGeometry() is similar to GetGeometry() except the returned
anthonyd58e60c2011-03-30 00:24:34 +00001209% geometry is modified as determined by the meta characters: %, !, <, >, @,
1210% and ^ in relation to image resizing.
1211%
1212% Final image dimensions are adjusted so as to preserve the aspect ratio as
glennrpdcabf6d2011-06-25 03:26:14 +00001213% much as possible, while generating a integer (pixel) size, and fitting the
anthonyd58e60c2011-03-30 00:24:34 +00001214% image within the specified geometry width and height.
1215%
1216% Flags are interpreted...
anthony6634c532012-06-15 06:56:39 +00001217% % geometry size is given percentage of original width and height given
cristy272f23a2011-03-30 00:37:11 +00001218% ! do not try to preserve aspect ratio
1219% < only enlarge images smaller that geometry
1220% > only shrink images larger than geometry
1221% @ Fit image to contain at most this many pixels
1222% ^ Contain the given geometry given, (minimal dimensions given)
cristy3ed852e2009-09-05 21:47:34 +00001223%
1224% The format of the ParseMetaGeometry method is:
1225%
anthonyd58e60c2011-03-30 00:24:34 +00001226% MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
cristy272f23a2011-03-30 00:37:11 +00001227% ssize_t *y, size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001228%
1229% A description of each parameter follows:
1230%
anthonyd58e60c2011-03-30 00:24:34 +00001231% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001232%
anthonyd58e60c2011-03-30 00:24:34 +00001233% o x,y: The x and y offset, set according to the geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001234%
cristy272f23a2011-03-30 00:37:11 +00001235% o width,height: The width and height of original image, modified by
1236% the given geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001237%
1238*/
1239
cristybb503372010-05-27 20:51:26 +00001240static inline size_t MagickMax(const size_t x,
1241 const size_t y)
cristy3ed852e2009-09-05 21:47:34 +00001242{
1243 if (x > y)
1244 return(x);
1245 return(y);
1246}
1247
cristybb503372010-05-27 20:51:26 +00001248MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1249 ssize_t *y,size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001250{
1251 GeometryInfo
1252 geometry_info;
1253
1254 MagickStatusType
1255 flags;
1256
cristybb503372010-05-27 20:51:26 +00001257 size_t
cristy3ed852e2009-09-05 21:47:34 +00001258 former_height,
1259 former_width;
1260
1261 /*
1262 Ensure the image geometry is valid.
1263 */
cristybb503372010-05-27 20:51:26 +00001264 assert(x != (ssize_t *) NULL);
1265 assert(y != (ssize_t *) NULL);
1266 assert(width != (size_t *) NULL);
1267 assert(height != (size_t *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001268 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1269 return(NoValue);
1270 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1271 /*
1272 Parse geometry using GetGeometry.
1273 */
1274 SetGeometryInfo(&geometry_info);
1275 former_width=(*width);
1276 former_height=(*height);
1277 flags=GetGeometry(geometry,x,y,width,height);
1278 if ((flags & PercentValue) != 0)
1279 {
1280 MagickStatusType
1281 flags;
1282
1283 PointInfo
1284 scale;
1285
1286 /*
1287 Geometry is a percentage of the image size.
1288 */
1289 flags=ParseGeometry(geometry,&geometry_info);
1290 scale.x=geometry_info.rho;
1291 if ((flags & RhoValue) == 0)
1292 scale.x=100.0;
1293 scale.y=geometry_info.sigma;
1294 if ((flags & SigmaValue) == 0)
1295 scale.y=scale.x;
cristybb503372010-05-27 20:51:26 +00001296 *width=(size_t) floor(scale.x*former_width/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001297 if (*width == 0)
1298 *width=1;
cristybb503372010-05-27 20:51:26 +00001299 *height=(size_t) floor(scale.y*former_height/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001300 if (*height == 0)
1301 *height=1;
1302 former_width=(*width);
1303 former_height=(*height);
1304 }
cristy07623352011-12-07 13:04:03 +00001305 if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1306 (*height == former_height)))
cristy3ed852e2009-09-05 21:47:34 +00001307 {
cristy07623352011-12-07 13:04:03 +00001308 if ((flags & RhoValue) == 0)
1309 *width=former_width;
1310 if ((flags & SigmaValue) == 0)
1311 *height=former_height;
1312 }
1313 else
1314 {
1315 double
cristy3ed852e2009-09-05 21:47:34 +00001316 scale_factor;
1317
1318 /*
1319 Respect aspect ratio of the image.
1320 */
1321 if ((former_width == 0) || (former_height == 0))
1322 scale_factor=1.0;
1323 else
1324 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1325 {
cristy07623352011-12-07 13:04:03 +00001326 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001327 if ((flags & MinimumValue) == 0)
1328 {
cristy07623352011-12-07 13:04:03 +00001329 if (scale_factor > ((double) *height/(double) former_height))
1330 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001331 }
1332 else
cristy07623352011-12-07 13:04:03 +00001333 if (scale_factor < ((double) *height/(double) former_height))
1334 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001335 }
1336 else
1337 if ((flags & RhoValue) != 0)
1338 {
cristy07623352011-12-07 13:04:03 +00001339 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001340 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001341 (scale_factor < ((double) *width/(double) former_height)))
1342 scale_factor=(double) *width/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001343 }
1344 else
1345 {
cristy07623352011-12-07 13:04:03 +00001346 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001347 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001348 (scale_factor < ((double) *height/(double) former_width)))
1349 scale_factor=(double) *height/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001350 }
cristy272f23a2011-03-30 00:37:11 +00001351 *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1352 *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
cristy3ed852e2009-09-05 21:47:34 +00001353 }
1354 if ((flags & GreaterValue) != 0)
1355 {
1356 if (former_width < *width)
1357 *width=former_width;
1358 if (former_height < *height)
1359 *height=former_height;
1360 }
1361 if ((flags & LessValue) != 0)
1362 {
1363 if (former_width > *width)
1364 *width=former_width;
1365 if (former_height > *height)
1366 *height=former_height;
1367 }
1368 if ((flags & AreaValue) != 0)
1369 {
cristy07623352011-12-07 13:04:03 +00001370 double
cristy3ed852e2009-09-05 21:47:34 +00001371 area,
1372 distance;
1373
1374 PointInfo
1375 scale;
1376
1377 /*
1378 Geometry is a maximum area in pixels.
1379 */
1380 (void) ParseGeometry(geometry,&geometry_info);
1381 area=geometry_info.rho;
1382 distance=sqrt((double) former_width*former_height);
cristye42f6582012-02-11 17:59:50 +00001383 scale.x=(double) former_width/(distance/sqrt((double) area));
1384 scale.y=(double) former_height/(distance/sqrt((double) area));
cristy3ed852e2009-09-05 21:47:34 +00001385 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1386 {
cristy11e0a872012-01-10 16:10:21 +00001387 *width=(size_t) (former_width/(distance/sqrt(area))+0.5);
1388 *height=(size_t) (former_height/(distance/sqrt(area))+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001389 }
1390 former_width=(*width);
1391 former_height=(*height);
1392 }
1393 return(flags);
1394}
1395
1396/*
1397%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1398% %
1399% %
1400% %
1401% P a r s e P a g e G e o m e t r y %
1402% %
1403% %
1404% %
1405%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1406%
1407% ParsePageGeometry() returns a region as defined by the geometry string with
anthonyd58e60c2011-03-30 00:24:34 +00001408% respect to the image page (canvas) dimensions.
1409%
1410% WARNING: Percentage dimensions remain relative to the actual image
1411% dimensions, and not canvas dimensions.
cristy3ed852e2009-09-05 21:47:34 +00001412%
1413% The format of the ParsePageGeometry method is:
1414%
1415% MagickStatusType ParsePageGeometry(const Image *image,
1416% const char *geometry,RectangeInfo *region_info,
1417% ExceptionInfo *exception)
1418%
1419% A description of each parameter follows:
1420%
anthonyd58e60c2011-03-30 00:24:34 +00001421% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001422%
1423% o region_info: the region as defined by the geometry string with
1424% respect to the image and its gravity.
1425%
1426% o exception: return any errors or warnings in this structure.
1427%
1428*/
1429MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1430 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1431{
1432 MagickStatusType
1433 flags;
1434
1435 SetGeometry(image,region_info);
1436 if (image->page.width != 0)
1437 region_info->width=image->page.width;
1438 if (image->page.height != 0)
1439 region_info->height=image->page.height;
1440 flags=ParseAbsoluteGeometry(geometry,region_info);
1441 if (flags == NoValue)
1442 {
1443 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001444 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001445 return(flags);
1446 }
1447 if ((flags & PercentValue) != 0)
1448 {
1449 region_info->width=image->columns;
1450 region_info->height=image->rows;
1451 }
1452 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1453 &region_info->width,&region_info->height);
cristy64ad8f92012-08-03 00:58:24 +00001454 if ((flags & PercentValue) != 0)
1455 {
1456 if ((flags & WidthValue) == 0)
1457 region_info->width=region_info->height;
1458 if ((flags & HeightValue) == 0)
1459 region_info->height=region_info->width;
1460 }
cristy3ed852e2009-09-05 21:47:34 +00001461 return(flags);
1462}
1463
1464/*
1465%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1466% %
1467% %
1468% %
1469% P a r s e R e g i o n G e o m e t r y %
1470% %
1471% %
1472% %
1473%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1474%
1475% ParseRegionGeometry() returns a region as defined by the geometry string
1476% with respect to the image dimensions and aspect ratio.
1477%
anthonyd58e60c2011-03-30 00:24:34 +00001478% This is basically a wrapper around ParseMetaGeometry. This is typically
1479% used to parse a geometry string to work out the final integer dimensions
1480% for image resizing.
1481%
cristy3ed852e2009-09-05 21:47:34 +00001482% The format of the ParseRegionGeometry method is:
1483%
1484% MagickStatusType ParseRegionGeometry(const Image *image,
1485% const char *geometry,RectangeInfo *region_info,
1486% ExceptionInfo *exception)
1487%
1488% A description of each parameter follows:
1489%
anthonyd58e60c2011-03-30 00:24:34 +00001490% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001491%
1492% o region_info: the region as defined by the geometry string.
1493%
1494% o exception: return any errors or warnings in this structure.
1495%
1496*/
1497MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1498 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1499{
1500 MagickStatusType
1501 flags;
1502
1503 SetGeometry(image,region_info);
1504 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1505 &region_info->width,&region_info->height);
1506 if (flags == NoValue)
1507 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001508 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001509 return(flags);
1510}
1511
1512/*
1513%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1514% %
1515% %
1516% %
1517% S e t G e o m e t r y %
1518% %
1519% %
1520% %
1521%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1522%
1523% SetGeometry() sets the geometry to its default values.
1524%
1525% The format of the SetGeometry method is:
1526%
1527% SetGeometry(const Image *image,RectangleInfo *geometry)
1528%
1529% A description of each parameter follows:
1530%
1531% o image: the image.
1532%
1533% o geometry: the geometry.
1534%
1535*/
1536MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1537{
1538 assert(image != (Image *) NULL);
1539 assert(image->signature == MagickSignature);
1540 if (image->debug != MagickFalse)
1541 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1542 assert(geometry != (RectangleInfo *) NULL);
1543 (void) ResetMagickMemory(geometry,0,sizeof(*geometry));
1544 geometry->width=image->columns;
1545 geometry->height=image->rows;
1546}
1547
1548/*
1549%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1550% %
1551% %
1552% %
1553% S e t G e o m e t r y I n f o %
1554% %
1555% %
1556% %
1557%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1558%
1559% SetGeometryInfo sets the GeometryInfo structure to its default values.
1560%
1561% The format of the SetGeometryInfo method is:
1562%
1563% SetGeometryInfo(GeometryInfo *geometry_info)
1564%
1565% A description of each parameter follows:
1566%
1567% o geometry_info: the geometry info structure.
1568%
1569*/
1570MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1571{
1572 assert(geometry_info != (GeometryInfo *) NULL);
1573 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1574 (void) ResetMagickMemory(geometry_info,0,sizeof(*geometry_info));
1575}