1/*
2%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
3%                                                                             %
4%                                                                             %
5%                                                                             %
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               %
11%                                                                             %
12%                                                                             %
13%                MagickCore Methods to Create Image Thumbnails                %
14%                                                                             %
15%                              Software Design                                %
16%                                   Cristy                                    %
17%                                 July 1992                                   %
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/*
41  Include declarations.
42*/
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"
75
76/*
77%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
78%                                                                             %
79%                                                                             %
80%                                                                             %
81%   C l o n e M o n t a g e I n f o                                           %
82%                                                                             %
83%                                                                             %
84%                                                                             %
85%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
86%
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
89%  default values.
90%
91%  The format of the CloneMontageInfo method is:
92%
93%      MontageInfo *CloneMontageInfo(const ImageInfo *image_info,
94%        const MontageInfo *montage_info)
95%
96%  A description of each parameter follows:
97%
98%    o image_info: the image info.
99%
100%    o montage_info: the montage info.
101%
102*/
103MagickExport MontageInfo *CloneMontageInfo(const ImageInfo *image_info,
104  const MontageInfo *montage_info)
105{
106  MontageInfo
107    *clone_info;
108
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)
114    return(clone_info);
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->alpha_color=montage_info->alpha_color;
133  clone_info->background_color=montage_info->background_color;
134  clone_info->border_color=montage_info->border_color;
135  clone_info->gravity=montage_info->gravity;
136  (void) CopyMagickString(clone_info->filename,montage_info->filename,
137    MagickPathExtent);
138  clone_info->debug=IsEventLogging();
139  return(clone_info);
140}
141
142/*
143%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
144%                                                                             %
145%                                                                             %
146%                                                                             %
147%   D e s t r o y M o n t a g e I n f o                                       %
148%                                                                             %
149%                                                                             %
150%                                                                             %
151%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
152%
153%  DestroyMontageInfo() deallocates memory associated with montage_info.
154%
155%  The format of the DestroyMontageInfo method is:
156%
157%      MontageInfo *DestroyMontageInfo(MontageInfo *montage_info)
158%
159%  A description of each parameter follows:
160%
161%    o montage_info: Specifies a pointer to an MontageInfo structure.
162%
163%
164*/
165MagickExport MontageInfo *DestroyMontageInfo(MontageInfo *montage_info)
166{
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);
188}
189
190/*
191%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
192%                                                                             %
193%                                                                             %
194%                                                                             %
195%   G e t M o n t a g e I n f o                                               %
196%                                                                             %
197%                                                                             %
198%                                                                             %
199%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
200%
201%  GetMontageInfo() initializes montage_info to default values.
202%
203%  The format of the GetMontageInfo method is:
204%
205%      void GetMontageInfo(const ImageInfo *image_info,
206%        MontageInfo *montage_info)
207%
208%  A description of each parameter follows:
209%
210%    o image_info: a structure of type ImageInfo.
211%
212%    o montage_info: Specifies a pointer to a MontageInfo structure.
213%
214*/
215MagickExport void GetMontageInfo(const ImageInfo *image_info,
216  MontageInfo *montage_info)
217{
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,
226    MagickPathExtent);
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->alpha_color=image_info->alpha_color;
235  montage_info->background_color=image_info->background_color;
236  montage_info->border_color=image_info->border_color;
237  montage_info->debug=IsEventLogging();
238  montage_info->signature=MagickCoreSignature;
239}
240
241/*
242%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
243%                                                                             %
244%                                                                             %
245%                                                                             %
246%   M o n t a g e I m a g e L i s t                                           %
247%                                                                             %
248%                                                                             %
249%                                                                             %
250%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
251%
252%  MontageImageList() is a layout manager that lets you tile one or more
253%  thumbnails across an image canvas.
254%
255%  The format of the MontageImageList method is:
256%
257%      Image *MontageImageList(const ImageInfo *image_info,
258%        const MontageInfo *montage_info,Image *images,
259%        ExceptionInfo *exception)
260%
261%  A description of each parameter follows:
262%
263%    o image_info: the image info.
264%
265%    o montage_info: Specifies a pointer to a MontageInfo structure.
266%
267%    o images: Specifies a pointer to an array of Image structures.
268%
269%    o exception: return any errors or warnings in this structure.
270%
271*/
272
273static 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)
276{
277  *tiles_per_column=0;
278  *tiles_per_row=0;
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));
286}
287
288#if defined(__cplusplus) || defined(c_plusplus)
289extern "C" {
290#endif
291
292static int SceneCompare(const void *x,const void *y)
293{
294  Image
295    **image_1,
296    **image_2;
297
298  image_1=(Image **) x;
299  image_2=(Image **) y;
300  return((int) ((*image_1)->scene-(*image_2)->scene));
301}
302
303#if defined(__cplusplus) || defined(c_plusplus)
304}
305#endif
306
307MagickExport Image *MontageImages(const Image *images,
308  const MontageInfo *montage_info,ExceptionInfo *exception)
309{
310  Image
311    *montage_image;
312
313  ImageInfo
314    *image_info;
315
316  image_info=AcquireImageInfo();
317  montage_image=MontageImageList(image_info,montage_info,images,exception);
318  image_info=DestroyImageInfo(image_info);
319  return(montage_image);
320}
321
322MagickExport Image *MontageImageList(const ImageInfo *image_info,
323  const MontageInfo *montage_info,const Image *images,ExceptionInfo *exception)
324{
325#define MontageImageTag  "Montage/Image"
326#define TileImageTag  "Tile/Image"
327
328  char
329    tile_geometry[MagickPathExtent],
330    *title;
331
332  const char
333    *value;
334
335  DrawInfo
336    *draw_info;
337
338  FrameInfo
339    frame_info;
340
341  Image
342    *image,
343    **image_list,
344    **master_list,
345    *montage,
346    *texture,
347    *tile_image,
348    *thumbnail;
349
350  ImageInfo
351    *clone_info;
352
353  MagickBooleanType
354    concatenate,
355    proceed,
356    status;
357
358  MagickOffsetType
359    tiles;
360
361  MagickProgressMonitor
362    progress_monitor;
363
364  MagickStatusType
365    flags;
366
367  register ssize_t
368    i;
369
370  RectangleInfo
371    bounds,
372    geometry,
373    extract_info;
374
375  size_t
376    bevel_width,
377    border_width,
378    extent,
379    height,
380    images_per_page,
381    max_height,
382    number_images,
383    number_lines,
384    sans,
385    tiles_per_column,
386    tiles_per_page,
387    tiles_per_row,
388    title_offset,
389    total_tiles,
390    width;
391
392  ssize_t
393    tile,
394    x,
395    x_offset,
396    y,
397    y_offset;
398
399  TypeMetric
400    metrics;
401
402  /*
403    Create image tiles.
404  */
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;
416  image=image_list[0];
417  if (master_list == (Image **) NULL)
418    ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
419  thumbnail=NewImageList();
420  for (i=0; i < (ssize_t) number_images; i++)
421  {
422    image=CloneImage(image_list[i],0,0,MagickTrue,exception);
423    if (image == (Image *) NULL)
424      break;
425    (void) ParseAbsoluteGeometry("0x0+0+0",&image->page);
426    progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL,
427      image->client_data);
428    flags=ParseRegionGeometry(image,montage_info->geometry,&geometry,exception);
429    thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
430    if (thumbnail == (Image *) NULL)
431      break;
432    image_list[i]=thumbnail;
433    (void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
434    proceed=SetImageProgress(image,TileImageTag,(MagickOffsetType) i,
435      number_images);
436    if (proceed == MagickFalse)
437      break;
438    image=DestroyImage(image);
439  }
440  if (i < (ssize_t) number_images)
441    {
442      if (thumbnail == (Image *) NULL)
443        i--;
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);
449    }
450  /*
451    Sort image list by increasing tile number.
452  */
453  for (i=0; i < (ssize_t) number_images; i++)
454    if (image_list[i]->scene == 0)
455      break;
456  if (i == (ssize_t) number_images)
457    qsort((void *) image_list,(size_t) number_images,sizeof(*image_list),
458      SceneCompare);
459  /*
460    Determine tiles per row and column.
461  */
462  tiles_per_column=(size_t) sqrt((double) number_images);
463  tiles_per_row=(size_t) ceil((double) number_images/tiles_per_column);
464  x_offset=0;
465  y_offset=0;
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);
469  /*
470    Determine tile sizes.
471  */
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)
477    {
478      /*
479        Initialize tile geometry.
480      */
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;
485    }
486  border_width=montage_info->border_width;
487  bevel_width=0;
488  (void) ResetMagickMemory(&frame_info,0,sizeof(frame_info));
489  if (montage_info->frame != (char *) NULL)
490    {
491      char
492        absolute_geometry[MagickPathExtent];
493
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);
512    }
513  for (i=0; i < (ssize_t) number_images; i++)
514  {
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;
519  }
520  /*
521    Initialize draw attributes.
522  */
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)
538    {
539      (void) CopyMagickString(clone_info->filename,montage_info->texture,
540        MagickPathExtent);
541      texture=ReadImage(clone_info,exception);
542    }
543  /*
544    Determine the number of lines in an next label.
545  */
546  title=InterpretImageProperties(clone_info,image_list[0],montage_info->title,
547    exception);
548  title_offset=0;
549  if (montage_info->title != (char *) NULL)
550    title_offset=(size_t) (2*(metrics.ascent-metrics.descent)*
551      MultilineCensus(title)+2*extract_info.y);
552  number_lines=0;
553  for (i=0; i < (ssize_t) number_images; i++)
554  {
555    value=GetImageProperty(image_list[i],"label",exception);
556    if (value == (const char *) NULL)
557      continue;
558    if (MultilineCensus(value) > number_lines)
559      number_lines=MultilineCensus(value);
560  }
561  /*
562    Allocate next structure.
563  */
564  tile_image=AcquireImage((ImageInfo *) NULL,exception);
565  montage=AcquireImage(clone_info,exception);
566  montage->background_color=montage_info->background_color;
567  montage->scene=0;
568  images_per_page=(number_images-1)/(tiles_per_row*tiles_per_column)+1;
569  tiles=0;
570  total_tiles=(size_t) number_images;
571  for (i=0; i < (ssize_t) images_per_page; i++)
572  {
573    /*
574      Determine bounding box.
575    */
576    tiles_per_page=tiles_per_row*tiles_per_column;
577    x_offset=0;
578    y_offset=0;
579    if (montage_info->tile != (char *) NULL)
580      GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
581        &sans,&sans);
582    tiles_per_page=tiles_per_row*tiles_per_column;
583    y_offset+=(ssize_t) title_offset;
584    max_height=0;
585    bounds.width=0;
586    bounds.height=0;
587    width=0;
588    for (tile=0; tile < (ssize_t) tiles_per_page; tile++)
589    {
590      if (tile < (ssize_t) number_images)
591        {
592          width=concatenate != MagickFalse ? image_list[tile]->columns :
593            extract_info.width;
594          if (image_list[tile]->rows > max_height)
595            max_height=image_list[tile]->rows;
596        }
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))
602        {
603          x_offset=0;
604          if (montage_info->tile != (char *) NULL)
605            GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y,
606              &sans,&sans);
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;
613          max_height=0;
614        }
615    }
616    if (montage_info->shadow != MagickFalse)
617      bounds.width+=4;
618    /*
619      Initialize montage image.
620    */
621    (void) CopyMagickString(montage->filename,montage_info->filename,
622      MagickPathExtent);
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);
626    /*
627      Set montage geometry.
628    */
629    montage->montage=AcquireString((char *) NULL);
630    tile=0;
631    extent=1;
632    while (tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images))
633    {
634      extent+=strlen(image_list[tile]->filename)+1;
635      tile++;
636    }
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");
642    x_offset=0;
643    y_offset=0;
644    if (montage_info->tile != (char *) NULL)
645      GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
646        &sans,&sans);
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';
655    tile=0;
656    while (tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images))
657    {
658      (void) ConcatenateMagickString(montage->directory,
659        image_list[tile]->filename,extent);
660      (void) ConcatenateMagickString(montage->directory,"\n",extent);
661      tile++;
662    }
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)
668      {
669        char
670          geometry[MagickPathExtent];
671
672        DrawInfo
673          *clone_info;
674
675        TypeMetric
676          metrics;
677
678        /*
679          Annotate composite image with title.
680        */
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);
692      }
693    (void) SetImageProgressMonitor(montage,progress_monitor,
694      montage->client_data);
695    /*
696      Copy tile to the composite.
697    */
698    x_offset=0;
699    y_offset=0;
700    if (montage_info->tile != (char *) NULL)
701      GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
702        &sans,&sans);
703    x_offset+=extract_info.x;
704    y_offset+=(ssize_t) title_offset+extract_info.y;
705    max_height=0;
706    status=MagickTrue;
707    for (tile=0; tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images); tile++)
708    {
709      /*
710        Copy this tile to the composite.
711      */
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)
720        {
721          Image
722            *border_image;
723
724          RectangleInfo
725            border_info;
726
727          /*
728            Put a border around the image.
729          */
730          border_info.width=border_width;
731          border_info.height=border_width;
732          if (montage_info->frame != (char *) NULL)
733            {
734              border_info.width=(width-image->columns+1)/2;
735              border_info.height=(height-image->rows+1)/2;
736            }
737          border_image=BorderImage(image,&border_info,image->compose,exception);
738          if (border_image != (Image *) NULL)
739            {
740              image=DestroyImage(image);
741              image=border_image;
742            }
743          if ((montage_info->frame != (char *) NULL) &&
744              (image->compose == DstOutCompositeOp))
745            {
746              (void) SetPixelChannelMask(image,AlphaChannel);
747              (void) NegateImage(image,MagickFalse,exception);
748              (void) SetPixelChannelMask(image,DefaultChannels);
749            }
750        }
751      /*
752        Gravitate as specified by the tile gravity.
753      */
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))
765        {
766          FrameInfo
767            extract_info;
768
769          Image
770            *frame_image;
771
772          /*
773            Put an ornamental border around this tile.
774          */
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)
784            {
785              image=DestroyImage(image);
786              image=frame_image;
787            }
788          x=0;
789          y=0;
790        }
791      if (LocaleCompare(image->magick,"NULL") != 0)
792        {
793          /*
794            Composite background with tile.
795          */
796          if (montage_info->shadow != MagickFalse)
797            {
798              Image
799                *shadow_image;
800
801              /*
802                Shadow image.
803              */
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)
808                {
809                  (void) CompositeImage(shadow_image,image,OverCompositeOp,
810                    MagickTrue,0,0,exception);
811                  image=DestroyImage(image);
812                  image=shadow_image;
813                }
814          }
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)
819            {
820              char
821                geometry[MagickPathExtent];
822
823              /*
824                Annotate composite tile with label.
825              */
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);
837            }
838        }
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))
842        {
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));
847          max_height=0;
848        }
849      if (images->progress_monitor != (MagickProgressMonitor) NULL)
850        {
851          MagickBooleanType
852            proceed;
853
854          proceed=SetImageProgress(image,MontageImageTag,tiles,total_tiles);
855          if (proceed == MagickFalse)
856            status=MagickFalse;
857        }
858      image_list[tile]=DestroyImage(image_list[tile]);
859      image=DestroyImage(image);
860      tiles++;
861    }
862    (void) status;
863    if ((i+1) < (ssize_t) images_per_page)
864      {
865        /*
866          Allocate next image structure.
867        */
868        AcquireNextImage(clone_info,montage,exception);
869        if (GetNextImageInList(montage) == (Image *) NULL)
870          {
871            montage=DestroyImageList(montage);
872            return((Image *) NULL);
873          }
874        montage=GetNextImageInList(montage);
875        montage->background_color=montage_info->background_color;
876        image_list+=tiles_per_page;
877        number_images-=tiles_per_page;
878      }
879  }
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));
888}
889