blob: 2484311d2b57d36a5984f5c63e12ac62dce0dd2a [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 }
cristy1ad6c872012-08-02 11:54:05 +0000289 if ((flags & SeparatorValue) == 0)
290 {
291 if (((flags & PercentValue) != 0) && ((flags & WidthValue) == 0))
292 {
293 *width=(*height);
294 flags|=WidthValue;
295 }
296 if (((flags & PercentValue) != 0) && ((flags & HeightValue) == 0))
297 {
298 *height=(*width);
299 flags|=HeightValue;
300 }
301 }
anthony6634c532012-06-15 06:56:39 +0000302#if 0
303 /* Debugging Geometry */
cristy57936e02012-07-03 00:33:28 +0000304 (void) fprintf(stderr,"GetGeometry...\n");
305 (void) fprintf(stderr,"Input: %s\n",geometry);
306 (void) fprintf(stderr,"Flags: %c %c %s %s\n",
307 (flags & WidthValue) ? 'W' : ' ',(flags & HeightValue) ? 'H' : ' ',
308 (flags & XValue) ? ((flags & XNegative) ? "-X" : "+X") : " ",
309 (flags & YValue) ? ((flags & YNegative) ? "-Y" : "+Y") : " ");
310 (void) fprintf(stderr,"Geometry: %ldx%ld%+ld%+ld\n",(long) *width,(long)
311 *height,(long) *x,(long) *y);
anthony6634c532012-06-15 06:56:39 +0000312#endif
cristy3ed852e2009-09-05 21:47:34 +0000313 return(flags);
314}
315
316/*
317%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
318% %
319% %
320% %
321% G e t P a g e G e o m e t r y %
322% %
323% %
324% %
325%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326%
327% GetPageGeometry() replaces any page mneumonic with the equivalent size in
328% picas.
329%
330% The format of the GetPageGeometry method is:
331%
332% char *GetPageGeometry(const char *page_geometry)
333%
334% A description of each parameter follows.
335%
cristy272f23a2011-03-30 00:37:11 +0000336% o page_geometry: Specifies a pointer to an array of characters. The
337% string is either a Postscript page name (e.g. A4) or a postscript page
338% geometry (e.g. 612x792+36+36).
cristy3ed852e2009-09-05 21:47:34 +0000339%
340*/
341MagickExport char *GetPageGeometry(const char *page_geometry)
342{
343 static const char
344 *PageSizes[][2]=
345 {
346 { "4x6", "288x432" },
347 { "5x7", "360x504" },
348 { "7x9", "504x648" },
349 { "8x10", "576x720" },
350 { "9x11", "648x792" },
351 { "9x12", "648x864" },
352 { "10x13", "720x936" },
353 { "10x14", "720x1008" },
354 { "11x17", "792x1224" },
355 { "a0", "2384x3370" },
356 { "a1", "1684x2384" },
357 { "a10", "73x105" },
358 { "a2", "1191x1684" },
359 { "a3", "842x1191" },
360 { "a4", "595x842" },
361 { "a4smaLL", "595x842" },
362 { "a5", "420x595" },
363 { "a6", "297x420" },
364 { "a7", "210x297" },
365 { "a8", "148x210" },
366 { "a9", "105x148" },
367 { "archa", "648x864" },
368 { "archb", "864x1296" },
369 { "archC", "1296x1728" },
370 { "archd", "1728x2592" },
371 { "arche", "2592x3456" },
372 { "b0", "2920x4127" },
373 { "b1", "2064x2920" },
374 { "b10", "91x127" },
375 { "b2", "1460x2064" },
376 { "b3", "1032x1460" },
377 { "b4", "729x1032" },
378 { "b5", "516x729" },
379 { "b6", "363x516" },
380 { "b7", "258x363" },
381 { "b8", "181x258" },
382 { "b9", "127x181" },
383 { "c0", "2599x3676" },
384 { "c1", "1837x2599" },
385 { "c2", "1298x1837" },
386 { "c3", "918x1296" },
387 { "c4", "649x918" },
388 { "c5", "459x649" },
389 { "c6", "323x459" },
390 { "c7", "230x323" },
391 { "executive", "540x720" },
392 { "flsa", "612x936" },
393 { "flse", "612x936" },
394 { "folio", "612x936" },
395 { "halfletter", "396x612" },
396 { "isob0", "2835x4008" },
397 { "isob1", "2004x2835" },
398 { "isob10", "88x125" },
399 { "isob2", "1417x2004" },
400 { "isob3", "1001x1417" },
401 { "isob4", "709x1001" },
402 { "isob5", "499x709" },
403 { "isob6", "354x499" },
404 { "isob7", "249x354" },
405 { "isob8", "176x249" },
406 { "isob9", "125x176" },
407 { "jisb0", "1030x1456" },
408 { "jisb1", "728x1030" },
409 { "jisb2", "515x728" },
410 { "jisb3", "364x515" },
411 { "jisb4", "257x364" },
412 { "jisb5", "182x257" },
413 { "jisb6", "128x182" },
414 { "ledger", "1224x792" },
415 { "legal", "612x1008" },
416 { "letter", "612x792" },
417 { "lettersmaLL", "612x792" },
418 { "quarto", "610x780" },
419 { "statement", "396x612" },
420 { "tabloid", "792x1224" },
421 { (char *) NULL, (char *) NULL }
422 };
423
424 char
425 *page;
426
cristybb503372010-05-27 20:51:26 +0000427 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000428 i;
429
430 assert(page_geometry != (char *) NULL);
431 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
432 page=AcquireString(page_geometry);
433 for (i=0; *PageSizes[i] != (char *) NULL; i++)
434 if (LocaleNCompare(PageSizes[i][0],page,strlen(PageSizes[i][0])) == 0)
435 {
436 RectangleInfo
437 geometry;
438
439 MagickStatusType
440 flags;
441
442 /*
443 Replace mneumonic with the equivalent size in dots-per-inch.
444 */
445 (void) CopyMagickString(page,PageSizes[i][1],MaxTextExtent);
446 (void) ConcatenateMagickString(page,page_geometry+
447 strlen(PageSizes[i][0]),MaxTextExtent);
448 flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
449 &geometry.height);
450 if ((flags & GreaterValue) == 0)
451 (void) ConcatenateMagickString(page,">",MaxTextExtent);
452 break;
453 }
454 return(page);
455}
456
457/*
458%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
459% %
460% %
461% %
462% G r a v i t y A d j u s t G e o m e t r y %
463% %
cristy272f23a2011-03-30 00:37:11 +0000464% % % %
cristy3ed852e2009-09-05 21:47:34 +0000465%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
466%
467% GravityAdjustGeometry() adjusts the offset of a region with regard to the
468% given: width, height and gravity; against which it is positioned.
469%
470% The region should also have an appropriate width and height to correctly
471% set the right offset of the top left corner of the region.
472%
473% The format of the GravityAdjustGeometry method is:
474%
cristy272f23a2011-03-30 00:37:11 +0000475% void GravityAdjustGeometry(const size_t width, const size_t height,
476% const GravityType gravity,RectangleInfo *region);
cristy3ed852e2009-09-05 21:47:34 +0000477%
478% A description of each parameter follows:
479%
480% o width, height: the larger area the region is relative to
481%
482% o gravity: the edge/corner the current offset is relative to
483%
484% o region: The region requiring a offset adjustment relative to gravity
485%
486*/
cristybb503372010-05-27 20:51:26 +0000487MagickExport void GravityAdjustGeometry(const size_t width,
488 const size_t height,const GravityType gravity,RectangleInfo *region)
cristy3ed852e2009-09-05 21:47:34 +0000489{
490 if (region->height == 0)
491 region->height=height;
492 if (region->width == 0)
493 region->width=width;
494 switch (gravity)
495 {
496 case NorthEastGravity:
497 case EastGravity:
498 case SouthEastGravity:
499 {
cristybb503372010-05-27 20:51:26 +0000500 region->x=(ssize_t) (width-region->width-region->x);
cristy3ed852e2009-09-05 21:47:34 +0000501 break;
502 }
503 case NorthGravity:
504 case SouthGravity:
505 case CenterGravity:
cristy3ed852e2009-09-05 21:47:34 +0000506 {
cristybb503372010-05-27 20:51:26 +0000507 region->x+=(ssize_t) (width/2-region->width/2);
cristy3ed852e2009-09-05 21:47:34 +0000508 break;
509 }
510 case ForgetGravity:
511 case NorthWestGravity:
512 case WestGravity:
513 case SouthWestGravity:
514 default:
515 break;
516 }
517 switch (gravity)
518 {
519 case SouthWestGravity:
520 case SouthGravity:
521 case SouthEastGravity:
522 {
cristybb503372010-05-27 20:51:26 +0000523 region->y=(ssize_t) (height-region->height-region->y);
cristy3ed852e2009-09-05 21:47:34 +0000524 break;
525 }
526 case EastGravity:
527 case WestGravity:
528 case CenterGravity:
cristy3ed852e2009-09-05 21:47:34 +0000529 {
cristybb503372010-05-27 20:51:26 +0000530 region->y+=(ssize_t) (height/2-region->height/2);
cristy3ed852e2009-09-05 21:47:34 +0000531 break;
532 }
533 case ForgetGravity:
534 case NorthWestGravity:
535 case NorthGravity:
536 case NorthEastGravity:
537 default:
538 break;
539 }
540 return;
541}
542
543/*
544%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
545% %
546% %
547% %
548+ I s G e o m e t r y %
549% %
550% %
551% %
552%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
553%
554% IsGeometry() returns MagickTrue if the geometry specification is valid.
555% Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
556%
557% The format of the IsGeometry method is:
558%
559% MagickBooleanType IsGeometry(const char *geometry)
560%
561% A description of each parameter follows:
562%
563% o geometry: This string is the geometry specification.
564%
565*/
566MagickExport MagickBooleanType IsGeometry(const char *geometry)
567{
568 GeometryInfo
569 geometry_info;
570
571 MagickStatusType
572 flags;
573
574 if (geometry == (const char *) NULL)
575 return(MagickFalse);
576 flags=ParseGeometry(geometry,&geometry_info);
cristy3ed852e2009-09-05 21:47:34 +0000577 return(flags != NoValue ? MagickTrue : MagickFalse);
578}
579
580/*
581%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
582% %
583% %
584% %
585+ I s S c e n e G e o m e t r y %
586% %
587% %
588% %
589%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
590%
591% IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
592% specification (e.g. [1], [1-9], [1,7,4]).
593%
594% The format of the IsSceneGeometry method is:
595%
596% MagickBooleanType IsSceneGeometry(const char *geometry,
597% const MagickBooleanType pedantic)
598%
599% A description of each parameter follows:
600%
601% o geometry: This string is the geometry specification.
602%
603% o pedantic: A value other than 0 invokes a more restrictive set of
604% conditions for a valid specification (e.g. [1], [1-4], [4-1]).
605%
606*/
607MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
608 const MagickBooleanType pedantic)
609{
610 char
611 *p;
612
cristy00976d82011-02-20 20:31:28 +0000613 double
614 value;
615
cristy3ed852e2009-09-05 21:47:34 +0000616 if (geometry == (const char *) NULL)
617 return(MagickFalse);
618 p=(char *) geometry;
cristydbdd0e32011-11-04 23:29:40 +0000619 value=StringToDouble(geometry,&p);
cristy00976d82011-02-20 20:31:28 +0000620 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000621 if (p == geometry)
622 return(MagickFalse);
623 if (strspn(geometry,"0123456789-, ") != strlen(geometry))
624 return(MagickFalse);
625 if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
626 return(MagickFalse);
627 return(MagickTrue);
628}
629
630/*
631%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
632% %
633% %
634% %
635% P a r s e A b s o l u t e G e o m e t r y %
636% %
637% %
638% %
639%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
640%
anthonyd58e60c2011-03-30 00:24:34 +0000641% ParseAbsoluteGeometry() returns a region as defined by the geometry string,
642% without any modification by percentages or gravity.
643%
644% It currently just a wrapper around GetGeometry(), but may be expanded in
645% the future to handle other positioning information.
cristy3ed852e2009-09-05 21:47:34 +0000646%
647% The format of the ParseAbsoluteGeometry method is:
648%
649% MagickStatusType ParseAbsoluteGeometry(const char *geometry,
650% RectangleInfo *region_info)
651%
652% A description of each parameter follows:
653%
anthonyd58e60c2011-03-30 00:24:34 +0000654% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000655%
656% o region_info: the region as defined by the geometry string.
657%
658*/
659MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
660 RectangleInfo *region_info)
661{
662 MagickStatusType
663 flags;
664
665 flags=GetGeometry(geometry,&region_info->x,&region_info->y,
666 &region_info->width,&region_info->height);
667 return(flags);
668}
669
670/*
671%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
672% %
673% %
674% %
675% P a r s e A f f i n e G e o m e t r y %
676% %
677% %
678% %
679%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
680%
cristy272f23a2011-03-30 00:37:11 +0000681% ParseAffineGeometry() returns an affine matrix as defined by a string of 4
682% to 6 comma/space separated floating point values.
anthonyd58e60c2011-03-30 00:24:34 +0000683%
684% The affine matrix determinant is checked for validity of the values.
cristy3ed852e2009-09-05 21:47:34 +0000685%
686% The format of the ParseAffineGeometry method is:
687%
688% MagickStatusType ParseAffineGeometry(const char *geometry,
689% AffineMatrix *affine_matrix,ExceptionInfo *exception)
690%
691% A description of each parameter follows:
692%
anthonyd58e60c2011-03-30 00:24:34 +0000693% 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 +0000694%
695% o affine_matrix: the affine matrix as defined by the geometry string.
696%
697% o exception: return any errors or warnings in this structure.
698%
699*/
700MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
701 AffineMatrix *affine_matrix,ExceptionInfo *exception)
702{
703 char
704 token[MaxTextExtent];
705
706 const char
707 *p;
708
709 double
710 determinant;
711
712 MagickStatusType
713 flags;
714
cristybb503372010-05-27 20:51:26 +0000715 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000716 i;
717
718 GetAffineMatrix(affine_matrix);
719 flags=NoValue;
720 p=(char *) geometry;
721 for (i=0; (*p != '\0') && (i < 6); i++)
722 {
723 GetMagickToken(p,&p,token);
724 if (*token == ',')
725 GetMagickToken(p,&p,token);
726 switch (i)
727 {
cristyc1acd842011-05-19 23:05:47 +0000728 case 0:
729 {
cristydbdd0e32011-11-04 23:29:40 +0000730 affine_matrix->sx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000731 break;
732 }
733 case 1:
734 {
cristydbdd0e32011-11-04 23:29:40 +0000735 affine_matrix->rx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000736 break;
737 }
738 case 2:
739 {
cristydbdd0e32011-11-04 23:29:40 +0000740 affine_matrix->ry=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000741 break;
742 }
743 case 3:
744 {
cristydbdd0e32011-11-04 23:29:40 +0000745 affine_matrix->sy=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000746 break;
747 }
748 case 4:
749 {
cristydbdd0e32011-11-04 23:29:40 +0000750 affine_matrix->tx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000751 flags|=XValue;
752 break;
753 }
754 case 5:
755 {
cristydbdd0e32011-11-04 23:29:40 +0000756 affine_matrix->ty=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000757 flags|=YValue;
758 break;
759 }
cristy3ed852e2009-09-05 21:47:34 +0000760 }
761 }
cristy57936e02012-07-03 00:33:28 +0000762 determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
763 affine_matrix->ry);
cristy3ed852e2009-09-05 21:47:34 +0000764 if (fabs(determinant) < MagickEpsilon)
765 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony92c93bd2012-03-19 14:02:47 +0000766 "InvalidArgument","'%s' : 'Indeterminate Matrix'",geometry);
cristy3ed852e2009-09-05 21:47:34 +0000767 return(flags);
768}
769
770/*
771%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
772% %
773% %
774% %
775% P a r s e G e o m e t r y %
776% %
777% %
778% %
779%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
780%
781% ParseGeometry() parses a geometry specification and returns the sigma,
782% rho, xi, and psi values. It also returns flags that indicates which
783% of the four values (sigma, rho, xi, psi) were located in the string, and
anthonyd58e60c2011-03-30 00:24:34 +0000784% whether the xi or pi values are negative.
785%
786% In addition, it reports if there are any of meta characters (%, !, <, >, @,
787% and ^) flags present. It does not report the location of the percentage
788% relative to the values.
cristy3ed852e2009-09-05 21:47:34 +0000789%
anthony6634c532012-06-15 06:56:39 +0000790% Values may also be seperated by commas, colons, or slashes, and offsets.
791% Offsets may be prefixed by multiple signs to make offset string
792% substitutions easier to handle from shell scripts.
793% For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
794% offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
795% offsets.
796%
cristy3ed852e2009-09-05 21:47:34 +0000797% The format of the ParseGeometry method is:
798%
799% MagickStatusType ParseGeometry(const char *geometry,
800% GeometryInfo *geometry_info)
801%
802% A description of each parameter follows:
803%
anthonyd58e60c2011-03-30 00:24:34 +0000804% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000805%
806% o geometry_info: returns the parsed width/height/x/y in this structure.
807%
808*/
809MagickExport MagickStatusType ParseGeometry(const char *geometry,
810 GeometryInfo *geometry_info)
811{
812 char
813 *p,
814 pedantic_geometry[MaxTextExtent],
815 *q;
816
817 double
818 value;
819
cristyf30f47d2011-09-06 14:40:59 +0000820 int
821 c;
822
cristy3ed852e2009-09-05 21:47:34 +0000823 MagickStatusType
824 flags;
825
826 /*
827 Remove whitespaces meta characters from geometry specification.
828 */
829 assert(geometry_info != (GeometryInfo *) NULL);
830 flags=NoValue;
831 if ((geometry == (char *) NULL) || (*geometry == '\0'))
832 return(flags);
cristy37e0b382011-06-07 13:31:21 +0000833 if (strlen(geometry) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000834 return(flags);
835 (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
836 for (p=pedantic_geometry; *p != '\0'; )
837 {
838 if (isspace((int) ((unsigned char) *p)) != 0)
839 {
840 (void) CopyMagickString(p,p+1,MaxTextExtent);
841 continue;
842 }
cristy35abcb72011-09-06 20:36:15 +0000843 c=(int) ((unsigned char) *p);
844 switch (c)
cristy3ed852e2009-09-05 21:47:34 +0000845 {
846 case '%':
847 {
848 flags|=PercentValue;
849 (void) CopyMagickString(p,p+1,MaxTextExtent);
850 break;
851 }
852 case '!':
853 {
854 flags|=AspectValue;
855 (void) CopyMagickString(p,p+1,MaxTextExtent);
856 break;
857 }
858 case '<':
859 {
860 flags|=LessValue;
861 (void) CopyMagickString(p,p+1,MaxTextExtent);
862 break;
863 }
864 case '>':
865 {
866 flags|=GreaterValue;
867 (void) CopyMagickString(p,p+1,MaxTextExtent);
868 break;
869 }
870 case '^':
871 {
872 flags|=MinimumValue;
873 (void) CopyMagickString(p,p+1,MaxTextExtent);
874 break;
875 }
876 case '@':
877 {
878 flags|=AreaValue;
879 (void) CopyMagickString(p,p+1,MaxTextExtent);
880 break;
881 }
882 case '(':
883 case ')':
884 {
885 (void) CopyMagickString(p,p+1,MaxTextExtent);
886 break;
887 }
cristy1ad6c872012-08-02 11:54:05 +0000888 case 'x':
889 case 'X':
890 {
891 flags|=SeparatorValue;
892 p++;
893 break;
894 }
cristy3ed852e2009-09-05 21:47:34 +0000895 case '-':
896 case '+':
897 case ',':
898 case '0':
899 case '1':
900 case '2':
901 case '3':
902 case '4':
903 case '5':
904 case '6':
905 case '7':
906 case '8':
907 case '9':
cristy3ed852e2009-09-05 21:47:34 +0000908 case '/':
909 case ':':
cristy35f75ac2011-09-06 20:33:35 +0000910 case 215:
cristy3ed852e2009-09-05 21:47:34 +0000911 {
912 p++;
913 break;
914 }
915 case '.':
916 {
917 p++;
918 flags|=DecimalValue;
919 break;
920 }
921 default:
922 return(flags);
923 }
924 }
925 /*
926 Parse rho, sigma, xi, psi, and optionally chi.
927 */
928 p=pedantic_geometry;
929 if (*p == '\0')
930 return(flags);
931 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000932 value=StringToDouble(p,&q);
cristy3ed852e2009-09-05 21:47:34 +0000933 if (LocaleNCompare(p,"0x",2) == 0)
934 value=(double) strtol(p,&q,10);
cristy35f75ac2011-09-06 20:33:35 +0000935 c=(int) ((unsigned char) *q);
936 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
anthonyd1303502010-05-11 12:00:43 +0000937 (*q == '/') || (*q == ':') || (*q =='\0'))
cristy3ed852e2009-09-05 21:47:34 +0000938 {
939 /*
940 Parse rho.
941 */
942 q=p;
943 if (LocaleNCompare(p,"0x",2) == 0)
944 value=(double) strtol(p,&p,10);
945 else
cristydbdd0e32011-11-04 23:29:40 +0000946 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000947 if (p != q)
948 {
949 flags|=RhoValue;
950 geometry_info->rho=value;
951 }
952 }
953 q=p;
cristy35f75ac2011-09-06 20:33:35 +0000954 c=(int) ((unsigned char) *p);
955 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ',') || (*p == '/') ||
cristyf30f47d2011-09-06 14:40:59 +0000956 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000957 {
958 /*
959 Parse sigma.
960 */
961 p++;
962 while (isspace((int) ((unsigned char) *p)) != 0)
963 p++;
cristy35f75ac2011-09-06 20:33:35 +0000964 c=(int) ((unsigned char) *q);
965 if (((c != 215) && (*q != 'x') && (*q != 'X')) || ((*p != '+') &&
cristyf30f47d2011-09-06 14:40:59 +0000966 (*p != '-')))
cristy3ed852e2009-09-05 21:47:34 +0000967 {
968 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000969 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000970 if (p != q)
971 {
972 flags|=SigmaValue;
973 geometry_info->sigma=value;
974 }
975 }
976 }
977 while (isspace((int) ((unsigned char) *p)) != 0)
978 p++;
anthonyd1303502010-05-11 12:00:43 +0000979 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000980 {
981 /*
982 Parse xi value.
983 */
anthony6634c532012-06-15 06:56:39 +0000984 if ((*p == ',') || (*p == '/') || (*p == ':') )
cristy3ed852e2009-09-05 21:47:34 +0000985 p++;
anthony6634c532012-06-15 06:56:39 +0000986 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +0000987 {
988 if (*p == '-')
989 flags^=XiNegative; /* negate sign */
990 p++;
991 }
cristy3ed852e2009-09-05 21:47:34 +0000992 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000993 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000994 if (p != q)
995 {
996 flags|=XiValue;
cristy57936e02012-07-03 00:33:28 +0000997 if ((flags & XiNegative) != 0)
998 value=(-value);
cristy3ed852e2009-09-05 21:47:34 +0000999 geometry_info->xi=value;
1000 }
1001 while (isspace((int) ((unsigned char) *p)) != 0)
1002 p++;
1003 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +00001004 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +00001005 {
1006 /*
1007 Parse psi value.
1008 */
anthony6634c532012-06-15 06:56:39 +00001009 if ((*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +00001010 p++;
anthony6634c532012-06-15 06:56:39 +00001011 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +00001012 {
1013 if (*p == '-')
1014 flags^=PsiNegative; /* negate sign */
1015 p++;
1016 }
cristy3ed852e2009-09-05 21:47:34 +00001017 q=p;
cristydbdd0e32011-11-04 23:29:40 +00001018 value=StringToDouble(p,&p);
cristy57936e02012-07-03 00:33:28 +00001019 if (p != q)
1020 {
1021 flags|=PsiValue;
1022 if ((flags & PsiNegative) != 0)
1023 value=(-value);
1024 geometry_info->psi=value;
1025 }
1026 }
cristy3ed852e2009-09-05 21:47:34 +00001027 while (isspace((int) ((unsigned char) *p)) != 0)
1028 p++;
1029 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +00001030 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +00001031 {
1032 /*
1033 Parse chi value.
1034 */
anthony6634c532012-06-15 06:56:39 +00001035 if ((*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +00001036 p++;
anthony6634c532012-06-15 06:56:39 +00001037 while ((*p == '+') || (*p == '-'))
cristy57936e02012-07-03 00:33:28 +00001038 {
1039 if (*p == '-')
1040 flags^=ChiNegative; /* negate sign */
1041 p++;
1042 }
cristy3ed852e2009-09-05 21:47:34 +00001043 q=p;
cristydbdd0e32011-11-04 23:29:40 +00001044 value=StringToDouble(p,&p);
cristy57936e02012-07-03 00:33:28 +00001045 if (p != q)
1046 {
1047 flags|=ChiValue;
1048 if ((flags & ChiNegative) != 0)
1049 value=(-value);
1050 geometry_info->chi=value;
1051 }
cristy3ed852e2009-09-05 21:47:34 +00001052 }
1053 }
1054 if (strchr(pedantic_geometry,':') != (char *) NULL)
1055 {
1056 /*
1057 Normalize sampling factor (e.g. 4:2:2 => 2x1).
1058 */
1059 geometry_info->rho/=geometry_info->sigma;
1060 geometry_info->sigma=1.0;
1061 if (geometry_info->xi == 0.0)
1062 geometry_info->sigma=2.0;
1063 }
1064 if (((flags & SigmaValue) == 0) && ((flags & XiValue) != 0) &&
1065 ((flags & PsiValue) == 0))
1066 {
1067 /*
1068 Support negative height values (e.g. 30x-20).
1069 */
1070 geometry_info->sigma=geometry_info->xi;
1071 geometry_info->xi=0.0;
1072 flags|=SigmaValue;
1073 flags&=(~XiValue);
1074 }
cristy1ad6c872012-08-02 11:54:05 +00001075 if ((flags & SeparatorValue) == 0)
1076 {
1077 if (((flags & PercentValue) != 0) && ((flags & RhoValue) == 0))
1078 {
1079 geometry_info->rho=geometry_info->sigma;
1080 flags|=RhoValue;
1081 }
1082 if (((flags & PercentValue) != 0) && ((flags & SigmaValue) == 0))
1083 {
1084 geometry_info->sigma=geometry_info->rho;
1085 flags|=SigmaValue;
1086 }
1087 }
anthony6634c532012-06-15 06:56:39 +00001088#if 0
1089 /* Debugging Geometry */
cristy57936e02012-07-03 00:33:28 +00001090 (void) fprintf(stderr,"ParseGeometry...\n");
1091 (void) fprintf(stderr,"Flags: %c %c %s %s %s\n",
1092 (flags & RhoValue) ? 'W' : ' ',(flags & SigmaValue) ? 'H' : ' ',
1093 (flags & XiValue) ? ((flags & XiNegative) ? "-X" : "+X") : " ",
1094 (flags & PsiValue) ? ((flags & PsiNegative) ? "-Y" : "+Y") : " ",
1095 (flags & ChiValue) ? ((flags & ChiNegative) ? "-Z" : "+Z") : " ");
1096 (void) fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",geometry_info->rho,
1097 geometry_info->sigma,geometry_info->xi,geometry_info->psi,
1098 geometry_info->chi);
anthony6634c532012-06-15 06:56:39 +00001099#endif
cristy3ed852e2009-09-05 21:47:34 +00001100 return(flags);
1101}
1102
1103/*
1104%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1105% %
1106% %
1107% %
1108% P a r s e G r a v i t y G e o m e t r y %
1109% %
1110% %
1111% %
1112%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1113%
1114% ParseGravityGeometry() returns a region as defined by the geometry string
anthonyd58e60c2011-03-30 00:24:34 +00001115% with respect to the given image page (canvas) dimensions and the images
1116% gravity setting.
1117%
1118% This is typically used for specifing a area within a given image for
1119% cropping images to a smaller size, chopping out rows and or columns, or
1120% resizing and positioning overlay images.
1121%
1122% Percentages are relative to image size and not page size, and are set to
1123% nearest integer (pixel) size.
cristy3ed852e2009-09-05 21:47:34 +00001124%
1125% The format of the ParseGravityGeometry method is:
1126%
1127% MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1128% RectangeInfo *region_info,ExceptionInfo *exception)
1129%
1130% A description of each parameter follows:
1131%
anthonyd58e60c2011-03-30 00:24:34 +00001132% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001133%
cristy272f23a2011-03-30 00:37:11 +00001134% o region_info: the region as defined by the geometry string with respect
1135% to the image dimensions and its gravity.
cristy3ed852e2009-09-05 21:47:34 +00001136%
1137% o exception: return any errors or warnings in this structure.
1138%
1139*/
1140MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1141 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1142{
1143 MagickStatusType
1144 flags;
1145
cristybb503372010-05-27 20:51:26 +00001146 size_t
cristy3ed852e2009-09-05 21:47:34 +00001147 height,
1148 width;
1149
1150 SetGeometry(image,region_info);
1151 if (image->page.width != 0)
1152 region_info->width=image->page.width;
1153 if (image->page.height != 0)
1154 region_info->height=image->page.height;
1155 flags=ParseAbsoluteGeometry(geometry,region_info);
1156 if (flags == NoValue)
1157 {
1158 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001159 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001160 return(flags);
1161 }
1162 if ((flags & PercentValue) != 0)
1163 {
1164 GeometryInfo
1165 geometry_info;
1166
1167 MagickStatusType
1168 status;
1169
1170 PointInfo
1171 scale;
1172
1173 /*
anthonyd58e60c2011-03-30 00:24:34 +00001174 Geometry is a percentage of the image size, not canvas size
cristy3ed852e2009-09-05 21:47:34 +00001175 */
1176 if (image->gravity != UndefinedGravity)
1177 flags|=XValue | YValue;
1178 status=ParseGeometry(geometry,&geometry_info);
1179 scale.x=geometry_info.rho;
1180 if ((status & RhoValue) == 0)
1181 scale.x=100.0;
1182 scale.y=geometry_info.sigma;
1183 if ((status & SigmaValue) == 0)
1184 scale.y=scale.x;
cristy272f23a2011-03-30 00:37:11 +00001185 region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1186 region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001187 }
1188 /*
1189 Adjust offset according to gravity setting.
1190 */
1191 width=region_info->width;
1192 height=region_info->height;
1193 if (width == 0)
1194 region_info->width=image->page.width | image->columns;
1195 if (height == 0)
1196 region_info->height=image->page.height | image->rows;
1197 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1198 region_info->width=width;
1199 region_info->height=height;
1200 return(flags);
1201}
1202
1203/*
1204%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1205% %
1206% %
1207% %
1208+ P a r s e M e t a G e o m e t r y %
1209% %
1210% %
1211% %
1212%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1213%
1214% ParseMetaGeometry() is similar to GetGeometry() except the returned
anthonyd58e60c2011-03-30 00:24:34 +00001215% geometry is modified as determined by the meta characters: %, !, <, >, @,
1216% and ^ in relation to image resizing.
1217%
1218% Final image dimensions are adjusted so as to preserve the aspect ratio as
glennrpdcabf6d2011-06-25 03:26:14 +00001219% much as possible, while generating a integer (pixel) size, and fitting the
anthonyd58e60c2011-03-30 00:24:34 +00001220% image within the specified geometry width and height.
1221%
1222% Flags are interpreted...
anthony6634c532012-06-15 06:56:39 +00001223% % geometry size is given percentage of original width and height given
cristy272f23a2011-03-30 00:37:11 +00001224% ! do not try to preserve aspect ratio
1225% < only enlarge images smaller that geometry
1226% > only shrink images larger than geometry
1227% @ Fit image to contain at most this many pixels
1228% ^ Contain the given geometry given, (minimal dimensions given)
cristy3ed852e2009-09-05 21:47:34 +00001229%
1230% The format of the ParseMetaGeometry method is:
1231%
anthonyd58e60c2011-03-30 00:24:34 +00001232% MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
cristy272f23a2011-03-30 00:37:11 +00001233% ssize_t *y, size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001234%
1235% A description of each parameter follows:
1236%
anthonyd58e60c2011-03-30 00:24:34 +00001237% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001238%
anthonyd58e60c2011-03-30 00:24:34 +00001239% o x,y: The x and y offset, set according to the geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001240%
cristy272f23a2011-03-30 00:37:11 +00001241% o width,height: The width and height of original image, modified by
1242% the given geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001243%
1244*/
1245
cristybb503372010-05-27 20:51:26 +00001246static inline size_t MagickMax(const size_t x,
1247 const size_t y)
cristy3ed852e2009-09-05 21:47:34 +00001248{
1249 if (x > y)
1250 return(x);
1251 return(y);
1252}
1253
cristybb503372010-05-27 20:51:26 +00001254MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1255 ssize_t *y,size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001256{
1257 GeometryInfo
1258 geometry_info;
1259
1260 MagickStatusType
1261 flags;
1262
cristybb503372010-05-27 20:51:26 +00001263 size_t
cristy3ed852e2009-09-05 21:47:34 +00001264 former_height,
1265 former_width;
1266
1267 /*
1268 Ensure the image geometry is valid.
1269 */
cristybb503372010-05-27 20:51:26 +00001270 assert(x != (ssize_t *) NULL);
1271 assert(y != (ssize_t *) NULL);
1272 assert(width != (size_t *) NULL);
1273 assert(height != (size_t *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001274 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1275 return(NoValue);
1276 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1277 /*
1278 Parse geometry using GetGeometry.
1279 */
1280 SetGeometryInfo(&geometry_info);
1281 former_width=(*width);
1282 former_height=(*height);
1283 flags=GetGeometry(geometry,x,y,width,height);
1284 if ((flags & PercentValue) != 0)
1285 {
1286 MagickStatusType
1287 flags;
1288
1289 PointInfo
1290 scale;
1291
1292 /*
1293 Geometry is a percentage of the image size.
1294 */
1295 flags=ParseGeometry(geometry,&geometry_info);
1296 scale.x=geometry_info.rho;
1297 if ((flags & RhoValue) == 0)
1298 scale.x=100.0;
1299 scale.y=geometry_info.sigma;
1300 if ((flags & SigmaValue) == 0)
1301 scale.y=scale.x;
cristybb503372010-05-27 20:51:26 +00001302 *width=(size_t) floor(scale.x*former_width/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001303 if (*width == 0)
1304 *width=1;
cristybb503372010-05-27 20:51:26 +00001305 *height=(size_t) floor(scale.y*former_height/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001306 if (*height == 0)
1307 *height=1;
1308 former_width=(*width);
1309 former_height=(*height);
1310 }
cristy07623352011-12-07 13:04:03 +00001311 if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1312 (*height == former_height)))
cristy3ed852e2009-09-05 21:47:34 +00001313 {
cristy07623352011-12-07 13:04:03 +00001314 if ((flags & RhoValue) == 0)
1315 *width=former_width;
1316 if ((flags & SigmaValue) == 0)
1317 *height=former_height;
1318 }
1319 else
1320 {
1321 double
cristy3ed852e2009-09-05 21:47:34 +00001322 scale_factor;
1323
1324 /*
1325 Respect aspect ratio of the image.
1326 */
1327 if ((former_width == 0) || (former_height == 0))
1328 scale_factor=1.0;
1329 else
1330 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1331 {
cristy07623352011-12-07 13:04:03 +00001332 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001333 if ((flags & MinimumValue) == 0)
1334 {
cristy07623352011-12-07 13:04:03 +00001335 if (scale_factor > ((double) *height/(double) former_height))
1336 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001337 }
1338 else
cristy07623352011-12-07 13:04:03 +00001339 if (scale_factor < ((double) *height/(double) former_height))
1340 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001341 }
1342 else
1343 if ((flags & RhoValue) != 0)
1344 {
cristy07623352011-12-07 13:04:03 +00001345 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001346 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001347 (scale_factor < ((double) *width/(double) former_height)))
1348 scale_factor=(double) *width/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001349 }
1350 else
1351 {
cristy07623352011-12-07 13:04:03 +00001352 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001353 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001354 (scale_factor < ((double) *height/(double) former_width)))
1355 scale_factor=(double) *height/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001356 }
cristy272f23a2011-03-30 00:37:11 +00001357 *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1358 *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
cristy3ed852e2009-09-05 21:47:34 +00001359 }
1360 if ((flags & GreaterValue) != 0)
1361 {
1362 if (former_width < *width)
1363 *width=former_width;
1364 if (former_height < *height)
1365 *height=former_height;
1366 }
1367 if ((flags & LessValue) != 0)
1368 {
1369 if (former_width > *width)
1370 *width=former_width;
1371 if (former_height > *height)
1372 *height=former_height;
1373 }
1374 if ((flags & AreaValue) != 0)
1375 {
cristy07623352011-12-07 13:04:03 +00001376 double
cristy3ed852e2009-09-05 21:47:34 +00001377 area,
1378 distance;
1379
1380 PointInfo
1381 scale;
1382
1383 /*
1384 Geometry is a maximum area in pixels.
1385 */
1386 (void) ParseGeometry(geometry,&geometry_info);
1387 area=geometry_info.rho;
1388 distance=sqrt((double) former_width*former_height);
cristye42f6582012-02-11 17:59:50 +00001389 scale.x=(double) former_width/(distance/sqrt((double) area));
1390 scale.y=(double) former_height/(distance/sqrt((double) area));
cristy3ed852e2009-09-05 21:47:34 +00001391 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1392 {
cristy11e0a872012-01-10 16:10:21 +00001393 *width=(size_t) (former_width/(distance/sqrt(area))+0.5);
1394 *height=(size_t) (former_height/(distance/sqrt(area))+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001395 }
1396 former_width=(*width);
1397 former_height=(*height);
1398 }
1399 return(flags);
1400}
1401
1402/*
1403%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1404% %
1405% %
1406% %
1407% P a r s e P a g e G e o m e t r y %
1408% %
1409% %
1410% %
1411%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1412%
1413% ParsePageGeometry() returns a region as defined by the geometry string with
anthonyd58e60c2011-03-30 00:24:34 +00001414% respect to the image page (canvas) dimensions.
1415%
1416% WARNING: Percentage dimensions remain relative to the actual image
1417% dimensions, and not canvas dimensions.
cristy3ed852e2009-09-05 21:47:34 +00001418%
1419% The format of the ParsePageGeometry method is:
1420%
1421% MagickStatusType ParsePageGeometry(const Image *image,
1422% const char *geometry,RectangeInfo *region_info,
1423% ExceptionInfo *exception)
1424%
1425% A description of each parameter follows:
1426%
anthonyd58e60c2011-03-30 00:24:34 +00001427% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001428%
1429% o region_info: the region as defined by the geometry string with
1430% respect to the image and its gravity.
1431%
1432% o exception: return any errors or warnings in this structure.
1433%
1434*/
1435MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1436 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1437{
1438 MagickStatusType
1439 flags;
1440
1441 SetGeometry(image,region_info);
1442 if (image->page.width != 0)
1443 region_info->width=image->page.width;
1444 if (image->page.height != 0)
1445 region_info->height=image->page.height;
1446 flags=ParseAbsoluteGeometry(geometry,region_info);
1447 if (flags == NoValue)
1448 {
1449 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001450 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001451 return(flags);
1452 }
1453 if ((flags & PercentValue) != 0)
1454 {
1455 region_info->width=image->columns;
1456 region_info->height=image->rows;
1457 }
1458 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1459 &region_info->width,&region_info->height);
1460 return(flags);
1461}
1462
1463/*
1464%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1465% %
1466% %
1467% %
1468% P a r s e R e g i o n G e o m e t r y %
1469% %
1470% %
1471% %
1472%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1473%
1474% ParseRegionGeometry() returns a region as defined by the geometry string
1475% with respect to the image dimensions and aspect ratio.
1476%
anthonyd58e60c2011-03-30 00:24:34 +00001477% This is basically a wrapper around ParseMetaGeometry. This is typically
1478% used to parse a geometry string to work out the final integer dimensions
1479% for image resizing.
1480%
cristy3ed852e2009-09-05 21:47:34 +00001481% The format of the ParseRegionGeometry method is:
1482%
1483% MagickStatusType ParseRegionGeometry(const Image *image,
1484% const char *geometry,RectangeInfo *region_info,
1485% ExceptionInfo *exception)
1486%
1487% A description of each parameter follows:
1488%
anthonyd58e60c2011-03-30 00:24:34 +00001489% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001490%
1491% o region_info: the region as defined by the geometry string.
1492%
1493% o exception: return any errors or warnings in this structure.
1494%
1495*/
1496MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1497 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1498{
1499 MagickStatusType
1500 flags;
1501
1502 SetGeometry(image,region_info);
1503 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1504 &region_info->width,&region_info->height);
1505 if (flags == NoValue)
1506 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001507 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001508 return(flags);
1509}
1510
1511/*
1512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1513% %
1514% %
1515% %
1516% S e t G e o m e t r y %
1517% %
1518% %
1519% %
1520%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1521%
1522% SetGeometry() sets the geometry to its default values.
1523%
1524% The format of the SetGeometry method is:
1525%
1526% SetGeometry(const Image *image,RectangleInfo *geometry)
1527%
1528% A description of each parameter follows:
1529%
1530% o image: the image.
1531%
1532% o geometry: the geometry.
1533%
1534*/
1535MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1536{
1537 assert(image != (Image *) NULL);
1538 assert(image->signature == MagickSignature);
1539 if (image->debug != MagickFalse)
1540 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1541 assert(geometry != (RectangleInfo *) NULL);
1542 (void) ResetMagickMemory(geometry,0,sizeof(*geometry));
1543 geometry->width=image->columns;
1544 geometry->height=image->rows;
1545}
1546
1547/*
1548%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1549% %
1550% %
1551% %
1552% S e t G e o m e t r y I n f o %
1553% %
1554% %
1555% %
1556%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1557%
1558% SetGeometryInfo sets the GeometryInfo structure to its default values.
1559%
1560% The format of the SetGeometryInfo method is:
1561%
1562% SetGeometryInfo(GeometryInfo *geometry_info)
1563%
1564% A description of each parameter follows:
1565%
1566% o geometry_info: the geometry info structure.
1567%
1568*/
1569MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1570{
1571 assert(geometry_info != (GeometryInfo *) NULL);
1572 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1573 (void) ResetMagickMemory(geometry_info,0,sizeof(*geometry_info));
1574}