1 /*
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 % Cristy %
17 % January 2003 %
18 % %
19 % %
20 % Copyright 1999-2016 ImageMagick Studio LLC, a non-profit organization %
21 % 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 */
42 #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/image-private.h"
49 #include "MagickCore/memory_.h"
50 #include "MagickCore/string_.h"
51 #include "MagickCore/string-private.h"
52 #include "MagickCore/token.h"
53
54 /*
55 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
56 % %
57 % %
58 % %
59 % G e t G e o m e t r y %
60 % %
61 % %
62 % %
63 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
64 %
65 % GetGeometry() parses a geometry specification and returns the width,
66 % height, x, and y values. It also returns flags that indicates which
67 % of the four values (width, height, x, y) were located in the string, and
68 % whether the x or y values are negative. In addition, there are flags to
69 % report any meta characters (%, !, <, or >).
70 %
71 % The value must form a proper geometry style specification of WxH+X+Y
72 % of integers only, and values can not be separated by comma, colon, or
73 % slash charcaters. See ParseGeometry() below.
74 %
75 % Offsets may be prefixed by multiple signs to make offset string
76 % substitutions easier to handle from shell scripts.
77 % For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
78 % offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
79 % offsets.
80 %
81 % The format of the GetGeometry method is:
82 %
83 % MagickStatusType GetGeometry(const char *geometry,ssize_t *x,ssize_t *y,
84 % size_t *width,size_t *height)
85 %
86 % A description of each parameter follows:
87 %
88 % o geometry: The geometry.
89 %
90 % o x,y: The x and y offset as determined by the geometry specification.
91 %
92 % o width,height: The width and height as determined by the geometry
93 % specification.
94 %
95 */
GetGeometry(const char * geometry,ssize_t * x,ssize_t * y,size_t * width,size_t * height)96 MagickExport MagickStatusType GetGeometry(const char *geometry,ssize_t *x,
97 ssize_t *y,size_t *width,size_t *height)
98 {
99 char
100 *p,
101 pedantic_geometry[MagickPathExtent],
102 *q;
103
104 double
105 value;
106
107 int
108 c;
109
110 MagickStatusType
111 flags;
112
113 /*
114 Remove whitespace and meta characters from geometry specification.
115 */
116 flags=NoValue;
117 if ((geometry == (char *) NULL) || (*geometry == '\0'))
118 return(flags);
119 if (strlen(geometry) >= (MagickPathExtent-1))
120 return(flags);
121 (void) CopyMagickString(pedantic_geometry,geometry,MagickPathExtent);
122 for (p=pedantic_geometry; *p != '\0'; )
123 {
124 if (isspace((int) ((unsigned char) *p)) != 0)
125 {
126 (void) CopyMagickString(p,p+1,MagickPathExtent);
127 continue;
128 }
129 c=(int)*p;
130 switch (c)
131 {
132 case '%':
133 {
134 flags|=PercentValue;
135 (void) CopyMagickString(p,p+1,MagickPathExtent);
136 break;
137 }
138 case '!':
139 {
140 flags|=AspectValue;
141 (void) CopyMagickString(p,p+1,MagickPathExtent);
142 break;
143 }
144 case '<':
145 {
146 flags|=LessValue;
147 (void) CopyMagickString(p,p+1,MagickPathExtent);
148 break;
149 }
150 case '>':
151 {
152 flags|=GreaterValue;
153 (void) CopyMagickString(p,p+1,MagickPathExtent);
154 break;
155 }
156 case '^':
157 {
158 flags|=MinimumValue;
159 (void) CopyMagickString(p,p+1,MagickPathExtent);
160 break;
161 }
162 case '@':
163 {
164 flags|=AreaValue;
165 (void) CopyMagickString(p,p+1,MagickPathExtent);
166 break;
167 }
168 case '(':
169 case ')':
170 {
171 (void) CopyMagickString(p,p+1,MagickPathExtent);
172 break;
173 }
174 case 'x':
175 case 'X':
176 {
177 flags|=SeparatorValue;
178 p++;
179 break;
180 }
181 case '-':
182 case '.':
183 case ',':
184 case '+':
185 case '0':
186 case '1':
187 case '2':
188 case '3':
189 case '4':
190 case '5':
191 case '6':
192 case '7':
193 case '8':
194 case '9':
195 case 215:
196 case 'e':
197 case 'E':
198 {
199 p++;
200 break;
201 }
202 default:
203 return(flags);
204 }
205 }
206 /*
207 Parse width, height, x, and y.
208 */
209 p=pedantic_geometry;
210 if (*p == '\0')
211 return(flags);
212 q=p;
213 value=StringToDouble(p,&q);
214 (void) value;
215 if (LocaleNCompare(p,"0x",2) == 0)
216 value=(double) strtol(p,&q,10);
217 if ((*p != '+') && (*p != '-'))
218 {
219 c=(int) ((unsigned char) *q);
220 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == '\0'))
221 {
222 /*
223 Parse width.
224 */
225 q=p;
226 if (width != (size_t *) NULL)
227 {
228 if (LocaleNCompare(p,"0x",2) == 0)
229 *width=(size_t) strtol(p,&p,10);
230 else
231 *width=(size_t) floor(StringToDouble(p,&p)+0.5);
232 }
233 if (p != q)
234 flags|=WidthValue;
235 }
236 }
237 if ((*p != '+') && (*p != '-'))
238 {
239 c=(int) ((unsigned char) *p);
240 if ((c == 215) || (*p == 'x') || (*p == 'X'))
241 {
242 p++;
243 if ((*p != '+') && (*p != '-'))
244 {
245 /*
246 Parse height.
247 */
248 q=p;
249 if (height != (size_t *) NULL)
250 *height=(size_t) floor(StringToDouble(p,&p)+0.5);
251 if (p != q)
252 flags|=HeightValue;
253 }
254 }
255 }
256 if ((*p == '+') || (*p == '-'))
257 {
258 /*
259 Parse x value.
260 */
261 while ((*p == '+') || (*p == '-'))
262 {
263 if (*p == '-')
264 flags^=XNegative; /* negate sign */
265 p++;
266 }
267 q=p;
268 if (x != (ssize_t *) NULL)
269 *x=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
270 if (p != q)
271 {
272 flags|=XValue;
273 if (((flags & XNegative) != 0) && (x != (ssize_t *) NULL))
274 *x=(-*x);
275 }
276 }
277 if ((*p == '+') || (*p == '-'))
278 {
279 /*
280 Parse y value.
281 */
282 while ((*p == '+') || (*p == '-'))
283 {
284 if (*p == '-')
285 flags^=YNegative; /* negate sign */
286 p++;
287 }
288 q=p;
289 if (y != (ssize_t *) NULL)
290 *y=(ssize_t) ceil(StringToDouble(p,&p)-0.5);
291 if (p != q)
292 {
293 flags|=YValue;
294 if (((flags & YNegative) != 0) && (y != (ssize_t *) NULL))
295 *y=(-*y);
296 }
297 }
298 if ((flags & PercentValue) != 0)
299 {
300 if (((flags & SeparatorValue) == 0) && ((flags & HeightValue) == 0))
301 {
302 if ((height != (size_t *) NULL) && (width != (size_t *) NULL))
303 *height=(*width);
304 flags|=HeightValue;
305 }
306 if (((flags & SeparatorValue) != 0) && ((flags & WidthValue) == 0) &&
307 (height != (size_t *) NULL) && (width != (size_t *) NULL))
308 *width=(*height);
309 }
310 #if 0
311 /* Debugging Geometry */
312 (void) fprintf(stderr,"GetGeometry...\n");
313 (void) fprintf(stderr,"Input: %s\n",geometry);
314 (void) fprintf(stderr,"Flags: %c %c %s %s\n",
315 (flags & WidthValue) ? 'W' : ' ',(flags & HeightValue) ? 'H' : ' ',
316 (flags & XValue) ? ((flags & XNegative) ? "-X" : "+X") : " ",
317 (flags & YValue) ? ((flags & YNegative) ? "-Y" : "+Y") : " ");
318 (void) fprintf(stderr,"Geometry: %ldx%ld%+ld%+ld\n",(long) *width,(long)
319 *height,(long) *x,(long) *y);
320 #endif
321 return(flags);
322 }
323
324 /*
325 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
326 % %
327 % %
328 % %
329 % G e t P a g e G e o m e t r y %
330 % %
331 % %
332 % %
333 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
334 %
335 % GetPageGeometry() replaces any page mneumonic with the equivalent size in
336 % picas.
337 %
338 % The format of the GetPageGeometry method is:
339 %
340 % char *GetPageGeometry(const char *page_geometry)
341 %
342 % A description of each parameter follows.
343 %
344 % o page_geometry: Specifies a pointer to an array of characters. The
345 % string is either a Postscript page name (e.g. A4) or a postscript page
346 % geometry (e.g. 612x792+36+36).
347 %
348 */
GetPageGeometry(const char * page_geometry)349 MagickExport char *GetPageGeometry(const char *page_geometry)
350 {
351 #define MagickPageSize(name,geometry) { (name), sizeof(name)-1, (geometry) }
352
353 typedef struct _PageInfo
354 {
355 const char
356 *name;
357
358 size_t
359 extent;
360
361 const char
362 *geometry;
363 } PageInfo;
364
365 static const PageInfo
366 PageSizes[] =
367 {
368 MagickPageSize("4x6", "288x432"),
369 MagickPageSize("5x7", "360x504"),
370 MagickPageSize("7x9", "504x648"),
371 MagickPageSize("8x10", "576x720"),
372 MagickPageSize("9x11", "648x792"),
373 MagickPageSize("9x12", "648x864"),
374 MagickPageSize("10x13", "720x936"),
375 MagickPageSize("10x14", "720x1008"),
376 MagickPageSize("11x17", "792x1224"),
377 MagickPageSize("a0", "2384x3370"),
378 MagickPageSize("a1", "1684x2384"),
379 MagickPageSize("a10", "73x105"),
380 MagickPageSize("a2", "1191x1684"),
381 MagickPageSize("a3", "842x1191"),
382 MagickPageSize("a4", "595x842"),
383 MagickPageSize("a4small", "595x842"),
384 MagickPageSize("a5", "420x595"),
385 MagickPageSize("a6", "297x420"),
386 MagickPageSize("a7", "210x297"),
387 MagickPageSize("a8", "148x210"),
388 MagickPageSize("a9", "105x148"),
389 MagickPageSize("archa", "648x864"),
390 MagickPageSize("archb", "864x1296"),
391 MagickPageSize("archC", "1296x1728"),
392 MagickPageSize("archd", "1728x2592"),
393 MagickPageSize("arche", "2592x3456"),
394 MagickPageSize("b0", "2920x4127"),
395 MagickPageSize("b1", "2064x2920"),
396 MagickPageSize("b10", "91x127"),
397 MagickPageSize("b2", "1460x2064"),
398 MagickPageSize("b3", "1032x1460"),
399 MagickPageSize("b4", "729x1032"),
400 MagickPageSize("b5", "516x729"),
401 MagickPageSize("b6", "363x516"),
402 MagickPageSize("b7", "258x363"),
403 MagickPageSize("b8", "181x258"),
404 MagickPageSize("b9", "127x181"),
405 MagickPageSize("c0", "2599x3676"),
406 MagickPageSize("c1", "1837x2599"),
407 MagickPageSize("c2", "1298x1837"),
408 MagickPageSize("c3", "918x1296"),
409 MagickPageSize("c4", "649x918"),
410 MagickPageSize("c5", "459x649"),
411 MagickPageSize("c6", "323x459"),
412 MagickPageSize("c7", "230x323"),
413 MagickPageSize("executive", "540x720"),
414 MagickPageSize("flsa", "612x936"),
415 MagickPageSize("flse", "612x936"),
416 MagickPageSize("folio", "612x936"),
417 MagickPageSize("halfletter", "396x612"),
418 MagickPageSize("isob0", "2835x4008"),
419 MagickPageSize("isob1", "2004x2835"),
420 MagickPageSize("isob10", "88x125"),
421 MagickPageSize("isob2", "1417x2004"),
422 MagickPageSize("isob3", "1001x1417"),
423 MagickPageSize("isob4", "709x1001"),
424 MagickPageSize("isob5", "499x709"),
425 MagickPageSize("isob6", "354x499"),
426 MagickPageSize("isob7", "249x354"),
427 MagickPageSize("isob8", "176x249"),
428 MagickPageSize("isob9", "125x176"),
429 MagickPageSize("jisb0", "1030x1456"),
430 MagickPageSize("jisb1", "728x1030"),
431 MagickPageSize("jisb2", "515x728"),
432 MagickPageSize("jisb3", "364x515"),
433 MagickPageSize("jisb4", "257x364"),
434 MagickPageSize("jisb5", "182x257"),
435 MagickPageSize("jisb6", "128x182"),
436 MagickPageSize("ledger", "1224x792"),
437 MagickPageSize("legal", "612x1008"),
438 MagickPageSize("letter", "612x792"),
439 MagickPageSize("lettersmall", "612x792"),
440 MagickPageSize("quarto", "610x780"),
441 MagickPageSize("statement", "396x612"),
442 MagickPageSize("tabloid", "792x1224")
443 };
444
445 char
446 page[MaxTextExtent];
447
448 register ssize_t
449 i;
450
451 assert(page_geometry != (char *) NULL);
452 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",page_geometry);
453 CopyMagickString(page,page_geometry,MaxTextExtent);
454 for (i=0; i < (ssize_t) (sizeof(PageSizes)/sizeof(PageSizes[0])); i++)
455 {
456 int
457 status;
458
459 status=LocaleNCompare(PageSizes[i].name,page_geometry,PageSizes[i].extent);
460 if (status == 0)
461 {
462 MagickStatusType
463 flags;
464
465 RectangleInfo
466 geometry;
467
468 /*
469 Replace mneumonic with the equivalent size in dots-per-inch.
470 */
471 (void) FormatLocaleString(page,MaxTextExtent,"%s%.80s",
472 PageSizes[i].geometry,page_geometry+PageSizes[i].extent);
473 flags=GetGeometry(page,&geometry.x,&geometry.y,&geometry.width,
474 &geometry.height);
475 if ((flags & GreaterValue) == 0)
476 (void) ConcatenateMagickString(page,">",MaxTextExtent);
477 break;
478 }
479 }
480 return(AcquireString(page));
481 }
482
483 /*
484 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
485 % %
486 % %
487 % %
488 % G r a v i t y A d j u s t G e o m e t r y %
489 % %
490 % %
491 % %
492 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
493 %
494 % GravityAdjustGeometry() adjusts the offset of a region with regard to the
495 % given: width, height and gravity; against which it is positioned.
496 %
497 % The region should also have an appropriate width and height to correctly
498 % set the right offset of the top left corner of the region.
499 %
500 % The format of the GravityAdjustGeometry method is:
501 %
502 % void GravityAdjustGeometry(const size_t width, const size_t height,
503 % const GravityType gravity,RectangleInfo *region);
504 %
505 % A description of each parameter follows:
506 %
507 % o width, height: the larger area the region is relative to
508 %
509 % o gravity: the edge/corner the current offset is relative to
510 %
511 % o region: The region requiring a offset adjustment relative to gravity
512 %
513 */
GravityAdjustGeometry(const size_t width,const size_t height,const GravityType gravity,RectangleInfo * region)514 MagickExport void GravityAdjustGeometry(const size_t width,
515 const size_t height,const GravityType gravity,RectangleInfo *region)
516 {
517 if (region->height == 0)
518 region->height=height;
519 if (region->width == 0)
520 region->width=width;
521 switch (gravity)
522 {
523 case NorthEastGravity:
524 case EastGravity:
525 case SouthEastGravity:
526 {
527 region->x=(ssize_t) (width-region->width-region->x);
528 break;
529 }
530 case NorthGravity:
531 case SouthGravity:
532 case CenterGravity:
533 {
534 region->x+=(ssize_t) (width/2-region->width/2);
535 break;
536 }
537 case ForgetGravity:
538 case NorthWestGravity:
539 case WestGravity:
540 case SouthWestGravity:
541 default:
542 break;
543 }
544 switch (gravity)
545 {
546 case SouthWestGravity:
547 case SouthGravity:
548 case SouthEastGravity:
549 {
550 region->y=(ssize_t) (height-region->height-region->y);
551 break;
552 }
553 case EastGravity:
554 case WestGravity:
555 case CenterGravity:
556 {
557 region->y+=(ssize_t) (height/2-region->height/2);
558 break;
559 }
560 case ForgetGravity:
561 case NorthWestGravity:
562 case NorthGravity:
563 case NorthEastGravity:
564 default:
565 break;
566 }
567 return;
568 }
569
570 /*
571 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
572 % %
573 % %
574 % %
575 + I s G e o m e t r y %
576 % %
577 % %
578 % %
579 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
580 %
581 % IsGeometry() returns MagickTrue if the geometry specification is valid.
582 % Examples are 100, 100x200, x200, 100x200+10+20, +10+20, 200%, 200x200!, etc.
583 %
584 % The format of the IsGeometry method is:
585 %
586 % MagickBooleanType IsGeometry(const char *geometry)
587 %
588 % A description of each parameter follows:
589 %
590 % o geometry: This string is the geometry specification.
591 %
592 */
IsGeometry(const char * geometry)593 MagickExport MagickBooleanType IsGeometry(const char *geometry)
594 {
595 GeometryInfo
596 geometry_info;
597
598 MagickStatusType
599 flags;
600
601 if (geometry == (const char *) NULL)
602 return(MagickFalse);
603 flags=ParseGeometry(geometry,&geometry_info);
604 return(flags != NoValue ? MagickTrue : MagickFalse);
605 }
606
607 /*
608 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
609 % %
610 % %
611 % %
612 + I s S c e n e G e o m e t r y %
613 % %
614 % %
615 % %
616 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
617 %
618 % IsSceneGeometry() returns MagickTrue if the geometry is a valid scene
619 % specification (e.g. [1], [1-9], [1,7,4]).
620 %
621 % The format of the IsSceneGeometry method is:
622 %
623 % MagickBooleanType IsSceneGeometry(const char *geometry,
624 % const MagickBooleanType pedantic)
625 %
626 % A description of each parameter follows:
627 %
628 % o geometry: This string is the geometry specification.
629 %
630 % o pedantic: A value other than 0 invokes a more restrictive set of
631 % conditions for a valid specification (e.g. [1], [1-4], [4-1]).
632 %
633 */
IsSceneGeometry(const char * geometry,const MagickBooleanType pedantic)634 MagickExport MagickBooleanType IsSceneGeometry(const char *geometry,
635 const MagickBooleanType pedantic)
636 {
637 char
638 *p;
639
640 double
641 value;
642
643 if (geometry == (const char *) NULL)
644 return(MagickFalse);
645 p=(char *) geometry;
646 value=StringToDouble(geometry,&p);
647 (void) value;
648 if (p == geometry)
649 return(MagickFalse);
650 if (strspn(geometry,"0123456789-, ") != strlen(geometry))
651 return(MagickFalse);
652 if ((pedantic != MagickFalse) && (strchr(geometry,',') != (char *) NULL))
653 return(MagickFalse);
654 return(MagickTrue);
655 }
656
657 /*
658 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
659 % %
660 % %
661 % %
662 % P a r s e A b s o l u t e G e o m e t r y %
663 % %
664 % %
665 % %
666 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
667 %
668 % ParseAbsoluteGeometry() returns a region as defined by the geometry string,
669 % without any modification by percentages or gravity.
670 %
671 % It currently just a wrapper around GetGeometry(), but may be expanded in
672 % the future to handle other positioning information.
673 %
674 % The format of the ParseAbsoluteGeometry method is:
675 %
676 % MagickStatusType ParseAbsoluteGeometry(const char *geometry,
677 % RectangleInfo *region_info)
678 %
679 % A description of each parameter follows:
680 %
681 % o geometry: The geometry string (e.g. "100x100+10+10").
682 %
683 % o region_info: the region as defined by the geometry string.
684 %
685 */
ParseAbsoluteGeometry(const char * geometry,RectangleInfo * region_info)686 MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry,
687 RectangleInfo *region_info)
688 {
689 MagickStatusType
690 flags;
691
692 flags=GetGeometry(geometry,®ion_info->x,®ion_info->y,
693 ®ion_info->width,®ion_info->height);
694 return(flags);
695 }
696
697 /*
698 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
699 % %
700 % %
701 % %
702 % P a r s e A f f i n e G e o m e t r y %
703 % %
704 % %
705 % %
706 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
707 %
708 % ParseAffineGeometry() returns an affine matrix as defined by a string of 4
709 % to 6 comma/space separated floating point values.
710 %
711 % The affine matrix determinant is checked for validity of the values.
712 %
713 % The format of the ParseAffineGeometry method is:
714 %
715 % MagickStatusType ParseAffineGeometry(const char *geometry,
716 % AffineMatrix *affine_matrix,ExceptionInfo *exception)
717 %
718 % A description of each parameter follows:
719 %
720 % o geometry: The geometry string (e.g. "1.0,0.0,0.0,1.0,3.2,1.2").
721 %
722 % o affine_matrix: the affine matrix as defined by the geometry string.
723 %
724 % o exception: return any errors or warnings in this structure.
725 %
726 */
ParseAffineGeometry(const char * geometry,AffineMatrix * affine_matrix,ExceptionInfo * exception)727 MagickExport MagickStatusType ParseAffineGeometry(const char *geometry,
728 AffineMatrix *affine_matrix,ExceptionInfo *exception)
729 {
730 char
731 token[MagickPathExtent];
732
733 const char
734 *p;
735
736 double
737 determinant;
738
739 MagickStatusType
740 flags;
741
742 register ssize_t
743 i;
744
745 GetAffineMatrix(affine_matrix);
746 flags=NoValue;
747 p=(char *) geometry;
748 for (i=0; (*p != '\0') && (i < 6); i++)
749 {
750 GetNextToken(p,&p,MagickPathExtent,token);
751 if (*token == ',')
752 GetNextToken(p,&p,MagickPathExtent,token);
753 switch (i)
754 {
755 case 0:
756 {
757 affine_matrix->sx=StringToDouble(token,(char **) NULL);
758 break;
759 }
760 case 1:
761 {
762 affine_matrix->rx=StringToDouble(token,(char **) NULL);
763 break;
764 }
765 case 2:
766 {
767 affine_matrix->ry=StringToDouble(token,(char **) NULL);
768 break;
769 }
770 case 3:
771 {
772 affine_matrix->sy=StringToDouble(token,(char **) NULL);
773 break;
774 }
775 case 4:
776 {
777 affine_matrix->tx=StringToDouble(token,(char **) NULL);
778 flags|=XValue;
779 break;
780 }
781 case 5:
782 {
783 affine_matrix->ty=StringToDouble(token,(char **) NULL);
784 flags|=YValue;
785 break;
786 }
787 }
788 }
789 determinant=(affine_matrix->sx*affine_matrix->sy-affine_matrix->rx*
790 affine_matrix->ry);
791 if (fabs(determinant) < MagickEpsilon)
792 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
793 "InvalidArgument","'%s' : 'Indeterminate Matrix'",geometry);
794 return(flags);
795 }
796
797 /*
798 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
799 % %
800 % %
801 % %
802 % P a r s e G e o m e t r y %
803 % %
804 % %
805 % %
806 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
807 %
808 % ParseGeometry() parses a geometry specification and returns the sigma,
809 % rho, xi, and psi values. It also returns flags that indicates which
810 % of the four values (sigma, rho, xi, psi) were located in the string, and
811 % whether the xi or pi values are negative.
812 %
813 % In addition, it reports if there are any of meta characters (%, !, <, >, @,
814 % and ^) flags present. It does not report the location of the percentage
815 % relative to the values.
816 %
817 % Values may also be separated by commas, colons, or slashes, and offsets.
818 % Offsets may be prefixed by multiple signs to make offset string
819 % substitutions easier to handle from shell scripts.
820 % For example: "-10-10", "-+10-+10", or "+-10+-10" will generate negtive
821 % offsets, while "+10+10", "++10++10", or "--10--10" will generate positive
822 % offsets.
823 %
824 % The format of the ParseGeometry method is:
825 %
826 % MagickStatusType ParseGeometry(const char *geometry,
827 % GeometryInfo *geometry_info)
828 %
829 % A description of each parameter follows:
830 %
831 % o geometry: The geometry string (e.g. "100x100+10+10").
832 %
833 % o geometry_info: returns the parsed width/height/x/y in this structure.
834 %
835 */
ParseGeometry(const char * geometry,GeometryInfo * geometry_info)836 MagickExport MagickStatusType ParseGeometry(const char *geometry,
837 GeometryInfo *geometry_info)
838 {
839 char
840 *p,
841 pedantic_geometry[MagickPathExtent],
842 *q;
843
844 double
845 value;
846
847 GeometryInfo
848 coordinate;
849
850 int
851 c;
852
853 MagickStatusType
854 flags;
855
856 /*
857 Remove whitespaces meta characters from geometry specification.
858 */
859 assert(geometry_info != (GeometryInfo *) NULL);
860 flags=NoValue;
861 if ((geometry == (char *) NULL) || (*geometry == '\0'))
862 return(flags);
863 if (strlen(geometry) >= (MagickPathExtent-1))
864 return(flags);
865 c=sscanf(geometry,"%lf%*[ ,]%lf%*[ ,]%lf%*[ ,]%lf",&coordinate.rho,
866 &coordinate.sigma,&coordinate.xi,&coordinate.psi);
867 if (c == 4)
868 {
869 /*
870 Special case: coordinate (e.g. 0,0 255,255).
871 */
872 geometry_info->rho=coordinate.rho;
873 geometry_info->sigma=coordinate.sigma;
874 geometry_info->xi=coordinate.xi;
875 geometry_info->psi=coordinate.psi;
876 flags|=RhoValue | SigmaValue | XiValue | PsiValue;
877 return(flags);
878 }
879 (void) CopyMagickString(pedantic_geometry,geometry,MagickPathExtent);
880 for (p=pedantic_geometry; *p != '\0'; )
881 {
882 c=(int) ((unsigned char) *p);
883 if (isspace(c) != 0)
884 {
885 (void) CopyMagickString(p,p+1,MagickPathExtent);
886 continue;
887 }
888 switch (c)
889 {
890 case '%':
891 {
892 flags|=PercentValue;
893 (void) CopyMagickString(p,p+1,MagickPathExtent);
894 break;
895 }
896 case '!':
897 {
898 flags|=AspectValue;
899 (void) CopyMagickString(p,p+1,MagickPathExtent);
900 break;
901 }
902 case '<':
903 {
904 flags|=LessValue;
905 (void) CopyMagickString(p,p+1,MagickPathExtent);
906 break;
907 }
908 case '>':
909 {
910 flags|=GreaterValue;
911 (void) CopyMagickString(p,p+1,MagickPathExtent);
912 break;
913 }
914 case '^':
915 {
916 flags|=MinimumValue;
917 (void) CopyMagickString(p,p+1,MagickPathExtent);
918 break;
919 }
920 case '@':
921 {
922 flags|=AreaValue;
923 (void) CopyMagickString(p,p+1,MagickPathExtent);
924 break;
925 }
926 case '(':
927 case ')':
928 {
929 (void) CopyMagickString(p,p+1,MagickPathExtent);
930 break;
931 }
932 case 'x':
933 case 'X':
934 {
935 flags|=SeparatorValue;
936 p++;
937 break;
938 }
939 case '-':
940 case '+':
941 case ',':
942 case '0':
943 case '1':
944 case '2':
945 case '3':
946 case '4':
947 case '5':
948 case '6':
949 case '7':
950 case '8':
951 case '9':
952 case '/':
953 case ':':
954 case 215:
955 case 'e':
956 case 'E':
957 {
958 p++;
959 break;
960 }
961 case '.':
962 {
963 p++;
964 flags|=DecimalValue;
965 break;
966 }
967 default:
968 return(NoValue);
969 }
970 }
971 /*
972 Parse rho, sigma, xi, psi, and optionally chi.
973 */
974 p=pedantic_geometry;
975 if (*p == '\0')
976 return(flags);
977 q=p;
978 value=StringToDouble(p,&q);
979 if (LocaleNCompare(p,"0x",2) == 0)
980 (void) strtol(p,&q,10);
981 c=(int) ((unsigned char) *q);
982 if ((c == 215) || (*q == 'x') || (*q == 'X') || (*q == ',') ||
983 (*q == '/') || (*q == ':') || (*q =='\0'))
984 {
985 /*
986 Parse rho.
987 */
988 q=p;
989 if (LocaleNCompare(p,"0x",2) == 0)
990 value=(double) strtol(p,&p,10);
991 else
992 value=StringToDouble(p,&p);
993 if (p != q)
994 {
995 flags|=RhoValue;
996 geometry_info->rho=value;
997 }
998 }
999 q=p;
1000 c=(int) ((unsigned char) *p);
1001 if ((c == 215) || (*p == 'x') || (*p == 'X') || (*p == ',') || (*p == '/') ||
1002 (*p == ':'))
1003 {
1004 /*
1005 Parse sigma.
1006 */
1007 p++;
1008 while (isspace((int) ((unsigned char) *p)) != 0)
1009 p++;
1010 c=(int) ((unsigned char) *q);
1011 if (((c != 215) && (*q != 'x') && (*q != 'X')) || ((*p != '+') &&
1012 (*p != '-')))
1013 {
1014 q=p;
1015 value=StringToDouble(p,&p);
1016 if (p != q)
1017 {
1018 flags|=SigmaValue;
1019 geometry_info->sigma=value;
1020 }
1021 }
1022 }
1023 while (isspace((int) ((unsigned char) *p)) != 0)
1024 p++;
1025 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') || (*p == ':'))
1026 {
1027 /*
1028 Parse xi value.
1029 */
1030 if ((*p == ',') || (*p == '/') || (*p == ':') )
1031 p++;
1032 while ((*p == '+') || (*p == '-'))
1033 {
1034 if (*p == '-')
1035 flags^=XiNegative; /* negate sign */
1036 p++;
1037 }
1038 q=p;
1039 value=StringToDouble(p,&p);
1040 if (p != q)
1041 {
1042 flags|=XiValue;
1043 if ((flags & XiNegative) != 0)
1044 value=(-value);
1045 geometry_info->xi=value;
1046 }
1047 while (isspace((int) ((unsigned char) *p)) != 0)
1048 p++;
1049 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1050 (*p == ':'))
1051 {
1052 /*
1053 Parse psi value.
1054 */
1055 if ((*p == ',') || (*p == '/') || (*p == ':'))
1056 p++;
1057 while ((*p == '+') || (*p == '-'))
1058 {
1059 if (*p == '-')
1060 flags^=PsiNegative; /* negate sign */
1061 p++;
1062 }
1063 q=p;
1064 value=StringToDouble(p,&p);
1065 if (p != q)
1066 {
1067 flags|=PsiValue;
1068 if ((flags & PsiNegative) != 0)
1069 value=(-value);
1070 geometry_info->psi=value;
1071 }
1072 }
1073 while (isspace((int) ((unsigned char) *p)) != 0)
1074 p++;
1075 if ((*p == '+') || (*p == '-') || (*p == ',') || (*p == '/') ||
1076 (*p == ':'))
1077 {
1078 /*
1079 Parse chi value.
1080 */
1081 if ((*p == ',') || (*p == '/') || (*p == ':'))
1082 p++;
1083 while ((*p == '+') || (*p == '-'))
1084 {
1085 if (*p == '-')
1086 flags^=ChiNegative; /* negate sign */
1087 p++;
1088 }
1089 q=p;
1090 value=StringToDouble(p,&p);
1091 if (p != q)
1092 {
1093 flags|=ChiValue;
1094 if ((flags & ChiNegative) != 0)
1095 value=(-value);
1096 geometry_info->chi=value;
1097 }
1098 }
1099 }
1100 if (strchr(pedantic_geometry,':') != (char *) NULL)
1101 {
1102 /*
1103 Normalize sampling factor (e.g. 4:2:2 => 2x1).
1104 */
1105 geometry_info->rho/=geometry_info->sigma;
1106 geometry_info->sigma=1.0;
1107 if (geometry_info->xi == 0.0)
1108 geometry_info->sigma=2.0;
1109 }
1110 if (((flags & SigmaValue) == 0) && ((flags & XiValue) != 0) &&
1111 ((flags & PsiValue) == 0))
1112 {
1113 /*
1114 Support negative height values (e.g. 30x-20).
1115 */
1116 geometry_info->sigma=geometry_info->xi;
1117 geometry_info->xi=0.0;
1118 flags|=SigmaValue;
1119 flags&=(~XiValue);
1120 }
1121 if ((flags & PercentValue) != 0)
1122 {
1123 if (((flags & SeparatorValue) == 0) && ((flags & SigmaValue) == 0))
1124 geometry_info->sigma=geometry_info->rho;
1125 if (((flags & SeparatorValue) != 0) && ((flags & RhoValue) == 0))
1126 geometry_info->rho=geometry_info->sigma;
1127 }
1128 #if 0
1129 /* Debugging Geometry */
1130 (void) fprintf(stderr,"ParseGeometry...\n");
1131 (void) fprintf(stderr,"Flags: %c %c %s %s %s\n",
1132 (flags & RhoValue) ? 'W' : ' ',(flags & SigmaValue) ? 'H' : ' ',
1133 (flags & XiValue) ? ((flags & XiNegative) ? "-X" : "+X") : " ",
1134 (flags & PsiValue) ? ((flags & PsiNegative) ? "-Y" : "+Y") : " ",
1135 (flags & ChiValue) ? ((flags & ChiNegative) ? "-Z" : "+Z") : " ");
1136 (void) fprintf(stderr,"Geometry: %lg,%lg,%lg,%lg,%lg\n",geometry_info->rho,
1137 geometry_info->sigma,geometry_info->xi,geometry_info->psi,
1138 geometry_info->chi);
1139 #endif
1140 return(flags);
1141 }
1142
1143 /*
1144 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1145 % %
1146 % %
1147 % %
1148 % P a r s e G r a v i t y G e o m e t r y %
1149 % %
1150 % %
1151 % %
1152 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1153 %
1154 % ParseGravityGeometry() returns a region as defined by the geometry string
1155 % with respect to the given image page (canvas) dimensions and the images
1156 % gravity setting.
1157 %
1158 % This is typically used for specifing a area within a given image for
1159 % cropping images to a smaller size, chopping out rows and or columns, or
1160 % resizing and positioning overlay images.
1161 %
1162 % Percentages are relative to image size and not page size, and are set to
1163 % nearest integer (pixel) size.
1164 %
1165 % The format of the ParseGravityGeometry method is:
1166 %
1167 % MagickStatusType ParseGravityGeometry(Image *image,const char *geometry,
1168 % RectangeInfo *region_info,ExceptionInfo *exception)
1169 %
1170 % A description of each parameter follows:
1171 %
1172 % o geometry: The geometry string (e.g. "100x100+10+10").
1173 %
1174 % o region_info: the region as defined by the geometry string with respect
1175 % to the image dimensions and its gravity.
1176 %
1177 % o exception: return any errors or warnings in this structure.
1178 %
1179 */
ParseGravityGeometry(const Image * image,const char * geometry,RectangleInfo * region_info,ExceptionInfo * exception)1180 MagickExport MagickStatusType ParseGravityGeometry(const Image *image,
1181 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1182 {
1183 MagickStatusType
1184 flags;
1185
1186 size_t
1187 height,
1188 width;
1189
1190 SetGeometry(image,region_info);
1191 if (image->page.width != 0)
1192 region_info->width=image->page.width;
1193 if (image->page.height != 0)
1194 region_info->height=image->page.height;
1195 flags=ParseAbsoluteGeometry(geometry,region_info);
1196 if (flags == NoValue)
1197 {
1198 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1199 "InvalidGeometry","`%s'",geometry);
1200 return(flags);
1201 }
1202 if ((flags & PercentValue) != 0)
1203 {
1204 GeometryInfo
1205 geometry_info;
1206
1207 MagickStatusType
1208 status;
1209
1210 PointInfo
1211 scale;
1212
1213 /*
1214 Geometry is a percentage of the image size, not canvas size
1215 */
1216 if (image->gravity != UndefinedGravity)
1217 flags|=XValue | YValue;
1218 status=ParseGeometry(geometry,&geometry_info);
1219 scale.x=geometry_info.rho;
1220 if ((status & RhoValue) == 0)
1221 scale.x=100.0;
1222 scale.y=geometry_info.sigma;
1223 if ((status & SigmaValue) == 0)
1224 scale.y=scale.x;
1225 region_info->width=(size_t) floor((scale.x*image->columns/100.0)+0.5);
1226 region_info->height=(size_t) floor((scale.y*image->rows/100.0)+0.5);
1227 }
1228 /*
1229 Adjust offset according to gravity setting.
1230 */
1231 width=region_info->width;
1232 height=region_info->height;
1233 if (width == 0)
1234 region_info->width=image->page.width | image->columns;
1235 if (height == 0)
1236 region_info->height=image->page.height | image->rows;
1237 GravityAdjustGeometry(image->columns,image->rows,image->gravity,region_info);
1238 region_info->width=width;
1239 region_info->height=height;
1240 return(flags);
1241 }
1242
1243 /*
1244 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1245 % %
1246 % %
1247 % %
1248 + P a r s e M e t a G e o m e t r y %
1249 % %
1250 % %
1251 % %
1252 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1253 %
1254 % ParseMetaGeometry() is similar to GetGeometry() except the returned
1255 % geometry is modified as determined by the meta characters: %, !, <, >, @,
1256 % and ^ in relation to image resizing.
1257 %
1258 % Final image dimensions are adjusted so as to preserve the aspect ratio as
1259 % much as possible, while generating a integer (pixel) size, and fitting the
1260 % image within the specified geometry width and height.
1261 %
1262 % Flags are interpreted...
1263 % % geometry size is given percentage of original width and height given
1264 % ! do not try to preserve aspect ratio
1265 % < only enlarge images smaller that geometry
1266 % > only shrink images larger than geometry
1267 % @ Fit image to contain at most this many pixels
1268 % ^ Contain the given geometry given, (minimal dimensions given)
1269 %
1270 % The format of the ParseMetaGeometry method is:
1271 %
1272 % MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1273 % ssize_t *y, size_t *width,size_t *height)
1274 %
1275 % A description of each parameter follows:
1276 %
1277 % o geometry: The geometry string (e.g. "100x100+10+10").
1278 %
1279 % o x,y: The x and y offset, set according to the geometry specification.
1280 %
1281 % o width,height: The width and height of original image, modified by
1282 % the given geometry specification.
1283 %
1284 */
1285
ParseMetaGeometry(const char * geometry,ssize_t * x,ssize_t * y,size_t * width,size_t * height)1286 MagickExport MagickStatusType ParseMetaGeometry(const char *geometry,ssize_t *x,
1287 ssize_t *y,size_t *width,size_t *height)
1288 {
1289 GeometryInfo
1290 geometry_info;
1291
1292 MagickStatusType
1293 flags;
1294
1295 size_t
1296 former_height,
1297 former_width;
1298
1299 /*
1300 Ensure the image geometry is valid.
1301 */
1302 assert(x != (ssize_t *) NULL);
1303 assert(y != (ssize_t *) NULL);
1304 assert(width != (size_t *) NULL);
1305 assert(height != (size_t *) NULL);
1306 if ((geometry == (char *) NULL) || (*geometry == '\0'))
1307 return(NoValue);
1308 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",geometry);
1309 /*
1310 Parse geometry using GetGeometry.
1311 */
1312 SetGeometryInfo(&geometry_info);
1313 former_width=(*width);
1314 former_height=(*height);
1315 flags=GetGeometry(geometry,x,y,width,height);
1316 if ((flags & PercentValue) != 0)
1317 {
1318 MagickStatusType
1319 flags;
1320
1321 PointInfo
1322 scale;
1323
1324 /*
1325 Geometry is a percentage of the image size.
1326 */
1327 flags=ParseGeometry(geometry,&geometry_info);
1328 scale.x=geometry_info.rho;
1329 if ((flags & RhoValue) == 0)
1330 scale.x=100.0;
1331 scale.y=geometry_info.sigma;
1332 if ((flags & SigmaValue) == 0)
1333 scale.y=scale.x;
1334 *width=(size_t) floor(scale.x*former_width/100.0+0.5);
1335 *height=(size_t) floor(scale.y*former_height/100.0+0.5);
1336 former_width=(*width);
1337 former_height=(*height);
1338 }
1339 if (((flags & AspectValue) != 0) || ((*width == former_width) &&
1340 (*height == former_height)))
1341 {
1342 if ((flags & RhoValue) == 0)
1343 *width=former_width;
1344 if ((flags & SigmaValue) == 0)
1345 *height=former_height;
1346 }
1347 else
1348 {
1349 double
1350 scale_factor;
1351
1352 /*
1353 Respect aspect ratio of the image.
1354 */
1355 if ((former_width == 0) || (former_height == 0))
1356 scale_factor=1.0;
1357 else
1358 if (((flags & RhoValue) != 0) && (flags & SigmaValue) != 0)
1359 {
1360 scale_factor=(double) *width/(double) former_width;
1361 if ((flags & MinimumValue) == 0)
1362 {
1363 if (scale_factor > ((double) *height/(double) former_height))
1364 scale_factor=(double) *height/(double) former_height;
1365 }
1366 else
1367 if (scale_factor < ((double) *height/(double) former_height))
1368 scale_factor=(double) *height/(double) former_height;
1369 }
1370 else
1371 if ((flags & RhoValue) != 0)
1372 {
1373 scale_factor=(double) *width/(double) former_width;
1374 if (((flags & MinimumValue) != 0) &&
1375 (scale_factor < ((double) *width/(double) former_height)))
1376 scale_factor=(double) *width/(double) former_height;
1377 }
1378 else
1379 {
1380 scale_factor=(double) *height/(double) former_height;
1381 if (((flags & MinimumValue) != 0) &&
1382 (scale_factor < ((double) *height/(double) former_width)))
1383 scale_factor=(double) *height/(double) former_width;
1384 }
1385 *width=MagickMax((size_t) floor(scale_factor*former_width+0.5),1UL);
1386 *height=MagickMax((size_t) floor(scale_factor*former_height+0.5),1UL);
1387 }
1388 if ((flags & GreaterValue) != 0)
1389 {
1390 if (former_width < *width)
1391 *width=former_width;
1392 if (former_height < *height)
1393 *height=former_height;
1394 }
1395 if ((flags & LessValue) != 0)
1396 {
1397 if (former_width > *width)
1398 *width=former_width;
1399 if (former_height > *height)
1400 *height=former_height;
1401 }
1402 if ((flags & AreaValue) != 0)
1403 {
1404 double
1405 area,
1406 distance;
1407
1408 PointInfo
1409 scale;
1410
1411 /*
1412 Geometry is a maximum area in pixels.
1413 */
1414 (void) ParseGeometry(geometry,&geometry_info);
1415 area=geometry_info.rho+sqrt(MagickEpsilon);
1416 distance=sqrt((double) former_width*former_height);
1417 scale.x=(double) former_width/(distance/sqrt(area));
1418 scale.y=(double) former_height/(distance/sqrt(area));
1419 if ((scale.x < (double) *width) || (scale.y < (double) *height))
1420 {
1421 *width=(unsigned long) (former_width/(distance/sqrt(area)));
1422 *height=(unsigned long) (former_height/(distance/sqrt(area)));
1423 }
1424 former_width=(*width);
1425 former_height=(*height);
1426 }
1427 return(flags);
1428 }
1429
1430 /*
1431 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1432 % %
1433 % %
1434 % %
1435 % P a r s e P a g e G e o m e t r y %
1436 % %
1437 % %
1438 % %
1439 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1440 %
1441 % ParsePageGeometry() returns a region as defined by the geometry string with
1442 % respect to the image page (canvas) dimensions.
1443 %
1444 % WARNING: Percentage dimensions remain relative to the actual image
1445 % dimensions, and not canvas dimensions.
1446 %
1447 % The format of the ParsePageGeometry method is:
1448 %
1449 % MagickStatusType ParsePageGeometry(const Image *image,
1450 % const char *geometry,RectangeInfo *region_info,
1451 % ExceptionInfo *exception)
1452 %
1453 % A description of each parameter follows:
1454 %
1455 % o geometry: The geometry string (e.g. "100x100+10+10").
1456 %
1457 % o region_info: the region as defined by the geometry string with
1458 % respect to the image and its gravity.
1459 %
1460 % o exception: return any errors or warnings in this structure.
1461 %
1462 */
ParsePageGeometry(const Image * image,const char * geometry,RectangleInfo * region_info,ExceptionInfo * exception)1463 MagickExport MagickStatusType ParsePageGeometry(const Image *image,
1464 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1465 {
1466 MagickStatusType
1467 flags;
1468
1469 SetGeometry(image,region_info);
1470 if (image->page.width != 0)
1471 region_info->width=image->page.width;
1472 if (image->page.height != 0)
1473 region_info->height=image->page.height;
1474 flags=ParseAbsoluteGeometry(geometry,region_info);
1475 if (flags == NoValue)
1476 {
1477 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1478 "InvalidGeometry","`%s'",geometry);
1479 return(flags);
1480 }
1481 if ((flags & PercentValue) != 0)
1482 {
1483 region_info->width=image->columns;
1484 region_info->height=image->rows;
1485 }
1486 flags=ParseMetaGeometry(geometry,®ion_info->x,®ion_info->y,
1487 ®ion_info->width,®ion_info->height);
1488 if ((((flags & WidthValue) != 0) || ((flags & HeightValue) != 0)) &&
1489 (((flags & PercentValue) != 0) || ((flags & SeparatorValue) == 0)))
1490 {
1491 if ((flags & WidthValue) == 0)
1492 region_info->width=region_info->height;
1493 if ((flags & HeightValue) == 0)
1494 region_info->height=region_info->width;
1495 }
1496 return(flags);
1497 }
1498
1499 /*
1500 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1501 % %
1502 % %
1503 % %
1504 % P a r s e R e g i o n G e o m e t r y %
1505 % %
1506 % %
1507 % %
1508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1509 %
1510 % ParseRegionGeometry() returns a region as defined by the geometry string
1511 % with respect to the image dimensions and aspect ratio.
1512 %
1513 % This is basically a wrapper around ParseMetaGeometry. This is typically
1514 % used to parse a geometry string to work out the final integer dimensions
1515 % for image resizing.
1516 %
1517 % The format of the ParseRegionGeometry method is:
1518 %
1519 % MagickStatusType ParseRegionGeometry(const Image *image,
1520 % const char *geometry,RectangeInfo *region_info,
1521 % ExceptionInfo *exception)
1522 %
1523 % A description of each parameter follows:
1524 %
1525 % o geometry: The geometry string (e.g. "100x100+10+10").
1526 %
1527 % o region_info: the region as defined by the geometry string.
1528 %
1529 % o exception: return any errors or warnings in this structure.
1530 %
1531 */
ParseRegionGeometry(const Image * image,const char * geometry,RectangleInfo * region_info,ExceptionInfo * exception)1532 MagickExport MagickStatusType ParseRegionGeometry(const Image *image,
1533 const char *geometry,RectangleInfo *region_info,ExceptionInfo *exception)
1534 {
1535 MagickStatusType
1536 flags;
1537
1538 SetGeometry(image,region_info);
1539 flags=ParseMetaGeometry(geometry,®ion_info->x,®ion_info->y,
1540 ®ion_info->width,®ion_info->height);
1541 if (flags == NoValue)
1542 (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
1543 "InvalidGeometry","`%s'",geometry);
1544 return(flags);
1545 }
1546
1547 /*
1548 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1549 % %
1550 % %
1551 % %
1552 % S e t G e o m e t r y %
1553 % %
1554 % %
1555 % %
1556 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1557 %
1558 % SetGeometry() sets the geometry to its default values.
1559 %
1560 % The format of the SetGeometry method is:
1561 %
1562 % SetGeometry(const Image *image,RectangleInfo *geometry)
1563 %
1564 % A description of each parameter follows:
1565 %
1566 % o image: the image.
1567 %
1568 % o geometry: the geometry.
1569 %
1570 */
SetGeometry(const Image * image,RectangleInfo * geometry)1571 MagickExport void SetGeometry(const Image *image,RectangleInfo *geometry)
1572 {
1573 assert(image != (Image *) NULL);
1574 assert(image->signature == MagickCoreSignature);
1575 if (image->debug != MagickFalse)
1576 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
1577 assert(geometry != (RectangleInfo *) NULL);
1578 (void) ResetMagickMemory(geometry,0,sizeof(*geometry));
1579 geometry->width=image->columns;
1580 geometry->height=image->rows;
1581 }
1582
1583 /*
1584 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1585 % %
1586 % %
1587 % %
1588 % S e t G e o m e t r y I n f o %
1589 % %
1590 % %
1591 % %
1592 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
1593 %
1594 % SetGeometryInfo sets the GeometryInfo structure to its default values.
1595 %
1596 % The format of the SetGeometryInfo method is:
1597 %
1598 % SetGeometryInfo(GeometryInfo *geometry_info)
1599 %
1600 % A description of each parameter follows:
1601 %
1602 % o geometry_info: the geometry info structure.
1603 %
1604 */
SetGeometryInfo(GeometryInfo * geometry_info)1605 MagickExport void SetGeometryInfo(GeometryInfo *geometry_info)
1606 {
1607 assert(geometry_info != (GeometryInfo *) NULL);
1608 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
1609 (void) ResetMagickMemory(geometry_info,0,sizeof(*geometry_info));
1610 }
1611