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-2015 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 "MagickCore/studio.h"
44 #include "MagickCore/annotate.h"
45 #include "MagickCore/client.h"
46 #include "MagickCore/color.h"
47 #include "MagickCore/composite.h"
48 #include "MagickCore/constitute.h"
49 #include "MagickCore/decorate.h"
50 #include "MagickCore/draw.h"
51 #include "MagickCore/effect.h"
52 #include "MagickCore/enhance.h"
53 #include "MagickCore/exception.h"
54 #include "MagickCore/exception-private.h"
55 #include "MagickCore/fx.h"
56 #include "MagickCore/gem.h"
57 #include "MagickCore/geometry.h"
58 #include "MagickCore/image.h"
59 #include "MagickCore/image-private.h"
60 #include "MagickCore/list.h"
61 #include "MagickCore/memory_.h"
62 #include "MagickCore/monitor.h"
63 #include "MagickCore/monitor-private.h"
64 #include "MagickCore/montage.h"
65 #include "MagickCore/option.h"
66 #include "MagickCore/pixel.h"
67 #include "MagickCore/quantize.h"
68 #include "MagickCore/property.h"
69 #include "MagickCore/resize.h"
70 #include "MagickCore/resource_.h"
71 #include "MagickCore/string_.h"
72 #include "MagickCore/utility.h"
73 #include "MagickCore/utility-private.h"
74 #include "MagickCore/version.h"
77 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
81 % C l o n e M o n t a g e I n f o %
85 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87 % CloneMontageInfo() makes a copy of the given montage info structure. If
88 % NULL is specified, a new image info structure is created initialized to
91 % The format of the CloneMontageInfo method is:
93 % MontageInfo *CloneMontageInfo(const ImageInfo *image_info,
94 % const MontageInfo *montage_info)
96 % A description of each parameter follows:
98 % o image_info: the image info.
100 % o montage_info: the montage info.
103 MagickExport MontageInfo *CloneMontageInfo(const ImageInfo *image_info,
104 const MontageInfo *montage_info)
109 clone_info=(MontageInfo *) AcquireMagickMemory(sizeof(*clone_info));
110 if (clone_info == (MontageInfo *) NULL)
111 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
112 GetMontageInfo(image_info,clone_info);
113 if (montage_info == (MontageInfo *) NULL)
115 if (montage_info->geometry != (char *) NULL)
116 clone_info->geometry=AcquireString(montage_info->geometry);
117 if (montage_info->tile != (char *) NULL)
118 clone_info->tile=AcquireString(montage_info->tile);
119 if (montage_info->title != (char *) NULL)
120 clone_info->title=AcquireString(montage_info->title);
121 if (montage_info->frame != (char *) NULL)
122 clone_info->frame=AcquireString(montage_info->frame);
123 if (montage_info->texture != (char *) NULL)
124 clone_info->texture=AcquireString(montage_info->texture);
125 if (montage_info->font != (char *) NULL)
126 clone_info->font=AcquireString(montage_info->font);
127 clone_info->pointsize=montage_info->pointsize;
128 clone_info->border_width=montage_info->border_width;
129 clone_info->shadow=montage_info->shadow;
130 clone_info->fill=montage_info->fill;
131 clone_info->stroke=montage_info->stroke;
132 clone_info->background_color=montage_info->background_color;
133 clone_info->border_color=montage_info->border_color;
134 clone_info->matte_color=montage_info->matte_color;
135 clone_info->gravity=montage_info->gravity;
136 (void) CopyMagickString(clone_info->filename,montage_info->filename,
138 clone_info->debug=IsEventLogging();
143 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
147 % D e s t r o y M o n t a g e I n f o %
151 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
153 % DestroyMontageInfo() deallocates memory associated with montage_info.
155 % The format of the DestroyMontageInfo method is:
157 % MontageInfo *DestroyMontageInfo(MontageInfo *montage_info)
159 % A description of each parameter follows:
161 % o montage_info: Specifies a pointer to an MontageInfo structure.
165 MagickExport MontageInfo *DestroyMontageInfo(MontageInfo *montage_info)
167 if (montage_info->debug != MagickFalse)
168 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
169 assert(montage_info != (MontageInfo *) NULL);
170 assert(montage_info->signature == MagickCoreSignature);
171 if (montage_info->geometry != (char *) NULL)
172 montage_info->geometry=(char *)
173 RelinquishMagickMemory(montage_info->geometry);
174 if (montage_info->tile != (char *) NULL)
175 montage_info->tile=DestroyString(montage_info->tile);
176 if (montage_info->title != (char *) NULL)
177 montage_info->title=DestroyString(montage_info->title);
178 if (montage_info->frame != (char *) NULL)
179 montage_info->frame=DestroyString(montage_info->frame);
180 if (montage_info->texture != (char *) NULL)
181 montage_info->texture=(char *) RelinquishMagickMemory(
182 montage_info->texture);
183 if (montage_info->font != (char *) NULL)
184 montage_info->font=DestroyString(montage_info->font);
185 montage_info->signature=(~MagickCoreSignature);
186 montage_info=(MontageInfo *) RelinquishMagickMemory(montage_info);
187 return(montage_info);
191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
195 % G e t M o n t a g e I n f o %
199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
201 % GetMontageInfo() initializes montage_info to default values.
203 % The format of the GetMontageInfo method is:
205 % void GetMontageInfo(const ImageInfo *image_info,
206 % MontageInfo *montage_info)
208 % A description of each parameter follows:
210 % o image_info: a structure of type ImageInfo.
212 % o montage_info: Specifies a pointer to a MontageInfo structure.
215 MagickExport void GetMontageInfo(const ImageInfo *image_info,
216 MontageInfo *montage_info)
218 assert(image_info != (const ImageInfo *) NULL);
219 assert(image_info->signature == MagickCoreSignature);
220 if (image_info->debug != MagickFalse)
221 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
222 image_info->filename);
223 assert(montage_info != (MontageInfo *) NULL);
224 (void) ResetMagickMemory(montage_info,0,sizeof(*montage_info));
225 (void) CopyMagickString(montage_info->filename,image_info->filename,
227 montage_info->geometry=AcquireString(DefaultTileGeometry);
228 if (image_info->font != (char *) NULL)
229 montage_info->font=AcquireString(image_info->font);
230 montage_info->gravity=CenterGravity;
231 montage_info->pointsize=image_info->pointsize;
232 montage_info->fill.alpha=OpaqueAlpha;
233 montage_info->stroke.alpha=(Quantum) TransparentAlpha;
234 montage_info->background_color=image_info->background_color;
235 montage_info->border_color=image_info->border_color;
236 montage_info->matte_color=image_info->matte_color;
237 montage_info->debug=IsEventLogging();
238 montage_info->signature=MagickCoreSignature;
242 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
246 % M o n t a g e I m a g e L i s t %
250 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
252 % MontageImageList() is a layout manager that lets you tile one or more
253 % thumbnails across an image canvas.
255 % The format of the MontageImageList method is:
257 % Image *MontageImageList(const ImageInfo *image_info,
258 % const MontageInfo *montage_info,Image *images,
259 % ExceptionInfo *exception)
261 % A description of each parameter follows:
263 % o image_info: the image info.
265 % o montage_info: Specifies a pointer to a MontageInfo structure.
267 % o images: Specifies a pointer to an array of Image structures.
269 % o exception: return any errors or warnings in this structure.
273 static void GetMontageGeometry(char *geometry,const size_t number_images,
274 ssize_t *x_offset,ssize_t *y_offset,size_t *tiles_per_column,
275 size_t *tiles_per_row)
279 (void) GetGeometry(geometry,x_offset,y_offset,tiles_per_row,tiles_per_column);
280 if ((*tiles_per_column == 0) && (*tiles_per_row == 0))
281 *tiles_per_column=(size_t) sqrt((double) number_images);
282 if ((*tiles_per_column == 0) && (*tiles_per_row != 0))
283 *tiles_per_column=(size_t) ceil((double) number_images/(*tiles_per_row));
284 if ((*tiles_per_row == 0) && (*tiles_per_column != 0))
285 *tiles_per_row=(size_t) ceil((double) number_images/(*tiles_per_column));
288 #if defined(__cplusplus) || defined(c_plusplus)
292 static int SceneCompare(const void *x,const void *y)
298 image_1=(Image **) x;
299 image_2=(Image **) y;
300 return((int) ((*image_1)->scene-(*image_2)->scene));
303 #if defined(__cplusplus) || defined(c_plusplus)
307 MagickExport Image *MontageImages(const Image *images,
308 const MontageInfo *montage_info,ExceptionInfo *exception)
316 image_info=AcquireImageInfo();
317 montage_image=MontageImageList(image_info,montage_info,images,exception);
318 image_info=DestroyImageInfo(image_info);
319 return(montage_image);
322 MagickExport Image *MontageImageList(const ImageInfo *image_info,
323 const MontageInfo *montage_info,const Image *images,ExceptionInfo *exception)
325 #define MontageImageTag "Montage/Image"
326 #define TileImageTag "Tile/Image"
329 tile_geometry[MagickPathExtent],
361 MagickProgressMonitor
405 assert(images != (Image *) NULL);
406 assert(images->signature == MagickCoreSignature);
407 if (images->debug != MagickFalse)
408 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
409 assert(montage_info != (MontageInfo *) NULL);
410 assert(montage_info->signature == MagickCoreSignature);
411 assert(exception != (ExceptionInfo *) NULL);
412 assert(exception->signature == MagickCoreSignature);
413 number_images=GetImageListLength(images);
414 master_list=ImageListToArray(images,exception);
415 image_list=master_list;
417 if (master_list == (Image **) NULL)
418 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
419 thumbnail=NewImageList();
420 for (i=0; i < (ssize_t) number_images; i++)
422 image=CloneImage(image_list[i],0,0,MagickTrue,exception);
423 if (image == (Image *) NULL)
425 (void) ParseAbsoluteGeometry("0x0+0+0",&image->page);
426 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL,
428 flags=ParseRegionGeometry(image,montage_info->geometry,&geometry,exception);
429 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
430 if (thumbnail == (Image *) NULL)
432 image_list[i]=thumbnail;
433 (void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
434 proceed=SetImageProgress(image,TileImageTag,(MagickOffsetType) i,
436 if (proceed == MagickFalse)
438 image=DestroyImage(image);
440 if (i < (ssize_t) number_images)
442 if (thumbnail == (Image *) NULL)
444 for (tile=0; (ssize_t) tile <= i; tile++)
445 if (image_list[tile] != (Image *) NULL)
446 image_list[tile]=DestroyImage(image_list[tile]);
447 master_list=(Image **) RelinquishMagickMemory(master_list);
448 return((Image *) NULL);
451 Sort image list by increasing tile number.
453 for (i=0; i < (ssize_t) number_images; i++)
454 if (image_list[i]->scene == 0)
456 if (i == (ssize_t) number_images)
457 qsort((void *) image_list,(size_t) number_images,sizeof(*image_list),
460 Determine tiles per row and column.
462 tiles_per_column=(size_t) sqrt((double) number_images);
463 tiles_per_row=(size_t) ceil((double) number_images/tiles_per_column);
466 if (montage_info->tile != (char *) NULL)
467 GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
468 &tiles_per_column,&tiles_per_row);
470 Determine tile sizes.
472 concatenate=MagickFalse;
473 SetGeometry(image_list[0],&extract_info);
474 extract_info.x=(ssize_t) montage_info->border_width;
475 extract_info.y=(ssize_t) montage_info->border_width;
476 if (montage_info->geometry != (char *) NULL)
479 Initialize tile geometry.
481 flags=GetGeometry(montage_info->geometry,&extract_info.x,&extract_info.y,
482 &extract_info.width,&extract_info.height);
483 concatenate=((flags & RhoValue) == 0) && ((flags & SigmaValue) == 0) ?
484 MagickTrue : MagickFalse;
486 border_width=montage_info->border_width;
488 (void) ResetMagickMemory(&frame_info,0,sizeof(frame_info));
489 if (montage_info->frame != (char *) NULL)
492 absolute_geometry[MagickPathExtent];
494 frame_info.width=extract_info.width;
495 frame_info.height=extract_info.height;
496 (void) FormatLocaleString(absolute_geometry,MagickPathExtent,"%s!",
497 montage_info->frame);
498 flags=ParseMetaGeometry(absolute_geometry,&frame_info.outer_bevel,
499 &frame_info.inner_bevel,&frame_info.width,&frame_info.height);
500 if ((flags & HeightValue) == 0)
501 frame_info.height=frame_info.width;
502 if ((flags & XiValue) == 0)
503 frame_info.outer_bevel=(ssize_t) frame_info.width/2;
504 if ((flags & PsiValue) == 0)
505 frame_info.inner_bevel=frame_info.outer_bevel;
506 frame_info.x=(ssize_t) frame_info.width;
507 frame_info.y=(ssize_t) frame_info.height;
508 bevel_width=(size_t) MagickMax(frame_info.inner_bevel,
509 frame_info.outer_bevel);
510 border_width=(size_t) MagickMax((ssize_t) frame_info.width,
511 (ssize_t) frame_info.height);
513 for (i=0; i < (ssize_t) number_images; i++)
515 if (image_list[i]->columns > extract_info.width)
516 extract_info.width=image_list[i]->columns;
517 if (image_list[i]->rows > extract_info.height)
518 extract_info.height=image_list[i]->rows;
521 Initialize draw attributes.
523 clone_info=CloneImageInfo(image_info);
524 clone_info->background_color=montage_info->background_color;
525 clone_info->border_color=montage_info->border_color;
526 draw_info=CloneDrawInfo(clone_info,(DrawInfo *) NULL);
527 if (montage_info->font != (char *) NULL)
528 (void) CloneString(&draw_info->font,montage_info->font);
529 if (montage_info->pointsize != 0.0)
530 draw_info->pointsize=montage_info->pointsize;
531 draw_info->gravity=CenterGravity;
532 draw_info->stroke=montage_info->stroke;
533 draw_info->fill=montage_info->fill;
534 draw_info->text=AcquireString("");
535 (void) GetTypeMetrics(image_list[0],draw_info,&metrics,exception);
536 texture=NewImageList();
537 if (montage_info->texture != (char *) NULL)
539 (void) CopyMagickString(clone_info->filename,montage_info->texture,
541 texture=ReadImage(clone_info,exception);
544 Determine the number of lines in an next label.
546 title=InterpretImageProperties(clone_info,image_list[0],montage_info->title,
549 if (montage_info->title != (char *) NULL)
550 title_offset=(size_t) (2*(metrics.ascent-metrics.descent)*
551 MultilineCensus(title)+2*extract_info.y);
553 for (i=0; i < (ssize_t) number_images; i++)
555 value=GetImageProperty(image_list[i],"label",exception);
556 if (value == (const char *) NULL)
558 if (MultilineCensus(value) > number_lines)
559 number_lines=MultilineCensus(value);
562 Allocate next structure.
564 tile_image=AcquireImage((ImageInfo *) NULL,exception);
565 montage=AcquireImage(clone_info,exception);
566 montage->background_color=montage_info->background_color;
568 images_per_page=(number_images-1)/(tiles_per_row*tiles_per_column)+1;
570 total_tiles=(size_t) number_images;
571 for (i=0; i < (ssize_t) images_per_page; i++)
574 Determine bounding box.
576 tiles_per_page=tiles_per_row*tiles_per_column;
579 if (montage_info->tile != (char *) NULL)
580 GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
582 tiles_per_page=tiles_per_row*tiles_per_column;
583 y_offset+=(ssize_t) title_offset;
588 for (tile=0; tile < (ssize_t) tiles_per_page; tile++)
590 if (tile < (ssize_t) number_images)
592 width=concatenate != MagickFalse ? image_list[tile]->columns :
594 if (image_list[tile]->rows > max_height)
595 max_height=image_list[tile]->rows;
597 x_offset+=(ssize_t) (width+2*(extract_info.x+border_width));
598 if (x_offset > (ssize_t) bounds.width)
599 bounds.width=(size_t) x_offset;
600 if (((tile+1) == (ssize_t) tiles_per_page) ||
601 (((tile+1) % tiles_per_row) == 0))
604 if (montage_info->tile != (char *) NULL)
605 GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y,
607 height=concatenate != MagickFalse ? max_height : extract_info.height;
608 y_offset+=(ssize_t) (height+(extract_info.y+(ssize_t) border_width)*2+
609 (metrics.ascent-metrics.descent+4)*number_lines+
610 (montage_info->shadow != MagickFalse ? 4 : 0));
611 if (y_offset > (ssize_t) bounds.height)
612 bounds.height=(size_t) y_offset;
616 if (montage_info->shadow != MagickFalse)
619 Initialize montage image.
621 (void) CopyMagickString(montage->filename,montage_info->filename,
623 montage->columns=(size_t) MagickMax((ssize_t) bounds.width,1);
624 montage->rows=(size_t) MagickMax((ssize_t) bounds.height,1);
625 (void) SetImageBackgroundColor(montage,exception);
627 Set montage geometry.
629 montage->montage=AcquireString((char *) NULL);
632 while (tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images))
634 extent+=strlen(image_list[tile]->filename)+1;
637 montage->directory=(char *) AcquireQuantumMemory(extent,
638 sizeof(*montage->directory));
639 if ((montage->montage == (char *) NULL) ||
640 (montage->directory == (char *) NULL))
641 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
644 if (montage_info->tile != (char *) NULL)
645 GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
647 y_offset+=(ssize_t) title_offset;
648 (void) FormatLocaleString(montage->montage,MagickPathExtent,
649 "%.20gx%.20g%+.20g%+.20g",(double) (extract_info.width+
650 (extract_info.x+border_width)*2),(double) (extract_info.height+
651 (extract_info.y+border_width)*2+(double) ((metrics.ascent-
652 metrics.descent+4)*number_lines+(montage_info->shadow != MagickFalse ? 4 :
653 0))),(double) x_offset,(double) y_offset);
654 *montage->directory='\0';
656 while (tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images))
658 (void) ConcatenateMagickString(montage->directory,
659 image_list[tile]->filename,extent);
660 (void) ConcatenateMagickString(montage->directory,"\n",extent);
663 progress_monitor=SetImageProgressMonitor(montage,(MagickProgressMonitor)
664 NULL,montage->client_data);
665 if (texture != (Image *) NULL)
666 (void) TextureImage(montage,texture,exception);
667 if (montage_info->title != (char *) NULL)
670 geometry[MagickPathExtent];
679 Annotate composite image with title.
681 clone_info=CloneDrawInfo(image_info,draw_info);
682 clone_info->gravity=CenterGravity;
683 clone_info->pointsize*=2.0;
684 (void) GetTypeMetrics(image_list[0],clone_info,&metrics,exception);
685 (void) FormatLocaleString(geometry,MagickPathExtent,
686 "%.20gx%.20g%+.20g%+.20g",(double) montage->columns,(double)
687 (metrics.ascent-metrics.descent),0.0,(double) extract_info.y+4);
688 (void) CloneString(&clone_info->geometry,geometry);
689 (void) CloneString(&clone_info->text,title);
690 (void) AnnotateImage(montage,clone_info,exception);
691 clone_info=DestroyDrawInfo(clone_info);
693 (void) SetImageProgressMonitor(montage,progress_monitor,
694 montage->client_data);
696 Copy tile to the composite.
700 if (montage_info->tile != (char *) NULL)
701 GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
703 x_offset+=extract_info.x;
704 y_offset+=(ssize_t) title_offset+extract_info.y;
707 for (tile=0; tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images); tile++)
710 Copy this tile to the composite.
712 image=CloneImage(image_list[tile],0,0,MagickTrue,exception);
713 progress_monitor=SetImageProgressMonitor(image,
714 (MagickProgressMonitor) NULL,image->client_data);
715 width=concatenate != MagickFalse ? image->columns : extract_info.width;
716 if (image->rows > max_height)
717 max_height=image->rows;
718 height=concatenate != MagickFalse ? max_height : extract_info.height;
719 if (border_width != 0)
728 Put a border around the image.
730 border_info.width=border_width;
731 border_info.height=border_width;
732 if (montage_info->frame != (char *) NULL)
734 border_info.width=(width-image->columns+1)/2;
735 border_info.height=(height-image->rows+1)/2;
737 border_image=BorderImage(image,&border_info,image->compose,exception);
738 if (border_image != (Image *) NULL)
740 image=DestroyImage(image);
743 if ((montage_info->frame != (char *) NULL) &&
744 (image->compose == DstOutCompositeOp))
746 (void) SetPixelChannelMask(image,AlphaChannel);
747 (void) NegateImage(image,MagickFalse,exception);
748 (void) SetPixelChannelMask(image,DefaultChannels);
752 Gravitate as specified by the tile gravity.
754 tile_image->columns=width;
755 tile_image->rows=height;
756 tile_image->gravity=montage_info->gravity;
757 if (image->gravity != UndefinedGravity)
758 tile_image->gravity=image->gravity;
759 (void) FormatLocaleString(tile_geometry,MagickPathExtent,"%.20gx%.20g+0+0",
760 (double) image->columns,(double) image->rows);
761 flags=ParseGravityGeometry(tile_image,tile_geometry,&geometry,exception);
762 x=(ssize_t) (geometry.x+border_width);
763 y=(ssize_t) (geometry.y+border_width);
764 if ((montage_info->frame != (char *) NULL) && (bevel_width != 0))
773 Put an ornamental border around this tile.
775 extract_info=frame_info;
776 extract_info.width=width+2*frame_info.width;
777 extract_info.height=height+2*frame_info.height;
778 value=GetImageProperty(image,"label",exception);
779 if (value != (const char *) NULL)
780 extract_info.height+=(size_t) ((metrics.ascent-metrics.descent+4)*
781 MultilineCensus(value));
782 frame_image=FrameImage(image,&extract_info,image->compose,exception);
783 if (frame_image != (Image *) NULL)
785 image=DestroyImage(image);
791 if (LocaleCompare(image->magick,"NULL") != 0)
794 Composite background with tile.
796 if (montage_info->shadow != MagickFalse)
804 (void) QueryColorCompliance("#0000",AllCompliance,
805 &image->background_color,exception);
806 shadow_image=ShadowImage(image,80.0,2.0,5,5,exception);
807 if (shadow_image != (Image *) NULL)
809 (void) CompositeImage(shadow_image,image,OverCompositeOp,
810 MagickTrue,0,0,exception);
811 image=DestroyImage(image);
815 (void) CompositeImage(montage,image,image->compose,MagickTrue,
816 x_offset+x,y_offset+y,exception);
817 value=GetImageProperty(image,"label",exception);
818 if (value != (const char *) NULL)
821 geometry[MagickPathExtent];
824 Annotate composite tile with label.
826 (void) FormatLocaleString(geometry,MagickPathExtent,
827 "%.20gx%.20g%+.20g%+.20g",(double) ((montage_info->frame ?
828 image->columns : width)-2*border_width),(double)
829 (metrics.ascent-metrics.descent+4)*MultilineCensus(value),
830 (double) (x_offset+border_width),(double)
831 ((montage_info->frame ? y_offset+height+border_width+4 :
832 y_offset+extract_info.height+border_width+
833 (montage_info->shadow != MagickFalse ? 4 : 0))+bevel_width));
834 (void) CloneString(&draw_info->geometry,geometry);
835 (void) CloneString(&draw_info->text,value);
836 (void) AnnotateImage(montage,draw_info,exception);
839 x_offset+=(ssize_t) (width+2*(extract_info.x+border_width));
840 if (((tile+1) == (ssize_t) tiles_per_page) ||
841 (((tile+1) % tiles_per_row) == 0))
843 x_offset=extract_info.x;
844 y_offset+=(ssize_t) (height+(extract_info.y+border_width)*2+
845 (metrics.ascent-metrics.descent+4)*number_lines+
846 (montage_info->shadow != MagickFalse ? 4 : 0));
849 if (images->progress_monitor != (MagickProgressMonitor) NULL)
854 proceed=SetImageProgress(image,MontageImageTag,tiles,total_tiles);
855 if (proceed == MagickFalse)
858 image_list[tile]=DestroyImage(image_list[tile]);
859 image=DestroyImage(image);
863 if ((i+1) < (ssize_t) images_per_page)
866 Allocate next image structure.
868 AcquireNextImage(clone_info,montage,exception);
869 if (GetNextImageInList(montage) == (Image *) NULL)
871 montage=DestroyImageList(montage);
872 return((Image *) NULL);
874 montage=GetNextImageInList(montage);
875 montage->background_color=montage_info->background_color;
876 image_list+=tiles_per_page;
877 number_images-=tiles_per_page;
880 tile_image=DestroyImage(tile_image);
881 if (texture != (Image *) NULL)
882 texture=DestroyImage(texture);
883 title=DestroyString(title);
884 master_list=(Image **) RelinquishMagickMemory(master_list);
885 draw_info=DestroyDrawInfo(draw_info);
886 clone_info=DestroyImageInfo(clone_info);
887 return(GetFirstImageInList(montage));