blob: dccce5cccfc79c152ec610e4c4b60ed08e652913 [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))
cristyf0458752012-08-12 20:55:34 +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))
cristy243769e2012-08-12 14:39:15 +00001075 geometry_info->sigma=geometry_info->rho;
cristy64ad8f92012-08-03 00:58:24 +00001076 if (((flags & SeparatorValue) != 0) && ((flags & RhoValue) == 0))
1077 geometry_info->rho=geometry_info->sigma;
cristy1ad6c872012-08-02 11:54:05 +00001078 }
anthony6634c532012-06-15 06:56:39 +00001079#if 0
1080 /* Debugging Geometry */
cristy57936e02012-07-03 00:33:28 +00001081 (void) fprintf(stderr,"ParseGeometry...\n");
1082 (void) fprintf(stderr,"Flags: %c %c %s %s %s\n",
1083 (flags & RhoValue) ? 'W' : ' ',(flags & SigmaValue) ? 'H' : ' ',
1084 (flags & XiValue) ? ((flags & XiNegative) ? "-X" : "+X") : " ",
1085 (flags & PsiValue) ? ((flags & PsiNegative) ? "-Y" : "+Y") : " ",
1086 (flags & ChiValue) ? ((flags & ChiNegative) ? "-Z" : "+Z") : " ");
1087 (void) fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",geometry_info->rho,
1088 geometry_info->sigma,geometry_info->xi,geometry_info->psi,
1089 geometry_info->chi);
anthony6634c532012-06-15 06:56:39 +00001090#endif
cristy3ed852e2009-09-05 21:47:34 +00001091 return(flags);
1092}
1093
1094/*
1095%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1096% %
1097% %
1098% %
1099% P a r s e G r a v i t y G e o m e t r y %
1100% %
1101% %
1102% %
1103%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1104%
1105% ParseGravityGeometry() returns a region as defined by the geometry string
anthonyd58e60c2011-03-30 00:24:34 +00001106% with respect to the given image page (canvas) dimensions and the images
1107% gravity setting.
1108%
1109% This is typically used for specifing a area within a given image for
1110% cropping images to a smaller size, chopping out rows and or columns, or
1111% resizing and positioning overlay images.
1112%
1113% Percentages are relative to image size and not page size, and are set to
1114% nearest integer (pixel) size.
cristy3ed852e2009-09-05 21:47:34 +00001115%
1116% The format of the ParseGravityGeometry method is:
1117%
1118% MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1119% RectangeInfo *region_info,ExceptionInfo *exception)
1120%
1121% A description of each parameter follows:
1122%
anthonyd58e60c2011-03-30 00:24:34 +00001123% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001124%
cristy272f23a2011-03-30 00:37:11 +00001125% o region_info: the region as defined by the geometry string with respect
1126% to the image dimensions and its gravity.
cristy3ed852e2009-09-05 21:47:34 +00001127%
1128% o exception: return any errors or warnings in this structure.
1129%
1130*/
1131MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1132 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1133{
1134 MagickStatusType
1135 flags;
1136
cristybb503372010-05-27 20:51:26 +00001137 size_t
cristy3ed852e2009-09-05 21:47:34 +00001138 height,
1139 width;
1140
1141 SetGeometry(image,region_info);
1142 if (image->page.width != 0)
1143 region_info->width=image->page.width;
1144 if (image->page.height != 0)
1145 region_info->height=image->page.height;
1146 flags=ParseAbsoluteGeometry(geometry,region_info);
1147 if (flags == NoValue)
1148 {
1149 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001150 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001151 return(flags);
1152 }
1153 if ((flags & PercentValue) != 0)
1154 {
1155 GeometryInfo
1156 geometry_info;
1157
1158 MagickStatusType
1159 status;
1160
1161 PointInfo
1162 scale;
1163
1164 /*
anthonyd58e60c2011-03-30 00:24:34 +00001165 Geometry is a percentage of the image size, not canvas size
cristy3ed852e2009-09-05 21:47:34 +00001166 */
1167 if (image->gravity != UndefinedGravity)
1168 flags|=XValue | YValue;
1169 status=ParseGeometry(geometry,&geometry_info);
1170 scale.x=geometry_info.rho;
1171 if ((status & RhoValue) == 0)
1172 scale.x=100.0;
1173 scale.y=geometry_info.sigma;
1174 if ((status & SigmaValue) == 0)
1175 scale.y=scale.x;
cristy272f23a2011-03-30 00:37:11 +00001176 region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1177 region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001178 }
1179 /*
1180 Adjust offset according to gravity setting.
1181 */
1182 width=region_info->width;
1183 height=region_info->height;
1184 if (width == 0)
1185 region_info->width=image->page.width | image->columns;
1186 if (height == 0)
1187 region_info->height=image->page.height | image->rows;
1188 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1189 region_info->width=width;
1190 region_info->height=height;
1191 return(flags);
1192}
1193
1194/*
1195%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1196% %
1197% %
1198% %
1199+ P a r s e M e t a G e o m e t r y %
1200% %
1201% %
1202% %
1203%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1204%
1205% ParseMetaGeometry() is similar to GetGeometry() except the returned
anthonyd58e60c2011-03-30 00:24:34 +00001206% geometry is modified as determined by the meta characters: %, !, <, >, @,
1207% and ^ in relation to image resizing.
1208%
1209% Final image dimensions are adjusted so as to preserve the aspect ratio as
glennrpdcabf6d2011-06-25 03:26:14 +00001210% much as possible, while generating a integer (pixel) size, and fitting the
anthonyd58e60c2011-03-30 00:24:34 +00001211% image within the specified geometry width and height.
1212%
1213% Flags are interpreted...
anthony6634c532012-06-15 06:56:39 +00001214% % geometry size is given percentage of original width and height given
cristy272f23a2011-03-30 00:37:11 +00001215% ! do not try to preserve aspect ratio
1216% < only enlarge images smaller that geometry
1217% > only shrink images larger than geometry
1218% @ Fit image to contain at most this many pixels
1219% ^ Contain the given geometry given, (minimal dimensions given)
cristy3ed852e2009-09-05 21:47:34 +00001220%
1221% The format of the ParseMetaGeometry method is:
1222%
anthonyd58e60c2011-03-30 00:24:34 +00001223% MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
cristy272f23a2011-03-30 00:37:11 +00001224% ssize_t *y, size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001225%
1226% A description of each parameter follows:
1227%
anthonyd58e60c2011-03-30 00:24:34 +00001228% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001229%
anthonyd58e60c2011-03-30 00:24:34 +00001230% o x,y: The x and y offset, set according to the geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001231%
cristy272f23a2011-03-30 00:37:11 +00001232% o width,height: The width and height of original image, modified by
1233% the given geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001234%
1235*/
1236
cristybb503372010-05-27 20:51:26 +00001237static inline size_t MagickMax(const size_t x,
1238 const size_t y)
cristy3ed852e2009-09-05 21:47:34 +00001239{
1240 if (x > y)
1241 return(x);
1242 return(y);
1243}
1244
cristybb503372010-05-27 20:51:26 +00001245MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1246 ssize_t *y,size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001247{
1248 GeometryInfo
1249 geometry_info;
1250
1251 MagickStatusType
1252 flags;
1253
cristybb503372010-05-27 20:51:26 +00001254 size_t
cristy3ed852e2009-09-05 21:47:34 +00001255 former_height,
1256 former_width;
1257
1258 /*
1259 Ensure the image geometry is valid.
1260 */
cristybb503372010-05-27 20:51:26 +00001261 assert(x != (ssize_t *) NULL);
1262 assert(y != (ssize_t *) NULL);
1263 assert(width != (size_t *) NULL);
1264 assert(height != (size_t *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001265 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1266 return(NoValue);
1267 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1268 /*
1269 Parse geometry using GetGeometry.
1270 */
1271 SetGeometryInfo(&geometry_info);
1272 former_width=(*width);
1273 former_height=(*height);
1274 flags=GetGeometry(geometry,x,y,width,height);
1275 if ((flags & PercentValue) != 0)
1276 {
1277 MagickStatusType
1278 flags;
1279
1280 PointInfo
1281 scale;
1282
1283 /*
1284 Geometry is a percentage of the image size.
1285 */
1286 flags=ParseGeometry(geometry,&geometry_info);
1287 scale.x=geometry_info.rho;
1288 if ((flags & RhoValue) == 0)
1289 scale.x=100.0;
1290 scale.y=geometry_info.sigma;
1291 if ((flags & SigmaValue) == 0)
1292 scale.y=scale.x;
cristybb503372010-05-27 20:51:26 +00001293 *width=(size_t) floor(scale.x*former_width/100.0+0.5);
cristybb503372010-05-27 20:51:26 +00001294 *height=(size_t) floor(scale.y*former_height/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001295 former_width=(*width);
1296 former_height=(*height);
1297 }
cristy07623352011-12-07 13:04:03 +00001298 if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1299 (*height == former_height)))
cristy3ed852e2009-09-05 21:47:34 +00001300 {
cristy07623352011-12-07 13:04:03 +00001301 if ((flags & RhoValue) == 0)
1302 *width=former_width;
1303 if ((flags & SigmaValue) == 0)
1304 *height=former_height;
1305 }
1306 else
1307 {
1308 double
cristy3ed852e2009-09-05 21:47:34 +00001309 scale_factor;
1310
1311 /*
1312 Respect aspect ratio of the image.
1313 */
1314 if ((former_width == 0) || (former_height == 0))
1315 scale_factor=1.0;
1316 else
1317 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1318 {
cristy07623352011-12-07 13:04:03 +00001319 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001320 if ((flags & MinimumValue) == 0)
1321 {
cristy07623352011-12-07 13:04:03 +00001322 if (scale_factor > ((double) *height/(double) former_height))
1323 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001324 }
1325 else
cristy07623352011-12-07 13:04:03 +00001326 if (scale_factor < ((double) *height/(double) former_height))
1327 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001328 }
1329 else
1330 if ((flags & RhoValue) != 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) &&
cristy07623352011-12-07 13:04:03 +00001334 (scale_factor < ((double) *width/(double) former_height)))
1335 scale_factor=(double) *width/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001336 }
1337 else
1338 {
cristy07623352011-12-07 13:04:03 +00001339 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001340 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001341 (scale_factor < ((double) *height/(double) former_width)))
1342 scale_factor=(double) *height/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001343 }
cristy272f23a2011-03-30 00:37:11 +00001344 *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1345 *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
cristy3ed852e2009-09-05 21:47:34 +00001346 }
1347 if ((flags & GreaterValue) != 0)
1348 {
1349 if (former_width < *width)
1350 *width=former_width;
1351 if (former_height < *height)
1352 *height=former_height;
1353 }
1354 if ((flags & LessValue) != 0)
1355 {
1356 if (former_width > *width)
1357 *width=former_width;
1358 if (former_height > *height)
1359 *height=former_height;
1360 }
1361 if ((flags & AreaValue) != 0)
1362 {
cristy07623352011-12-07 13:04:03 +00001363 double
cristy3ed852e2009-09-05 21:47:34 +00001364 area,
1365 distance;
1366
1367 PointInfo
1368 scale;
1369
1370 /*
1371 Geometry is a maximum area in pixels.
1372 */
1373 (void) ParseGeometry(geometry,&geometry_info);
1374 area=geometry_info.rho;
1375 distance=sqrt((double) former_width*former_height);
cristye42f6582012-02-11 17:59:50 +00001376 scale.x=(double) former_width/(distance/sqrt((double) area));
1377 scale.y=(double) former_height/(distance/sqrt((double) area));
cristy3ed852e2009-09-05 21:47:34 +00001378 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1379 {
cristy11e0a872012-01-10 16:10:21 +00001380 *width=(size_t) (former_width/(distance/sqrt(area))+0.5);
1381 *height=(size_t) (former_height/(distance/sqrt(area))+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001382 }
1383 former_width=(*width);
1384 former_height=(*height);
1385 }
1386 return(flags);
1387}
1388
1389/*
1390%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1391% %
1392% %
1393% %
1394% P a r s e P a g e G e o m e t r y %
1395% %
1396% %
1397% %
1398%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1399%
1400% ParsePageGeometry() returns a region as defined by the geometry string with
anthonyd58e60c2011-03-30 00:24:34 +00001401% respect to the image page (canvas) dimensions.
1402%
1403% WARNING: Percentage dimensions remain relative to the actual image
1404% dimensions, and not canvas dimensions.
cristy3ed852e2009-09-05 21:47:34 +00001405%
1406% The format of the ParsePageGeometry method is:
1407%
1408% MagickStatusType ParsePageGeometry(const Image *image,
1409% const char *geometry,RectangeInfo *region_info,
1410% ExceptionInfo *exception)
1411%
1412% A description of each parameter follows:
1413%
anthonyd58e60c2011-03-30 00:24:34 +00001414% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001415%
1416% o region_info: the region as defined by the geometry string with
1417% respect to the image and its gravity.
1418%
1419% o exception: return any errors or warnings in this structure.
1420%
1421*/
1422MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1423 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1424{
1425 MagickStatusType
1426 flags;
1427
1428 SetGeometry(image,region_info);
1429 if (image->page.width != 0)
1430 region_info->width=image->page.width;
1431 if (image->page.height != 0)
1432 region_info->height=image->page.height;
1433 flags=ParseAbsoluteGeometry(geometry,region_info);
1434 if (flags == NoValue)
1435 {
1436 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001437 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001438 return(flags);
1439 }
1440 if ((flags & PercentValue) != 0)
1441 {
1442 region_info->width=image->columns;
1443 region_info->height=image->rows;
1444 }
1445 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1446 &region_info->width,&region_info->height);
cristy78afe012012-08-03 23:46:41 +00001447 if ((((flags & WidthValue) != 0) || ((flags & HeightValue) != 0)) &&
1448 (((flags & PercentValue) != 0) || ((flags & SeparatorValue) == 0)))
cristy64ad8f92012-08-03 00:58:24 +00001449 {
1450 if ((flags & WidthValue) == 0)
1451 region_info->width=region_info->height;
1452 if ((flags & HeightValue) == 0)
1453 region_info->height=region_info->width;
1454 }
cristy3ed852e2009-09-05 21:47:34 +00001455 return(flags);
1456}
1457
1458/*
1459%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1460% %
1461% %
1462% %
1463% P a r s e R e g i o n G e o m e t r y %
1464% %
1465% %
1466% %
1467%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1468%
1469% ParseRegionGeometry() returns a region as defined by the geometry string
1470% with respect to the image dimensions and aspect ratio.
1471%
anthonyd58e60c2011-03-30 00:24:34 +00001472% This is basically a wrapper around ParseMetaGeometry. This is typically
1473% used to parse a geometry string to work out the final integer dimensions
1474% for image resizing.
1475%
cristy3ed852e2009-09-05 21:47:34 +00001476% The format of the ParseRegionGeometry method is:
1477%
1478% MagickStatusType ParseRegionGeometry(const Image *image,
1479% const char *geometry,RectangeInfo *region_info,
1480% ExceptionInfo *exception)
1481%
1482% A description of each parameter follows:
1483%
anthonyd58e60c2011-03-30 00:24:34 +00001484% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001485%
1486% o region_info: the region as defined by the geometry string.
1487%
1488% o exception: return any errors or warnings in this structure.
1489%
1490*/
1491MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1492 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1493{
1494 MagickStatusType
1495 flags;
1496
1497 SetGeometry(image,region_info);
1498 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1499 &region_info->width,&region_info->height);
1500 if (flags == NoValue)
1501 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001502 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001503 return(flags);
1504}
1505
1506/*
1507%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1508% %
1509% %
1510% %
1511% S e t G e o m e t r y %
1512% %
1513% %
1514% %
1515%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1516%
1517% SetGeometry() sets the geometry to its default values.
1518%
1519% The format of the SetGeometry method is:
1520%
1521% SetGeometry(const Image *image,RectangleInfo *geometry)
1522%
1523% A description of each parameter follows:
1524%
1525% o image: the image.
1526%
1527% o geometry: the geometry.
1528%
1529*/
1530MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1531{
1532 assert(image != (Image *) NULL);
1533 assert(image->signature == MagickSignature);
1534 if (image->debug != MagickFalse)
1535 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1536 assert(geometry != (RectangleInfo *) NULL);
1537 (void) ResetMagickMemory(geometry,0,sizeof(*geometry));
1538 geometry->width=image->columns;
1539 geometry->height=image->rows;
1540}
1541
1542/*
1543%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1544% %
1545% %
1546% %
1547% S e t G e o m e t r y I n f o %
1548% %
1549% %
1550% %
1551%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1552%
1553% SetGeometryInfo sets the GeometryInfo structure to its default values.
1554%
1555% The format of the SetGeometryInfo method is:
1556%
1557% SetGeometryInfo(GeometryInfo *geometry_info)
1558%
1559% A description of each parameter follows:
1560%
1561% o geometry_info: the geometry info structure.
1562%
1563*/
1564MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1565{
1566 assert(geometry_info != (GeometryInfo *) NULL);
1567 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1568 (void) ResetMagickMemory(geometry_info,0,sizeof(*geometry_info));
1569}