blob: f138dde798c3945fb3d4a612f7e1700c2606d9dd [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 }
cristy35f75ac2011-09-06 20:33:35 +0000118 c=(int) ((unsigned int) *p);
119 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 }
cristyf30f47d2011-09-06 14:40:59 +0000213 c=(*p);
cristy35f75ac2011-09-06 20:33:35 +0000214 c=(int) ((unsigned char) *p);
215 if ((c == 215) || (*p == 'x') || (*p == 'X'))
cristy3ed852e2009-09-05 21:47:34 +0000216 {
217 p++;
218 if ((*p != '+') && (*p != '-'))
219 {
220 /*
221 Parse height.
222 */
223 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000224 *height=(size_t) floor(StringToDouble(p,&p)+0.5);
cristy3ed852e2009-09-05 21:47:34 +0000225 if (p != q)
226 flags|=HeightValue;
227 }
228 }
229 if ((*p == '+') || (*p == '-'))
230 {
231 /*
232 Parse x value.
233 */
234 if (*p == '-')
235 flags|=XNegative;
236 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000237 *x=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
cristy3ed852e2009-09-05 21:47:34 +0000238 if (p != q)
239 flags|=XValue;
240 if ((*p == '+') || (*p == '-'))
241 {
242 /*
243 Parse y value.
244 */
245 if (*p == '-')
246 flags|=YNegative;
247 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000248 *y=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
cristy3ed852e2009-09-05 21:47:34 +0000249 if (p != q)
250 flags|=YValue;
251 }
252 }
253 return(flags);
254}
255
256/*
257%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
258% %
259% %
260% %
261% G e t P a g e G e o m e t r y %
262% %
263% %
264% %
265%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
266%
267% GetPageGeometry() replaces any page mneumonic with the equivalent size in
268% picas.
269%
270% The format of the GetPageGeometry method is:
271%
272% char *GetPageGeometry(const char *page_geometry)
273%
274% A description of each parameter follows.
275%
cristy272f23a2011-03-30 00:37:11 +0000276% o page_geometry: Specifies a pointer to an array of characters. The
277% string is either a Postscript page name (e.g. A4) or a postscript page
278% geometry (e.g. 612x792+36+36).
cristy3ed852e2009-09-05 21:47:34 +0000279%
280*/
281MagickExport char *GetPageGeometry(const char *page_geometry)
282{
283 static const char
284 *PageSizes[][2]=
285 {
286 { "4x6", "288x432" },
287 { "5x7", "360x504" },
288 { "7x9", "504x648" },
289 { "8x10", "576x720" },
290 { "9x11", "648x792" },
291 { "9x12", "648x864" },
292 { "10x13", "720x936" },
293 { "10x14", "720x1008" },
294 { "11x17", "792x1224" },
295 { "a0", "2384x3370" },
296 { "a1", "1684x2384" },
297 { "a10", "73x105" },
298 { "a2", "1191x1684" },
299 { "a3", "842x1191" },
300 { "a4", "595x842" },
301 { "a4smaLL", "595x842" },
302 { "a5", "420x595" },
303 { "a6", "297x420" },
304 { "a7", "210x297" },
305 { "a8", "148x210" },
306 { "a9", "105x148" },
307 { "archa", "648x864" },
308 { "archb", "864x1296" },
309 { "archC", "1296x1728" },
310 { "archd", "1728x2592" },
311 { "arche", "2592x3456" },
312 { "b0", "2920x4127" },
313 { "b1", "2064x2920" },
314 { "b10", "91x127" },
315 { "b2", "1460x2064" },
316 { "b3", "1032x1460" },
317 { "b4", "729x1032" },
318 { "b5", "516x729" },
319 { "b6", "363x516" },
320 { "b7", "258x363" },
321 { "b8", "181x258" },
322 { "b9", "127x181" },
323 { "c0", "2599x3676" },
324 { "c1", "1837x2599" },
325 { "c2", "1298x1837" },
326 { "c3", "918x1296" },
327 { "c4", "649x918" },
328 { "c5", "459x649" },
329 { "c6", "323x459" },
330 { "c7", "230x323" },
331 { "executive", "540x720" },
332 { "flsa", "612x936" },
333 { "flse", "612x936" },
334 { "folio", "612x936" },
335 { "halfletter", "396x612" },
336 { "isob0", "2835x4008" },
337 { "isob1", "2004x2835" },
338 { "isob10", "88x125" },
339 { "isob2", "1417x2004" },
340 { "isob3", "1001x1417" },
341 { "isob4", "709x1001" },
342 { "isob5", "499x709" },
343 { "isob6", "354x499" },
344 { "isob7", "249x354" },
345 { "isob8", "176x249" },
346 { "isob9", "125x176" },
347 { "jisb0", "1030x1456" },
348 { "jisb1", "728x1030" },
349 { "jisb2", "515x728" },
350 { "jisb3", "364x515" },
351 { "jisb4", "257x364" },
352 { "jisb5", "182x257" },
353 { "jisb6", "128x182" },
354 { "ledger", "1224x792" },
355 { "legal", "612x1008" },
356 { "letter", "612x792" },
357 { "lettersmaLL", "612x792" },
358 { "quarto", "610x780" },
359 { "statement", "396x612" },
360 { "tabloid", "792x1224" },
361 { (char *) NULL, (char *) NULL }
362 };
363
364 char
365 *page;
366
cristybb503372010-05-27 20:51:26 +0000367 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000368 i;
369
370 assert(page_geometry != (char *) NULL);
371 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
372 page=AcquireString(page_geometry);
373 for (i=0; *PageSizes[i] != (char *) NULL; i++)
374 if (LocaleNCompare(PageSizes[i][0],page,strlen(PageSizes[i][0])) == 0)
375 {
376 RectangleInfo
377 geometry;
378
379 MagickStatusType
380 flags;
381
382 /*
383 Replace mneumonic with the equivalent size in dots-per-inch.
384 */
385 (void) CopyMagickString(page,PageSizes[i][1],MaxTextExtent);
386 (void) ConcatenateMagickString(page,page_geometry+
387 strlen(PageSizes[i][0]),MaxTextExtent);
388 flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
389 &geometry.height);
390 if ((flags & GreaterValue) == 0)
391 (void) ConcatenateMagickString(page,">",MaxTextExtent);
392 break;
393 }
394 return(page);
395}
396
397/*
398%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
399% %
400% %
401% %
402% G r a v i t y A d j u s t G e o m e t r y %
403% %
cristy272f23a2011-03-30 00:37:11 +0000404% % % %
cristy3ed852e2009-09-05 21:47:34 +0000405%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
406%
407% GravityAdjustGeometry() adjusts the offset of a region with regard to the
408% given: width, height and gravity; against which it is positioned.
409%
410% The region should also have an appropriate width and height to correctly
411% set the right offset of the top left corner of the region.
412%
413% The format of the GravityAdjustGeometry method is:
414%
cristy272f23a2011-03-30 00:37:11 +0000415% void GravityAdjustGeometry(const size_t width, const size_t height,
416% const GravityType gravity,RectangleInfo *region);
cristy3ed852e2009-09-05 21:47:34 +0000417%
418% A description of each parameter follows:
419%
420% o width, height: the larger area the region is relative to
421%
422% o gravity: the edge/corner the current offset is relative to
423%
424% o region: The region requiring a offset adjustment relative to gravity
425%
426*/
cristybb503372010-05-27 20:51:26 +0000427MagickExport void GravityAdjustGeometry(const size_t width,
428 const size_t height,const GravityType gravity,RectangleInfo *region)
cristy3ed852e2009-09-05 21:47:34 +0000429{
430 if (region->height == 0)
431 region->height=height;
432 if (region->width == 0)
433 region->width=width;
434 switch (gravity)
435 {
436 case NorthEastGravity:
437 case EastGravity:
438 case SouthEastGravity:
439 {
cristybb503372010-05-27 20:51:26 +0000440 region->x=(ssize_t) (width-region->width-region->x);
cristy3ed852e2009-09-05 21:47:34 +0000441 break;
442 }
443 case NorthGravity:
444 case SouthGravity:
445 case CenterGravity:
446 case StaticGravity:
447 {
cristybb503372010-05-27 20:51:26 +0000448 region->x+=(ssize_t) (width/2-region->width/2);
cristy3ed852e2009-09-05 21:47:34 +0000449 break;
450 }
451 case ForgetGravity:
452 case NorthWestGravity:
453 case WestGravity:
454 case SouthWestGravity:
455 default:
456 break;
457 }
458 switch (gravity)
459 {
460 case SouthWestGravity:
461 case SouthGravity:
462 case SouthEastGravity:
463 {
cristybb503372010-05-27 20:51:26 +0000464 region->y=(ssize_t) (height-region->height-region->y);
cristy3ed852e2009-09-05 21:47:34 +0000465 break;
466 }
467 case EastGravity:
468 case WestGravity:
469 case CenterGravity:
470 case StaticGravity:
471 {
cristybb503372010-05-27 20:51:26 +0000472 region->y+=(ssize_t) (height/2-region->height/2);
cristy3ed852e2009-09-05 21:47:34 +0000473 break;
474 }
475 case ForgetGravity:
476 case NorthWestGravity:
477 case NorthGravity:
478 case NorthEastGravity:
479 default:
480 break;
481 }
482 return;
483}
484
485/*
486%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
487% %
488% %
489% %
490+ I s G e o m e t r y %
491% %
492% %
493% %
494%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
495%
496% IsGeometry() returns MagickTrue if the geometry specification is valid.
497% Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
498%
499% The format of the IsGeometry method is:
500%
501% MagickBooleanType IsGeometry(const char *geometry)
502%
503% A description of each parameter follows:
504%
505% o geometry: This string is the geometry specification.
506%
507*/
508MagickExport MagickBooleanType IsGeometry(const char *geometry)
509{
510 GeometryInfo
511 geometry_info;
512
513 MagickStatusType
514 flags;
515
516 if (geometry == (const char *) NULL)
517 return(MagickFalse);
518 flags=ParseGeometry(geometry,&geometry_info);
519 if (flags == NoValue)
520 flags=ParseGeometry(geometry+1,&geometry_info); /* i.e. +-4+-4 */
521 return(flags != NoValue ? MagickTrue : MagickFalse);
522}
523
524/*
525%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
526% %
527% %
528% %
529+ I s S c e n e G e o m e t r y %
530% %
531% %
532% %
533%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
534%
535% IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
536% specification (e.g. [1], [1-9], [1,7,4]).
537%
538% The format of the IsSceneGeometry method is:
539%
540% MagickBooleanType IsSceneGeometry(const char *geometry,
541% const MagickBooleanType pedantic)
542%
543% A description of each parameter follows:
544%
545% o geometry: This string is the geometry specification.
546%
547% o pedantic: A value other than 0 invokes a more restrictive set of
548% conditions for a valid specification (e.g. [1], [1-4], [4-1]).
549%
550*/
551MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
552 const MagickBooleanType pedantic)
553{
554 char
555 *p;
556
cristy00976d82011-02-20 20:31:28 +0000557 double
558 value;
559
cristy3ed852e2009-09-05 21:47:34 +0000560 if (geometry == (const char *) NULL)
561 return(MagickFalse);
562 p=(char *) geometry;
cristydbdd0e32011-11-04 23:29:40 +0000563 value=StringToDouble(geometry,&p);
cristy00976d82011-02-20 20:31:28 +0000564 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000565 if (p == geometry)
566 return(MagickFalse);
567 if (strspn(geometry,"0123456789-, ") != strlen(geometry))
568 return(MagickFalse);
569 if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
570 return(MagickFalse);
571 return(MagickTrue);
572}
573
574/*
575%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
576% %
577% %
578% %
579% P a r s e A b s o l u t e G e o m e t r y %
580% %
581% %
582% %
583%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
584%
anthonyd58e60c2011-03-30 00:24:34 +0000585% ParseAbsoluteGeometry() returns a region as defined by the geometry string,
586% without any modification by percentages or gravity.
587%
588% It currently just a wrapper around GetGeometry(), but may be expanded in
589% the future to handle other positioning information.
cristy3ed852e2009-09-05 21:47:34 +0000590%
591% The format of the ParseAbsoluteGeometry method is:
592%
593% MagickStatusType ParseAbsoluteGeometry(const char *geometry,
594% RectangleInfo *region_info)
595%
596% A description of each parameter follows:
597%
anthonyd58e60c2011-03-30 00:24:34 +0000598% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000599%
600% o region_info: the region as defined by the geometry string.
601%
602*/
603MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
604 RectangleInfo *region_info)
605{
606 MagickStatusType
607 flags;
608
609 flags=GetGeometry(geometry,&region_info->x,&region_info->y,
610 &region_info->width,&region_info->height);
611 return(flags);
612}
613
614/*
615%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
616% %
617% %
618% %
619% P a r s e A f f i n e G e o m e t r y %
620% %
621% %
622% %
623%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
624%
cristy272f23a2011-03-30 00:37:11 +0000625% ParseAffineGeometry() returns an affine matrix as defined by a string of 4
626% to 6 comma/space separated floating point values.
anthonyd58e60c2011-03-30 00:24:34 +0000627%
628% The affine matrix determinant is checked for validity of the values.
cristy3ed852e2009-09-05 21:47:34 +0000629%
630% The format of the ParseAffineGeometry method is:
631%
632% MagickStatusType ParseAffineGeometry(const char *geometry,
633% AffineMatrix *affine_matrix,ExceptionInfo *exception)
634%
635% A description of each parameter follows:
636%
anthonyd58e60c2011-03-30 00:24:34 +0000637% 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 +0000638%
639% o affine_matrix: the affine matrix as defined by the geometry string.
640%
641% o exception: return any errors or warnings in this structure.
642%
643*/
644MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
645 AffineMatrix *affine_matrix,ExceptionInfo *exception)
646{
647 char
648 token[MaxTextExtent];
649
650 const char
651 *p;
652
653 double
654 determinant;
655
656 MagickStatusType
657 flags;
658
cristybb503372010-05-27 20:51:26 +0000659 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000660 i;
661
662 GetAffineMatrix(affine_matrix);
663 flags=NoValue;
664 p=(char *) geometry;
665 for (i=0; (*p != '\0') && (i < 6); i++)
666 {
667 GetMagickToken(p,&p,token);
668 if (*token == ',')
669 GetMagickToken(p,&p,token);
670 switch (i)
671 {
cristyc1acd842011-05-19 23:05:47 +0000672 case 0:
673 {
cristydbdd0e32011-11-04 23:29:40 +0000674 affine_matrix->sx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000675 break;
676 }
677 case 1:
678 {
cristydbdd0e32011-11-04 23:29:40 +0000679 affine_matrix->rx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000680 break;
681 }
682 case 2:
683 {
cristydbdd0e32011-11-04 23:29:40 +0000684 affine_matrix->ry=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000685 break;
686 }
687 case 3:
688 {
cristydbdd0e32011-11-04 23:29:40 +0000689 affine_matrix->sy=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000690 break;
691 }
692 case 4:
693 {
cristydbdd0e32011-11-04 23:29:40 +0000694 affine_matrix->tx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000695 flags|=XValue;
696 break;
697 }
698 case 5:
699 {
cristydbdd0e32011-11-04 23:29:40 +0000700 affine_matrix->ty=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000701 flags|=YValue;
702 break;
703 }
cristy3ed852e2009-09-05 21:47:34 +0000704 }
705 }
706 determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
707 affine_matrix->ry);
708 if (fabs(determinant) < MagickEpsilon)
709 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
710 "InvalidGeometry","`%s'",geometry);
711 return(flags);
712}
713
714/*
715%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
716% %
717% %
718% %
719% P a r s e G e o m e t r y %
720% %
721% %
722% %
723%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
724%
725% ParseGeometry() parses a geometry specification and returns the sigma,
726% rho, xi, and psi values. It also returns flags that indicates which
727% of the four values (sigma, rho, xi, psi) were located in the string, and
anthonyd58e60c2011-03-30 00:24:34 +0000728% whether the xi or pi values are negative.
729%
730% In addition, it reports if there are any of meta characters (%, !, <, >, @,
731% and ^) flags present. It does not report the location of the percentage
732% relative to the values.
cristy3ed852e2009-09-05 21:47:34 +0000733%
734% The format of the ParseGeometry method is:
735%
736% MagickStatusType ParseGeometry(const char *geometry,
737% GeometryInfo *geometry_info)
738%
739% A description of each parameter follows:
740%
anthonyd58e60c2011-03-30 00:24:34 +0000741% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000742%
743% o geometry_info: returns the parsed width/height/x/y in this structure.
744%
745*/
746MagickExport MagickStatusType ParseGeometry(const char *geometry,
747 GeometryInfo *geometry_info)
748{
749 char
750 *p,
751 pedantic_geometry[MaxTextExtent],
752 *q;
753
754 double
755 value;
756
cristyf30f47d2011-09-06 14:40:59 +0000757 int
758 c;
759
cristy3ed852e2009-09-05 21:47:34 +0000760 MagickStatusType
761 flags;
762
763 /*
764 Remove whitespaces meta characters from geometry specification.
765 */
766 assert(geometry_info != (GeometryInfo *) NULL);
767 flags=NoValue;
768 if ((geometry == (char *) NULL) || (*geometry == '\0'))
769 return(flags);
cristy37e0b382011-06-07 13:31:21 +0000770 if (strlen(geometry) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000771 return(flags);
772 (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
773 for (p=pedantic_geometry; *p != '\0'; )
774 {
775 if (isspace((int) ((unsigned char) *p)) != 0)
776 {
777 (void) CopyMagickString(p,p+1,MaxTextExtent);
778 continue;
779 }
cristy35abcb72011-09-06 20:36:15 +0000780 c=(int) ((unsigned char) *p);
781 switch (c)
cristy3ed852e2009-09-05 21:47:34 +0000782 {
783 case '%':
784 {
785 flags|=PercentValue;
786 (void) CopyMagickString(p,p+1,MaxTextExtent);
787 break;
788 }
789 case '!':
790 {
791 flags|=AspectValue;
792 (void) CopyMagickString(p,p+1,MaxTextExtent);
793 break;
794 }
795 case '<':
796 {
797 flags|=LessValue;
798 (void) CopyMagickString(p,p+1,MaxTextExtent);
799 break;
800 }
801 case '>':
802 {
803 flags|=GreaterValue;
804 (void) CopyMagickString(p,p+1,MaxTextExtent);
805 break;
806 }
807 case '^':
808 {
809 flags|=MinimumValue;
810 (void) CopyMagickString(p,p+1,MaxTextExtent);
811 break;
812 }
813 case '@':
814 {
815 flags|=AreaValue;
816 (void) CopyMagickString(p,p+1,MaxTextExtent);
817 break;
818 }
819 case '(':
820 case ')':
821 {
822 (void) CopyMagickString(p,p+1,MaxTextExtent);
823 break;
824 }
825 case '-':
826 case '+':
827 case ',':
828 case '0':
829 case '1':
830 case '2':
831 case '3':
832 case '4':
833 case '5':
834 case '6':
835 case '7':
836 case '8':
837 case '9':
cristy3ed852e2009-09-05 21:47:34 +0000838 case 'x':
839 case 'X':
840 case '/':
841 case ':':
cristy35f75ac2011-09-06 20:33:35 +0000842 case 215:
cristy3ed852e2009-09-05 21:47:34 +0000843 {
844 p++;
845 break;
846 }
847 case '.':
848 {
849 p++;
850 flags|=DecimalValue;
851 break;
852 }
853 default:
854 return(flags);
855 }
856 }
857 /*
858 Parse rho, sigma, xi, psi, and optionally chi.
859 */
860 p=pedantic_geometry;
861 if (*p == '\0')
862 return(flags);
863 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000864 value=StringToDouble(p,&q);
cristy3ed852e2009-09-05 21:47:34 +0000865 if (LocaleNCompare(p,"0x",2) == 0)
866 value=(double) strtol(p,&q,10);
cristy35f75ac2011-09-06 20:33:35 +0000867 c=(int) ((unsigned char) *q);
868 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
anthonyd1303502010-05-11 12:00:43 +0000869 (*q == '/') || (*q == ':') || (*q =='\0'))
cristy3ed852e2009-09-05 21:47:34 +0000870 {
871 /*
872 Parse rho.
873 */
874 q=p;
875 if (LocaleNCompare(p,"0x",2) == 0)
876 value=(double) strtol(p,&p,10);
877 else
cristydbdd0e32011-11-04 23:29:40 +0000878 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000879 if (p != q)
880 {
881 flags|=RhoValue;
882 geometry_info->rho=value;
883 }
884 }
885 q=p;
cristy35f75ac2011-09-06 20:33:35 +0000886 c=(int) ((unsigned char) *p);
887 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ',') || (*p == '/') ||
cristyf30f47d2011-09-06 14:40:59 +0000888 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000889 {
890 /*
891 Parse sigma.
892 */
893 p++;
894 while (isspace((int) ((unsigned char) *p)) != 0)
895 p++;
cristy35f75ac2011-09-06 20:33:35 +0000896 c=(int) ((unsigned char) *q);
897 if (((c != 215) && (*q != 'x') && (*q != 'X')) || ((*p != '+') &&
cristyf30f47d2011-09-06 14:40:59 +0000898 (*p != '-')))
cristy3ed852e2009-09-05 21:47:34 +0000899 {
900 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000901 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000902 if (p != q)
903 {
904 flags|=SigmaValue;
905 geometry_info->sigma=value;
906 }
907 }
908 }
909 while (isspace((int) ((unsigned char) *p)) != 0)
910 p++;
anthonyd1303502010-05-11 12:00:43 +0000911 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000912 {
913 /*
914 Parse xi value.
915 */
anthonyd1303502010-05-11 12:00:43 +0000916 if ((*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000917 p++;
918 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000919 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000920 if (p != q)
921 {
922 flags|=XiValue;
923 if (*q == '-')
924 flags|=XiNegative;
925 geometry_info->xi=value;
926 }
927 while (isspace((int) ((unsigned char) *p)) != 0)
928 p++;
929 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +0000930 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000931 {
932 /*
933 Parse psi value.
934 */
anthonyd1303502010-05-11 12:00:43 +0000935 if ((*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000936 p++;
937 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000938 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000939 if (p != q)
940 {
941 flags|=PsiValue;
942 if (*q == '-')
943 flags|=PsiNegative;
944 geometry_info->psi=value;
945 }
946 }
947 while (isspace((int) ((unsigned char) *p)) != 0)
948 p++;
949 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +0000950 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000951 {
952 /*
953 Parse chi value.
954 */
955 if ((*p == ',') || (*p == '/') || (*p == ':'))
956 p++;
957 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000958 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000959 if (p != q)
960 {
961 flags|=ChiValue;
962 if (*q == '-')
963 flags|=ChiNegative;
964 geometry_info->chi=value;
965 }
966 }
967 }
968 if (strchr(pedantic_geometry,':') != (char *) NULL)
969 {
970 /*
971 Normalize sampling factor (e.g. 4:2:2 => 2x1).
972 */
973 geometry_info->rho/=geometry_info->sigma;
974 geometry_info->sigma=1.0;
975 if (geometry_info->xi == 0.0)
976 geometry_info->sigma=2.0;
977 }
978 if (((flags & SigmaValue) == 0) && ((flags & XiValue) != 0) &&
979 ((flags & PsiValue) == 0))
980 {
981 /*
982 Support negative height values (e.g. 30x-20).
983 */
984 geometry_info->sigma=geometry_info->xi;
985 geometry_info->xi=0.0;
986 flags|=SigmaValue;
987 flags&=(~XiValue);
988 }
989 return(flags);
990}
991
992/*
993%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
994% %
995% %
996% %
997% P a r s e G r a v i t y G e o m e t r y %
998% %
999% %
1000% %
1001%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1002%
1003% ParseGravityGeometry() returns a region as defined by the geometry string
anthonyd58e60c2011-03-30 00:24:34 +00001004% with respect to the given image page (canvas) dimensions and the images
1005% gravity setting.
1006%
1007% This is typically used for specifing a area within a given image for
1008% cropping images to a smaller size, chopping out rows and or columns, or
1009% resizing and positioning overlay images.
1010%
1011% Percentages are relative to image size and not page size, and are set to
1012% nearest integer (pixel) size.
cristy3ed852e2009-09-05 21:47:34 +00001013%
1014% The format of the ParseGravityGeometry method is:
1015%
1016% MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1017% RectangeInfo *region_info,ExceptionInfo *exception)
1018%
1019% A description of each parameter follows:
1020%
anthonyd58e60c2011-03-30 00:24:34 +00001021% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001022%
cristy272f23a2011-03-30 00:37:11 +00001023% o region_info: the region as defined by the geometry string with respect
1024% to the image dimensions and its gravity.
cristy3ed852e2009-09-05 21:47:34 +00001025%
1026% o exception: return any errors or warnings in this structure.
1027%
1028*/
1029MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1030 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1031{
1032 MagickStatusType
1033 flags;
1034
cristybb503372010-05-27 20:51:26 +00001035 size_t
cristy3ed852e2009-09-05 21:47:34 +00001036 height,
1037 width;
1038
1039 SetGeometry(image,region_info);
1040 if (image->page.width != 0)
1041 region_info->width=image->page.width;
1042 if (image->page.height != 0)
1043 region_info->height=image->page.height;
1044 flags=ParseAbsoluteGeometry(geometry,region_info);
1045 if (flags == NoValue)
1046 {
1047 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1048 "InvalidGeometry","`%s'",geometry);
1049 return(flags);
1050 }
1051 if ((flags & PercentValue) != 0)
1052 {
1053 GeometryInfo
1054 geometry_info;
1055
1056 MagickStatusType
1057 status;
1058
1059 PointInfo
1060 scale;
1061
1062 /*
anthonyd58e60c2011-03-30 00:24:34 +00001063 Geometry is a percentage of the image size, not canvas size
cristy3ed852e2009-09-05 21:47:34 +00001064 */
1065 if (image->gravity != UndefinedGravity)
1066 flags|=XValue | YValue;
1067 status=ParseGeometry(geometry,&geometry_info);
1068 scale.x=geometry_info.rho;
1069 if ((status & RhoValue) == 0)
1070 scale.x=100.0;
1071 scale.y=geometry_info.sigma;
1072 if ((status & SigmaValue) == 0)
1073 scale.y=scale.x;
cristy272f23a2011-03-30 00:37:11 +00001074 region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1075 region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001076 }
1077 /*
1078 Adjust offset according to gravity setting.
1079 */
1080 width=region_info->width;
1081 height=region_info->height;
1082 if (width == 0)
1083 region_info->width=image->page.width | image->columns;
1084 if (height == 0)
1085 region_info->height=image->page.height | image->rows;
1086 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1087 region_info->width=width;
1088 region_info->height=height;
1089 return(flags);
1090}
1091
1092/*
1093%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1094% %
1095% %
1096% %
1097+ P a r s e M e t a G e o m e t r y %
1098% %
1099% %
1100% %
1101%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1102%
1103% ParseMetaGeometry() is similar to GetGeometry() except the returned
anthonyd58e60c2011-03-30 00:24:34 +00001104% geometry is modified as determined by the meta characters: %, !, <, >, @,
1105% and ^ in relation to image resizing.
1106%
1107% Final image dimensions are adjusted so as to preserve the aspect ratio as
glennrpdcabf6d2011-06-25 03:26:14 +00001108% much as possible, while generating a integer (pixel) size, and fitting the
anthonyd58e60c2011-03-30 00:24:34 +00001109% image within the specified geometry width and height.
1110%
1111% Flags are interpreted...
cristy272f23a2011-03-30 00:37:11 +00001112% % geometry size is given percentage of original image size
1113% ! do not try to preserve aspect ratio
1114% < only enlarge images smaller that geometry
1115% > only shrink images larger than geometry
1116% @ Fit image to contain at most this many pixels
1117% ^ Contain the given geometry given, (minimal dimensions given)
cristy3ed852e2009-09-05 21:47:34 +00001118%
1119% The format of the ParseMetaGeometry method is:
1120%
anthonyd58e60c2011-03-30 00:24:34 +00001121% MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
cristy272f23a2011-03-30 00:37:11 +00001122% ssize_t *y, size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001123%
1124% A description of each parameter follows:
1125%
anthonyd58e60c2011-03-30 00:24:34 +00001126% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001127%
anthonyd58e60c2011-03-30 00:24:34 +00001128% o x,y: The x and y offset, set according to the geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001129%
cristy272f23a2011-03-30 00:37:11 +00001130% o width,height: The width and height of original image, modified by
1131% the given geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001132%
1133*/
1134
cristybb503372010-05-27 20:51:26 +00001135static inline size_t MagickMax(const size_t x,
1136 const size_t y)
cristy3ed852e2009-09-05 21:47:34 +00001137{
1138 if (x > y)
1139 return(x);
1140 return(y);
1141}
1142
cristybb503372010-05-27 20:51:26 +00001143MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1144 ssize_t *y,size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001145{
1146 GeometryInfo
1147 geometry_info;
1148
1149 MagickStatusType
1150 flags;
1151
cristybb503372010-05-27 20:51:26 +00001152 size_t
cristy3ed852e2009-09-05 21:47:34 +00001153 former_height,
1154 former_width;
1155
1156 /*
1157 Ensure the image geometry is valid.
1158 */
cristybb503372010-05-27 20:51:26 +00001159 assert(x != (ssize_t *) NULL);
1160 assert(y != (ssize_t *) NULL);
1161 assert(width != (size_t *) NULL);
1162 assert(height != (size_t *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001163 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1164 return(NoValue);
1165 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1166 /*
1167 Parse geometry using GetGeometry.
1168 */
1169 SetGeometryInfo(&geometry_info);
1170 former_width=(*width);
1171 former_height=(*height);
1172 flags=GetGeometry(geometry,x,y,width,height);
1173 if ((flags & PercentValue) != 0)
1174 {
1175 MagickStatusType
1176 flags;
1177
1178 PointInfo
1179 scale;
1180
1181 /*
1182 Geometry is a percentage of the image size.
1183 */
1184 flags=ParseGeometry(geometry,&geometry_info);
1185 scale.x=geometry_info.rho;
1186 if ((flags & RhoValue) == 0)
1187 scale.x=100.0;
1188 scale.y=geometry_info.sigma;
1189 if ((flags & SigmaValue) == 0)
1190 scale.y=scale.x;
cristybb503372010-05-27 20:51:26 +00001191 *width=(size_t) floor(scale.x*former_width/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001192 if (*width == 0)
1193 *width=1;
cristybb503372010-05-27 20:51:26 +00001194 *height=(size_t) floor(scale.y*former_height/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001195 if (*height == 0)
1196 *height=1;
1197 former_width=(*width);
1198 former_height=(*height);
1199 }
cristy07623352011-12-07 13:04:03 +00001200 if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1201 (*height == former_height)))
cristy3ed852e2009-09-05 21:47:34 +00001202 {
cristy07623352011-12-07 13:04:03 +00001203 if ((flags & RhoValue) == 0)
1204 *width=former_width;
1205 if ((flags & SigmaValue) == 0)
1206 *height=former_height;
1207 }
1208 else
1209 {
1210 double
cristy3ed852e2009-09-05 21:47:34 +00001211 scale_factor;
1212
1213 /*
1214 Respect aspect ratio of the image.
1215 */
1216 if ((former_width == 0) || (former_height == 0))
1217 scale_factor=1.0;
1218 else
1219 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1220 {
cristy07623352011-12-07 13:04:03 +00001221 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001222 if ((flags & MinimumValue) == 0)
1223 {
cristy07623352011-12-07 13:04:03 +00001224 if (scale_factor > ((double) *height/(double) former_height))
1225 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001226 }
1227 else
cristy07623352011-12-07 13:04:03 +00001228 if (scale_factor < ((double) *height/(double) former_height))
1229 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001230 }
1231 else
1232 if ((flags & RhoValue) != 0)
1233 {
cristy07623352011-12-07 13:04:03 +00001234 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001235 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001236 (scale_factor < ((double) *width/(double) former_height)))
1237 scale_factor=(double) *width/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001238 }
1239 else
1240 {
cristy07623352011-12-07 13:04:03 +00001241 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001242 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001243 (scale_factor < ((double) *height/(double) former_width)))
1244 scale_factor=(double) *height/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001245 }
cristy272f23a2011-03-30 00:37:11 +00001246 *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1247 *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
cristy3ed852e2009-09-05 21:47:34 +00001248 }
1249 if ((flags & GreaterValue) != 0)
1250 {
1251 if (former_width < *width)
1252 *width=former_width;
1253 if (former_height < *height)
1254 *height=former_height;
1255 }
1256 if ((flags & LessValue) != 0)
1257 {
1258 if (former_width > *width)
1259 *width=former_width;
1260 if (former_height > *height)
1261 *height=former_height;
1262 }
1263 if ((flags & AreaValue) != 0)
1264 {
cristy07623352011-12-07 13:04:03 +00001265 double
cristy3ed852e2009-09-05 21:47:34 +00001266 area,
1267 distance;
1268
1269 PointInfo
1270 scale;
1271
1272 /*
1273 Geometry is a maximum area in pixels.
1274 */
1275 (void) ParseGeometry(geometry,&geometry_info);
1276 area=geometry_info.rho;
1277 distance=sqrt((double) former_width*former_height);
cristy07623352011-12-07 13:04:03 +00001278 scale.x=former_width/(distance/sqrt((double) area));
1279 scale.y=former_height/(distance/sqrt((double) area));
cristy3ed852e2009-09-05 21:47:34 +00001280 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1281 {
cristy11e0a872012-01-10 16:10:21 +00001282 *width=(size_t) (former_width/(distance/sqrt(area))+0.5);
1283 *height=(size_t) (former_height/(distance/sqrt(area))+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001284 }
1285 former_width=(*width);
1286 former_height=(*height);
1287 }
1288 return(flags);
1289}
1290
1291/*
1292%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1293% %
1294% %
1295% %
1296% P a r s e P a g e G e o m e t r y %
1297% %
1298% %
1299% %
1300%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1301%
1302% ParsePageGeometry() returns a region as defined by the geometry string with
anthonyd58e60c2011-03-30 00:24:34 +00001303% respect to the image page (canvas) dimensions.
1304%
1305% WARNING: Percentage dimensions remain relative to the actual image
1306% dimensions, and not canvas dimensions.
cristy3ed852e2009-09-05 21:47:34 +00001307%
1308% The format of the ParsePageGeometry method is:
1309%
1310% MagickStatusType ParsePageGeometry(const Image *image,
1311% const char *geometry,RectangeInfo *region_info,
1312% ExceptionInfo *exception)
1313%
1314% A description of each parameter follows:
1315%
anthonyd58e60c2011-03-30 00:24:34 +00001316% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001317%
1318% o region_info: the region as defined by the geometry string with
1319% respect to the image and its gravity.
1320%
1321% o exception: return any errors or warnings in this structure.
1322%
1323*/
1324MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1325 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1326{
1327 MagickStatusType
1328 flags;
1329
1330 SetGeometry(image,region_info);
1331 if (image->page.width != 0)
1332 region_info->width=image->page.width;
1333 if (image->page.height != 0)
1334 region_info->height=image->page.height;
1335 flags=ParseAbsoluteGeometry(geometry,region_info);
1336 if (flags == NoValue)
1337 {
1338 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1339 "InvalidGeometry","`%s'",geometry);
1340 return(flags);
1341 }
1342 if ((flags & PercentValue) != 0)
1343 {
1344 region_info->width=image->columns;
1345 region_info->height=image->rows;
1346 }
1347 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1348 &region_info->width,&region_info->height);
1349 return(flags);
1350}
1351
1352/*
1353%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1354% %
1355% %
1356% %
1357% P a r s e R e g i o n G e o m e t r y %
1358% %
1359% %
1360% %
1361%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1362%
1363% ParseRegionGeometry() returns a region as defined by the geometry string
1364% with respect to the image dimensions and aspect ratio.
1365%
anthonyd58e60c2011-03-30 00:24:34 +00001366% This is basically a wrapper around ParseMetaGeometry. This is typically
1367% used to parse a geometry string to work out the final integer dimensions
1368% for image resizing.
1369%
cristy3ed852e2009-09-05 21:47:34 +00001370% The format of the ParseRegionGeometry method is:
1371%
1372% MagickStatusType ParseRegionGeometry(const Image *image,
1373% const char *geometry,RectangeInfo *region_info,
1374% ExceptionInfo *exception)
1375%
1376% A description of each parameter follows:
1377%
anthonyd58e60c2011-03-30 00:24:34 +00001378% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001379%
1380% o region_info: the region as defined by the geometry string.
1381%
1382% o exception: return any errors or warnings in this structure.
1383%
1384*/
1385MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1386 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1387{
1388 MagickStatusType
1389 flags;
1390
1391 SetGeometry(image,region_info);
1392 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1393 &region_info->width,&region_info->height);
1394 if (flags == NoValue)
1395 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1396 "InvalidGeometry","`%s'",geometry);
1397 return(flags);
1398}
1399
1400/*
1401%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1402% %
1403% %
1404% %
1405% S e t G e o m e t r y %
1406% %
1407% %
1408% %
1409%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1410%
1411% SetGeometry() sets the geometry to its default values.
1412%
1413% The format of the SetGeometry method is:
1414%
1415% SetGeometry(const Image *image,RectangleInfo *geometry)
1416%
1417% A description of each parameter follows:
1418%
1419% o image: the image.
1420%
1421% o geometry: the geometry.
1422%
1423*/
1424MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1425{
1426 assert(image != (Image *) NULL);
1427 assert(image->signature == MagickSignature);
1428 if (image->debug != MagickFalse)
1429 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1430 assert(geometry != (RectangleInfo *) NULL);
1431 (void) ResetMagickMemory(geometry,0,sizeof(*geometry));
1432 geometry->width=image->columns;
1433 geometry->height=image->rows;
1434}
1435
1436/*
1437%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1438% %
1439% %
1440% %
1441% S e t G e o m e t r y I n f o %
1442% %
1443% %
1444% %
1445%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1446%
1447% SetGeometryInfo sets the GeometryInfo structure to its default values.
1448%
1449% The format of the SetGeometryInfo method is:
1450%
1451% SetGeometryInfo(GeometryInfo *geometry_info)
1452%
1453% A description of each parameter follows:
1454%
1455% o geometry_info: the geometry info structure.
1456%
1457*/
1458MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1459{
1460 assert(geometry_info != (GeometryInfo *) NULL);
1461 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1462 (void) ResetMagickMemory(geometry_info,0,sizeof(*geometry_info));
1463}