MagickCore  7.0.8
Convert, Edit, Or Compose Bitmap Images
montage.c
Go to the documentation of this file.
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-2018 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 % https://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"
55 #include "MagickCore/fx.h"
56 #include "MagickCore/gem.h"
57 #include "MagickCore/geometry.h"
58 #include "MagickCore/image.h"
60 #include "MagickCore/list.h"
61 #include "MagickCore/memory_.h"
63 #include "MagickCore/monitor.h"
65 #include "MagickCore/montage.h"
66 #include "MagickCore/option.h"
67 #include "MagickCore/pixel.h"
68 #include "MagickCore/quantize.h"
69 #include "MagickCore/property.h"
70 #include "MagickCore/resize.h"
71 #include "MagickCore/resource_.h"
72 #include "MagickCore/string_.h"
73 #include "MagickCore/utility.h"
75 #include "MagickCore/version.h"
76 
77 /*
78 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
79 % %
80 % %
81 % %
82 % C l o n e M o n t a g e I n f o %
83 % %
84 % %
85 % %
86 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
87 %
88 % CloneMontageInfo() makes a copy of the given montage info structure. If
89 % NULL is specified, a new image info structure is created initialized to
90 % default values.
91 %
92 % The format of the CloneMontageInfo method is:
93 %
94 % MontageInfo *CloneMontageInfo(const ImageInfo *image_info,
95 % const MontageInfo *montage_info)
96 %
97 % A description of each parameter follows:
98 %
99 % o image_info: the image info.
100 %
101 % o montage_info: the montage info.
102 %
103 */
105  const MontageInfo *montage_info)
106 {
108  *clone_info;
109 
110  clone_info=(MontageInfo *) AcquireCriticalMemory(sizeof(*clone_info));
111  GetMontageInfo(image_info,clone_info);
112  if (montage_info == (MontageInfo *) NULL)
113  return(clone_info);
114  if (montage_info->geometry != (char *) NULL)
115  clone_info->geometry=AcquireString(montage_info->geometry);
116  if (montage_info->tile != (char *) NULL)
117  clone_info->tile=AcquireString(montage_info->tile);
118  if (montage_info->title != (char *) NULL)
119  clone_info->title=AcquireString(montage_info->title);
120  if (montage_info->frame != (char *) NULL)
121  clone_info->frame=AcquireString(montage_info->frame);
122  if (montage_info->texture != (char *) NULL)
123  clone_info->texture=AcquireString(montage_info->texture);
124  if (montage_info->font != (char *) NULL)
125  clone_info->font=AcquireString(montage_info->font);
126  clone_info->pointsize=montage_info->pointsize;
127  clone_info->border_width=montage_info->border_width;
128  clone_info->shadow=montage_info->shadow;
129  clone_info->fill=montage_info->fill;
130  clone_info->stroke=montage_info->stroke;
131  clone_info->matte_color=montage_info->matte_color;
132  clone_info->background_color=montage_info->background_color;
133  clone_info->border_color=montage_info->border_color;
134  clone_info->gravity=montage_info->gravity;
135  (void) CopyMagickString(clone_info->filename,montage_info->filename,
137  clone_info->debug=IsEventLogging();
138  return(clone_info);
139 }
140 
141 /*
142 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
143 % %
144 % %
145 % %
146 % D e s t r o y M o n t a g e I n f o %
147 % %
148 % %
149 % %
150 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
151 %
152 % DestroyMontageInfo() deallocates memory associated with montage_info.
153 %
154 % The format of the DestroyMontageInfo method is:
155 %
156 % MontageInfo *DestroyMontageInfo(MontageInfo *montage_info)
157 %
158 % A description of each parameter follows:
159 %
160 % o montage_info: Specifies a pointer to an MontageInfo structure.
161 %
162 %
163 */
165 {
166  assert(montage_info != (MontageInfo *) NULL);
167  if (montage_info->debug != MagickFalse)
168  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
169  assert(montage_info->signature == MagickCoreSignature);
170  if (montage_info->geometry != (char *) NULL)
171  montage_info->geometry=(char *)
172  RelinquishMagickMemory(montage_info->geometry);
173  if (montage_info->tile != (char *) NULL)
174  montage_info->tile=DestroyString(montage_info->tile);
175  if (montage_info->title != (char *) NULL)
176  montage_info->title=DestroyString(montage_info->title);
177  if (montage_info->frame != (char *) NULL)
178  montage_info->frame=DestroyString(montage_info->frame);
179  if (montage_info->texture != (char *) NULL)
180  montage_info->texture=(char *) RelinquishMagickMemory(
181  montage_info->texture);
182  if (montage_info->font != (char *) NULL)
183  montage_info->font=DestroyString(montage_info->font);
184  montage_info->signature=(~MagickCoreSignature);
185  montage_info=(MontageInfo *) RelinquishMagickMemory(montage_info);
186  return(montage_info);
187 }
188 
189 /*
190 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
191 % %
192 % %
193 % %
194 % G e t M o n t a g e I n f o %
195 % %
196 % %
197 % %
198 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
199 %
200 % GetMontageInfo() initializes montage_info to default values.
201 %
202 % The format of the GetMontageInfo method is:
203 %
204 % void GetMontageInfo(const ImageInfo *image_info,
205 % MontageInfo *montage_info)
206 %
207 % A description of each parameter follows:
208 %
209 % o image_info: a structure of type ImageInfo.
210 %
211 % o montage_info: Specifies a pointer to a MontageInfo structure.
212 %
213 */
214 MagickExport void GetMontageInfo(const ImageInfo *image_info,
215  MontageInfo *montage_info)
216 {
217  assert(image_info != (const ImageInfo *) NULL);
218  assert(image_info->signature == MagickCoreSignature);
219  if (image_info->debug != MagickFalse)
221  image_info->filename);
222  assert(montage_info != (MontageInfo *) NULL);
223  (void) memset(montage_info,0,sizeof(*montage_info));
224  (void) CopyMagickString(montage_info->filename,image_info->filename,
227  if (image_info->font != (char *) NULL)
228  montage_info->font=AcquireString(image_info->font);
229  montage_info->gravity=CenterGravity;
230  montage_info->pointsize=image_info->pointsize;
231  montage_info->fill.alpha=(MagickRealType) OpaqueAlpha;
233  montage_info->matte_color=image_info->matte_color;
234  montage_info->background_color=image_info->background_color;
235  montage_info->border_color=image_info->border_color;
236  montage_info->debug=IsEventLogging();
237  montage_info->signature=MagickCoreSignature;
238 }
239 
240 /*
241 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
242 % %
243 % %
244 % %
245 % M o n t a g e I m a g e L i s t %
246 % %
247 % %
248 % %
249 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
250 %
251 % MontageImageList() is a layout manager that lets you tile one or more
252 % thumbnails across an image canvas.
253 %
254 % The format of the MontageImageList method is:
255 %
256 % Image *MontageImageList(const ImageInfo *image_info,
257 % const MontageInfo *montage_info,Image *images,
258 % ExceptionInfo *exception)
259 %
260 % A description of each parameter follows:
261 %
262 % o image_info: the image info.
263 %
264 % o montage_info: Specifies a pointer to a MontageInfo structure.
265 %
266 % o images: Specifies a pointer to an array of Image structures.
267 %
268 % o exception: return any errors or warnings in this structure.
269 %
270 */
271 
272 static void GetMontageGeometry(char *geometry,const size_t number_images,
273  ssize_t *x_offset,ssize_t *y_offset,size_t *tiles_per_column,
274  size_t *tiles_per_row)
275 {
276  *tiles_per_column=0;
277  *tiles_per_row=0;
278  (void) GetGeometry(geometry,x_offset,y_offset,tiles_per_row,tiles_per_column);
279  if ((*tiles_per_column == 0) && (*tiles_per_row == 0))
280  *tiles_per_column=(size_t) sqrt((double) number_images);
281  if ((*tiles_per_column == 0) && (*tiles_per_row != 0))
282  *tiles_per_column=(size_t) ceil((double) number_images/(*tiles_per_row));
283  if ((*tiles_per_row == 0) && (*tiles_per_column != 0))
284  *tiles_per_row=(size_t) ceil((double) number_images/(*tiles_per_column));
285 }
286 
287 #if defined(__cplusplus) || defined(c_plusplus)
288 extern "C" {
289 #endif
290 
291 static int SceneCompare(const void *x,const void *y)
292 {
293  Image
294  **image_1,
295  **image_2;
296 
297  image_1=(Image **) x;
298  image_2=(Image **) y;
299  return((int) ((*image_1)->scene-(*image_2)->scene));
300 }
301 
302 #if defined(__cplusplus) || defined(c_plusplus)
303 }
304 #endif
305 
307  const MontageInfo *montage_info,ExceptionInfo *exception)
308 {
309  Image
310  *montage_image;
311 
312  ImageInfo
313  *image_info;
314 
315  image_info=AcquireImageInfo();
316  montage_image=MontageImageList(image_info,montage_info,images,exception);
317  image_info=DestroyImageInfo(image_info);
318  return(montage_image);
319 }
320 
322  const MontageInfo *montage_info,const Image *images,ExceptionInfo *exception)
323 {
324 #define MontageImageTag "Montage/Image"
325 #define TileImageTag "Tile/Image"
326 
327  char
328  tile_geometry[MagickPathExtent],
329  *title;
330 
331  const char
332  *value;
333 
334  DrawInfo
335  *draw_info;
336 
337  FrameInfo
338  frame_info;
339 
340  Image
341  *image,
342  **image_list,
343  **master_list,
344  *montage,
345  *texture,
346  *tile_image,
347  *thumbnail;
348 
349  ImageInfo
350  *clone_info;
351 
353  concatenate,
354  proceed,
355  status;
356 
358  tiles;
359 
361  progress_monitor;
362 
364  flags;
365 
366  register ssize_t
367  i;
368 
370  bounds,
371  geometry,
372  extract_info;
373 
374  size_t
375  border_width,
376  extent,
377  height,
378  images_per_page,
379  max_height,
380  number_images,
381  number_lines,
382  sans,
383  tiles_per_column,
384  tiles_per_page,
385  tiles_per_row,
386  title_offset,
387  total_tiles,
388  width;
389 
390  ssize_t
391  bevel_width,
392  tile,
393  x,
394  x_offset,
395  y,
396  y_offset;
397 
398  TypeMetric
399  metrics;
400 
401  /*
402  Create image tiles.
403  */
404  assert(images != (Image *) NULL);
405  assert(images->signature == MagickCoreSignature);
406  if (images->debug != MagickFalse)
407  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
408  assert(montage_info != (MontageInfo *) NULL);
409  assert(montage_info->signature == MagickCoreSignature);
410  assert(exception != (ExceptionInfo *) NULL);
411  assert(exception->signature == MagickCoreSignature);
412  number_images=GetImageListLength(images);
413  master_list=ImageListToArray(images,exception);
414  if (master_list == (Image **) NULL)
415  return((Image *) NULL);
416  image_list=master_list;
417  image=image_list[0];
418  thumbnail=NewImageList();
419  for (i=0; i < (ssize_t) number_images; i++)
420  {
421  image=CloneImage(image_list[i],0,0,MagickTrue,exception);
422  if (image == (Image *) NULL)
423  break;
424  (void) ParseAbsoluteGeometry("0x0+0+0",&image->page);
425  progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL,
426  image->client_data);
427  flags=ParseRegionGeometry(image,montage_info->geometry,&geometry,exception);
428  thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
429  if (thumbnail == (Image *) NULL)
430  break;
431  image_list[i]=thumbnail;
432  (void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
434  number_images);
435  if (proceed == MagickFalse)
436  break;
437  image=DestroyImage(image);
438  }
439  if (i < (ssize_t) number_images)
440  {
441  if (image != (Image *) NULL)
442  image=DestroyImage(image);
443  if (thumbnail == (Image *) NULL)
444  i--;
445  for (tile=0; (ssize_t) tile <= i; tile++)
446  if (image_list[tile] != (Image *) NULL)
447  image_list[tile]=DestroyImage(image_list[tile]);
448  master_list=(Image **) RelinquishMagickMemory(master_list);
449  return((Image *) NULL);
450  }
451  /*
452  Sort image list by increasing tile number.
453  */
454  for (i=0; i < (ssize_t) number_images; i++)
455  if (image_list[i]->scene == 0)
456  break;
457  if (i == (ssize_t) number_images)
458  qsort((void *) image_list,(size_t) number_images,sizeof(*image_list),
459  SceneCompare);
460  /*
461  Determine tiles per row and column.
462  */
463  tiles_per_column=(size_t) sqrt((double) number_images);
464  tiles_per_row=(size_t) ceil((double) number_images/tiles_per_column);
465  x_offset=0;
466  y_offset=0;
467  if (montage_info->tile != (char *) NULL)
468  GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
469  &tiles_per_column,&tiles_per_row);
470  /*
471  Determine tile sizes.
472  */
473  concatenate=MagickFalse;
474  SetGeometry(image_list[0],&extract_info);
475  extract_info.x=(ssize_t) montage_info->border_width;
476  extract_info.y=(ssize_t) montage_info->border_width;
477  if (montage_info->geometry != (char *) NULL)
478  {
479  /*
480  Initialize tile geometry.
481  */
482  flags=GetGeometry(montage_info->geometry,&extract_info.x,&extract_info.y,
483  &extract_info.width,&extract_info.height);
484  concatenate=((flags & RhoValue) == 0) && ((flags & SigmaValue) == 0) ?
486  }
487  border_width=montage_info->border_width;
488  bevel_width=0;
489  (void) memset(&frame_info,0,sizeof(frame_info));
490  if (montage_info->frame != (char *) NULL)
491  {
492  char
493  absolute_geometry[MagickPathExtent];
494 
495  frame_info.width=extract_info.width;
496  frame_info.height=extract_info.height;
497  (void) FormatLocaleString(absolute_geometry,MagickPathExtent,"%s!",
498  montage_info->frame);
499  flags=ParseMetaGeometry(absolute_geometry,&frame_info.outer_bevel,
500  &frame_info.inner_bevel,&frame_info.width,&frame_info.height);
501  if ((flags & HeightValue) == 0)
502  frame_info.height=frame_info.width;
503  if ((flags & XiValue) == 0)
504  frame_info.outer_bevel=(ssize_t) frame_info.width/2-1;
505  if ((flags & PsiValue) == 0)
506  frame_info.inner_bevel=frame_info.outer_bevel;
507  frame_info.x=(ssize_t) frame_info.width;
508  frame_info.y=(ssize_t) frame_info.height;
509  bevel_width=(ssize_t) MagickMax(frame_info.inner_bevel,
510  frame_info.outer_bevel);
511  border_width=(size_t) MagickMax((ssize_t) frame_info.width,
512  (ssize_t) frame_info.height);
513  }
514  for (i=0; i < (ssize_t) number_images; i++)
515  {
516  if (image_list[i]->columns > extract_info.width)
517  extract_info.width=image_list[i]->columns;
518  if (image_list[i]->rows > extract_info.height)
519  extract_info.height=image_list[i]->rows;
520  }
521  /*
522  Initialize draw attributes.
523  */
524  clone_info=CloneImageInfo(image_info);
525  clone_info->background_color=montage_info->background_color;
526  clone_info->border_color=montage_info->border_color;
527  draw_info=CloneDrawInfo(clone_info,(DrawInfo *) NULL);
528  if (montage_info->font != (char *) NULL)
529  (void) CloneString(&draw_info->font,montage_info->font);
530  if (montage_info->pointsize != 0.0)
531  draw_info->pointsize=montage_info->pointsize;
532  draw_info->gravity=CenterGravity;
533  draw_info->stroke=montage_info->stroke;
534  draw_info->fill=montage_info->fill;
535  draw_info->text=AcquireString("");
536  (void) GetTypeMetrics(image_list[0],draw_info,&metrics,exception);
537  texture=NewImageList();
538  if (montage_info->texture != (char *) NULL)
539  {
540  (void) CopyMagickString(clone_info->filename,montage_info->texture,
542  texture=ReadImage(clone_info,exception);
543  }
544  /*
545  Determine the number of lines in an next label.
546  */
547  title=InterpretImageProperties(clone_info,image_list[0],montage_info->title,
548  exception);
549  title_offset=0;
550  if (montage_info->title != (char *) NULL)
551  title_offset=(size_t) (2*(metrics.ascent-metrics.descent)*
552  MultilineCensus(title)+2*extract_info.y);
553  number_lines=0;
554  for (i=0; i < (ssize_t) number_images; i++)
555  {
556  value=GetImageProperty(image_list[i],"label",exception);
557  if (value == (const char *) NULL)
558  continue;
559  if (MultilineCensus(value) > number_lines)
560  number_lines=MultilineCensus(value);
561  }
562  /*
563  Allocate next structure.
564  */
565  tile_image=AcquireImage((ImageInfo *) NULL,exception);
566  montage=AcquireImage(clone_info,exception);
567  montage->background_color=montage_info->background_color;
568  montage->scene=0;
569  images_per_page=(number_images-1)/(tiles_per_row*tiles_per_column)+1;
570  tiles=0;
571  total_tiles=(size_t) number_images;
572  for (i=0; i < (ssize_t) images_per_page; i++)
573  {
574  /*
575  Determine bounding box.
576  */
577  tiles_per_page=tiles_per_row*tiles_per_column;
578  x_offset=0;
579  y_offset=0;
580  if (montage_info->tile != (char *) NULL)
581  GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
582  &sans,&sans);
583  tiles_per_page=tiles_per_row*tiles_per_column;
584  y_offset+=(ssize_t) title_offset;
585  max_height=0;
586  bounds.width=0;
587  bounds.height=0;
588  width=0;
589  for (tile=0; tile < (ssize_t) tiles_per_page; tile++)
590  {
591  if (tile < (ssize_t) number_images)
592  {
593  width=concatenate != MagickFalse ? image_list[tile]->columns :
594  extract_info.width;
595  if (image_list[tile]->rows > max_height)
596  max_height=image_list[tile]->rows;
597  }
598  x_offset+=(ssize_t) (width+2*(extract_info.x+border_width));
599  if (x_offset > (ssize_t) bounds.width)
600  bounds.width=(size_t) x_offset;
601  if (((tile+1) == (ssize_t) tiles_per_page) ||
602  (((tile+1) % tiles_per_row) == 0))
603  {
604  x_offset=0;
605  if (montage_info->tile != (char *) NULL)
606  GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y,
607  &sans,&sans);
608  height=concatenate != MagickFalse ? max_height : extract_info.height;
609  y_offset+=(ssize_t) (height+(extract_info.y+(ssize_t) border_width)*2+
610  (metrics.ascent-metrics.descent+4)*number_lines+
611  (montage_info->shadow != MagickFalse ? 4 : 0));
612  if (y_offset > (ssize_t) bounds.height)
613  bounds.height=(size_t) y_offset;
614  max_height=0;
615  }
616  }
617  if (montage_info->shadow != MagickFalse)
618  bounds.width+=4;
619  /*
620  Initialize montage image.
621  */
622  (void) CopyMagickString(montage->filename,montage_info->filename,
624  montage->columns=(size_t) MagickMax((ssize_t) bounds.width,1);
625  montage->rows=(size_t) MagickMax((ssize_t) bounds.height,1);
626  (void) SetImageBackgroundColor(montage,exception);
627  /*
628  Set montage geometry.
629  */
630  montage->montage=AcquireString((char *) NULL);
631  tile=0;
632  extent=1;
633  while (tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images))
634  {
635  extent+=strlen(image_list[tile]->filename)+1;
636  tile++;
637  }
638  montage->directory=(char *) AcquireQuantumMemory(extent,
639  sizeof(*montage->directory));
640  if ((montage->montage == (char *) NULL) ||
641  (montage->directory == (char *) NULL))
642  {
643  if (montage->montage != (char *) NULL)
644  montage->montage=(char *) RelinquishMagickMemory(montage->montage);
645  if (montage->directory != (char *) NULL)
646  montage->directory=(char *) RelinquishMagickMemory(
647  montage->directory);
648  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
649  }
650  x_offset=0;
651  y_offset=0;
652  if (montage_info->tile != (char *) NULL)
653  GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
654  &sans,&sans);
655  y_offset+=(ssize_t) title_offset;
657  "%.20gx%.20g%+.20g%+.20g",(double) (extract_info.width+
658  (extract_info.x+border_width)*2),(double) (extract_info.height+
659  (extract_info.y+border_width)*2+(double) ((metrics.ascent-
660  metrics.descent+4)*number_lines+(montage_info->shadow != MagickFalse ? 4 :
661  0))),(double) x_offset,(double) y_offset);
662  *montage->directory='\0';
663  tile=0;
664  while (tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images))
665  {
666  (void) ConcatenateMagickString(montage->directory,
667  image_list[tile]->filename,extent);
668  (void) ConcatenateMagickString(montage->directory,"\xff",extent);
669  tile++;
670  }
671  progress_monitor=SetImageProgressMonitor(montage,(MagickProgressMonitor)
672  NULL,montage->client_data);
673  if (texture != (Image *) NULL)
674  (void) TextureImage(montage,texture,exception);
675  if (montage_info->title != (char *) NULL)
676  {
677  DrawInfo
678  *draw_clone_info;
679 
680  TypeMetric
681  tile_metrics;
682 
683  /*
684  Annotate composite image with title.
685  */
686  draw_clone_info=CloneDrawInfo(image_info,draw_info);
687  draw_clone_info->gravity=CenterGravity;
688  draw_clone_info->pointsize*=2.0;
689  (void) GetTypeMetrics(image_list[0],draw_clone_info,&tile_metrics,
690  exception);
691  (void) FormatLocaleString(tile_geometry,MagickPathExtent,
692  "%.20gx%.20g%+.20g%+.20g",(double) montage->columns,(double)
693  (tile_metrics.ascent-tile_metrics.descent),0.0,
694  (double) extract_info.y+4);
695  (void) CloneString(&draw_clone_info->geometry,tile_geometry);
696  (void) CloneString(&draw_clone_info->text,title);
697  (void) AnnotateImage(montage,draw_clone_info,exception);
698  draw_clone_info=DestroyDrawInfo(draw_clone_info);
699  }
700  (void) SetImageProgressMonitor(montage,progress_monitor,
701  montage->client_data);
702  /*
703  Copy tile to the composite.
704  */
705  x_offset=0;
706  y_offset=0;
707  if (montage_info->tile != (char *) NULL)
708  GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
709  &sans,&sans);
710  x_offset+=extract_info.x;
711  y_offset+=(ssize_t) title_offset+extract_info.y;
712  max_height=0;
713  status=MagickTrue;
714  for (tile=0; tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images); tile++)
715  {
716  /*
717  Copy this tile to the composite.
718  */
719  image=CloneImage(image_list[tile],0,0,MagickTrue,exception);
720  if (image == (Image *) NULL)
721  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
722  progress_monitor=SetImageProgressMonitor(image,
723  (MagickProgressMonitor) NULL,image->client_data);
724  width=concatenate != MagickFalse ? image->columns : extract_info.width;
725  if (image->rows > max_height)
726  max_height=image->rows;
727  height=concatenate != MagickFalse ? max_height : extract_info.height;
728  if (border_width != 0)
729  {
730  Image
731  *border_image;
732 
734  border_info;
735 
736  /*
737  Put a border around the image.
738  */
739  border_info.width=border_width;
740  border_info.height=border_width;
741  if (montage_info->frame != (char *) NULL)
742  {
743  border_info.width=(width-image->columns+1)/2;
744  border_info.height=(height-image->rows+1)/2;
745  }
746  border_image=BorderImage(image,&border_info,image->compose,exception);
747  if (border_image != (Image *) NULL)
748  {
749  image=DestroyImage(image);
750  image=border_image;
751  }
752  if ((montage_info->frame != (char *) NULL) &&
753  (image->compose == DstOutCompositeOp))
754  {
755  (void) SetPixelChannelMask(image,AlphaChannel);
756  (void) NegateImage(image,MagickFalse,exception);
757  (void) SetPixelChannelMask(image,DefaultChannels);
758  }
759  }
760  /*
761  Gravitate as specified by the tile gravity.
762  */
763  tile_image->columns=width;
764  tile_image->rows=height;
765  tile_image->gravity=montage_info->gravity;
766  if (image->gravity != UndefinedGravity)
767  tile_image->gravity=image->gravity;
768  (void) FormatLocaleString(tile_geometry,MagickPathExtent,
769  "%.20gx%.20g+0+0",(double) image->columns,(double) image->rows);
770  flags=ParseGravityGeometry(tile_image,tile_geometry,&geometry,exception);
771  x=(ssize_t) (geometry.x+border_width);
772  y=(ssize_t) (geometry.y+border_width);
773  if ((montage_info->frame != (char *) NULL) && (bevel_width > 0))
774  {
775  FrameInfo
776  frame_clone;
777 
778  Image
779  *frame_image;
780 
781  /*
782  Put an ornamental border around this tile.
783  */
784  frame_clone=frame_info;
785  frame_clone.width=width+2*frame_info.width;
786  frame_clone.height=height+2*frame_info.height;
787  value=GetImageProperty(image,"label",exception);
788  if (value != (const char *) NULL)
789  frame_clone.height+=(size_t) ((metrics.ascent-metrics.descent+4)*
790  MultilineCensus(value));
791  frame_image=FrameImage(image,&frame_clone,image->compose,exception);
792  if (frame_image != (Image *) NULL)
793  {
794  image=DestroyImage(image);
795  image=frame_image;
796  }
797  x=0;
798  y=0;
799  }
800  if (LocaleCompare(image->magick,"NULL") != 0)
801  {
802  /*
803  Composite background with tile.
804  */
805  if (montage_info->shadow != MagickFalse)
806  {
807  Image
808  *shadow_image;
809 
810  /*
811  Shadow image.
812  */
813  (void) QueryColorCompliance("#0000",AllCompliance,
814  &image->background_color,exception);
815  shadow_image=ShadowImage(image,80.0,2.0,5,5,exception);
816  if (shadow_image != (Image *) NULL)
817  {
818  (void) CompositeImage(shadow_image,image,OverCompositeOp,
819  MagickTrue,0,0,exception);
820  image=DestroyImage(image);
821  image=shadow_image;
822  }
823  }
824  (void) CompositeImage(montage,image,image->compose,MagickTrue,
825  x_offset+x,y_offset+y,exception);
826  value=GetImageProperty(image,"label",exception);
827  if (value != (const char *) NULL)
828  {
829  /*
830  Annotate composite tile with label.
831  */
832  (void) FormatLocaleString(tile_geometry,MagickPathExtent,
833  "%.20gx%.20g%+.20g%+.20g",(double) ((montage_info->frame ?
834  image->columns : width)-2*border_width),(double)
835  (metrics.ascent-metrics.descent+4)*MultilineCensus(value),
836  (double) (x_offset+border_width),(double)
837  ((montage_info->frame ? y_offset+height+border_width+4 :
838  y_offset+extract_info.height+border_width+
839  (montage_info->shadow != MagickFalse ? 4 : 0))+bevel_width));
840  (void) CloneString(&draw_info->geometry,tile_geometry);
841  (void) CloneString(&draw_info->text,value);
842  (void) AnnotateImage(montage,draw_info,exception);
843  }
844  }
845  x_offset+=(ssize_t) (width+2*(extract_info.x+border_width));
846  if (((tile+1) == (ssize_t) tiles_per_page) ||
847  (((tile+1) % tiles_per_row) == 0))
848  {
849  x_offset=extract_info.x;
850  y_offset+=(ssize_t) (height+(extract_info.y+border_width)*2+
851  (metrics.ascent-metrics.descent+4)*number_lines+
852  (montage_info->shadow != MagickFalse ? 4 : 0));
853  max_height=0;
854  }
855  if (images->progress_monitor != (MagickProgressMonitor) NULL)
856  {
857  proceed=SetImageProgress(image,MontageImageTag,tiles,total_tiles);
858  if (proceed == MagickFalse)
859  status=MagickFalse;
860  }
861  image_list[tile]=DestroyImage(image_list[tile]);
862  image=DestroyImage(image);
863  tiles++;
864  }
865  (void) status;
866  if ((i+1) < (ssize_t) images_per_page)
867  {
868  /*
869  Allocate next image structure.
870  */
871  AcquireNextImage(clone_info,montage,exception);
872  if (GetNextImageInList(montage) == (Image *) NULL)
873  {
874  montage=DestroyImageList(montage);
875  return((Image *) NULL);
876  }
877  montage=GetNextImageInList(montage);
878  montage->background_color=montage_info->background_color;
879  image_list+=tiles_per_page;
880  number_images-=tiles_per_page;
881  }
882  }
883  tile_image=DestroyImage(tile_image);
884  if (texture != (Image *) NULL)
885  texture=DestroyImage(texture);
886  title=DestroyString(title);
887  master_list=(Image **) RelinquishMagickMemory(master_list);
888  draw_info=DestroyDrawInfo(draw_info);
889  clone_info=DestroyImageInfo(clone_info);
890  return(GetFirstImageInList(montage));
891 }
size_t rows
Definition: image.h:172
MagickExport MagickBooleanType TextureImage(Image *image, const Image *texture, ExceptionInfo *exception)
Definition: composite.c:2382
PixelInfo fill
Definition: draw.h:214
MagickDoubleType MagickRealType
Definition: magick-type.h:120
MagickExport MagickBooleanType NegateImage(Image *image, const MagickBooleanType grayscale, ExceptionInfo *exception)
Definition: enhance.c:3352
#define MontageImageTag
#define TransparentAlpha
Definition: image.h:26
size_t signature
Definition: image.h:488
static MagickBooleanType SetImageProgress(const Image *image, const char *tag, const MagickOffsetType offset, const MagickSizeType extent)
MagickExport ImageInfo * AcquireImageInfo(void)
Definition: image.c:343
MagickExport Image * FrameImage(const Image *image, const FrameInfo *frame_info, const CompositeOperator compose, ExceptionInfo *exception)
Definition: decorate.c:169
MagickExport Image * MontageImages(const Image *images, const MontageInfo *montage_info, ExceptionInfo *exception)
Definition: montage.c:306
MagickProgressMonitor progress_monitor
Definition: image.h:303
PixelInfo fill
Definition: montage.h:53
GravityType gravity
Definition: draw.h:285
#define TileImageTag
double pointsize
Definition: image.h:420
char * geometry
Definition: montage.h:36
char * tile
Definition: montage.h:36
PixelInfo stroke
Definition: draw.h:214
size_t signature
Definition: exception.h:123
MagickExport const char DefaultTileGeometry[]
Definition: image.c:113
MagickExport MagickStatusType ParseAbsoluteGeometry(const char *geometry, RectangleInfo *region_info)
Definition: geometry.c:703
PixelInfo stroke
Definition: montage.h:53
MagickExport size_t ConcatenateMagickString(char *destination, const char *source, const size_t length)
Definition: string.c:426
MagickBooleanType debug
Definition: image.h:485
char * font
Definition: image.h:413
#define OpaqueAlpha
Definition: image.h:25
MagickExport MagickStatusType ParseMetaGeometry(const char *geometry, ssize_t *x, ssize_t *y, size_t *width, size_t *height)
Definition: geometry.c:1340
ssize_t outer_bevel
Definition: decorate.h:34
double pointsize
Definition: draw.h:276
MagickExport ssize_t FormatLocaleString(char *magick_restrict string, const size_t length, const char *magick_restrict format,...)
Definition: locale.c:504
static void * AcquireCriticalMemory(const size_t size)
MagickExport void GetMontageInfo(const ImageInfo *image_info, MontageInfo *montage_info)
Definition: montage.c:214
char magick[MagickPathExtent]
Definition: image.h:319
double pointsize
Definition: montage.h:44
size_t border_width
Definition: montage.h:47
MagickRealType alpha
Definition: pixel.h:191
char * montage
Definition: image.h:201
MagickExport MagickBooleanType CompositeImage(Image *image, const Image *composite, const CompositeOperator compose, const MagickBooleanType clip_to_self, const ssize_t x_offset, const ssize_t y_offset, ExceptionInfo *exception)
Definition: composite.c:528
size_t width
Definition: geometry.h:130
PixelInfo background_color
Definition: montage.h:53
Definition: log.h:52
ssize_t MagickOffsetType
Definition: magick-type.h:129
MagickExport Image * ThumbnailImage(const Image *image, const size_t columns, const size_t rows, ExceptionInfo *exception)
Definition: resize.c:3667
Definition: image.h:151
GravityType gravity
Definition: montage.h:60
char * frame
Definition: montage.h:36
static void GetMontageGeometry(char *geometry, const size_t number_images, ssize_t *x_offset, ssize_t *y_offset, size_t *tiles_per_column, size_t *tiles_per_row)
Definition: montage.c:272
#define MagickCoreSignature
char * font
Definition: montage.h:36
MagickExport Image * GetFirstImageInList(const Image *images)
Definition: list.c:555
MagickExport size_t MultilineCensus(const char *label)
Definition: utility.c:1765
MagickExport Image * MontageImageList(const ImageInfo *image_info, const MontageInfo *montage_info, const Image *images, ExceptionInfo *exception)
Definition: montage.c:321
MagickBooleanType
Definition: magick-type.h:158
PixelInfo matte_color
Definition: image.h:494
MagickExport Image * NewImageList(void)
Definition: list.c:932
size_t scene
Definition: image.h:240
unsigned int MagickStatusType
Definition: magick-type.h:121
MagickExport char * AcquireString(const char *source)
Definition: string.c:129
MagickExport MagickBooleanType AnnotateImage(Image *image, const DrawInfo *draw_info, ExceptionInfo *exception)
Definition: annotate.c:272
PixelInfo matte_color
Definition: montage.h:72
char filename[MagickPathExtent]
Definition: montage.h:63
double descent
Definition: draw.h:369
MagickExport void * AcquireQuantumMemory(const size_t count, const size_t quantum)
Definition: memory.c:533
char filename[MagickPathExtent]
Definition: image.h:480
MagickBooleanType debug
Definition: montage.h:66
GravityType gravity
Definition: image.h:231
RectangleInfo page
Definition: image.h:212
#define MagickPathExtent
ssize_t y
Definition: decorate.h:34
MagickExport MagickBooleanType IsEventLogging(void)
Definition: log.c:717
MagickExport void AcquireNextImage(const ImageInfo *image_info, Image *image, ExceptionInfo *exception)
Definition: image.c:384
MagickExport ChannelType SetPixelChannelMask(Image *image, const ChannelType channel_mask)
Definition: pixel.c:6276
MagickExport Image * ReadImage(const ImageInfo *image_info, ExceptionInfo *exception)
Definition: constitute.c:415
size_t signature
Definition: montage.h:69
MagickExport MagickBooleanType LogMagickEvent(const LogEventType type, const char *module, const char *function, const size_t line, const char *format,...)
Definition: log.c:1398
MagickExport MagickBooleanType SetImageBackgroundColor(Image *image, ExceptionInfo *exception)
Definition: image.c:2413
size_t signature
Definition: image.h:354
size_t columns
Definition: image.h:172
MagickExport Image * AcquireImage(const ImageInfo *image_info, ExceptionInfo *exception)
Definition: image.c:155
ssize_t x
Definition: geometry.h:134
MagickBooleanType(* MagickProgressMonitor)(const char *, const MagickOffsetType, const MagickSizeType, void *)
Definition: monitor.h:26
MagickExport DrawInfo * CloneDrawInfo(const ImageInfo *image_info, const DrawInfo *draw_info)
Definition: draw.c:269
size_t height
Definition: geometry.h:130
MagickExport MontageInfo * DestroyMontageInfo(MontageInfo *montage_info)
Definition: montage.c:164
MagickExport MagickBooleanType QueryColorCompliance(const char *name, const ComplianceType compliance, PixelInfo *color, ExceptionInfo *exception)
Definition: color.c:2216
MagickExport MagickProgressMonitor SetImageProgressMonitor(Image *image, const MagickProgressMonitor progress_monitor, void *client_data)
Definition: monitor.c:85
MagickExport size_t CopyMagickString(char *destination, const char *source, const size_t length)
Definition: string.c:755
MagickExport Image * DestroyImageList(Image *images)
Definition: list.c:456
ssize_t x
Definition: decorate.h:34
static int SceneCompare(const void *x, const void *y)
Definition: montage.c:291
#define MagickMax(x, y)
Definition: image-private.h:26
PixelInfo border_color
Definition: montage.h:53
MagickExport int LocaleCompare(const char *p, const char *q)
Definition: locale.c:1440
PixelInfo border_color
Definition: image.h:424
char filename[MagickPathExtent]
Definition: image.h:319
char * texture
Definition: montage.h:36
#define GetMagickModule()
Definition: log.h:28
MagickExport Image * ShadowImage(const Image *image, const double alpha, const double sigma, const ssize_t x_offset, const ssize_t y_offset, ExceptionInfo *exception)
Definition: fx.c:4362
#define ThrowImageException(severity, tag)
MagickExport Image ** ImageListToArray(const Image *images, ExceptionInfo *exception)
Definition: list.c:838
MagickExport char * InterpretImageProperties(ImageInfo *image_info, Image *image, const char *embed_text, ExceptionInfo *exception)
Definition: property.c:3426
MagickExport ImageInfo * DestroyImageInfo(ImageInfo *image_info)
Definition: image.c:1248
MagickExport MagickBooleanType GetTypeMetrics(Image *image, const DrawInfo *draw_info, TypeMetric *metrics, ExceptionInfo *exception)
Definition: annotate.c:850
char * geometry
Definition: draw.h:204
MagickExport MagickStatusType GetGeometry(const char *geometry, ssize_t *x, ssize_t *y, size_t *width, size_t *height)
Definition: geometry.c:97
ssize_t inner_bevel
Definition: decorate.h:34
MagickExport Image * BorderImage(const Image *image, const RectangleInfo *border_info, const CompositeOperator compose, ExceptionInfo *exception)
Definition: decorate.c:103
MagickExport DrawInfo * DestroyDrawInfo(DrawInfo *draw_info)
Definition: draw.c:878
MagickExport MagickStatusType ParseGravityGeometry(const Image *image, const char *geometry, RectangleInfo *region_info, ExceptionInfo *exception)
Definition: geometry.c:1204
MagickExport Image * GetNextImageInList(const Image *images)
Definition: list.c:765
MagickExport char * DestroyString(char *string)
Definition: string.c:823
size_t height
Definition: decorate.h:30
MagickExport const char * GetImageProperty(const Image *image, const char *property, ExceptionInfo *exception)
Definition: property.c:2266
MagickExport ImageInfo * CloneImageInfo(const ImageInfo *image_info)
Definition: image.c:937
char * directory
Definition: image.h:201
size_t width
Definition: decorate.h:30
#define MagickMin(x, y)
Definition: image-private.h:27
MagickExport void SetGeometry(const Image *image, RectangleInfo *geometry)
Definition: geometry.c:1658
char * text
Definition: draw.h:255
MagickExport void * RelinquishMagickMemory(void *memory)
Definition: memory.c:1054
char * title
Definition: montage.h:36
MagickBooleanType shadow
Definition: montage.h:50
double ascent
Definition: draw.h:369
MagickExport char * CloneString(char **destination, const char *source)
Definition: string.c:286
CompositeOperator compose
Definition: image.h:234
#define MagickExport
ssize_t y
Definition: geometry.h:134
PixelInfo background_color
Definition: image.h:179
MagickExport size_t GetImageListLength(const Image *images)
Definition: list.c:690
char * font
Definition: draw.h:255
MagickExport MontageInfo * CloneMontageInfo(const ImageInfo *image_info, const MontageInfo *montage_info)
Definition: montage.c:104
void * client_data
Definition: image.h:306
MagickExport Image * DestroyImage(Image *image)
Definition: image.c:1177
MagickExport Image * CloneImage(const Image *image, const size_t columns, const size_t rows, const MagickBooleanType detach, ExceptionInfo *exception)
Definition: image.c:794
PixelInfo background_color
Definition: image.h:424
MagickExport MagickStatusType ParseRegionGeometry(const Image *image, const char *geometry, RectangleInfo *region_info, ExceptionInfo *exception)
Definition: geometry.c:1619
MagickBooleanType debug
Definition: image.h:334