blob: 07b8fea46f2dd8d67907f3bac4fb6f64eafdb8e4 [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:
cristy3ed852e2009-09-05 21:47:34 +0000445 {
cristybb503372010-05-27 20:51:26 +0000446 region->x+=(ssize_t) (width/2-region->width/2);
cristy3ed852e2009-09-05 21:47:34 +0000447 break;
448 }
449 case ForgetGravity:
450 case NorthWestGravity:
451 case WestGravity:
452 case SouthWestGravity:
453 default:
454 break;
455 }
456 switch (gravity)
457 {
458 case SouthWestGravity:
459 case SouthGravity:
460 case SouthEastGravity:
461 {
cristybb503372010-05-27 20:51:26 +0000462 region->y=(ssize_t) (height-region->height-region->y);
cristy3ed852e2009-09-05 21:47:34 +0000463 break;
464 }
465 case EastGravity:
466 case WestGravity:
467 case CenterGravity:
cristy3ed852e2009-09-05 21:47:34 +0000468 {
cristybb503372010-05-27 20:51:26 +0000469 region->y+=(ssize_t) (height/2-region->height/2);
cristy3ed852e2009-09-05 21:47:34 +0000470 break;
471 }
472 case ForgetGravity:
473 case NorthWestGravity:
474 case NorthGravity:
475 case NorthEastGravity:
476 default:
477 break;
478 }
479 return;
480}
481
482/*
483%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
484% %
485% %
486% %
487+ I s G e o m e t r y %
488% %
489% %
490% %
491%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
492%
493% IsGeometry() returns MagickTrue if the geometry specification is valid.
494% Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
495%
496% The format of the IsGeometry method is:
497%
498% MagickBooleanType IsGeometry(const char *geometry)
499%
500% A description of each parameter follows:
501%
502% o geometry: This string is the geometry specification.
503%
504*/
505MagickExport MagickBooleanType IsGeometry(const char *geometry)
506{
507 GeometryInfo
508 geometry_info;
509
510 MagickStatusType
511 flags;
512
513 if (geometry == (const char *) NULL)
514 return(MagickFalse);
515 flags=ParseGeometry(geometry,&geometry_info);
cristy3ed852e2009-09-05 21:47:34 +0000516 return(flags != NoValue ? MagickTrue : MagickFalse);
517}
518
519/*
520%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
521% %
522% %
523% %
524+ I s S c e n e G e o m e t r y %
525% %
526% %
527% %
528%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
529%
530% IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
531% specification (e.g. [1], [1-9], [1,7,4]).
532%
533% The format of the IsSceneGeometry method is:
534%
535% MagickBooleanType IsSceneGeometry(const char *geometry,
536% const MagickBooleanType pedantic)
537%
538% A description of each parameter follows:
539%
540% o geometry: This string is the geometry specification.
541%
542% o pedantic: A value other than 0 invokes a more restrictive set of
543% conditions for a valid specification (e.g. [1], [1-4], [4-1]).
544%
545*/
546MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
547 const MagickBooleanType pedantic)
548{
549 char
550 *p;
551
cristy00976d82011-02-20 20:31:28 +0000552 double
553 value;
554
cristy3ed852e2009-09-05 21:47:34 +0000555 if (geometry == (const char *) NULL)
556 return(MagickFalse);
557 p=(char *) geometry;
cristydbdd0e32011-11-04 23:29:40 +0000558 value=StringToDouble(geometry,&p);
cristy00976d82011-02-20 20:31:28 +0000559 (void) value;
cristy3ed852e2009-09-05 21:47:34 +0000560 if (p == geometry)
561 return(MagickFalse);
562 if (strspn(geometry,"0123456789-, ") != strlen(geometry))
563 return(MagickFalse);
564 if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
565 return(MagickFalse);
566 return(MagickTrue);
567}
568
569/*
570%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
571% %
572% %
573% %
574% P a r s e A b s o l u t e G e o m e t r y %
575% %
576% %
577% %
578%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
579%
anthonyd58e60c2011-03-30 00:24:34 +0000580% ParseAbsoluteGeometry() returns a region as defined by the geometry string,
581% without any modification by percentages or gravity.
582%
583% It currently just a wrapper around GetGeometry(), but may be expanded in
584% the future to handle other positioning information.
cristy3ed852e2009-09-05 21:47:34 +0000585%
586% The format of the ParseAbsoluteGeometry method is:
587%
588% MagickStatusType ParseAbsoluteGeometry(const char *geometry,
589% RectangleInfo *region_info)
590%
591% A description of each parameter follows:
592%
anthonyd58e60c2011-03-30 00:24:34 +0000593% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000594%
595% o region_info: the region as defined by the geometry string.
596%
597*/
598MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
599 RectangleInfo *region_info)
600{
601 MagickStatusType
602 flags;
603
604 flags=GetGeometry(geometry,&region_info->x,&region_info->y,
605 &region_info->width,&region_info->height);
606 return(flags);
607}
608
609/*
610%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
611% %
612% %
613% %
614% P a r s e A f f i n e G e o m e t r y %
615% %
616% %
617% %
618%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
619%
cristy272f23a2011-03-30 00:37:11 +0000620% ParseAffineGeometry() returns an affine matrix as defined by a string of 4
621% to 6 comma/space separated floating point values.
anthonyd58e60c2011-03-30 00:24:34 +0000622%
623% The affine matrix determinant is checked for validity of the values.
cristy3ed852e2009-09-05 21:47:34 +0000624%
625% The format of the ParseAffineGeometry method is:
626%
627% MagickStatusType ParseAffineGeometry(const char *geometry,
628% AffineMatrix *affine_matrix,ExceptionInfo *exception)
629%
630% A description of each parameter follows:
631%
anthonyd58e60c2011-03-30 00:24:34 +0000632% 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 +0000633%
634% o affine_matrix: the affine matrix as defined by the geometry string.
635%
636% o exception: return any errors or warnings in this structure.
637%
638*/
639MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
640 AffineMatrix *affine_matrix,ExceptionInfo *exception)
641{
642 char
643 token[MaxTextExtent];
644
645 const char
646 *p;
647
648 double
649 determinant;
650
651 MagickStatusType
652 flags;
653
cristybb503372010-05-27 20:51:26 +0000654 register ssize_t
cristy3ed852e2009-09-05 21:47:34 +0000655 i;
656
657 GetAffineMatrix(affine_matrix);
658 flags=NoValue;
659 p=(char *) geometry;
660 for (i=0; (*p != '\0') && (i < 6); i++)
661 {
662 GetMagickToken(p,&p,token);
663 if (*token == ',')
664 GetMagickToken(p,&p,token);
665 switch (i)
666 {
cristyc1acd842011-05-19 23:05:47 +0000667 case 0:
668 {
cristydbdd0e32011-11-04 23:29:40 +0000669 affine_matrix->sx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000670 break;
671 }
672 case 1:
673 {
cristydbdd0e32011-11-04 23:29:40 +0000674 affine_matrix->rx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000675 break;
676 }
677 case 2:
678 {
cristydbdd0e32011-11-04 23:29:40 +0000679 affine_matrix->ry=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000680 break;
681 }
682 case 3:
683 {
cristydbdd0e32011-11-04 23:29:40 +0000684 affine_matrix->sy=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000685 break;
686 }
687 case 4:
688 {
cristydbdd0e32011-11-04 23:29:40 +0000689 affine_matrix->tx=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000690 flags|=XValue;
691 break;
692 }
693 case 5:
694 {
cristydbdd0e32011-11-04 23:29:40 +0000695 affine_matrix->ty=StringToDouble(token,(char **) NULL);
cristyc1acd842011-05-19 23:05:47 +0000696 flags|=YValue;
697 break;
698 }
cristy3ed852e2009-09-05 21:47:34 +0000699 }
700 }
anthony92c93bd2012-03-19 14:02:47 +0000701 determinant=(affine_matrix->sx*affine_matrix->sy
702 - affine_matrix->rx*affine_matrix->ry);
cristy3ed852e2009-09-05 21:47:34 +0000703 if (fabs(determinant) < MagickEpsilon)
704 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthony92c93bd2012-03-19 14:02:47 +0000705 "InvalidArgument","'%s' : 'Indeterminate Matrix'",geometry);
cristy3ed852e2009-09-05 21:47:34 +0000706 return(flags);
707}
708
709/*
710%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
711% %
712% %
713% %
714% P a r s e G e o m e t r y %
715% %
716% %
717% %
718%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
719%
720% ParseGeometry() parses a geometry specification and returns the sigma,
721% rho, xi, and psi values. It also returns flags that indicates which
722% of the four values (sigma, rho, xi, psi) were located in the string, and
anthonyd58e60c2011-03-30 00:24:34 +0000723% whether the xi or pi values are negative.
724%
725% In addition, it reports if there are any of meta characters (%, !, <, >, @,
726% and ^) flags present. It does not report the location of the percentage
727% relative to the values.
cristy3ed852e2009-09-05 21:47:34 +0000728%
729% The format of the ParseGeometry method is:
730%
731% MagickStatusType ParseGeometry(const char *geometry,
732% GeometryInfo *geometry_info)
733%
734% A description of each parameter follows:
735%
anthonyd58e60c2011-03-30 00:24:34 +0000736% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +0000737%
738% o geometry_info: returns the parsed width/height/x/y in this structure.
739%
740*/
741MagickExport MagickStatusType ParseGeometry(const char *geometry,
742 GeometryInfo *geometry_info)
743{
744 char
745 *p,
746 pedantic_geometry[MaxTextExtent],
747 *q;
748
749 double
750 value;
751
cristyf30f47d2011-09-06 14:40:59 +0000752 int
753 c;
754
cristy3ed852e2009-09-05 21:47:34 +0000755 MagickStatusType
756 flags;
757
758 /*
759 Remove whitespaces meta characters from geometry specification.
760 */
761 assert(geometry_info != (GeometryInfo *) NULL);
762 flags=NoValue;
763 if ((geometry == (char *) NULL) || (*geometry == '\0'))
764 return(flags);
cristy37e0b382011-06-07 13:31:21 +0000765 if (strlen(geometry) >= (MaxTextExtent-1))
cristy3ed852e2009-09-05 21:47:34 +0000766 return(flags);
767 (void) CopyMagickString(pedantic_geometry,geometry,MaxTextExtent);
768 for (p=pedantic_geometry; *p != '\0'; )
769 {
770 if (isspace((int) ((unsigned char) *p)) != 0)
771 {
772 (void) CopyMagickString(p,p+1,MaxTextExtent);
773 continue;
774 }
cristy35abcb72011-09-06 20:36:15 +0000775 c=(int) ((unsigned char) *p);
776 switch (c)
cristy3ed852e2009-09-05 21:47:34 +0000777 {
778 case '%':
779 {
780 flags|=PercentValue;
781 (void) CopyMagickString(p,p+1,MaxTextExtent);
782 break;
783 }
784 case '!':
785 {
786 flags|=AspectValue;
787 (void) CopyMagickString(p,p+1,MaxTextExtent);
788 break;
789 }
790 case '<':
791 {
792 flags|=LessValue;
793 (void) CopyMagickString(p,p+1,MaxTextExtent);
794 break;
795 }
796 case '>':
797 {
798 flags|=GreaterValue;
799 (void) CopyMagickString(p,p+1,MaxTextExtent);
800 break;
801 }
802 case '^':
803 {
804 flags|=MinimumValue;
805 (void) CopyMagickString(p,p+1,MaxTextExtent);
806 break;
807 }
808 case '@':
809 {
810 flags|=AreaValue;
811 (void) CopyMagickString(p,p+1,MaxTextExtent);
812 break;
813 }
814 case '(':
815 case ')':
816 {
817 (void) CopyMagickString(p,p+1,MaxTextExtent);
818 break;
819 }
820 case '-':
821 case '+':
822 case ',':
823 case '0':
824 case '1':
825 case '2':
826 case '3':
827 case '4':
828 case '5':
829 case '6':
830 case '7':
831 case '8':
832 case '9':
cristy3ed852e2009-09-05 21:47:34 +0000833 case 'x':
834 case 'X':
835 case '/':
836 case ':':
cristy35f75ac2011-09-06 20:33:35 +0000837 case 215:
cristy3ed852e2009-09-05 21:47:34 +0000838 {
839 p++;
840 break;
841 }
842 case '.':
843 {
844 p++;
845 flags|=DecimalValue;
846 break;
847 }
848 default:
849 return(flags);
850 }
851 }
852 /*
853 Parse rho, sigma, xi, psi, and optionally chi.
854 */
855 p=pedantic_geometry;
856 if (*p == '\0')
857 return(flags);
858 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000859 value=StringToDouble(p,&q);
cristy3ed852e2009-09-05 21:47:34 +0000860 if (LocaleNCompare(p,"0x",2) == 0)
861 value=(double) strtol(p,&q,10);
cristy35f75ac2011-09-06 20:33:35 +0000862 c=(int) ((unsigned char) *q);
863 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
anthonyd1303502010-05-11 12:00:43 +0000864 (*q == '/') || (*q == ':') || (*q =='\0'))
cristy3ed852e2009-09-05 21:47:34 +0000865 {
866 /*
867 Parse rho.
868 */
869 q=p;
870 if (LocaleNCompare(p,"0x",2) == 0)
871 value=(double) strtol(p,&p,10);
872 else
cristydbdd0e32011-11-04 23:29:40 +0000873 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000874 if (p != q)
875 {
876 flags|=RhoValue;
877 geometry_info->rho=value;
878 }
879 }
880 q=p;
cristy35f75ac2011-09-06 20:33:35 +0000881 c=(int) ((unsigned char) *p);
882 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ',') || (*p == '/') ||
cristyf30f47d2011-09-06 14:40:59 +0000883 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000884 {
885 /*
886 Parse sigma.
887 */
888 p++;
889 while (isspace((int) ((unsigned char) *p)) != 0)
890 p++;
cristy35f75ac2011-09-06 20:33:35 +0000891 c=(int) ((unsigned char) *q);
892 if (((c != 215) && (*q != 'x') && (*q != 'X')) || ((*p != '+') &&
cristyf30f47d2011-09-06 14:40:59 +0000893 (*p != '-')))
cristy3ed852e2009-09-05 21:47:34 +0000894 {
895 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000896 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000897 if (p != q)
898 {
899 flags|=SigmaValue;
900 geometry_info->sigma=value;
901 }
902 }
903 }
904 while (isspace((int) ((unsigned char) *p)) != 0)
905 p++;
anthonyd1303502010-05-11 12:00:43 +0000906 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000907 {
908 /*
909 Parse xi value.
910 */
anthonyc330e152012-04-19 14:40:36 +0000911 if ((*p == '+') || (*p == ',') || (*p == '/') || (*p == ':') )
cristy3ed852e2009-09-05 21:47:34 +0000912 p++;
913 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000914 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000915 if (p != q)
916 {
917 flags|=XiValue;
918 if (*q == '-')
919 flags|=XiNegative;
920 geometry_info->xi=value;
921 }
922 while (isspace((int) ((unsigned char) *p)) != 0)
923 p++;
924 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +0000925 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000926 {
927 /*
928 Parse psi value.
929 */
anthonyc330e152012-04-19 14:40:36 +0000930 if ((*p == '+') || (*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000931 p++;
932 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000933 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000934 if (p != q)
935 {
936 flags|=PsiValue;
937 if (*q == '-')
938 flags|=PsiNegative;
939 geometry_info->psi=value;
940 }
941 }
942 while (isspace((int) ((unsigned char) *p)) != 0)
943 p++;
944 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
anthonyd1303502010-05-11 12:00:43 +0000945 (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000946 {
947 /*
948 Parse chi value.
949 */
anthonyc330e152012-04-19 14:40:36 +0000950 if ((*p == '+') || (*p == ',') || (*p == '/') || (*p == ':'))
cristy3ed852e2009-09-05 21:47:34 +0000951 p++;
952 q=p;
cristydbdd0e32011-11-04 23:29:40 +0000953 value=StringToDouble(p,&p);
cristy3ed852e2009-09-05 21:47:34 +0000954 if (p != q)
955 {
956 flags|=ChiValue;
957 if (*q == '-')
958 flags|=ChiNegative;
959 geometry_info->chi=value;
960 }
961 }
962 }
963 if (strchr(pedantic_geometry,':') != (char *) NULL)
964 {
965 /*
966 Normalize sampling factor (e.g. 4:2:2 => 2x1).
967 */
968 geometry_info->rho/=geometry_info->sigma;
969 geometry_info->sigma=1.0;
970 if (geometry_info->xi == 0.0)
971 geometry_info->sigma=2.0;
972 }
973 if (((flags & SigmaValue) == 0) && ((flags & XiValue) != 0) &&
974 ((flags & PsiValue) == 0))
975 {
976 /*
977 Support negative height values (e.g. 30x-20).
978 */
979 geometry_info->sigma=geometry_info->xi;
980 geometry_info->xi=0.0;
981 flags|=SigmaValue;
982 flags&=(~XiValue);
983 }
984 return(flags);
985}
986
987/*
988%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
989% %
990% %
991% %
992% P a r s e G r a v i t y G e o m e t r y %
993% %
994% %
995% %
996%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
997%
998% ParseGravityGeometry() returns a region as defined by the geometry string
anthonyd58e60c2011-03-30 00:24:34 +0000999% with respect to the given image page (canvas) dimensions and the images
1000% gravity setting.
1001%
1002% This is typically used for specifing a area within a given image for
1003% cropping images to a smaller size, chopping out rows and or columns, or
1004% resizing and positioning overlay images.
1005%
1006% Percentages are relative to image size and not page size, and are set to
1007% nearest integer (pixel) size.
cristy3ed852e2009-09-05 21:47:34 +00001008%
1009% The format of the ParseGravityGeometry method is:
1010%
1011% MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1012% RectangeInfo *region_info,ExceptionInfo *exception)
1013%
1014% A description of each parameter follows:
1015%
anthonyd58e60c2011-03-30 00:24:34 +00001016% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001017%
cristy272f23a2011-03-30 00:37:11 +00001018% o region_info: the region as defined by the geometry string with respect
1019% to the image dimensions and its gravity.
cristy3ed852e2009-09-05 21:47:34 +00001020%
1021% o exception: return any errors or warnings in this structure.
1022%
1023*/
1024MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1025 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1026{
1027 MagickStatusType
1028 flags;
1029
cristybb503372010-05-27 20:51:26 +00001030 size_t
cristy3ed852e2009-09-05 21:47:34 +00001031 height,
1032 width;
1033
1034 SetGeometry(image,region_info);
1035 if (image->page.width != 0)
1036 region_info->width=image->page.width;
1037 if (image->page.height != 0)
1038 region_info->height=image->page.height;
1039 flags=ParseAbsoluteGeometry(geometry,region_info);
1040 if (flags == NoValue)
1041 {
1042 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001043 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001044 return(flags);
1045 }
1046 if ((flags & PercentValue) != 0)
1047 {
1048 GeometryInfo
1049 geometry_info;
1050
1051 MagickStatusType
1052 status;
1053
1054 PointInfo
1055 scale;
1056
1057 /*
anthonyd58e60c2011-03-30 00:24:34 +00001058 Geometry is a percentage of the image size, not canvas size
cristy3ed852e2009-09-05 21:47:34 +00001059 */
1060 if (image->gravity != UndefinedGravity)
1061 flags|=XValue | YValue;
1062 status=ParseGeometry(geometry,&geometry_info);
1063 scale.x=geometry_info.rho;
1064 if ((status & RhoValue) == 0)
1065 scale.x=100.0;
1066 scale.y=geometry_info.sigma;
1067 if ((status & SigmaValue) == 0)
1068 scale.y=scale.x;
cristy272f23a2011-03-30 00:37:11 +00001069 region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1070 region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001071 }
1072 /*
1073 Adjust offset according to gravity setting.
1074 */
1075 width=region_info->width;
1076 height=region_info->height;
1077 if (width == 0)
1078 region_info->width=image->page.width | image->columns;
1079 if (height == 0)
1080 region_info->height=image->page.height | image->rows;
1081 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1082 region_info->width=width;
1083 region_info->height=height;
1084 return(flags);
1085}
1086
1087/*
1088%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1089% %
1090% %
1091% %
1092+ P a r s e M e t a G e o m e t r y %
1093% %
1094% %
1095% %
1096%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1097%
1098% ParseMetaGeometry() is similar to GetGeometry() except the returned
anthonyd58e60c2011-03-30 00:24:34 +00001099% geometry is modified as determined by the meta characters: %, !, <, >, @,
1100% and ^ in relation to image resizing.
1101%
1102% Final image dimensions are adjusted so as to preserve the aspect ratio as
glennrpdcabf6d2011-06-25 03:26:14 +00001103% much as possible, while generating a integer (pixel) size, and fitting the
anthonyd58e60c2011-03-30 00:24:34 +00001104% image within the specified geometry width and height.
1105%
1106% Flags are interpreted...
cristy272f23a2011-03-30 00:37:11 +00001107% % geometry size is given percentage of original image size
1108% ! do not try to preserve aspect ratio
1109% < only enlarge images smaller that geometry
1110% > only shrink images larger than geometry
1111% @ Fit image to contain at most this many pixels
1112% ^ Contain the given geometry given, (minimal dimensions given)
cristy3ed852e2009-09-05 21:47:34 +00001113%
1114% The format of the ParseMetaGeometry method is:
1115%
anthonyd58e60c2011-03-30 00:24:34 +00001116% MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
cristy272f23a2011-03-30 00:37:11 +00001117% ssize_t *y, size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001118%
1119% A description of each parameter follows:
1120%
anthonyd58e60c2011-03-30 00:24:34 +00001121% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001122%
anthonyd58e60c2011-03-30 00:24:34 +00001123% o x,y: The x and y offset, set according to the geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001124%
cristy272f23a2011-03-30 00:37:11 +00001125% o width,height: The width and height of original image, modified by
1126% the given geometry specification.
cristy3ed852e2009-09-05 21:47:34 +00001127%
1128*/
1129
cristybb503372010-05-27 20:51:26 +00001130static inline size_t MagickMax(const size_t x,
1131 const size_t y)
cristy3ed852e2009-09-05 21:47:34 +00001132{
1133 if (x > y)
1134 return(x);
1135 return(y);
1136}
1137
cristybb503372010-05-27 20:51:26 +00001138MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1139 ssize_t *y,size_t *width,size_t *height)
cristy3ed852e2009-09-05 21:47:34 +00001140{
1141 GeometryInfo
1142 geometry_info;
1143
1144 MagickStatusType
1145 flags;
1146
cristybb503372010-05-27 20:51:26 +00001147 size_t
cristy3ed852e2009-09-05 21:47:34 +00001148 former_height,
1149 former_width;
1150
1151 /*
1152 Ensure the image geometry is valid.
1153 */
cristybb503372010-05-27 20:51:26 +00001154 assert(x != (ssize_t *) NULL);
1155 assert(y != (ssize_t *) NULL);
1156 assert(width != (size_t *) NULL);
1157 assert(height != (size_t *) NULL);
cristy3ed852e2009-09-05 21:47:34 +00001158 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1159 return(NoValue);
1160 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1161 /*
1162 Parse geometry using GetGeometry.
1163 */
1164 SetGeometryInfo(&geometry_info);
1165 former_width=(*width);
1166 former_height=(*height);
1167 flags=GetGeometry(geometry,x,y,width,height);
1168 if ((flags & PercentValue) != 0)
1169 {
1170 MagickStatusType
1171 flags;
1172
1173 PointInfo
1174 scale;
1175
1176 /*
1177 Geometry is a percentage of the image size.
1178 */
1179 flags=ParseGeometry(geometry,&geometry_info);
1180 scale.x=geometry_info.rho;
1181 if ((flags & RhoValue) == 0)
1182 scale.x=100.0;
1183 scale.y=geometry_info.sigma;
1184 if ((flags & SigmaValue) == 0)
1185 scale.y=scale.x;
cristybb503372010-05-27 20:51:26 +00001186 *width=(size_t) floor(scale.x*former_width/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001187 if (*width == 0)
1188 *width=1;
cristybb503372010-05-27 20:51:26 +00001189 *height=(size_t) floor(scale.y*former_height/100.0+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001190 if (*height == 0)
1191 *height=1;
1192 former_width=(*width);
1193 former_height=(*height);
1194 }
cristy07623352011-12-07 13:04:03 +00001195 if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1196 (*height == former_height)))
cristy3ed852e2009-09-05 21:47:34 +00001197 {
cristy07623352011-12-07 13:04:03 +00001198 if ((flags & RhoValue) == 0)
1199 *width=former_width;
1200 if ((flags & SigmaValue) == 0)
1201 *height=former_height;
1202 }
1203 else
1204 {
1205 double
cristy3ed852e2009-09-05 21:47:34 +00001206 scale_factor;
1207
1208 /*
1209 Respect aspect ratio of the image.
1210 */
1211 if ((former_width == 0) || (former_height == 0))
1212 scale_factor=1.0;
1213 else
1214 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1215 {
cristy07623352011-12-07 13:04:03 +00001216 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001217 if ((flags & MinimumValue) == 0)
1218 {
cristy07623352011-12-07 13:04:03 +00001219 if (scale_factor > ((double) *height/(double) former_height))
1220 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001221 }
1222 else
cristy07623352011-12-07 13:04:03 +00001223 if (scale_factor < ((double) *height/(double) former_height))
1224 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001225 }
1226 else
1227 if ((flags & RhoValue) != 0)
1228 {
cristy07623352011-12-07 13:04:03 +00001229 scale_factor=(double) *width/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001230 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001231 (scale_factor < ((double) *width/(double) former_height)))
1232 scale_factor=(double) *width/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001233 }
1234 else
1235 {
cristy07623352011-12-07 13:04:03 +00001236 scale_factor=(double) *height/(double) former_height;
cristy3ed852e2009-09-05 21:47:34 +00001237 if (((flags & MinimumValue) != 0) &&
cristy07623352011-12-07 13:04:03 +00001238 (scale_factor < ((double) *height/(double) former_width)))
1239 scale_factor=(double) *height/(double) former_width;
cristy3ed852e2009-09-05 21:47:34 +00001240 }
cristy272f23a2011-03-30 00:37:11 +00001241 *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1242 *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
cristy3ed852e2009-09-05 21:47:34 +00001243 }
1244 if ((flags & GreaterValue) != 0)
1245 {
1246 if (former_width < *width)
1247 *width=former_width;
1248 if (former_height < *height)
1249 *height=former_height;
1250 }
1251 if ((flags & LessValue) != 0)
1252 {
1253 if (former_width > *width)
1254 *width=former_width;
1255 if (former_height > *height)
1256 *height=former_height;
1257 }
1258 if ((flags & AreaValue) != 0)
1259 {
cristy07623352011-12-07 13:04:03 +00001260 double
cristy3ed852e2009-09-05 21:47:34 +00001261 area,
1262 distance;
1263
1264 PointInfo
1265 scale;
1266
1267 /*
1268 Geometry is a maximum area in pixels.
1269 */
1270 (void) ParseGeometry(geometry,&geometry_info);
1271 area=geometry_info.rho;
1272 distance=sqrt((double) former_width*former_height);
cristye42f6582012-02-11 17:59:50 +00001273 scale.x=(double) former_width/(distance/sqrt((double) area));
1274 scale.y=(double) former_height/(distance/sqrt((double) area));
cristy3ed852e2009-09-05 21:47:34 +00001275 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1276 {
cristy11e0a872012-01-10 16:10:21 +00001277 *width=(size_t) (former_width/(distance/sqrt(area))+0.5);
1278 *height=(size_t) (former_height/(distance/sqrt(area))+0.5);
cristy3ed852e2009-09-05 21:47:34 +00001279 }
1280 former_width=(*width);
1281 former_height=(*height);
1282 }
1283 return(flags);
1284}
1285
1286/*
1287%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1288% %
1289% %
1290% %
1291% P a r s e P a g e G e o m e t r y %
1292% %
1293% %
1294% %
1295%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1296%
1297% ParsePageGeometry() returns a region as defined by the geometry string with
anthonyd58e60c2011-03-30 00:24:34 +00001298% respect to the image page (canvas) dimensions.
1299%
1300% WARNING: Percentage dimensions remain relative to the actual image
1301% dimensions, and not canvas dimensions.
cristy3ed852e2009-09-05 21:47:34 +00001302%
1303% The format of the ParsePageGeometry method is:
1304%
1305% MagickStatusType ParsePageGeometry(const Image *image,
1306% const char *geometry,RectangeInfo *region_info,
1307% ExceptionInfo *exception)
1308%
1309% A description of each parameter follows:
1310%
anthonyd58e60c2011-03-30 00:24:34 +00001311% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001312%
1313% o region_info: the region as defined by the geometry string with
1314% respect to the image and its gravity.
1315%
1316% o exception: return any errors or warnings in this structure.
1317%
1318*/
1319MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1320 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1321{
1322 MagickStatusType
1323 flags;
1324
1325 SetGeometry(image,region_info);
1326 if (image->page.width != 0)
1327 region_info->width=image->page.width;
1328 if (image->page.height != 0)
1329 region_info->height=image->page.height;
1330 flags=ParseAbsoluteGeometry(geometry,region_info);
1331 if (flags == NoValue)
1332 {
1333 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001334 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001335 return(flags);
1336 }
1337 if ((flags & PercentValue) != 0)
1338 {
1339 region_info->width=image->columns;
1340 region_info->height=image->rows;
1341 }
1342 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1343 &region_info->width,&region_info->height);
1344 return(flags);
1345}
1346
1347/*
1348%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1349% %
1350% %
1351% %
1352% P a r s e R e g i o n G e o m e t r y %
1353% %
1354% %
1355% %
1356%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1357%
1358% ParseRegionGeometry() returns a region as defined by the geometry string
1359% with respect to the image dimensions and aspect ratio.
1360%
anthonyd58e60c2011-03-30 00:24:34 +00001361% This is basically a wrapper around ParseMetaGeometry. This is typically
1362% used to parse a geometry string to work out the final integer dimensions
1363% for image resizing.
1364%
cristy3ed852e2009-09-05 21:47:34 +00001365% The format of the ParseRegionGeometry method is:
1366%
1367% MagickStatusType ParseRegionGeometry(const Image *image,
1368% const char *geometry,RectangeInfo *region_info,
1369% ExceptionInfo *exception)
1370%
1371% A description of each parameter follows:
1372%
anthonyd58e60c2011-03-30 00:24:34 +00001373% o geometry: The geometry string (e.g. "100x100+10+10").
cristy3ed852e2009-09-05 21:47:34 +00001374%
1375% o region_info: the region as defined by the geometry string.
1376%
1377% o exception: return any errors or warnings in this structure.
1378%
1379*/
1380MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1381 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1382{
1383 MagickStatusType
1384 flags;
1385
1386 SetGeometry(image,region_info);
1387 flags=ParseMetaGeometry(geometry,&region_info->x,&region_info->y,
1388 &region_info->width,&region_info->height);
1389 if (flags == NoValue)
1390 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
anthonye5b39652012-04-21 05:37:29 +00001391 "InvalidGeometry","'%s'",geometry);
cristy3ed852e2009-09-05 21:47:34 +00001392 return(flags);
1393}
1394
1395/*
1396%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1397% %
1398% %
1399% %
1400% S e t G e o m e t r y %
1401% %
1402% %
1403% %
1404%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1405%
1406% SetGeometry() sets the geometry to its default values.
1407%
1408% The format of the SetGeometry method is:
1409%
1410% SetGeometry(const Image *image,RectangleInfo *geometry)
1411%
1412% A description of each parameter follows:
1413%
1414% o image: the image.
1415%
1416% o geometry: the geometry.
1417%
1418*/
1419MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1420{
1421 assert(image != (Image *) NULL);
1422 assert(image->signature == MagickSignature);
1423 if (image->debug != MagickFalse)
1424 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1425 assert(geometry != (RectangleInfo *) NULL);
1426 (void) ResetMagickMemory(geometry,0,sizeof(*geometry));
1427 geometry->width=image->columns;
1428 geometry->height=image->rows;
1429}
1430
1431/*
1432%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1433% %
1434% %
1435% %
1436% S e t G e o m e t r y I n f o %
1437% %
1438% %
1439% %
1440%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1441%
1442% SetGeometryInfo sets the GeometryInfo structure to its default values.
1443%
1444% The format of the SetGeometryInfo method is:
1445%
1446% SetGeometryInfo(GeometryInfo *geometry_info)
1447%
1448% A description of each parameter follows:
1449%
1450% o geometry_info: the geometry info structure.
1451%
1452*/
1453MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1454{
1455 assert(geometry_info != (GeometryInfo *) NULL);
1456 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1457 (void) ResetMagickMemory(geometry_info,0,sizeof(*geometry_info));
1458}