blob: dba5c33b357d1cf95e55f65d2de8c33d04feaa5a [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%
70% The format of the GetGeometry method is:
71%
cristybb503372010-05-27 20:51:26 +000072% MagickStatusType GetGeometry(const char *geometry,ssize_t *x,ssize_t *y,
73% size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +000074%
75% A description of each parameter follows:
76%
77% o geometry: The geometry.
78%
79% o x,y: The x and y offset as determined by the geometry specification.
80%
81% o width,height: The width and height as determined by the geometry
82% specification.
83%
84*/
cristy272f23a2011-03-30 00:37:11 +000085MagickExport MagickStatusType GetGeometry(const char *geometry,ssize_t *x,
86 ssize_t *y,size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +000087{
88 char
89 *p,
90 pedantic_geometry[MaxTextExtent],
91 *q;
92
cristy00976d82011-02-20 20:31:28 +000093 double
94 value;
95
cristyf30f47d2011-09-06 14:40:59 +000096 int
97 c;
98
cristy3ed852e2009-09-05 21:47:34 +000099 MagickStatusType
100 flags;
101
102 /*
103 Remove whitespace and meta characters from geometry specification.
104 */
105 flags=NoValue;
106 if ((geometry == (char *) NULL) || (*geometry == '\0'))
107 return(flags);
cristy37e0b382011-06-07 13:31:21 +0000108 if (strlen(geometry) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000109 return(flags);
110 (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
111 for (p=pedantic_geometry; *p != '\0'; )
112 {
113 if (isspace((int) ((unsigned char) *p)) != 0)
114 {
115 (void) CopyMagickString(p,p+1,MaxTextExtent);
116 continue;
117 }
anthonyc330e152012-04-19 14:40:36 +0000118 c=(int)*p;
cristy35f75ac2011-09-06 20:33:35 +0000119 switch (c)
cristy3ed852e2009-09-05 21:47:34 +0000120 {
121 case '%':
122 {
123 flags|=PercentValue;
124 (void) CopyMagickString(p,p+1,MaxTextExtent);
125 break;
126 }
127 case '!':
128 {
129 flags|=AspectValue;
130 (void) CopyMagickString(p,p+1,MaxTextExtent);
131 break;
132 }
133 case '<':
134 {
135 flags|=LessValue;
136 (void) CopyMagickString(p,p+1,MaxTextExtent);
137 break;
138 }
139 case '>':
140 {
141 flags|=GreaterValue;
142 (void) CopyMagickString(p,p+1,MaxTextExtent);
143 break;
144 }
145 case '^':
146 {
147 flags|=MinimumValue;
148 (void) CopyMagickString(p,p+1,MaxTextExtent);
149 break;
150 }
151 case '@':
152 {
153 flags|=AreaValue;
154 (void) CopyMagickString(p,p+1,MaxTextExtent);
155 break;
156 }
157 case '(':
158 case ')':
159 {
160 (void) CopyMagickString(p,p+1,MaxTextExtent);
161 break;
162 }
163 case '-':
164 case '.':
165 case ',':
166 case '+':
167 case '0':
168 case '1':
169 case '2':
170 case '3':
171 case '4':
172 case '5':
173 case '6':
174 case '7':
175 case '8':
176 case '9':
cristy3ed852e2009-09-05 21:47:34 +0000177 case 'x':
178 case 'X':
cristy35f75ac2011-09-06 20:33:35 +0000179 case 215:
cristy3ed852e2009-09-05 21:47:34 +0000180 {
181 p++;
182 break;
183 }
184 default:
185 return(flags);
186 }
187 }
188 /*
189 Parse width, height, x, and y.
190 */
191 p=pedantic_geometry;
192 if (*p == '\0')
193 return(flags);
194 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000195 value=StringToDouble(p,&q);
cristy00976d82011-02-20 20:31:28 +0000196 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000197 if (LocaleNCompare(p,"0x",2) == 0)
cristyf53f26f2011-05-16 00:56:05 +0000198 value=(double) strtol(p,&q,10);
cristy35f75ac2011-09-06 20:33:35 +0000199 c=(int) ((unsigned char) *q);
200 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == '\0'))
cristy3ed852e2009-09-05 21:47:34 +0000201 {
202 /*
203 Parse width.
204 */
205 q=p;
206 if (LocaleNCompare(p,"0x",2) == 0)
cristybb503372010-05-27 20:51:26 +0000207 *width=(size_t) strtol(p,&p,10);
cristy3ed852e2009-09-05 21:47:34 +0000208 else
cristydbdd0e32011-11-04 23:29:40 +0000209 *width=(size_t) floor(StringToDouble(p,&p)+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000210 if (p != q)
211 flags|=WidthValue;
212 }
cristy35f75ac2011-09-06 20:33:35 +0000213 c=(int) ((unsigned char) *p);
214 if ((c == 215) || (*p == 'x') || (*p == 'X'))
cristy3ed852e2009-09-05 21:47:34 +0000215 {
216 p++;
217 if ((*p != '+') && (*p != '-'))
218 {
219 /*
220 Parse height.
221 */
222 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000223 *height=(size_t) floor(StringToDouble(p,&p)+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000224 if (p != q)
225 flags|=HeightValue;
226 }
227 }
228 if ((*p == '+') || (*p == '-'))
229 {
230 /*
231 Parse x value.
232 */
233 if (*p == '-')
234 flags|=XNegative;
235 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000236 *x=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
cristy3ed852e2009-09-05 21:47:34 +0000237 if (p != q)
238 flags|=XValue;
239 if ((*p == '+') || (*p == '-'))
240 {
241 /*
242 Parse y value.
243 */
244 if (*p == '-')
245 flags|=YNegative;
246 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000247 *y=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
cristy3ed852e2009-09-05 21:47:34 +0000248 if (p != q)
249 flags|=YValue;
250 }
251 }
252 return(flags);
253}
254
255/*
256%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
257% %
258% %
259% %
260% G e t P a g e G e o m e t r y %
261% %
262% %
263% %
264%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
265%
266% GetPageGeometry() replaces any page mneumonic with the equivalent size in
267% picas.
268%
269% The format of the GetPageGeometry method is:
270%
271% char *GetPageGeometry(const char *page_geometry)
272%
273% A description of each parameter follows.
274%
cristy272f23a2011-03-30 00:37:11 +0000275% o page_geometry: Specifies a pointer to an array of characters. The
276% string is either a Postscript page name (e.g. A4) or a postscript page
277% geometry (e.g. 612x792+36+36).
cristy3ed852e2009-09-05 21:47:34 +0000278%
279*/
280MagickExport char *GetPageGeometry(const char *page_geometry)
281{
282 static const char
283 *PageSizes[][2]=
284 {
285 { "4x6", "288x432" },
286 { "5x7", "360x504" },
287 { "7x9", "504x648" },
288 { "8x10", "576x720" },
289 { "9x11", "648x792" },
290 { "9x12", "648x864" },
291 { "10x13", "720x936" },
292 { "10x14", "720x1008" },
293 { "11x17", "792x1224" },
294 { "a0", "2384x3370" },
295 { "a1", "1684x2384" },
296 { "a10", "73x105" },
297 { "a2", "1191x1684" },
298 { "a3", "842x1191" },
299 { "a4", "595x842" },
300 { "a4smaLL", "595x842" },
301 { "a5", "420x595" },
302 { "a6", "297x420" },
303 { "a7", "210x297" },
304 { "a8", "148x210" },
305 { "a9", "105x148" },
306 { "archa", "648x864" },
307 { "archb", "864x1296" },
308 { "archC", "1296x1728" },
309 { "archd", "1728x2592" },
310 { "arche", "2592x3456" },
311 { "b0", "2920x4127" },
312 { "b1", "2064x2920" },
313 { "b10", "91x127" },
314 { "b2", "1460x2064" },
315 { "b3", "1032x1460" },
316 { "b4", "729x1032" },
317 { "b5", "516x729" },
318 { "b6", "363x516" },
319 { "b7", "258x363" },
320 { "b8", "181x258" },
321 { "b9", "127x181" },
322 { "c0", "2599x3676" },
323 { "c1", "1837x2599" },
324 { "c2", "1298x1837" },
325 { "c3", "918x1296" },
326 { "c4", "649x918" },
327 { "c5", "459x649" },
328 { "c6", "323x459" },
329 { "c7", "230x323" },
330 { "executive", "540x720" },
331 { "flsa", "612x936" },
332 { "flse", "612x936" },
333 { "folio", "612x936" },
334 { "halfletter", "396x612" },
335 { "isob0", "2835x4008" },
336 { "isob1", "2004x2835" },
337 { "isob10", "88x125" },
338 { "isob2", "1417x2004" },
339 { "isob3", "1001x1417" },
340 { "isob4", "709x1001" },
341 { "isob5", "499x709" },
342 { "isob6", "354x499" },
343 { "isob7", "249x354" },
344 { "isob8", "176x249" },
345 { "isob9", "125x176" },
346 { "jisb0", "1030x1456" },
347 { "jisb1", "728x1030" },
348 { "jisb2", "515x728" },
349 { "jisb3", "364x515" },
350 { "jisb4", "257x364" },
351 { "jisb5", "182x257" },
352 { "jisb6", "128x182" },
353 { "ledger", "1224x792" },
354 { "legal", "612x1008" },
355 { "letter", "612x792" },
356 { "lettersmaLL", "612x792" },
357 { "quarto", "610x780" },
358 { "statement", "396x612" },
359 { "tabloid", "792x1224" },
360 { (char *) NULL, (char *) NULL }
361 };
362
363 char
364 *page;
365
cristybb503372010-05-27 20:51:26 +0000366 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000367 i;
368
369 assert(page_geometry != (char *) NULL);
370 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
371 page=AcquireString(page_geometry);
372 for (i=0; *PageSizes[i] != (char *) NULL; i++)
373 if (LocaleNCompare(PageSizes[i][0],page,strlen(PageSizes[i][0])) == 0)
374 {
375 RectangleInfo
376 geometry;
377
378 MagickStatusType
379 flags;
380
381 /*
382 Replace mneumonic with the equivalent size in dots-per-inch.
383 */
384 (void) CopyMagickString(page,PageSizes[i][1],MaxTextExtent);
385 (void) ConcatenateMagickString(page,page_geometry+
386 strlen(PageSizes[i][0]),MaxTextExtent);
387 flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
388 &geometry.height);
389 if ((flags & GreaterValue) == 0)
390 (void) ConcatenateMagickString(page,">",MaxTextExtent);
391 break;
392 }
393 return(page);
394}
395
396/*
397%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
398% %
399% %
400% %
401% G r a v i t y A d j u s t G e o m e t r y %
402% %
cristy272f23a2011-03-30 00:37:11 +0000403% % % %
cristy3ed852e2009-09-05 21:47:34 +0000404%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
405%
406% GravityAdjustGeometry() adjusts the offset of a region with regard to the
407% given: width, height and gravity; against which it is positioned.
408%
409% The region should also have an appropriate width and height to correctly
410% set the right offset of the top left corner of the region.
411%
412% The format of the GravityAdjustGeometry method is:
413%
cristy272f23a2011-03-30 00:37:11 +0000414% void GravityAdjustGeometry(const size_t width, const size_t height,
415% const GravityType gravity,RectangleInfo *region);
cristy3ed852e2009-09-05 21:47:34 +0000416%
417% A description of each parameter follows:
418%
419% o width, height: the larger area the region is relative to
420%
421% o gravity: the edge/corner the current offset is relative to
422%
423% o region: The region requiring a offset adjustment relative to gravity
424%
425*/
cristybb503372010-05-27 20:51:26 +0000426MagickExport void GravityAdjustGeometry(const size_t width,
427 const size_t height,const GravityType gravity,RectangleInfo *region)
cristy3ed852e2009-09-05 21:47:34 +0000428{
429 if (region->height == 0)
430 region->height=height;
431 if (region->width == 0)
432 region->width=width;
433 switch (gravity)
434 {
435 case NorthEastGravity:
436 case EastGravity:
437 case SouthEastGravity:
438 {
cristybb503372010-05-27 20:51:26 +0000439 region->x=(ssize_t) (width-region->width-region->x);
cristy3ed852e2009-09-05 21:47:34 +0000440 break;
441 }
442 case NorthGravity:
443 case SouthGravity:
444 case CenterGravity:
445 case StaticGravity:
446 {
cristybb503372010-05-27 20:51:26 +0000447 region->x+=(ssize_t) (width/2-region->width/2);
cristy3ed852e2009-09-05 21:47:34 +0000448 break;
449 }
450 case ForgetGravity:
451 case NorthWestGravity:
452 case WestGravity:
453 case SouthWestGravity:
454 default:
455 break;
456 }
457 switch (gravity)
458 {
459 case SouthWestGravity:
460 case SouthGravity:
461 case SouthEastGravity:
462 {
cristybb503372010-05-27 20:51:26 +0000463 region->y=(ssize_t) (height-region->height-region->y);
cristy3ed852e2009-09-05 21:47:34 +0000464 break;
465 }
466 case EastGravity:
467 case WestGravity:
468 case CenterGravity:
469 case StaticGravity:
470 {
cristybb503372010-05-27 20:51:26 +0000471 region->y+=(ssize_t) (height/2-region->height/2);
cristy3ed852e2009-09-05 21:47:34 +0000472 break;
473 }
474 case ForgetGravity:
475 case NorthWestGravity:
476 case NorthGravity:
477 case NorthEastGravity:
478 default:
479 break;
480 }
481 return;
482}
483
484/*
485%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
486% %
487% %
488% %
489+ I s G e o m e t r y %
490% %
491% %
492% %
493%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
494%
495% IsGeometry() returns MagickTrue if the geometry specification is valid.
496% Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
497%
498% The format of the IsGeometry method is:
499%
500% MagickBooleanType IsGeometry(const char *geometry)
501%
502% A description of each parameter follows:
503%
504% o geometry: This string is the geometry specification.
505%
506*/
507MagickExport MagickBooleanType IsGeometry(const char *geometry)
508{
509 GeometryInfo
510 geometry_info;
511
512 MagickStatusType
513 flags;
514
515 if (geometry == (const char *) NULL)
516 return(MagickFalse);
517 flags=ParseGeometry(geometry,&geometry_info);
cristy3ed852e2009-09-05 21:47:34 +0000518 return(flags != NoValue ? MagickTrue : MagickFalse);
519}
520
521/*
522%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
523% %
524% %
525% %
526+ I s S c e n e G e o m e t r y %
527% %
528% %
529% %
530%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
531%
532% IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
533% specification (e.g. [1], [1-9], [1,7,4]).
534%
535% The format of the IsSceneGeometry method is:
536%
537% MagickBooleanType IsSceneGeometry(const char *geometry,
538% const MagickBooleanType pedantic)
539%
540% A description of each parameter follows:
541%
542% o geometry: This string is the geometry specification.
543%
544% o pedantic: A value other than 0 invokes a more restrictive set of
545% conditions for a valid specification (e.g. [1], [1-4], [4-1]).
546%
547*/
548MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
549 const MagickBooleanType pedantic)
550{
551 char
552 *p;
553
cristy00976d82011-02-20 20:31:28 +0000554 double
555 value;
556
cristy3ed852e2009-09-05 21:47:34 +0000557 if (geometry == (const char *) NULL)
558 return(MagickFalse);
559 p=(char *) geometry;
cristydbdd0e32011-11-04 23:29:40 +0000560 value=StringToDouble(geometry,&p);
cristy00976d82011-02-20 20:31:28 +0000561 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000562 if (p == geometry)
563 return(MagickFalse);
564 if (strspn(geometry,"0123456789-, ") != strlen(geometry))
565 return(MagickFalse);
566 if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
567 return(MagickFalse);
568 return(MagickTrue);
569}
570
571/*
572%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
573% %
574% %
575% %
576% P a r s e A b s o l u t e G e o m e t r y %
577% %
578% %
579% %
580%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
581%
anthonyd58e60c2011-03-30 00:24:34 +0000582% ParseAbsoluteGeometry() returns a region as defined by the geometry string,
583% without any modification by percentages or gravity.
584%
585% It currently just a wrapper around GetGeometry(), but may be expanded in
586% the future to handle other positioning information.
cristy3ed852e2009-09-05 21:47:34 +0000587%
588% The format of the ParseAbsoluteGeometry method is:
589%
590% MagickStatusType ParseAbsoluteGeometry(const char *geometry,
591% RectangleInfo *region_info)
592%
593% A description of each parameter follows:
594%
anthonyd58e60c2011-03-30 00:24:34 +0000595% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000596%
597% o region_info: the region as defined by the geometry string.
598%
599*/
600MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
601 RectangleInfo *region_info)
602{
603 MagickStatusType
604 flags;
605
606 flags=GetGeometry(geometry,&region_info->x,&region_info->y,
607 &region_info->width,&region_info->height);
608 return(flags);
609}
610
611/*
612%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
613% %
614% %
615% %
616% P a r s e A f f i n e G e o m e t r y %
617% %
618% %
619% %
620%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
621%
cristy272f23a2011-03-30 00:37:11 +0000622% ParseAffineGeometry() returns an affine matrix as defined by a string of 4
623% to 6 comma/space separated floating point values.
anthonyd58e60c2011-03-30 00:24:34 +0000624%
625% The affine matrix determinant is checked for validity of the values.
cristy3ed852e2009-09-05 21:47:34 +0000626%
627% The format of the ParseAffineGeometry method is:
628%
629% MagickStatusType ParseAffineGeometry(const char *geometry,
630% AffineMatrix *affine_matrix,ExceptionInfo *exception)
631%
632% A description of each parameter follows:
633%
anthonyd58e60c2011-03-30 00:24:34 +0000634% 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 +0000635%
636% o affine_matrix: the affine matrix as defined by the geometry string.
637%
638% o exception: return any errors or warnings in this structure.
639%
640*/
641MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
642 AffineMatrix *affine_matrix,ExceptionInfo *exception)
643{
644 char
645 token[MaxTextExtent];
646
647 const char
648 *p;
649
650 double
651 determinant;
652
653 MagickStatusType
654 flags;
655
cristybb503372010-05-27 20:51:26 +0000656 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000657 i;
658
659 GetAffineMatrix(affine_matrix);
660 flags=NoValue;
661 p=(char *) geometry;
662 for (i=0; (*p != '\0') && (i < 6); i++)
663 {
664 GetMagickToken(p,&p,token);
665 if (*token == ',')
666 GetMagickToken(p,&p,token);
667 switch (i)
668 {
cristyc1acd842011-05-19 23:05:47 +0000669 case 0:
670 {
cristydbdd0e32011-11-04 23:29:40 +0000671 affine_matrix->sx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000672 break;
673 }
674 case 1:
675 {
cristydbdd0e32011-11-04 23:29:40 +0000676 affine_matrix->rx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000677 break;
678 }
679 case 2:
680 {
cristydbdd0e32011-11-04 23:29:40 +0000681 affine_matrix->ry=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000682 break;
683 }
684 case 3:
685 {
cristydbdd0e32011-11-04 23:29:40 +0000686 affine_matrix->sy=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000687 break;
688 }
689 case 4:
690 {
cristydbdd0e32011-11-04 23:29:40 +0000691 affine_matrix->tx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000692 flags|=XValue;
693 break;
694 }
695 case 5:
696 {
cristydbdd0e32011-11-04 23:29:40 +0000697 affine_matrix->ty=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000698 flags|=YValue;
699 break;
700 }
cristy3ed852e2009-09-05 21:47:34 +0000701 }
702 }
anthony92c93bd2012-03-19 14:02:47 +0000703 determinant=(affine_matrix->sx*affine_matrix->sy
704 - affine_matrix->rx*affine_matrix->ry);
cristy3ed852e2009-09-05 21:47:34 +0000705 if (fabs(determinant) < MagickEpsilon)
706 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony92c93bd2012-03-19 14:02:47 +0000707 "InvalidArgument","'%s' : 'Indeterminate Matrix'",geometry);
cristy3ed852e2009-09-05 21:47:34 +0000708 return(flags);
709}
710
711/*
712%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
713% %
714% %
715% %
716% P a r s e G e o m e t r y %
717% %
718% %
719% %
720%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
721%
722% ParseGeometry() parses a geometry specification and returns the sigma,
723% rho, xi, and psi values. It also returns flags that indicates which
724% of the four values (sigma, rho, xi, psi) were located in the string, and
anthonyd58e60c2011-03-30 00:24:34 +0000725% whether the xi or pi values are negative.
726%
727% In addition, it reports if there are any of meta characters (%, !, <, >, @,
728% and ^) flags present. It does not report the location of the percentage
729% relative to the values.
cristy3ed852e2009-09-05 21:47:34 +0000730%
731% The format of the ParseGeometry method is:
732%
733% MagickStatusType ParseGeometry(const char *geometry,
734% GeometryInfo *geometry_info)
735%
736% A description of each parameter follows:
737%
anthonyd58e60c2011-03-30 00:24:34 +0000738% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000739%
740% o geometry_info: returns the parsed width/height/x/y in this structure.
741%
742*/
743MagickExport MagickStatusType ParseGeometry(const char *geometry,
744 GeometryInfo *geometry_info)
745{
746 char
747 *p,
748 pedantic_geometry[MaxTextExtent],
749 *q;
750
751 double
752 value;
753
cristyf30f47d2011-09-06 14:40:59 +0000754 int
755 c;
756
cristy3ed852e2009-09-05 21:47:34 +0000757 MagickStatusType
758 flags;
759
760 /*
761 Remove whitespaces meta characters from geometry specification.
762 */
763 assert(geometry_info != (GeometryInfo *) NULL);
764 flags=NoValue;
765 if ((geometry == (char *) NULL) || (*geometry == '\0'))
766 return(flags);
cristy37e0b382011-06-07 13:31:21 +0000767 if (strlen(geometry) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000768 return(flags);
769 (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
770 for (p=pedantic_geometry; *p != '\0'; )
771 {
772 if (isspace((int) ((unsigned char) *p)) != 0)
773 {
774 (void) CopyMagickString(p,p+1,MaxTextExtent);
775 continue;
776 }
cristy35abcb72011-09-06 20:36:15 +0000777 c=(int) ((unsigned char) *p);
778 switch (c)
cristy3ed852e2009-09-05 21:47:34 +0000779 {
780 case '%':
781 {
782 flags|=PercentValue;
783 (void) CopyMagickString(p,p+1,MaxTextExtent);
784 break;
785 }
786 case '!':
787 {
788 flags|=AspectValue;
789 (void) CopyMagickString(p,p+1,MaxTextExtent);
790 break;
791 }
792 case '<':
793 {
794 flags|=LessValue;
795 (void) CopyMagickString(p,p+1,MaxTextExtent);
796 break;
797 }
798 case '>':
799 {
800 flags|=GreaterValue;
801 (void) CopyMagickString(p,p+1,MaxTextExtent);
802 break;
803 }
804 case '^':
805 {
806 flags|=MinimumValue;
807 (void) CopyMagickString(p,p+1,MaxTextExtent);
808 break;
809 }
810 case '@':
811 {
812 flags|=AreaValue;
813 (void) CopyMagickString(p,p+1,MaxTextExtent);
814 break;
815 }
816 case '(':
817 case ')':
818 {
819 (void) CopyMagickString(p,p+1,MaxTextExtent);
820 break;
821 }
822 case '-':
823 case '+':
824 case ',':
825 case '0':
826 case '1':
827 case '2':
828 case '3':
829 case '4':
830 case '5':
831 case '6':
832 case '7':
833 case '8':
834 case '9':
cristy3ed852e2009-09-05 21:47:34 +0000835 case 'x':
836 case 'X':
837 case '/':
838 case ':':
cristy35f75ac2011-09-06 20:33:35 +0000839 case 215:
cristy3ed852e2009-09-05 21:47:34 +0000840 {
841 p++;
842 break;
843 }
844 case '.':
845 {
846 p++;
847 flags|=DecimalValue;
848 break;
849 }
850 default:
851 return(flags);
852 }
853 }
854 /*
855 Parse rho, sigma, xi, psi, and optionally chi.
856 */
857 p=pedantic_geometry;
858 if (*p == '\0')
859 return(flags);
860 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000861 value=StringToDouble(p,&q);
cristy3ed852e2009-09-05 21:47:34 +0000862 if (LocaleNCompare(p,"0x",2) == 0)
863 value=(double) strtol(p,&q,10);
cristy35f75ac2011-09-06 20:33:35 +0000864 c=(int) ((unsigned char) *q);
865 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
anthonyd1303502010-05-11 12:00:43 +0000866 (*q == '/') || (*q == ':') || (*q =='\0'))
cristy3ed852e2009-09-05 21:47:34 +0000867 {
868 /*
869 Parse rho.
870 */
871 q=p;
872 if (LocaleNCompare(p,"0x",2) == 0)
873 value=(double) strtol(p,&p,10);
874 else
cristydbdd0e32011-11-04 23:29:40 +0000875 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000876 if (p != q)
877 {
878 flags|=RhoValue;
879 geometry_info->rho=value;
880 }
881 }
882 q=p;
cristy35f75ac2011-09-06 20:33:35 +0000883 c=(int) ((unsigned char) *p);
884 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ',') || (*p == '/') ||
cristyf30f47d2011-09-06 14:40:59 +0000885 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000886 {
887 /*
888 Parse sigma.
889 */
890 p++;
891 while (isspace((int) ((unsigned char) *p)) != 0)
892 p++;
cristy35f75ac2011-09-06 20:33:35 +0000893 c=(int) ((unsigned char) *q);
894 if (((c != 215) && (*q != 'x') && (*q != 'X')) || ((*p != '+') &&
cristyf30f47d2011-09-06 14:40:59 +0000895 (*p != '-')))
cristy3ed852e2009-09-05 21:47:34 +0000896 {
897 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000898 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000899 if (p != q)
900 {
901 flags|=SigmaValue;
902 geometry_info->sigma=value;
903 }
904 }
905 }
906 while (isspace((int) ((unsigned char) *p)) != 0)
907 p++;
anthonyd1303502010-05-11 12:00:43 +0000908 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000909 {
910 /*
911 Parse xi value.
912 */
anthonyc330e152012-04-19 14:40:36 +0000913 if ((*p == '+') || (*p == ',') || (*p == '/') || (*p == ':') )
cristy3ed852e2009-09-05 21:47:34 +0000914 p++;
915 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000916 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000917 if (p != q)
918 {
919 flags|=XiValue;
920 if (*q == '-')
921 flags|=XiNegative;
922 geometry_info->xi=value;
923 }
924 while (isspace((int) ((unsigned char) *p)) != 0)
925 p++;
926 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +0000927 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000928 {
929 /*
930 Parse psi value.
931 */
anthonyc330e152012-04-19 14:40:36 +0000932 if ((*p == '+') || (*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000933 p++;
934 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000935 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000936 if (p != q)
937 {
938 flags|=PsiValue;
939 if (*q == '-')
940 flags|=PsiNegative;
941 geometry_info->psi=value;
942 }
943 }
944 while (isspace((int) ((unsigned char) *p)) != 0)
945 p++;
946 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +0000947 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000948 {
949 /*
950 Parse chi value.
951 */
anthonyc330e152012-04-19 14:40:36 +0000952 if ((*p == '+') || (*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000953 p++;
954 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000955 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000956 if (p != q)
957 {
958 flags|=ChiValue;
959 if (*q == '-')
960 flags|=ChiNegative;
961 geometry_info->chi=value;
962 }
963 }
964 }
965 if (strchr(pedantic_geometry,':') != (char *) NULL)
966 {
967 /*
968 Normalize sampling factor (e.g. 4:2:2 => 2x1).
969 */
970 geometry_info->rho/=geometry_info->sigma;
971 geometry_info->sigma=1.0;
972 if (geometry_info->xi == 0.0)
973 geometry_info->sigma=2.0;
974 }
975 if (((flags & SigmaValue) == 0) && ((flags & XiValue) != 0) &&
976 ((flags & PsiValue) == 0))
977 {
978 /*
979 Support negative height values (e.g. 30x-20).
980 */
981 geometry_info->sigma=geometry_info->xi;
982 geometry_info->xi=0.0;
983 flags|=SigmaValue;
984 flags&=(~XiValue);
985 }
986 return(flags);
987}
988
989/*
990%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
991% %
992% %
993% %
994% P a r s e G r a v i t y G e o m e t r y %
995% %
996% %
997% %
998%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
999%
1000% ParseGravityGeometry() returns a region as defined by the geometry string
anthonyd58e60c2011-03-30 00:24:34 +00001001% with respect to the given image page (canvas) dimensions and the images
1002% gravity setting.
1003%
1004% This is typically used for specifing a area within a given image for
1005% cropping images to a smaller size, chopping out rows and or columns, or
1006% resizing and positioning overlay images.
1007%
1008% Percentages are relative to image size and not page size, and are set to
1009% nearest integer (pixel) size.
cristy3ed852e2009-09-05 21:47:34 +00001010%
1011% The format of the ParseGravityGeometry method is:
1012%
1013% MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1014% RectangeInfo *region_info,ExceptionInfo *exception)
1015%
1016% A description of each parameter follows:
1017%
anthonyd58e60c2011-03-30 00:24:34 +00001018% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001019%
cristy272f23a2011-03-30 00:37:11 +00001020% o region_info: the region as defined by the geometry string with respect
1021% to the image dimensions and its gravity.
cristy3ed852e2009-09-05 21:47:34 +00001022%
1023% o exception: return any errors or warnings in this structure.
1024%
1025*/
1026MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1027 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1028{
1029 MagickStatusType
1030 flags;
1031
cristybb503372010-05-27 20:51:26 +00001032 size_t
cristy3ed852e2009-09-05 21:47:34 +00001033 height,
1034 width;
1035
1036 SetGeometry(image,region_info);
1037 if (image->page.width != 0)
1038 region_info->width=image->page.width;
1039 if (image->page.height != 0)
1040 region_info->height=image->page.height;
1041 flags=ParseAbsoluteGeometry(geometry,region_info);
1042 if (flags == NoValue)
1043 {
1044 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1045 "InvalidGeometry","`%s'",geometry);
1046 return(flags);
1047 }
1048 if ((flags & PercentValue) != 0)
1049 {
1050 GeometryInfo
1051 geometry_info;
1052
1053 MagickStatusType
1054 status;
1055
1056 PointInfo
1057 scale;
1058
1059 /*
anthonyd58e60c2011-03-30 00:24:34 +00001060 Geometry is a percentage of the image size, not canvas size
cristy3ed852e2009-09-05 21:47:34 +00001061 */
1062 if (image->gravity != UndefinedGravity)
1063 flags|=XValue | YValue;
1064 status=ParseGeometry(geometry,&geometry_info);
1065 scale.x=geometry_info.rho;
1066 if ((status & RhoValue) == 0)
1067 scale.x=100.0;
1068 scale.y=geometry_info.sigma;
1069 if ((status & SigmaValue) == 0)
1070 scale.y=scale.x;
cristy272f23a2011-03-30 00:37:11 +00001071 region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1072 region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001073 }
1074 /*
1075 Adjust offset according to gravity setting.
1076 */
1077 width=region_info->width;
1078 height=region_info->height;
1079 if (width == 0)
1080 region_info->width=image->page.width | image->columns;
1081 if (height == 0)
1082 region_info->height=image->page.height | image->rows;
1083 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1084 region_info->width=width;
1085 region_info->height=height;
1086 return(flags);
1087}
1088
1089/*
1090%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1091% %
1092% %
1093% %
1094+ P a r s e M e t a G e o m e t r y %
1095% %
1096% %
1097% %
1098%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1099%
1100% ParseMetaGeometry() is similar to GetGeometry() except the returned
anthonyd58e60c2011-03-30 00:24:34 +00001101% geometry is modified as determined by the meta characters: %, !, <, >, @,
1102% and ^ in relation to image resizing.
1103%
1104% Final image dimensions are adjusted so as to preserve the aspect ratio as
glennrpdcabf6d2011-06-25 03:26:14 +00001105% much as possible, while generating a integer (pixel) size, and fitting the
anthonyd58e60c2011-03-30 00:24:34 +00001106% image within the specified geometry width and height.
1107%
1108% Flags are interpreted...
cristy272f23a2011-03-30 00:37:11 +00001109% % geometry size is given percentage of original image size
1110% ! do not try to preserve aspect ratio
1111% < only enlarge images smaller that geometry
1112% > only shrink images larger than geometry
1113% @ Fit image to contain at most this many pixels
1114% ^ Contain the given geometry given, (minimal dimensions given)
cristy3ed852e2009-09-05 21:47:34 +00001115%
1116% The format of the ParseMetaGeometry method is:
1117%
anthonyd58e60c2011-03-30 00:24:34 +00001118% MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
cristy272f23a2011-03-30 00:37:11 +00001119% ssize_t *y, size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001120%
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%
anthonyd58e60c2011-03-30 00:24:34 +00001125% o x,y: The x and y offset, set according to the geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001126%
cristy272f23a2011-03-30 00:37:11 +00001127% o width,height: The width and height of original image, modified by
1128% the given geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001129%
1130*/
1131
cristybb503372010-05-27 20:51:26 +00001132static inline size_t MagickMax(const size_t x,
1133 const size_t y)
cristy3ed852e2009-09-05 21:47:34 +00001134{
1135 if (x > y)
1136 return(x);
1137 return(y);
1138}
1139
cristybb503372010-05-27 20:51:26 +00001140MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1141 ssize_t *y,size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001142{
1143 GeometryInfo
1144 geometry_info;
1145
1146 MagickStatusType
1147 flags;
1148
cristybb503372010-05-27 20:51:26 +00001149 size_t
cristy3ed852e2009-09-05 21:47:34 +00001150 former_height,
1151 former_width;
1152
1153 /*
1154 Ensure the image geometry is valid.
1155 */
cristybb503372010-05-27 20:51:26 +00001156 assert(x != (ssize_t *) NULL);
1157 assert(y != (ssize_t *) NULL);
1158 assert(width != (size_t *) NULL);
1159 assert(height != (size_t *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001160 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1161 return(NoValue);
1162 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1163 /*
1164 Parse geometry using GetGeometry.
1165 */
1166 SetGeometryInfo(&geometry_info);
1167 former_width=(*width);
1168 former_height=(*height);
1169 flags=GetGeometry(geometry,x,y,width,height);
1170 if ((flags & PercentValue) != 0)
1171 {
1172 MagickStatusType
1173 flags;
1174
1175 PointInfo
1176 scale;
1177
1178 /*
1179 Geometry is a percentage of the image size.
1180 */
1181 flags=ParseGeometry(geometry,&geometry_info);
1182 scale.x=geometry_info.rho;
1183 if ((flags & RhoValue) == 0)
1184 scale.x=100.0;
1185 scale.y=geometry_info.sigma;
1186 if ((flags & SigmaValue) == 0)
1187 scale.y=scale.x;
cristybb503372010-05-27 20:51:26 +00001188 *width=(size_t) floor(scale.x*former_width/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001189 if (*width == 0)
1190 *width=1;
cristybb503372010-05-27 20:51:26 +00001191 *height=(size_t) floor(scale.y*former_height/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001192 if (*height == 0)
1193 *height=1;
1194 former_width=(*width);
1195 former_height=(*height);
1196 }
cristy07623352011-12-07 13:04:03 +00001197 if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1198 (*height == former_height)))
cristy3ed852e2009-09-05 21:47:34 +00001199 {
cristy07623352011-12-07 13:04:03 +00001200 if ((flags & RhoValue) == 0)
1201 *width=former_width;
1202 if ((flags & SigmaValue) == 0)
1203 *height=former_height;
1204 }
1205 else
1206 {
1207 double
cristy3ed852e2009-09-05 21:47:34 +00001208 scale_factor;
1209
1210 /*
1211 Respect aspect ratio of the image.
1212 */
1213 if ((former_width == 0) || (former_height == 0))
1214 scale_factor=1.0;
1215 else
1216 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1217 {
cristy07623352011-12-07 13:04:03 +00001218 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001219 if ((flags & MinimumValue) == 0)
1220 {
cristy07623352011-12-07 13:04:03 +00001221 if (scale_factor > ((double) *height/(double) former_height))
1222 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001223 }
1224 else
cristy07623352011-12-07 13:04:03 +00001225 if (scale_factor < ((double) *height/(double) former_height))
1226 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001227 }
1228 else
1229 if ((flags & RhoValue) != 0)
1230 {
cristy07623352011-12-07 13:04:03 +00001231 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001232 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001233 (scale_factor < ((double) *width/(double) former_height)))
1234 scale_factor=(double) *width/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001235 }
1236 else
1237 {
cristy07623352011-12-07 13:04:03 +00001238 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001239 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001240 (scale_factor < ((double) *height/(double) former_width)))
1241 scale_factor=(double) *height/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001242 }
cristy272f23a2011-03-30 00:37:11 +00001243 *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1244 *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
cristy3ed852e2009-09-05 21:47:34 +00001245 }
1246 if ((flags & GreaterValue) != 0)
1247 {
1248 if (former_width < *width)
1249 *width=former_width;
1250 if (former_height < *height)
1251 *height=former_height;
1252 }
1253 if ((flags & LessValue) != 0)
1254 {
1255 if (former_width > *width)
1256 *width=former_width;
1257 if (former_height > *height)
1258 *height=former_height;
1259 }
1260 if ((flags & AreaValue) != 0)
1261 {
cristy07623352011-12-07 13:04:03 +00001262 double
cristy3ed852e2009-09-05 21:47:34 +00001263 area,
1264 distance;
1265
1266 PointInfo
1267 scale;
1268
1269 /*
1270 Geometry is a maximum area in pixels.
1271 */
1272 (void) ParseGeometry(geometry,&geometry_info);
1273 area=geometry_info.rho;
1274 distance=sqrt((double) former_width*former_height);
cristye42f6582012-02-11 17:59:50 +00001275 scale.x=(double) former_width/(distance/sqrt((double) area));
1276 scale.y=(double) former_height/(distance/sqrt((double) area));
cristy3ed852e2009-09-05 21:47:34 +00001277 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1278 {
cristy11e0a872012-01-10 16:10:21 +00001279 *width=(size_t) (former_width/(distance/sqrt(area))+0.5);
1280 *height=(size_t) (former_height/(distance/sqrt(area))+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001281 }
1282 former_width=(*width);
1283 former_height=(*height);
1284 }
1285 return(flags);
1286}
1287
1288/*
1289%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1290% %
1291% %
1292% %
1293% P a r s e P a g e G e o m e t r y %
1294% %
1295% %
1296% %
1297%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1298%
1299% ParsePageGeometry() returns a region as defined by the geometry string with
anthonyd58e60c2011-03-30 00:24:34 +00001300% respect to the image page (canvas) dimensions.
1301%
1302% WARNING: Percentage dimensions remain relative to the actual image
1303% dimensions, and not canvas dimensions.
cristy3ed852e2009-09-05 21:47:34 +00001304%
1305% The format of the ParsePageGeometry method is:
1306%
1307% MagickStatusType ParsePageGeometry(const Image *image,
1308% const char *geometry,RectangeInfo *region_info,
1309% ExceptionInfo *exception)
1310%
1311% A description of each parameter follows:
1312%
anthonyd58e60c2011-03-30 00:24:34 +00001313% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001314%
1315% o region_info: the region as defined by the geometry string with
1316% respect to the image and its gravity.
1317%
1318% o exception: return any errors or warnings in this structure.
1319%
1320*/
1321MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1322 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1323{
1324 MagickStatusType
1325 flags;
1326
1327 SetGeometry(image,region_info);
1328 if (image->page.width != 0)
1329 region_info->width=image->page.width;
1330 if (image->page.height != 0)
1331 region_info->height=image->page.height;
1332 flags=ParseAbsoluteGeometry(geometry,region_info);
1333 if (flags == NoValue)
1334 {
1335 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1336 "InvalidGeometry","`%s'",geometry);
1337 return(flags);
1338 }
1339 if ((flags & PercentValue) != 0)
1340 {
1341 region_info->width=image->columns;
1342 region_info->height=image->rows;
1343 }
1344 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1345 &region_info->width,&region_info->height);
1346 return(flags);
1347}
1348
1349/*
1350%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1351% %
1352% %
1353% %
1354% P a r s e R e g i o n G e o m e t r y %
1355% %
1356% %
1357% %
1358%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1359%
1360% ParseRegionGeometry() returns a region as defined by the geometry string
1361% with respect to the image dimensions and aspect ratio.
1362%
anthonyd58e60c2011-03-30 00:24:34 +00001363% This is basically a wrapper around ParseMetaGeometry. This is typically
1364% used to parse a geometry string to work out the final integer dimensions
1365% for image resizing.
1366%
cristy3ed852e2009-09-05 21:47:34 +00001367% The format of the ParseRegionGeometry method is:
1368%
1369% MagickStatusType ParseRegionGeometry(const Image *image,
1370% const char *geometry,RectangeInfo *region_info,
1371% ExceptionInfo *exception)
1372%
1373% A description of each parameter follows:
1374%
anthonyd58e60c2011-03-30 00:24:34 +00001375% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001376%
1377% o region_info: the region as defined by the geometry string.
1378%
1379% o exception: return any errors or warnings in this structure.
1380%
1381*/
1382MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1383 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1384{
1385 MagickStatusType
1386 flags;
1387
1388 SetGeometry(image,region_info);
1389 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1390 &region_info->width,&region_info->height);
1391 if (flags == NoValue)
1392 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1393 "InvalidGeometry","`%s'",geometry);
1394 return(flags);
1395}
1396
1397/*
1398%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1399% %
1400% %
1401% %
1402% S e t G e o m e t r y %
1403% %
1404% %
1405% %
1406%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1407%
1408% SetGeometry() sets the geometry to its default values.
1409%
1410% The format of the SetGeometry method is:
1411%
1412% SetGeometry(const Image *image,RectangleInfo *geometry)
1413%
1414% A description of each parameter follows:
1415%
1416% o image: the image.
1417%
1418% o geometry: the geometry.
1419%
1420*/
1421MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1422{
1423 assert(image != (Image *) NULL);
1424 assert(image->signature == MagickSignature);
1425 if (image->debug != MagickFalse)
1426 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1427 assert(geometry != (RectangleInfo *) NULL);
1428 (void) ResetMagickMemory(geometry,0,sizeof(*geometry));
1429 geometry->width=image->columns;
1430 geometry->height=image->rows;
1431}
1432
1433/*
1434%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1435% %
1436% %
1437% %
1438% S e t G e o m e t r y I n f o %
1439% %
1440% %
1441% %
1442%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1443%
1444% SetGeometryInfo sets the GeometryInfo structure to its default values.
1445%
1446% The format of the SetGeometryInfo method is:
1447%
1448% SetGeometryInfo(GeometryInfo *geometry_info)
1449%
1450% A description of each parameter follows:
1451%
1452% o geometry_info: the geometry info structure.
1453%
1454*/
1455MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1456{
1457 assert(geometry_info != (GeometryInfo *) NULL);
1458 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1459 (void) ResetMagickMemory(geometry_info,0,sizeof(*geometry_info));
1460}