blob: f1c3b66881706a3432db1312f8cd4982b4e46012 [file] [log] [blame]
cristy3ed852e2009-09-05 21:47:34 +00001/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3% %
4% %
5% %
6% GGGG EEEEE OOO M M EEEEE TTTTT RRRR Y Y %
7% G E O O MM MM E T R R Y Y %
8% G GG EEE O O M M M EEE T RRRR Y %
9% G G E O O M M E T R R Y %
10% GGGG EEEEE OOO M M EEEEE T R R Y %
11% %
12% %
13% MagickCore Geometry Methods %
14% %
15% Software Design %
16% John Cristy %
17% January 2003 %
18% %
19% %
cristy1454be72011-12-19 01:52:48 +000020% Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization %
cristy3ed852e2009-09-05 21:47:34 +000021% dedicated to making software imaging solutions freely available. %
22% %
23% You may not use this file except in compliance with the License. You may %
24% obtain a copy of the License at %
25% %
26% http://www.imagemagick.org/script/license.php %
27% %
28% Unless required by applicable law or agreed to in writing, software %
29% distributed under the License is distributed on an "AS IS" BASIS, %
30% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
31% See the License for the specific language governing permissions and %
32% limitations under the License. %
33% %
34%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
35%
36%
37*/
38
39/*
40 Include declarations.
41*/
cristy4c08aed2011-07-01 19:47:50 +000042#include "MagickCore/studio.h"
43#include "MagickCore/constitute.h"
44#include "MagickCore/draw.h"
45#include "MagickCore/exception.h"
46#include "MagickCore/exception-private.h"
47#include "MagickCore/geometry.h"
48#include "MagickCore/memory_.h"
49#include "MagickCore/string_.h"
50#include "MagickCore/string-private.h"
51#include "MagickCore/token.h"
cristy3ed852e2009-09-05 21:47:34 +000052
53/*
54%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
55% %
56% %
57% %
58% G e t G e o m e t r y %
59% %
60% %
61% %
62%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
63%
64% GetGeometry() parses a geometry specification and returns the width,
65% height, x, and y values. It also returns flags that indicates which
66% of the four values (width, height, x, y) were located in the string, and
67% whether the x or y values are negative. In addition, there are flags to
68% report any meta characters (%, !, <, or >).
69%
anthony6634c532012-06-15 06:56:39 +000070% The value must form a proper geometry style specification of WxH+X+Y
71% of integers only, and values can not be separated by comma, colon, or
72% slash charcaters. See ParseGeometry() below.
73%
74% Offsets may be prefixed by multiple signs to make offset string
75% substitutions easier to handle from shell scripts.
76% For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
77% offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
78% offsets.
79%
cristy3ed852e2009-09-05 21:47:34 +000080% The format of the GetGeometry method is:
81%
cristybb503372010-05-27 20:51:26 +000082% MagickStatusType GetGeometry(const char *geometry,ssize_t *x,ssize_t *y,
83% size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +000084%
85% A description of each parameter follows:
86%
87% o geometry: The geometry.
88%
89% o x,y: The x and y offset as determined by the geometry specification.
90%
91% o width,height: The width and height as determined by the geometry
92% specification.
93%
94*/
cristy272f23a2011-03-30 00:37:11 +000095MagickExport MagickStatusType GetGeometry(const char *geometry,ssize_t *x,
96 ssize_t *y,size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +000097{
98 char
99 *p,
100 pedantic_geometry[MaxTextExtent],
101 *q;
102
cristy00976d82011-02-20 20:31:28 +0000103 double
104 value;
105
cristyf30f47d2011-09-06 14:40:59 +0000106 int
107 c;
108
cristy3ed852e2009-09-05 21:47:34 +0000109 MagickStatusType
110 flags;
111
112 /*
113 Remove whitespace and meta characters from geometry specification.
114 */
115 flags=NoValue;
116 if ((geometry == (char *) NULL) || (*geometry == '\0'))
117 return(flags);
cristy37e0b382011-06-07 13:31:21 +0000118 if (strlen(geometry) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000119 return(flags);
120 (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
121 for (p=pedantic_geometry; *p != '\0'; )
122 {
123 if (isspace((int) ((unsigned char) *p)) != 0)
124 {
125 (void) CopyMagickString(p,p+1,MaxTextExtent);
126 continue;
127 }
anthonyc330e152012-04-19 14:40:36 +0000128 c=(int)*p;
cristy35f75ac2011-09-06 20:33:35 +0000129 switch (c)
cristy3ed852e2009-09-05 21:47:34 +0000130 {
131 case '%':
132 {
133 flags|=PercentValue;
134 (void) CopyMagickString(p,p+1,MaxTextExtent);
135 break;
136 }
137 case '!':
138 {
139 flags|=AspectValue;
140 (void) CopyMagickString(p,p+1,MaxTextExtent);
141 break;
142 }
143 case '<':
144 {
145 flags|=LessValue;
146 (void) CopyMagickString(p,p+1,MaxTextExtent);
147 break;
148 }
149 case '>':
150 {
151 flags|=GreaterValue;
152 (void) CopyMagickString(p,p+1,MaxTextExtent);
153 break;
154 }
155 case '^':
156 {
157 flags|=MinimumValue;
158 (void) CopyMagickString(p,p+1,MaxTextExtent);
159 break;
160 }
161 case '@':
162 {
163 flags|=AreaValue;
164 (void) CopyMagickString(p,p+1,MaxTextExtent);
165 break;
166 }
167 case '(':
168 case ')':
169 {
170 (void) CopyMagickString(p,p+1,MaxTextExtent);
171 break;
172 }
173 case '-':
174 case '.':
175 case ',':
176 case '+':
177 case '0':
178 case '1':
179 case '2':
180 case '3':
181 case '4':
182 case '5':
183 case '6':
184 case '7':
185 case '8':
186 case '9':
cristy3ed852e2009-09-05 21:47:34 +0000187 case 'x':
188 case 'X':
cristy35f75ac2011-09-06 20:33:35 +0000189 case 215:
cristy3ed852e2009-09-05 21:47:34 +0000190 {
191 p++;
192 break;
193 }
194 default:
195 return(flags);
196 }
197 }
198 /*
199 Parse width, height, x, and y.
200 */
201 p=pedantic_geometry;
202 if (*p == '\0')
203 return(flags);
204 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000205 value=StringToDouble(p,&q);
cristy00976d82011-02-20 20:31:28 +0000206 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000207 if (LocaleNCompare(p,"0x",2) == 0)
cristyf53f26f2011-05-16 00:56:05 +0000208 value=(double) strtol(p,&q,10);
cristy35f75ac2011-09-06 20:33:35 +0000209 c=(int) ((unsigned char) *q);
210 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == '\0'))
cristy3ed852e2009-09-05 21:47:34 +0000211 {
212 /*
213 Parse width.
214 */
215 q=p;
216 if (LocaleNCompare(p,"0x",2) == 0)
cristybb503372010-05-27 20:51:26 +0000217 *width=(size_t) strtol(p,&p,10);
cristy3ed852e2009-09-05 21:47:34 +0000218 else
cristydbdd0e32011-11-04 23:29:40 +0000219 *width=(size_t) floor(StringToDouble(p,&p)+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000220 if (p != q)
221 flags|=WidthValue;
222 }
cristy35f75ac2011-09-06 20:33:35 +0000223 c=(int) ((unsigned char) *p);
224 if ((c == 215) || (*p == 'x') || (*p == 'X'))
cristy3ed852e2009-09-05 21:47:34 +0000225 {
226 p++;
227 if ((*p != '+') && (*p != '-'))
228 {
229 /*
230 Parse height.
231 */
232 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000233 *height=(size_t) floor(StringToDouble(p,&p)+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000234 if (p != q)
235 flags|=HeightValue;
236 }
237 }
238 if ((*p == '+') || (*p == '-'))
239 {
240 /*
241 Parse x value.
242 */
anthony6634c532012-06-15 06:56:39 +0000243 while ((*p == '+') || (*p == '-'))
244 {
245 if (*p == '-')
246 flags^=XNegative; /* negate sign */
247 p++;
248 }
cristy3ed852e2009-09-05 21:47:34 +0000249 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000250 *x=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
cristy3ed852e2009-09-05 21:47:34 +0000251 if (p != q)
cristy3ed852e2009-09-05 21:47:34 +0000252 {
anthony6634c532012-06-15 06:56:39 +0000253 flags|=XValue;
254 if ( (flags&XNegative) != 0 )
255 *x = -*x;
cristy3ed852e2009-09-05 21:47:34 +0000256 }
257 }
anthony6634c532012-06-15 06:56:39 +0000258 if ((*p == '+') || (*p == '-'))
259 {
260 /*
261 Parse y value.
262 */
263 while ((*p == '+') || (*p == '-'))
264 {
265 if (*p == '-')
266 flags^=YNegative; /* negate sign */
267 p++;
268 }
269 q=p;
270 *y=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
271 if (p != q)
272 {
273 flags|=YValue;
274 if ( (flags&YNegative) != 0 )
275 *y = -*y;
276 }
277 }
278#if 0
279 /* Debugging Geometry */
280 fprintf(stderr,"GetGeometry...\n");
281 fprintf(stderr,"Input: %s\n",geometry);
282 fprintf(stderr,"Flags: %c %c %s %s\n",
283 (flags&WidthValue)?'W':' ',(flags&HeightValue)?'H':' ',
284 (flags&XValue)?((flags&XNegative)?"-X":"+X"):" ",
285 (flags&YValue)?((flags&YNegative)?"-Y":"+Y"):" ");
286 fprintf(stderr,"Geometry: %ldx%ld%+ld%+ld\n",
287 (long)*width,(long)*height,(long)*x,(long)*y);
288#endif
cristy3ed852e2009-09-05 21:47:34 +0000289 return(flags);
290}
291
292/*
293%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
294% %
295% %
296% %
297% G e t P a g e G e o m e t r y %
298% %
299% %
300% %
301%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
302%
303% GetPageGeometry() replaces any page mneumonic with the equivalent size in
304% picas.
305%
306% The format of the GetPageGeometry method is:
307%
308% char *GetPageGeometry(const char *page_geometry)
309%
310% A description of each parameter follows.
311%
cristy272f23a2011-03-30 00:37:11 +0000312% o page_geometry: Specifies a pointer to an array of characters. The
313% string is either a Postscript page name (e.g. A4) or a postscript page
314% geometry (e.g. 612x792+36+36).
cristy3ed852e2009-09-05 21:47:34 +0000315%
316*/
317MagickExport char *GetPageGeometry(const char *page_geometry)
318{
319 static const char
320 *PageSizes[][2]=
321 {
322 { "4x6", "288x432" },
323 { "5x7", "360x504" },
324 { "7x9", "504x648" },
325 { "8x10", "576x720" },
326 { "9x11", "648x792" },
327 { "9x12", "648x864" },
328 { "10x13", "720x936" },
329 { "10x14", "720x1008" },
330 { "11x17", "792x1224" },
331 { "a0", "2384x3370" },
332 { "a1", "1684x2384" },
333 { "a10", "73x105" },
334 { "a2", "1191x1684" },
335 { "a3", "842x1191" },
336 { "a4", "595x842" },
337 { "a4smaLL", "595x842" },
338 { "a5", "420x595" },
339 { "a6", "297x420" },
340 { "a7", "210x297" },
341 { "a8", "148x210" },
342 { "a9", "105x148" },
343 { "archa", "648x864" },
344 { "archb", "864x1296" },
345 { "archC", "1296x1728" },
346 { "archd", "1728x2592" },
347 { "arche", "2592x3456" },
348 { "b0", "2920x4127" },
349 { "b1", "2064x2920" },
350 { "b10", "91x127" },
351 { "b2", "1460x2064" },
352 { "b3", "1032x1460" },
353 { "b4", "729x1032" },
354 { "b5", "516x729" },
355 { "b6", "363x516" },
356 { "b7", "258x363" },
357 { "b8", "181x258" },
358 { "b9", "127x181" },
359 { "c0", "2599x3676" },
360 { "c1", "1837x2599" },
361 { "c2", "1298x1837" },
362 { "c3", "918x1296" },
363 { "c4", "649x918" },
364 { "c5", "459x649" },
365 { "c6", "323x459" },
366 { "c7", "230x323" },
367 { "executive", "540x720" },
368 { "flsa", "612x936" },
369 { "flse", "612x936" },
370 { "folio", "612x936" },
371 { "halfletter", "396x612" },
372 { "isob0", "2835x4008" },
373 { "isob1", "2004x2835" },
374 { "isob10", "88x125" },
375 { "isob2", "1417x2004" },
376 { "isob3", "1001x1417" },
377 { "isob4", "709x1001" },
378 { "isob5", "499x709" },
379 { "isob6", "354x499" },
380 { "isob7", "249x354" },
381 { "isob8", "176x249" },
382 { "isob9", "125x176" },
383 { "jisb0", "1030x1456" },
384 { "jisb1", "728x1030" },
385 { "jisb2", "515x728" },
386 { "jisb3", "364x515" },
387 { "jisb4", "257x364" },
388 { "jisb5", "182x257" },
389 { "jisb6", "128x182" },
390 { "ledger", "1224x792" },
391 { "legal", "612x1008" },
392 { "letter", "612x792" },
393 { "lettersmaLL", "612x792" },
394 { "quarto", "610x780" },
395 { "statement", "396x612" },
396 { "tabloid", "792x1224" },
397 { (char *) NULL, (char *) NULL }
398 };
399
400 char
401 *page;
402
cristybb503372010-05-27 20:51:26 +0000403 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000404 i;
405
406 assert(page_geometry != (char *) NULL);
407 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
408 page=AcquireString(page_geometry);
409 for (i=0; *PageSizes[i] != (char *) NULL; i++)
410 if (LocaleNCompare(PageSizes[i][0],page,strlen(PageSizes[i][0])) == 0)
411 {
412 RectangleInfo
413 geometry;
414
415 MagickStatusType
416 flags;
417
418 /*
419 Replace mneumonic with the equivalent size in dots-per-inch.
420 */
421 (void) CopyMagickString(page,PageSizes[i][1],MaxTextExtent);
422 (void) ConcatenateMagickString(page,page_geometry+
423 strlen(PageSizes[i][0]),MaxTextExtent);
424 flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
425 &geometry.height);
426 if ((flags & GreaterValue) == 0)
427 (void) ConcatenateMagickString(page,">",MaxTextExtent);
428 break;
429 }
430 return(page);
431}
432
433/*
434%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
435% %
436% %
437% %
438% G r a v i t y A d j u s t G e o m e t r y %
439% %
cristy272f23a2011-03-30 00:37:11 +0000440% % % %
cristy3ed852e2009-09-05 21:47:34 +0000441%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
442%
443% GravityAdjustGeometry() adjusts the offset of a region with regard to the
444% given: width, height and gravity; against which it is positioned.
445%
446% The region should also have an appropriate width and height to correctly
447% set the right offset of the top left corner of the region.
448%
449% The format of the GravityAdjustGeometry method is:
450%
cristy272f23a2011-03-30 00:37:11 +0000451% void GravityAdjustGeometry(const size_t width, const size_t height,
452% const GravityType gravity,RectangleInfo *region);
cristy3ed852e2009-09-05 21:47:34 +0000453%
454% A description of each parameter follows:
455%
456% o width, height: the larger area the region is relative to
457%
458% o gravity: the edge/corner the current offset is relative to
459%
460% o region: The region requiring a offset adjustment relative to gravity
461%
462*/
cristybb503372010-05-27 20:51:26 +0000463MagickExport void GravityAdjustGeometry(const size_t width,
464 const size_t height,const GravityType gravity,RectangleInfo *region)
cristy3ed852e2009-09-05 21:47:34 +0000465{
466 if (region->height == 0)
467 region->height=height;
468 if (region->width == 0)
469 region->width=width;
470 switch (gravity)
471 {
472 case NorthEastGravity:
473 case EastGravity:
474 case SouthEastGravity:
475 {
cristybb503372010-05-27 20:51:26 +0000476 region->x=(ssize_t) (width-region->width-region->x);
cristy3ed852e2009-09-05 21:47:34 +0000477 break;
478 }
479 case NorthGravity:
480 case SouthGravity:
481 case CenterGravity:
cristy3ed852e2009-09-05 21:47:34 +0000482 {
cristybb503372010-05-27 20:51:26 +0000483 region->x+=(ssize_t) (width/2-region->width/2);
cristy3ed852e2009-09-05 21:47:34 +0000484 break;
485 }
486 case ForgetGravity:
487 case NorthWestGravity:
488 case WestGravity:
489 case SouthWestGravity:
490 default:
491 break;
492 }
493 switch (gravity)
494 {
495 case SouthWestGravity:
496 case SouthGravity:
497 case SouthEastGravity:
498 {
cristybb503372010-05-27 20:51:26 +0000499 region->y=(ssize_t) (height-region->height-region->y);
cristy3ed852e2009-09-05 21:47:34 +0000500 break;
501 }
502 case EastGravity:
503 case WestGravity:
504 case CenterGravity:
cristy3ed852e2009-09-05 21:47:34 +0000505 {
cristybb503372010-05-27 20:51:26 +0000506 region->y+=(ssize_t) (height/2-region->height/2);
cristy3ed852e2009-09-05 21:47:34 +0000507 break;
508 }
509 case ForgetGravity:
510 case NorthWestGravity:
511 case NorthGravity:
512 case NorthEastGravity:
513 default:
514 break;
515 }
516 return;
517}
518
519/*
520%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
521% %
522% %
523% %
524+ I s G e o m e t r y %
525% %
526% %
527% %
528%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
529%
530% IsGeometry() returns MagickTrue if the geometry specification is valid.
531% Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
532%
533% The format of the IsGeometry method is:
534%
535% MagickBooleanType IsGeometry(const char *geometry)
536%
537% A description of each parameter follows:
538%
539% o geometry: This string is the geometry specification.
540%
541*/
542MagickExport MagickBooleanType IsGeometry(const char *geometry)
543{
544 GeometryInfo
545 geometry_info;
546
547 MagickStatusType
548 flags;
549
550 if (geometry == (const char *) NULL)
551 return(MagickFalse);
552 flags=ParseGeometry(geometry,&geometry_info);
cristy3ed852e2009-09-05 21:47:34 +0000553 return(flags != NoValue ? MagickTrue : MagickFalse);
554}
555
556/*
557%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
558% %
559% %
560% %
561+ I s S c e n e G e o m e t r y %
562% %
563% %
564% %
565%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
566%
567% IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
568% specification (e.g. [1], [1-9], [1,7,4]).
569%
570% The format of the IsSceneGeometry method is:
571%
572% MagickBooleanType IsSceneGeometry(const char *geometry,
573% const MagickBooleanType pedantic)
574%
575% A description of each parameter follows:
576%
577% o geometry: This string is the geometry specification.
578%
579% o pedantic: A value other than 0 invokes a more restrictive set of
580% conditions for a valid specification (e.g. [1], [1-4], [4-1]).
581%
582*/
583MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
584 const MagickBooleanType pedantic)
585{
586 char
587 *p;
588
cristy00976d82011-02-20 20:31:28 +0000589 double
590 value;
591
cristy3ed852e2009-09-05 21:47:34 +0000592 if (geometry == (const char *) NULL)
593 return(MagickFalse);
594 p=(char *) geometry;
cristydbdd0e32011-11-04 23:29:40 +0000595 value=StringToDouble(geometry,&p);
cristy00976d82011-02-20 20:31:28 +0000596 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000597 if (p == geometry)
598 return(MagickFalse);
599 if (strspn(geometry,"0123456789-, ") != strlen(geometry))
600 return(MagickFalse);
601 if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
602 return(MagickFalse);
603 return(MagickTrue);
604}
605
606/*
607%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
608% %
609% %
610% %
611% P a r s e A b s o l u t e G e o m e t r y %
612% %
613% %
614% %
615%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
616%
anthonyd58e60c2011-03-30 00:24:34 +0000617% ParseAbsoluteGeometry() returns a region as defined by the geometry string,
618% without any modification by percentages or gravity.
619%
620% It currently just a wrapper around GetGeometry(), but may be expanded in
621% the future to handle other positioning information.
cristy3ed852e2009-09-05 21:47:34 +0000622%
623% The format of the ParseAbsoluteGeometry method is:
624%
625% MagickStatusType ParseAbsoluteGeometry(const char *geometry,
626% RectangleInfo *region_info)
627%
628% A description of each parameter follows:
629%
anthonyd58e60c2011-03-30 00:24:34 +0000630% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000631%
632% o region_info: the region as defined by the geometry string.
633%
634*/
635MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
636 RectangleInfo *region_info)
637{
638 MagickStatusType
639 flags;
640
641 flags=GetGeometry(geometry,&region_info->x,&region_info->y,
642 &region_info->width,&region_info->height);
643 return(flags);
644}
645
646/*
647%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
648% %
649% %
650% %
651% P a r s e A f f i n e G e o m e t r y %
652% %
653% %
654% %
655%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
656%
cristy272f23a2011-03-30 00:37:11 +0000657% ParseAffineGeometry() returns an affine matrix as defined by a string of 4
658% to 6 comma/space separated floating point values.
anthonyd58e60c2011-03-30 00:24:34 +0000659%
660% The affine matrix determinant is checked for validity of the values.
cristy3ed852e2009-09-05 21:47:34 +0000661%
662% The format of the ParseAffineGeometry method is:
663%
664% MagickStatusType ParseAffineGeometry(const char *geometry,
665% AffineMatrix *affine_matrix,ExceptionInfo *exception)
666%
667% A description of each parameter follows:
668%
anthonyd58e60c2011-03-30 00:24:34 +0000669% 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 +0000670%
671% o affine_matrix: the affine matrix as defined by the geometry string.
672%
673% o exception: return any errors or warnings in this structure.
674%
675*/
676MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
677 AffineMatrix *affine_matrix,ExceptionInfo *exception)
678{
679 char
680 token[MaxTextExtent];
681
682 const char
683 *p;
684
685 double
686 determinant;
687
688 MagickStatusType
689 flags;
690
cristybb503372010-05-27 20:51:26 +0000691 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000692 i;
693
694 GetAffineMatrix(affine_matrix);
695 flags=NoValue;
696 p=(char *) geometry;
697 for (i=0; (*p != '\0') && (i < 6); i++)
698 {
699 GetMagickToken(p,&p,token);
700 if (*token == ',')
701 GetMagickToken(p,&p,token);
702 switch (i)
703 {
cristyc1acd842011-05-19 23:05:47 +0000704 case 0:
705 {
cristydbdd0e32011-11-04 23:29:40 +0000706 affine_matrix->sx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000707 break;
708 }
709 case 1:
710 {
cristydbdd0e32011-11-04 23:29:40 +0000711 affine_matrix->rx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000712 break;
713 }
714 case 2:
715 {
cristydbdd0e32011-11-04 23:29:40 +0000716 affine_matrix->ry=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000717 break;
718 }
719 case 3:
720 {
cristydbdd0e32011-11-04 23:29:40 +0000721 affine_matrix->sy=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000722 break;
723 }
724 case 4:
725 {
cristydbdd0e32011-11-04 23:29:40 +0000726 affine_matrix->tx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000727 flags|=XValue;
728 break;
729 }
730 case 5:
731 {
cristydbdd0e32011-11-04 23:29:40 +0000732 affine_matrix->ty=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000733 flags|=YValue;
734 break;
735 }
cristy3ed852e2009-09-05 21:47:34 +0000736 }
737 }
anthony92c93bd2012-03-19 14:02:47 +0000738 determinant=(affine_matrix->sx*affine_matrix->sy
739 - affine_matrix->rx*affine_matrix->ry);
cristy3ed852e2009-09-05 21:47:34 +0000740 if (fabs(determinant) < MagickEpsilon)
741 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony92c93bd2012-03-19 14:02:47 +0000742 "InvalidArgument","'%s' : 'Indeterminate Matrix'",geometry);
cristy3ed852e2009-09-05 21:47:34 +0000743 return(flags);
744}
745
746/*
747%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
748% %
749% %
750% %
751% P a r s e G e o m e t r y %
752% %
753% %
754% %
755%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
756%
757% ParseGeometry() parses a geometry specification and returns the sigma,
758% rho, xi, and psi values. It also returns flags that indicates which
759% of the four values (sigma, rho, xi, psi) were located in the string, and
anthonyd58e60c2011-03-30 00:24:34 +0000760% whether the xi or pi values are negative.
761%
762% In addition, it reports if there are any of meta characters (%, !, <, >, @,
763% and ^) flags present. It does not report the location of the percentage
764% relative to the values.
cristy3ed852e2009-09-05 21:47:34 +0000765%
anthony6634c532012-06-15 06:56:39 +0000766% Values may also be seperated by commas, colons, or slashes, and offsets.
767% Offsets may be prefixed by multiple signs to make offset string
768% substitutions easier to handle from shell scripts.
769% For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
770% offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
771% offsets.
772%
cristy3ed852e2009-09-05 21:47:34 +0000773% The format of the ParseGeometry method is:
774%
775% MagickStatusType ParseGeometry(const char *geometry,
776% GeometryInfo *geometry_info)
777%
778% A description of each parameter follows:
779%
anthonyd58e60c2011-03-30 00:24:34 +0000780% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000781%
782% o geometry_info: returns the parsed width/height/x/y in this structure.
783%
784*/
785MagickExport MagickStatusType ParseGeometry(const char *geometry,
786 GeometryInfo *geometry_info)
787{
788 char
789 *p,
790 pedantic_geometry[MaxTextExtent],
791 *q;
792
793 double
794 value;
795
cristyf30f47d2011-09-06 14:40:59 +0000796 int
797 c;
798
cristy3ed852e2009-09-05 21:47:34 +0000799 MagickStatusType
800 flags;
801
802 /*
803 Remove whitespaces meta characters from geometry specification.
804 */
805 assert(geometry_info != (GeometryInfo *) NULL);
806 flags=NoValue;
807 if ((geometry == (char *) NULL) || (*geometry == '\0'))
808 return(flags);
cristy37e0b382011-06-07 13:31:21 +0000809 if (strlen(geometry) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000810 return(flags);
811 (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
812 for (p=pedantic_geometry; *p != '\0'; )
813 {
814 if (isspace((int) ((unsigned char) *p)) != 0)
815 {
816 (void) CopyMagickString(p,p+1,MaxTextExtent);
817 continue;
818 }
cristy35abcb72011-09-06 20:36:15 +0000819 c=(int) ((unsigned char) *p);
820 switch (c)
cristy3ed852e2009-09-05 21:47:34 +0000821 {
822 case '%':
823 {
824 flags|=PercentValue;
825 (void) CopyMagickString(p,p+1,MaxTextExtent);
826 break;
827 }
828 case '!':
829 {
830 flags|=AspectValue;
831 (void) CopyMagickString(p,p+1,MaxTextExtent);
832 break;
833 }
834 case '<':
835 {
836 flags|=LessValue;
837 (void) CopyMagickString(p,p+1,MaxTextExtent);
838 break;
839 }
840 case '>':
841 {
842 flags|=GreaterValue;
843 (void) CopyMagickString(p,p+1,MaxTextExtent);
844 break;
845 }
846 case '^':
847 {
848 flags|=MinimumValue;
849 (void) CopyMagickString(p,p+1,MaxTextExtent);
850 break;
851 }
852 case '@':
853 {
854 flags|=AreaValue;
855 (void) CopyMagickString(p,p+1,MaxTextExtent);
856 break;
857 }
858 case '(':
859 case ')':
860 {
861 (void) CopyMagickString(p,p+1,MaxTextExtent);
862 break;
863 }
864 case '-':
865 case '+':
866 case ',':
867 case '0':
868 case '1':
869 case '2':
870 case '3':
871 case '4':
872 case '5':
873 case '6':
874 case '7':
875 case '8':
876 case '9':
cristy3ed852e2009-09-05 21:47:34 +0000877 case 'x':
878 case 'X':
879 case '/':
880 case ':':
cristy35f75ac2011-09-06 20:33:35 +0000881 case 215:
cristy3ed852e2009-09-05 21:47:34 +0000882 {
883 p++;
884 break;
885 }
886 case '.':
887 {
888 p++;
889 flags|=DecimalValue;
890 break;
891 }
892 default:
893 return(flags);
894 }
895 }
896 /*
897 Parse rho, sigma, xi, psi, and optionally chi.
898 */
899 p=pedantic_geometry;
900 if (*p == '\0')
901 return(flags);
902 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000903 value=StringToDouble(p,&q);
cristy3ed852e2009-09-05 21:47:34 +0000904 if (LocaleNCompare(p,"0x",2) == 0)
905 value=(double) strtol(p,&q,10);
cristy35f75ac2011-09-06 20:33:35 +0000906 c=(int) ((unsigned char) *q);
907 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
anthonyd1303502010-05-11 12:00:43 +0000908 (*q == '/') || (*q == ':') || (*q =='\0'))
cristy3ed852e2009-09-05 21:47:34 +0000909 {
910 /*
911 Parse rho.
912 */
913 q=p;
914 if (LocaleNCompare(p,"0x",2) == 0)
915 value=(double) strtol(p,&p,10);
916 else
cristydbdd0e32011-11-04 23:29:40 +0000917 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000918 if (p != q)
919 {
920 flags|=RhoValue;
921 geometry_info->rho=value;
922 }
923 }
924 q=p;
cristy35f75ac2011-09-06 20:33:35 +0000925 c=(int) ((unsigned char) *p);
926 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ',') || (*p == '/') ||
cristyf30f47d2011-09-06 14:40:59 +0000927 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000928 {
929 /*
930 Parse sigma.
931 */
932 p++;
933 while (isspace((int) ((unsigned char) *p)) != 0)
934 p++;
cristy35f75ac2011-09-06 20:33:35 +0000935 c=(int) ((unsigned char) *q);
936 if (((c != 215) && (*q != 'x') && (*q != 'X')) || ((*p != '+') &&
cristyf30f47d2011-09-06 14:40:59 +0000937 (*p != '-')))
cristy3ed852e2009-09-05 21:47:34 +0000938 {
939 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000940 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000941 if (p != q)
942 {
943 flags|=SigmaValue;
944 geometry_info->sigma=value;
945 }
946 }
947 }
948 while (isspace((int) ((unsigned char) *p)) != 0)
949 p++;
anthonyd1303502010-05-11 12:00:43 +0000950 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000951 {
952 /*
953 Parse xi value.
954 */
anthony6634c532012-06-15 06:56:39 +0000955 if ((*p == ',') || (*p == '/') || (*p == ':') )
cristy3ed852e2009-09-05 21:47:34 +0000956 p++;
anthony6634c532012-06-15 06:56:39 +0000957 while ((*p == '+') || (*p == '-'))
958 {
959 if (*p == '-')
960 flags^=XiNegative; /* negate sign */
961 p++;
962 }
cristy3ed852e2009-09-05 21:47:34 +0000963 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000964 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000965 if (p != q)
966 {
967 flags|=XiValue;
anthony6634c532012-06-15 06:56:39 +0000968 if ( (flags&XiNegative) != 0 )
969 value = -value;
cristy3ed852e2009-09-05 21:47:34 +0000970 geometry_info->xi=value;
971 }
972 while (isspace((int) ((unsigned char) *p)) != 0)
973 p++;
974 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +0000975 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000976 {
977 /*
978 Parse psi value.
979 */
anthony6634c532012-06-15 06:56:39 +0000980 if ((*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000981 p++;
anthony6634c532012-06-15 06:56:39 +0000982 while ((*p == '+') || (*p == '-'))
983 {
984 if (*p == '-')
985 flags^=PsiNegative; /* negate sign */
986 p++;
987 }
cristy3ed852e2009-09-05 21:47:34 +0000988 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000989 value=StringToDouble(p,&p);
anthony6634c532012-06-15 06:56:39 +0000990 if (p != q) {
991 flags|=PsiValue;
992 if ( (flags&PsiNegative) != 0 )
993 value = -value;
994 geometry_info->psi=value;
995 }
cristy3ed852e2009-09-05 21:47:34 +0000996 }
997 while (isspace((int) ((unsigned char) *p)) != 0)
998 p++;
999 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +00001000 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +00001001 {
1002 /*
1003 Parse chi value.
1004 */
anthony6634c532012-06-15 06:56:39 +00001005 if ((*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +00001006 p++;
anthony6634c532012-06-15 06:56:39 +00001007 while ((*p == '+') || (*p == '-'))
1008 {
1009 if (*p == '-')
1010 flags^=ChiNegative; /* negate sign */
1011 p++;
1012 }
cristy3ed852e2009-09-05 21:47:34 +00001013 q=p;
cristydbdd0e32011-11-04 23:29:40 +00001014 value=StringToDouble(p,&p);
anthony6634c532012-06-15 06:56:39 +00001015 if (p != q) {
1016 flags|=ChiValue;
1017 if ( (flags&ChiNegative) != 0 )
1018 value = -value;
1019 geometry_info->chi=value;
1020 }
cristy3ed852e2009-09-05 21:47:34 +00001021 }
1022 }
1023 if (strchr(pedantic_geometry,':') != (char *) NULL)
1024 {
1025 /*
1026 Normalize sampling factor (e.g. 4:2:2 => 2x1).
1027 */
1028 geometry_info->rho/=geometry_info->sigma;
1029 geometry_info->sigma=1.0;
1030 if (geometry_info->xi == 0.0)
1031 geometry_info->sigma=2.0;
1032 }
1033 if (((flags & SigmaValue) == 0) && ((flags & XiValue) != 0) &&
1034 ((flags & PsiValue) == 0))
1035 {
1036 /*
1037 Support negative height values (e.g. 30x-20).
1038 */
1039 geometry_info->sigma=geometry_info->xi;
1040 geometry_info->xi=0.0;
1041 flags|=SigmaValue;
1042 flags&=(~XiValue);
1043 }
anthony6634c532012-06-15 06:56:39 +00001044#if 0
1045 /* Debugging Geometry */
1046 fprintf(stderr,"ParseGeometry...\n");
1047 fprintf(stderr,"Flags: %c %c %s %s %s\n",
1048 (flags&RhoValue)?'W':' ',(flags&SigmaValue)?'H':' ',
1049 (flags&XiValue)?((flags&XiNegative)?"-X":"+X"):" ",
1050 (flags&PsiValue)?((flags&PsiNegative)?"-Y":"+Y"):" ",
1051 (flags&ChiValue)?((flags&ChiNegative)?"-Z":"+Z"):" ");
1052 fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",
1053 geometry_info->rho,geometry_info->sigma,
1054 geometry_info->xi,geometry_info->psi,geometry_info->chi);
1055#endif
cristy3ed852e2009-09-05 21:47:34 +00001056 return(flags);
1057}
1058
1059/*
1060%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1061% %
1062% %
1063% %
1064% P a r s e G r a v i t y G e o m e t r y %
1065% %
1066% %
1067% %
1068%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1069%
1070% ParseGravityGeometry() returns a region as defined by the geometry string
anthonyd58e60c2011-03-30 00:24:34 +00001071% with respect to the given image page (canvas) dimensions and the images
1072% gravity setting.
1073%
1074% This is typically used for specifing a area within a given image for
1075% cropping images to a smaller size, chopping out rows and or columns, or
1076% resizing and positioning overlay images.
1077%
1078% Percentages are relative to image size and not page size, and are set to
1079% nearest integer (pixel) size.
cristy3ed852e2009-09-05 21:47:34 +00001080%
1081% The format of the ParseGravityGeometry method is:
1082%
1083% MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1084% RectangeInfo *region_info,ExceptionInfo *exception)
1085%
1086% A description of each parameter follows:
1087%
anthonyd58e60c2011-03-30 00:24:34 +00001088% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001089%
cristy272f23a2011-03-30 00:37:11 +00001090% o region_info: the region as defined by the geometry string with respect
1091% to the image dimensions and its gravity.
cristy3ed852e2009-09-05 21:47:34 +00001092%
1093% o exception: return any errors or warnings in this structure.
1094%
1095*/
1096MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1097 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1098{
1099 MagickStatusType
1100 flags;
1101
cristybb503372010-05-27 20:51:26 +00001102 size_t
cristy3ed852e2009-09-05 21:47:34 +00001103 height,
1104 width;
1105
1106 SetGeometry(image,region_info);
1107 if (image->page.width != 0)
1108 region_info->width=image->page.width;
1109 if (image->page.height != 0)
1110 region_info->height=image->page.height;
1111 flags=ParseAbsoluteGeometry(geometry,region_info);
1112 if (flags == NoValue)
1113 {
1114 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001115 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001116 return(flags);
1117 }
1118 if ((flags & PercentValue) != 0)
1119 {
1120 GeometryInfo
1121 geometry_info;
1122
1123 MagickStatusType
1124 status;
1125
1126 PointInfo
1127 scale;
1128
1129 /*
anthonyd58e60c2011-03-30 00:24:34 +00001130 Geometry is a percentage of the image size, not canvas size
cristy3ed852e2009-09-05 21:47:34 +00001131 */
1132 if (image->gravity != UndefinedGravity)
1133 flags|=XValue | YValue;
1134 status=ParseGeometry(geometry,&geometry_info);
1135 scale.x=geometry_info.rho;
1136 if ((status & RhoValue) == 0)
1137 scale.x=100.0;
1138 scale.y=geometry_info.sigma;
1139 if ((status & SigmaValue) == 0)
1140 scale.y=scale.x;
cristy272f23a2011-03-30 00:37:11 +00001141 region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1142 region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001143 }
1144 /*
1145 Adjust offset according to gravity setting.
1146 */
1147 width=region_info->width;
1148 height=region_info->height;
1149 if (width == 0)
1150 region_info->width=image->page.width | image->columns;
1151 if (height == 0)
1152 region_info->height=image->page.height | image->rows;
1153 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1154 region_info->width=width;
1155 region_info->height=height;
1156 return(flags);
1157}
1158
1159/*
1160%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1161% %
1162% %
1163% %
1164+ P a r s e M e t a G e o m e t r y %
1165% %
1166% %
1167% %
1168%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1169%
1170% ParseMetaGeometry() is similar to GetGeometry() except the returned
anthonyd58e60c2011-03-30 00:24:34 +00001171% geometry is modified as determined by the meta characters: %, !, <, >, @,
1172% and ^ in relation to image resizing.
1173%
1174% Final image dimensions are adjusted so as to preserve the aspect ratio as
glennrpdcabf6d2011-06-25 03:26:14 +00001175% much as possible, while generating a integer (pixel) size, and fitting the
anthonyd58e60c2011-03-30 00:24:34 +00001176% image within the specified geometry width and height.
1177%
1178% Flags are interpreted...
anthony6634c532012-06-15 06:56:39 +00001179% % geometry size is given percentage of original width and height given
cristy272f23a2011-03-30 00:37:11 +00001180% ! do not try to preserve aspect ratio
1181% < only enlarge images smaller that geometry
1182% > only shrink images larger than geometry
1183% @ Fit image to contain at most this many pixels
1184% ^ Contain the given geometry given, (minimal dimensions given)
cristy3ed852e2009-09-05 21:47:34 +00001185%
1186% The format of the ParseMetaGeometry method is:
1187%
anthonyd58e60c2011-03-30 00:24:34 +00001188% MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
cristy272f23a2011-03-30 00:37:11 +00001189% ssize_t *y, size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001190%
1191% A description of each parameter follows:
1192%
anthonyd58e60c2011-03-30 00:24:34 +00001193% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001194%
anthonyd58e60c2011-03-30 00:24:34 +00001195% o x,y: The x and y offset, set according to the geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001196%
cristy272f23a2011-03-30 00:37:11 +00001197% o width,height: The width and height of original image, modified by
1198% the given geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001199%
1200*/
1201
cristybb503372010-05-27 20:51:26 +00001202static inline size_t MagickMax(const size_t x,
1203 const size_t y)
cristy3ed852e2009-09-05 21:47:34 +00001204{
1205 if (x > y)
1206 return(x);
1207 return(y);
1208}
1209
cristybb503372010-05-27 20:51:26 +00001210MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1211 ssize_t *y,size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001212{
1213 GeometryInfo
1214 geometry_info;
1215
1216 MagickStatusType
1217 flags;
1218
cristybb503372010-05-27 20:51:26 +00001219 size_t
cristy3ed852e2009-09-05 21:47:34 +00001220 former_height,
1221 former_width;
1222
1223 /*
1224 Ensure the image geometry is valid.
1225 */
cristybb503372010-05-27 20:51:26 +00001226 assert(x != (ssize_t *) NULL);
1227 assert(y != (ssize_t *) NULL);
1228 assert(width != (size_t *) NULL);
1229 assert(height != (size_t *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001230 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1231 return(NoValue);
1232 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1233 /*
1234 Parse geometry using GetGeometry.
1235 */
1236 SetGeometryInfo(&geometry_info);
1237 former_width=(*width);
1238 former_height=(*height);
1239 flags=GetGeometry(geometry,x,y,width,height);
1240 if ((flags & PercentValue) != 0)
1241 {
1242 MagickStatusType
1243 flags;
1244
1245 PointInfo
1246 scale;
1247
1248 /*
1249 Geometry is a percentage of the image size.
1250 */
1251 flags=ParseGeometry(geometry,&geometry_info);
1252 scale.x=geometry_info.rho;
1253 if ((flags & RhoValue) == 0)
1254 scale.x=100.0;
1255 scale.y=geometry_info.sigma;
1256 if ((flags & SigmaValue) == 0)
1257 scale.y=scale.x;
cristybb503372010-05-27 20:51:26 +00001258 *width=(size_t) floor(scale.x*former_width/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001259 if (*width == 0)
1260 *width=1;
cristybb503372010-05-27 20:51:26 +00001261 *height=(size_t) floor(scale.y*former_height/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001262 if (*height == 0)
1263 *height=1;
1264 former_width=(*width);
1265 former_height=(*height);
1266 }
cristy07623352011-12-07 13:04:03 +00001267 if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1268 (*height == former_height)))
cristy3ed852e2009-09-05 21:47:34 +00001269 {
cristy07623352011-12-07 13:04:03 +00001270 if ((flags & RhoValue) == 0)
1271 *width=former_width;
1272 if ((flags & SigmaValue) == 0)
1273 *height=former_height;
1274 }
1275 else
1276 {
1277 double
cristy3ed852e2009-09-05 21:47:34 +00001278 scale_factor;
1279
1280 /*
1281 Respect aspect ratio of the image.
1282 */
1283 if ((former_width == 0) || (former_height == 0))
1284 scale_factor=1.0;
1285 else
1286 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1287 {
cristy07623352011-12-07 13:04:03 +00001288 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001289 if ((flags & MinimumValue) == 0)
1290 {
cristy07623352011-12-07 13:04:03 +00001291 if (scale_factor > ((double) *height/(double) former_height))
1292 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001293 }
1294 else
cristy07623352011-12-07 13:04:03 +00001295 if (scale_factor < ((double) *height/(double) former_height))
1296 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001297 }
1298 else
1299 if ((flags & RhoValue) != 0)
1300 {
cristy07623352011-12-07 13:04:03 +00001301 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001302 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001303 (scale_factor < ((double) *width/(double) former_height)))
1304 scale_factor=(double) *width/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001305 }
1306 else
1307 {
cristy07623352011-12-07 13:04:03 +00001308 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001309 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001310 (scale_factor < ((double) *height/(double) former_width)))
1311 scale_factor=(double) *height/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001312 }
cristy272f23a2011-03-30 00:37:11 +00001313 *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1314 *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
cristy3ed852e2009-09-05 21:47:34 +00001315 }
1316 if ((flags & GreaterValue) != 0)
1317 {
1318 if (former_width < *width)
1319 *width=former_width;
1320 if (former_height < *height)
1321 *height=former_height;
1322 }
1323 if ((flags & LessValue) != 0)
1324 {
1325 if (former_width > *width)
1326 *width=former_width;
1327 if (former_height > *height)
1328 *height=former_height;
1329 }
1330 if ((flags & AreaValue) != 0)
1331 {
cristy07623352011-12-07 13:04:03 +00001332 double
cristy3ed852e2009-09-05 21:47:34 +00001333 area,
1334 distance;
1335
1336 PointInfo
1337 scale;
1338
1339 /*
1340 Geometry is a maximum area in pixels.
1341 */
1342 (void) ParseGeometry(geometry,&geometry_info);
1343 area=geometry_info.rho;
1344 distance=sqrt((double) former_width*former_height);
cristye42f6582012-02-11 17:59:50 +00001345 scale.x=(double) former_width/(distance/sqrt((double) area));
1346 scale.y=(double) former_height/(distance/sqrt((double) area));
cristy3ed852e2009-09-05 21:47:34 +00001347 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1348 {
cristy11e0a872012-01-10 16:10:21 +00001349 *width=(size_t) (former_width/(distance/sqrt(area))+0.5);
1350 *height=(size_t) (former_height/(distance/sqrt(area))+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001351 }
1352 former_width=(*width);
1353 former_height=(*height);
1354 }
1355 return(flags);
1356}
1357
1358/*
1359%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1360% %
1361% %
1362% %
1363% P a r s e P a g e G e o m e t r y %
1364% %
1365% %
1366% %
1367%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1368%
1369% ParsePageGeometry() returns a region as defined by the geometry string with
anthonyd58e60c2011-03-30 00:24:34 +00001370% respect to the image page (canvas) dimensions.
1371%
1372% WARNING: Percentage dimensions remain relative to the actual image
1373% dimensions, and not canvas dimensions.
cristy3ed852e2009-09-05 21:47:34 +00001374%
1375% The format of the ParsePageGeometry method is:
1376%
1377% MagickStatusType ParsePageGeometry(const Image *image,
1378% const char *geometry,RectangeInfo *region_info,
1379% ExceptionInfo *exception)
1380%
1381% A description of each parameter follows:
1382%
anthonyd58e60c2011-03-30 00:24:34 +00001383% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001384%
1385% o region_info: the region as defined by the geometry string with
1386% respect to the image and its gravity.
1387%
1388% o exception: return any errors or warnings in this structure.
1389%
1390*/
1391MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1392 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1393{
1394 MagickStatusType
1395 flags;
1396
1397 SetGeometry(image,region_info);
1398 if (image->page.width != 0)
1399 region_info->width=image->page.width;
1400 if (image->page.height != 0)
1401 region_info->height=image->page.height;
1402 flags=ParseAbsoluteGeometry(geometry,region_info);
1403 if (flags == NoValue)
1404 {
1405 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001406 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001407 return(flags);
1408 }
1409 if ((flags & PercentValue) != 0)
1410 {
1411 region_info->width=image->columns;
1412 region_info->height=image->rows;
1413 }
1414 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1415 &region_info->width,&region_info->height);
1416 return(flags);
1417}
1418
1419/*
1420%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1421% %
1422% %
1423% %
1424% P a r s e R e g i o n G e o m e t r y %
1425% %
1426% %
1427% %
1428%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1429%
1430% ParseRegionGeometry() returns a region as defined by the geometry string
1431% with respect to the image dimensions and aspect ratio.
1432%
anthonyd58e60c2011-03-30 00:24:34 +00001433% This is basically a wrapper around ParseMetaGeometry. This is typically
1434% used to parse a geometry string to work out the final integer dimensions
1435% for image resizing.
1436%
cristy3ed852e2009-09-05 21:47:34 +00001437% The format of the ParseRegionGeometry method is:
1438%
1439% MagickStatusType ParseRegionGeometry(const Image *image,
1440% const char *geometry,RectangeInfo *region_info,
1441% ExceptionInfo *exception)
1442%
1443% A description of each parameter follows:
1444%
anthonyd58e60c2011-03-30 00:24:34 +00001445% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001446%
1447% o region_info: the region as defined by the geometry string.
1448%
1449% o exception: return any errors or warnings in this structure.
1450%
1451*/
1452MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1453 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1454{
1455 MagickStatusType
1456 flags;
1457
1458 SetGeometry(image,region_info);
1459 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1460 &region_info->width,&region_info->height);
1461 if (flags == NoValue)
1462 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001463 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001464 return(flags);
1465}
1466
1467/*
1468%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1469% %
1470% %
1471% %
1472% S e t G e o m e t r y %
1473% %
1474% %
1475% %
1476%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1477%
1478% SetGeometry() sets the geometry to its default values.
1479%
1480% The format of the SetGeometry method is:
1481%
1482% SetGeometry(const Image *image,RectangleInfo *geometry)
1483%
1484% A description of each parameter follows:
1485%
1486% o image: the image.
1487%
1488% o geometry: the geometry.
1489%
1490*/
1491MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1492{
1493 assert(image != (Image *) NULL);
1494 assert(image->signature == MagickSignature);
1495 if (image->debug != MagickFalse)
1496 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1497 assert(geometry != (RectangleInfo *) NULL);
1498 (void) ResetMagickMemory(geometry,0,sizeof(*geometry));
1499 geometry->width=image->columns;
1500 geometry->height=image->rows;
1501}
1502
1503/*
1504%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1505% %
1506% %
1507% %
1508% S e t G e o m e t r y I n f o %
1509% %
1510% %
1511% %
1512%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1513%
1514% SetGeometryInfo sets the GeometryInfo structure to its default values.
1515%
1516% The format of the SetGeometryInfo method is:
1517%
1518% SetGeometryInfo(GeometryInfo *geometry_info)
1519%
1520% A description of each parameter follows:
1521%
1522% o geometry_info: the geometry info structure.
1523%
1524*/
1525MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1526{
1527 assert(geometry_info != (GeometryInfo *) NULL);
1528 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1529 (void) ResetMagickMemory(geometry_info,0,sizeof(*geometry_info));
1530}