2 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
6 % M M OOO N N TTTTT AAA GGGG EEEEE %
7 % MM MM O O NN N T A A G E %
8 % M M M O O N N N T AAAAA G GG EEE %
9 % M M O O N NN T A A G G E %
10 % M M OOO N N T A A GGG EEEEE %
13 % MagickCore Methods to Create Image Thumbnails %
20 % Copyright 1999-2011 ImageMagick Studio LLC, a non-profit organization %
21 % dedicated to making software imaging solutions freely available. %
23 % You may not use this file except in compliance with the License. You may %
24 % obtain a copy of the License at %
26 % http://www.imagemagick.org/script/license.php %
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. %
34 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
43 #include "magick/studio.h"
44 #include "magick/annotate.h"
45 #include "magick/client.h"
46 #include "magick/color.h"
47 #include "magick/composite.h"
48 #include "magick/constitute.h"
49 #include "magick/decorate.h"
50 #include "magick/draw.h"
51 #include "magick/effect.h"
52 #include "magick/enhance.h"
53 #include "magick/exception.h"
54 #include "magick/exception-private.h"
55 #include "magick/fx.h"
56 #include "magick/gem.h"
57 #include "magick/geometry.h"
58 #include "magick/image.h"
59 #include "magick/image-private.h"
60 #include "magick/list.h"
61 #include "magick/memory_.h"
62 #include "magick/monitor.h"
63 #include "magick/monitor-private.h"
64 #include "magick/montage.h"
65 #include "magick/option.h"
66 #include "magick/quantize.h"
67 #include "magick/property.h"
68 #include "magick/resize.h"
69 #include "magick/resource_.h"
70 #include "magick/string_.h"
71 #include "magick/utility.h"
72 #include "magick/version.h"
75 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79 % C l o n e M o n t a g e I n f o %
83 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
85 % CloneMontageInfo() makes a copy of the given montage info structure. If
86 % NULL is specified, a new image info structure is created initialized to
89 % The format of the CloneMontageInfo method is:
91 % MontageInfo *CloneMontageInfo(const ImageInfo *image_info,
92 % const MontageInfo *montage_info)
94 % A description of each parameter follows:
96 % o image_info: the image info.
98 % o montage_info: the montage info.
101 MagickExport MontageInfo *CloneMontageInfo(const ImageInfo *image_info,
102 const MontageInfo *montage_info)
107 clone_info=(MontageInfo *) AcquireMagickMemory(sizeof(*clone_info));
108 if (clone_info == (MontageInfo *) NULL)
109 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
110 GetMontageInfo(image_info,clone_info);
111 if (montage_info == (MontageInfo *) NULL)
113 if (montage_info->geometry != (char *) NULL)
114 clone_info->geometry=AcquireString(montage_info->geometry);
115 if (montage_info->tile != (char *) NULL)
116 clone_info->tile=AcquireString(montage_info->tile);
117 if (montage_info->title != (char *) NULL)
118 clone_info->title=AcquireString(montage_info->title);
119 if (montage_info->frame != (char *) NULL)
120 clone_info->frame=AcquireString(montage_info->frame);
121 if (montage_info->texture != (char *) NULL)
122 clone_info->texture=AcquireString(montage_info->texture);
123 if (montage_info->font != (char *) NULL)
124 clone_info->font=AcquireString(montage_info->font);
125 clone_info->pointsize=montage_info->pointsize;
126 clone_info->border_width=montage_info->border_width;
127 clone_info->shadow=montage_info->shadow;
128 clone_info->fill=montage_info->fill;
129 clone_info->stroke=montage_info->stroke;
130 clone_info->background_color=montage_info->background_color;
131 clone_info->border_color=montage_info->border_color;
132 clone_info->matte_color=montage_info->matte_color;
133 clone_info->gravity=montage_info->gravity;
134 (void) CopyMagickString(clone_info->filename,montage_info->filename,
136 clone_info->debug=IsEventLogging();
141 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
145 % D e s t r o y M o n t a g e I n f o %
149 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151 % DestroyMontageInfo() deallocates memory associated with montage_info.
153 % The format of the DestroyMontageInfo method is:
155 % MontageInfo *DestroyMontageInfo(MontageInfo *montage_info)
157 % A description of each parameter follows:
159 % o montage_info: Specifies a pointer to an MontageInfo structure.
163 MagickExport MontageInfo *DestroyMontageInfo(MontageInfo *montage_info)
165 if (montage_info->debug != MagickFalse)
166 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
167 assert(montage_info != (MontageInfo *) NULL);
168 assert(montage_info->signature == MagickSignature);
169 if (montage_info->geometry != (char *) NULL)
170 montage_info->geometry=(char *)
171 RelinquishMagickMemory(montage_info->geometry);
172 if (montage_info->tile != (char *) NULL)
173 montage_info->tile=DestroyString(montage_info->tile);
174 if (montage_info->title != (char *) NULL)
175 montage_info->title=DestroyString(montage_info->title);
176 if (montage_info->frame != (char *) NULL)
177 montage_info->frame=DestroyString(montage_info->frame);
178 if (montage_info->texture != (char *) NULL)
179 montage_info->texture=(char *) RelinquishMagickMemory(
180 montage_info->texture);
181 if (montage_info->font != (char *) NULL)
182 montage_info->font=DestroyString(montage_info->font);
183 montage_info->signature=(~MagickSignature);
184 montage_info=(MontageInfo *) RelinquishMagickMemory(montage_info);
185 return(montage_info);
189 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
193 % G e t M o n t a g e I n f o %
197 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
199 % GetMontageInfo() initializes montage_info to default values.
201 % The format of the GetMontageInfo method is:
203 % void GetMontageInfo(const ImageInfo *image_info,
204 % MontageInfo *montage_info)
206 % A description of each parameter follows:
208 % o image_info: a structure of type ImageInfo.
210 % o montage_info: Specifies a pointer to a MontageInfo structure.
213 MagickExport void GetMontageInfo(const ImageInfo *image_info,
214 MontageInfo *montage_info)
216 assert(image_info != (const ImageInfo *) NULL);
217 assert(image_info->signature == MagickSignature);
218 if (image_info->debug != MagickFalse)
219 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
220 image_info->filename);
221 assert(montage_info != (MontageInfo *) NULL);
222 (void) ResetMagickMemory(montage_info,0,sizeof(*montage_info));
223 (void) CopyMagickString(montage_info->filename,image_info->filename,
225 montage_info->geometry=AcquireString(DefaultTileGeometry);
226 if (image_info->font != (char *) NULL)
227 montage_info->font=AcquireString(image_info->font);
228 montage_info->gravity=CenterGravity;
229 montage_info->pointsize=image_info->pointsize;
230 montage_info->fill.opacity=OpaqueOpacity;
231 montage_info->stroke.opacity=(Quantum) TransparentOpacity;
232 montage_info->background_color=image_info->background_color;
233 montage_info->border_color=image_info->border_color;
234 montage_info->matte_color=image_info->matte_color;
235 montage_info->debug=IsEventLogging();
236 montage_info->signature=MagickSignature;
240 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
244 % M o n t a g e I m a g e L i s t %
248 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
250 % MontageImageList() is a layout manager that lets you tile one or more
251 % thumbnails across an image canvas.
253 % The format of the MontageImageList method is:
255 % Image *MontageImageList(const ImageInfo *image_info,
256 % const MontageInfo *montage_info,Image *images,
257 % ExceptionInfo *exception)
259 % A description of each parameter follows:
261 % o image_info: the image info.
263 % o montage_info: Specifies a pointer to a MontageInfo structure.
265 % o images: Specifies a pointer to an array of Image structures.
267 % o exception: return any errors or warnings in this structure.
271 static void GetMontageGeometry(char *geometry,const size_t number_images,
272 ssize_t *x_offset,ssize_t *y_offset,size_t *tiles_per_column,
273 size_t *tiles_per_row)
277 (void) GetGeometry(geometry,x_offset,y_offset,tiles_per_row,tiles_per_column);
278 if ((*tiles_per_column == 0) && (*tiles_per_row == 0))
279 *tiles_per_column=(size_t) sqrt((double) number_images);
280 if (*tiles_per_column == 0)
281 *tiles_per_column=(size_t)
282 ceil((double) number_images/(*tiles_per_row));
283 if (*tiles_per_row == 0)
284 *tiles_per_row=(size_t)
285 ceil((double) number_images/(*tiles_per_column));
288 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y)
295 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y)
302 #if defined(__cplusplus) || defined(c_plusplus)
306 static int SceneCompare(const void *x,const void *y)
312 image_1=(Image **) x;
313 image_2=(Image **) y;
314 return((int) ((*image_1)->scene-(*image_2)->scene));
317 #if defined(__cplusplus) || defined(c_plusplus)
321 MagickExport Image *MontageImages(const Image *images,
322 const MontageInfo *montage_info,ExceptionInfo *exception)
330 image_info=AcquireImageInfo();
331 montage_image=MontageImageList(image_info,montage_info,images,exception);
332 image_info=DestroyImageInfo(image_info);
333 return(montage_image);
336 MagickExport Image *MontageImageList(const ImageInfo *image_info,
337 const MontageInfo *montage_info,const Image *images,ExceptionInfo *exception)
339 #define MontageImageTag "Montage/Image"
340 #define TileImageTag "Tile/Image"
343 tile_geometry[MaxTextExtent],
375 MagickProgressMonitor
420 assert(images != (Image *) NULL);
421 assert(images->signature == MagickSignature);
422 if (images->debug != MagickFalse)
423 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
424 assert(montage_info != (MontageInfo *) NULL);
425 assert(montage_info->signature == MagickSignature);
426 assert(exception != (ExceptionInfo *) NULL);
427 assert(exception->signature == MagickSignature);
428 number_images=GetImageListLength(images);
429 master_list=ImageListToArray(images,exception);
430 image_list=master_list;
432 if (master_list == (Image **) NULL)
433 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
434 thumbnail=NewImageList();
435 for (i=0; i < (ssize_t) number_images; i++)
437 image=CloneImage(image_list[i],0,0,MagickTrue,exception);
438 if (image == (Image *) NULL)
440 (void) ParseAbsoluteGeometry("0x0+0+0",&image->page);
441 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL,
443 flags=ParseRegionGeometry(image,montage_info->geometry,&geometry,exception);
444 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
445 if (thumbnail == (Image *) NULL)
447 image_list[i]=thumbnail;
448 (void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
449 proceed=SetImageProgress(image,TileImageTag,(MagickOffsetType) i,
451 if (proceed == MagickFalse)
453 image=DestroyImage(image);
455 if (i < (ssize_t) number_images)
457 if (thumbnail == (Image *) NULL)
459 for (tile=0; (ssize_t) tile <= i; tile++)
460 if (image_list[tile] != (Image *) NULL)
461 image_list[tile]=DestroyImage(image_list[tile]);
462 master_list=(Image **) RelinquishMagickMemory(master_list);
463 return((Image *) NULL);
466 Sort image list by increasing tile number.
468 for (i=0; i < (ssize_t) number_images; i++)
469 if (image_list[i]->scene == 0)
471 if (i == (ssize_t) number_images)
472 qsort((void *) image_list,(size_t) number_images,sizeof(*image_list),
475 Determine tiles per row and column.
477 tiles_per_column=(size_t) sqrt((double) number_images);
478 tiles_per_row=(size_t) ceil((double) number_images/tiles_per_column);
481 if (montage_info->tile != (char *) NULL)
482 GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
483 &tiles_per_column,&tiles_per_row);
485 Determine tile sizes.
487 concatenate=MagickFalse;
488 SetGeometry(image_list[0],&extract_info);
489 extract_info.x=(ssize_t) montage_info->border_width;
490 extract_info.y=(ssize_t) montage_info->border_width;
491 if (montage_info->geometry != (char *) NULL)
494 Initialize tile geometry.
496 flags=GetGeometry(montage_info->geometry,&extract_info.x,&extract_info.y,
497 &extract_info.width,&extract_info.height);
498 if ((extract_info.x == 0) && (extract_info.y == 0))
499 concatenate=((flags & RhoValue) == 0) && ((flags & SigmaValue) == 0) ?
500 MagickTrue : MagickFalse;
502 border_width=montage_info->border_width;
504 if (montage_info->frame != (char *) NULL)
507 absolute_geometry[MaxTextExtent];
509 (void) ResetMagickMemory(&frame_info,0,sizeof(frame_info));
510 frame_info.width=extract_info.width;
511 frame_info.height=extract_info.height;
512 (void) FormatMagickString(absolute_geometry,MaxTextExtent,"%s!",
513 montage_info->frame);
514 flags=ParseMetaGeometry(absolute_geometry,&frame_info.outer_bevel,
515 &frame_info.inner_bevel,&frame_info.width,&frame_info.height);
516 if ((flags & HeightValue) == 0)
517 frame_info.height=frame_info.width;
518 if ((flags & XiValue) == 0)
519 frame_info.outer_bevel=(ssize_t) frame_info.width/2;
520 if ((flags & PsiValue) == 0)
521 frame_info.inner_bevel=frame_info.outer_bevel;
522 frame_info.x=(ssize_t) frame_info.width;
523 frame_info.y=(ssize_t) frame_info.height;
524 bevel_width=(size_t) MagickMax(frame_info.inner_bevel,
525 frame_info.outer_bevel);
526 border_width=(size_t) MagickMax((ssize_t) frame_info.width,
527 (ssize_t) frame_info.height);
529 for (i=0; i < (ssize_t) number_images; i++)
531 if (image_list[i]->columns > extract_info.width)
532 extract_info.width=image_list[i]->columns;
533 if (image_list[i]->rows > extract_info.height)
534 extract_info.height=image_list[i]->rows;
537 Initialize draw attributes.
539 clone_info=CloneImageInfo(image_info);
540 clone_info->background_color=montage_info->background_color;
541 clone_info->border_color=montage_info->border_color;
542 draw_info=CloneDrawInfo(clone_info,(DrawInfo *) NULL);
543 if (montage_info->font != (char *) NULL)
544 (void) CloneString(&draw_info->font,montage_info->font);
545 if (montage_info->pointsize != 0.0)
546 draw_info->pointsize=montage_info->pointsize;
547 draw_info->gravity=CenterGravity;
548 draw_info->stroke=montage_info->stroke;
549 draw_info->fill=montage_info->fill;
550 draw_info->text=AcquireString("");
551 (void) GetTypeMetrics(image_list[0],draw_info,&metrics);
552 texture=NewImageList();
553 if (montage_info->texture != (char *) NULL)
555 (void) CopyMagickString(clone_info->filename,montage_info->texture,
557 texture=ReadImage(clone_info,exception);
560 Determine the number of lines in an next label.
562 title=InterpretImageProperties(clone_info,image_list[0],montage_info->title);
564 if (montage_info->title != (char *) NULL)
565 title_offset=(size_t) (2*(metrics.ascent-metrics.descent)*
566 MultilineCensus(title)+2*extract_info.y);
568 for (i=0; i < (ssize_t) number_images; i++)
570 value=GetImageProperty(image_list[i],"label");
571 if (value == (const char *) NULL)
573 if (MultilineCensus(value) > number_lines)
574 number_lines=MultilineCensus(value);
577 Allocate next structure.
579 tile_image=AcquireImage(NULL);
580 montage=AcquireImage(clone_info);
581 montage->background_color=montage_info->background_color;
583 images_per_page=(number_images-1)/(tiles_per_row*tiles_per_column)+1;
585 total_tiles=(size_t) number_images;
586 for (i=0; i < (ssize_t) images_per_page; i++)
589 Determine bounding box.
591 tiles_per_page=tiles_per_row*tiles_per_column;
594 if (montage_info->tile != (char *) NULL)
595 GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
597 tiles_per_page=tiles_per_row*tiles_per_column;
598 y_offset+=(ssize_t) title_offset;
603 for (tile=0; tile < (ssize_t) tiles_per_page; tile++)
605 if (tile < (ssize_t) number_images)
607 width=concatenate != MagickFalse ? image_list[tile]->columns :
609 if (image_list[tile]->rows > max_height)
610 max_height=image_list[tile]->rows;
612 x_offset+=(ssize_t) (width+2*(extract_info.x+border_width));
613 if (x_offset > (ssize_t) bounds.width)
614 bounds.width=(size_t) x_offset;
615 if (((tile+1) == (ssize_t) tiles_per_page) ||
616 (((tile+1) % tiles_per_row) == 0))
619 if (montage_info->tile != (char *) NULL)
620 GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y,
622 height=concatenate != MagickFalse ? max_height : extract_info.height;
623 y_offset+=(ssize_t) (height+(extract_info.y+(ssize_t) border_width)*2+
624 (metrics.ascent-metrics.descent+4)*number_lines+
625 (montage_info->shadow != MagickFalse ? 4 : 0));
626 if (y_offset > (ssize_t) bounds.height)
627 bounds.height=(size_t) y_offset;
631 if (montage_info->shadow != MagickFalse)
634 Initialize montage image.
636 (void) CopyMagickString(montage->filename,montage_info->filename,
638 montage->columns=bounds.width;
639 montage->rows=bounds.height;
640 (void) SetImageBackgroundColor(montage);
642 Set montage geometry.
644 montage->montage=AcquireString((char *) NULL);
647 while (tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images))
649 extent+=strlen(image_list[tile]->filename)+1;
652 montage->directory=(char *) AcquireQuantumMemory(extent,
653 sizeof(*montage->directory));
654 if ((montage->montage == (char *) NULL) ||
655 (montage->directory == (char *) NULL))
656 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
659 if (montage_info->tile != (char *) NULL)
660 GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
662 y_offset+=(ssize_t) title_offset;
663 (void) FormatMagickString(montage->montage,MaxTextExtent,
664 "%.20gx%.20g%+.20g%+.20g",(double) (extract_info.width+
665 (extract_info.x+border_width)*2),(double) (extract_info.height+
666 (extract_info.y+border_width)*2+(double) ((metrics.ascent-
667 metrics.descent+4)*number_lines+(montage_info->shadow != MagickFalse ? 4 :
668 0))),(double) x_offset,(double) y_offset);
669 *montage->directory='\0';
671 while (tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images))
673 (void) ConcatenateMagickString(montage->directory,
674 image_list[tile]->filename,extent);
675 (void) ConcatenateMagickString(montage->directory,"\n",extent);
678 progress_monitor=SetImageProgressMonitor(montage,(MagickProgressMonitor)
679 NULL,montage->client_data);
680 if (texture != (Image *) NULL)
681 (void) TextureImage(montage,texture);
682 if (montage_info->title != (char *) NULL)
685 geometry[MaxTextExtent];
694 Annotate composite image with title.
696 clone_info=CloneDrawInfo(image_info,draw_info);
697 clone_info->gravity=CenterGravity;
698 clone_info->pointsize*=2.0;
699 (void) GetTypeMetrics(image_list[0],clone_info,&metrics);
700 (void) FormatMagickString(geometry,MaxTextExtent,
701 "%.20gx%.20g%+.20g%+.20g",(double) montage->columns,(double)
702 (metrics.ascent-metrics.descent),0.0,(double) extract_info.y+4);
703 (void) CloneString(&clone_info->geometry,geometry);
704 (void) CloneString(&clone_info->text,title);
705 (void) AnnotateImage(montage,clone_info);
706 clone_info=DestroyDrawInfo(clone_info);
708 (void) SetImageProgressMonitor(montage,progress_monitor,
709 montage->client_data);
711 Copy tile to the composite.
715 if (montage_info->tile != (char *) NULL)
716 GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
718 x_offset+=extract_info.x;
719 y_offset+=(ssize_t) title_offset+extract_info.y;
722 for (tile=0; tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images); tile++)
725 Copy this tile to the composite.
727 image=CloneImage(image_list[tile],0,0,MagickTrue,exception);
728 progress_monitor=SetImageProgressMonitor(image,
729 (MagickProgressMonitor) NULL,image->client_data);
730 width=concatenate != MagickFalse ? image->columns : extract_info.width;
731 if (image->rows > max_height)
732 max_height=image->rows;
733 height=concatenate != MagickFalse ? max_height : extract_info.height;
734 if (border_width != 0)
743 Put a border around the image.
745 border_info.width=border_width;
746 border_info.height=border_width;
747 if (montage_info->frame != (char *) NULL)
749 border_info.width=(width-image->columns+1)/2;
750 border_info.height=(height-image->rows+1)/2;
752 border_image=BorderImage(image,&border_info,exception);
753 if (border_image != (Image *) NULL)
755 image=DestroyImage(image);
758 if ((montage_info->frame != (char *) NULL) &&
759 (image->compose == DstOutCompositeOp))
760 (void) NegateImageChannel(image,OpacityChannel,MagickFalse);
763 Gravitate as specified by the tile gravity.
765 tile_image->columns=width;
766 tile_image->rows=height;
767 tile_image->gravity=montage_info->gravity;
768 if (image->gravity != UndefinedGravity)
769 tile_image->gravity=image->gravity;
770 (void) FormatMagickString(tile_geometry,MaxTextExtent,"%.20gx%.20g+0+0",
771 (double) image->columns,(double) image->rows);
772 flags=ParseGravityGeometry(tile_image,tile_geometry,&geometry,exception);
773 x=(ssize_t) (geometry.x+border_width);
774 y=(ssize_t) (geometry.y+border_width);
775 if ((montage_info->frame != (char *) NULL) && (bevel_width != 0))
784 Put an ornamental border around this tile.
786 extract_info=frame_info;
787 extract_info.width=width+2*frame_info.width;
788 extract_info.height=height+2*frame_info.height;
789 value=GetImageProperty(image,"label");
790 if (value != (const char *) NULL)
791 extract_info.height+=(size_t) ((metrics.ascent-
792 metrics.descent+4)*MultilineCensus(value));
793 frame_image=FrameImage(image,&extract_info,exception);
794 if (frame_image != (Image *) NULL)
796 image=DestroyImage(image);
802 if (LocaleCompare(image->magick,"NULL") != 0)
805 Composite background with tile.
807 if (montage_info->shadow != MagickFalse)
815 (void) QueryColorDatabase("#000000",&image->background_color,
817 shadow_image=ShadowImage(image,80.0,2.0,5,5,exception);
818 if (shadow_image != (Image *) NULL)
820 InheritException(&shadow_image->exception,exception);
821 (void) CompositeImage(shadow_image,OverCompositeOp,image,0,0);
822 image=DestroyImage(image);
826 (void) CompositeImage(montage,image->compose,image,x_offset+x,
828 value=GetImageProperty(image,"label");
829 if (value != (const char *) NULL)
832 geometry[MaxTextExtent];
835 Annotate composite tile with label.
837 (void) FormatMagickString(geometry,MaxTextExtent,
838 "%.20gx%.20g%+.20g%+.20g",(double) ((montage_info->frame ?
839 image->columns : width)-2*border_width),(double)
840 (metrics.ascent-metrics.descent+4)*MultilineCensus(value),
841 (double) (x_offset+border_width),(double)
842 ((montage_info->frame ? y_offset+height+border_width+4 :
843 y_offset+extract_info.height+border_width+
844 (montage_info->shadow != MagickFalse ? 4 : 0))+bevel_width));
845 (void) CloneString(&draw_info->geometry,geometry);
846 (void) CloneString(&draw_info->text,value);
847 (void) AnnotateImage(montage,draw_info);
850 x_offset+=(ssize_t) (width+2*(extract_info.x+border_width));
851 if (((tile+1) == (ssize_t) tiles_per_page) ||
852 (((tile+1) % tiles_per_row) == 0))
854 x_offset=extract_info.x;
855 y_offset+=(ssize_t) (height+(extract_info.y+border_width)*2+
856 (metrics.ascent-metrics.descent+4)*number_lines+
857 (montage_info->shadow != MagickFalse ? 4 : 0));
860 if (images->progress_monitor != (MagickProgressMonitor) NULL)
865 proceed=SetImageProgress(image,MontageImageTag,tiles,total_tiles);
866 if (proceed == MagickFalse)
869 image_list[tile]=DestroyImage(image_list[tile]);
870 image=DestroyImage(image);
874 if ((i+1) < (ssize_t) images_per_page)
877 Allocate next image structure.
879 AcquireNextImage(clone_info,montage);
880 if (GetNextImageInList(montage) == (Image *) NULL)
882 montage=DestroyImageList(montage);
883 return((Image *) NULL);
885 montage=GetNextImageInList(montage);
886 montage->background_color=montage_info->background_color;
887 image_list+=tiles_per_page;
888 number_images-=tiles_per_page;
891 tile_image=DestroyImage(tile_image);
892 if (texture != (Image *) NULL)
893 texture=DestroyImage(texture);
894 master_list=(Image **) RelinquishMagickMemory(master_list);
895 draw_info=DestroyDrawInfo(draw_info);
896 clone_info=DestroyImageInfo(clone_info);
897 return(GetFirstImageInList(montage));