MagickCore  7.1.1-43
Convert, Edit, Or Compose Bitmap Images
montage.c
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 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"
54 #include "MagickCore/exception-private.h"
55 #include "MagickCore/gem.h"
56 #include "MagickCore/geometry.h"
57 #include "MagickCore/image.h"
58 #include "MagickCore/image-private.h"
59 #include "MagickCore/list.h"
60 #include "MagickCore/memory_.h"
61 #include "MagickCore/memory-private.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 #include "MagickCore/visual-effects.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 */
104 MagickExport MontageInfo *CloneMontageInfo(const ImageInfo *image_info,
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,
136  MagickPathExtent);
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 */
164 MagickExport MontageInfo *DestroyMontageInfo(MontageInfo *montage_info)
165 {
166  assert(montage_info != (MontageInfo *) NULL);
167  assert(montage_info->signature == MagickCoreSignature);
168  if (IsEventLogging() != MagickFalse)
169  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
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  assert(montage_info != (MontageInfo *) NULL);
220  if (IsEventLogging() != MagickFalse)
221  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
222  image_info->filename);
223  (void) memset(montage_info,0,sizeof(*montage_info));
224  (void) CopyMagickString(montage_info->filename,image_info->filename,
225  MagickPathExtent);
226  montage_info->geometry=AcquireString(DefaultTileGeometry);
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;
232  montage_info->stroke.alpha=(MagickRealType) TransparentAlpha;
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 
306 MagickExport Image *MontageImages(const Image *images,
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 
321 MagickExport Image *MontageImageList(const ImageInfo *image_info,
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  **primary_list,
344  *montage,
345  *texture,
346  *tile_image,
347  *thumbnail;
348 
349  ImageInfo
350  *clone_info;
351 
352  MagickBooleanType
353  concatenate,
354  proceed,
355  status;
356 
357  MagickOffsetType
358  tiles;
359 
360  MagickProgressMonitor
361  progress_monitor;
362 
363  MagickStatusType
364  flags;
365 
366  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  assert(montage_info != (MontageInfo *) NULL);
407  assert(montage_info->signature == MagickCoreSignature);
408  assert(exception != (ExceptionInfo *) NULL);
409  assert(exception->signature == MagickCoreSignature);
410  if (IsEventLogging() != MagickFalse)
411  (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
412  number_images=GetImageListLength(images);
413  primary_list=ImageListToArray(images,exception);
414  if (primary_list == (Image **) NULL)
415  return((Image *) NULL);
416  image_list=primary_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);
433  proceed=SetImageProgress(image,TileImageTag,(MagickOffsetType) i,
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  primary_list=(Image **) RelinquishMagickMemory(primary_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) ?
485  MagickTrue : MagickFalse;
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,
541  MagickPathExtent);
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+(ssize_t) 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) % (ssize_t) 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)*(ssize_t) 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,
623  MagickPathExtent);
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;
656  (void) FormatLocaleString(montage->montage,MagickPathExtent,
657  "%.20gx%.20g%+.20g%+.20g",(double) ((ssize_t) extract_info.width+
658  (extract_info.x+(ssize_t) border_width)*2),(double) ((ssize_t)
659  extract_info.height+(extract_info.y+(ssize_t) border_width)*2+(double)
660  ((metrics.ascent-metrics.descent+4)*number_lines+
661  (montage_info->shadow != MagickFalse ? 4 : 0))),(double) x_offset,
662  (double) y_offset);
663  *montage->directory='\0';
664  tile=0;
665  while (tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images))
666  {
667  if (strchr(image_list[tile]->filename,(int) '\xff') == (char *) NULL)
668  (void) ConcatenateMagickString(montage->directory,
669  image_list[tile]->filename,extent);
670  else
671  (void) ThrowMagickException(exception,GetMagickModule(),OptionError,
672  "InvalidArgument","'%s'",image_list[tile]->filename);
673  (void) ConcatenateMagickString(montage->directory,"\xff",extent);
674  tile++;
675  }
676  progress_monitor=SetImageProgressMonitor(montage,(MagickProgressMonitor)
677  NULL,montage->client_data);
678  if (texture != (Image *) NULL)
679  (void) TextureImage(montage,texture,exception);
680  if (montage_info->title != (char *) NULL)
681  {
682  DrawInfo
683  *draw_clone_info;
684 
685  TypeMetric
686  tile_metrics;
687 
688  /*
689  Annotate composite image with title.
690  */
691  draw_clone_info=CloneDrawInfo(image_info,draw_info);
692  draw_clone_info->gravity=CenterGravity;
693  draw_clone_info->pointsize*=2.0;
694  (void) GetTypeMetrics(image_list[0],draw_clone_info,&tile_metrics,
695  exception);
696  (void) FormatLocaleString(tile_geometry,MagickPathExtent,
697  "%.20gx%.20g%+.20g%+.20g",(double) montage->columns,(double)
698  (tile_metrics.ascent-tile_metrics.descent),0.0,
699  (double) extract_info.y+4);
700  (void) CloneString(&draw_clone_info->geometry,tile_geometry);
701  (void) CloneString(&draw_clone_info->text,title);
702  (void) AnnotateImage(montage,draw_clone_info,exception);
703  draw_clone_info=DestroyDrawInfo(draw_clone_info);
704  }
705  (void) SetImageProgressMonitor(montage,progress_monitor,
706  montage->client_data);
707  /*
708  Copy tile to the composite.
709  */
710  x_offset=0;
711  y_offset=0;
712  if (montage_info->tile != (char *) NULL)
713  GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
714  &sans,&sans);
715  x_offset+=extract_info.x;
716  y_offset+=(ssize_t) title_offset+extract_info.y;
717  max_height=0;
718  status=MagickTrue;
719  for (tile=0; tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images); tile++)
720  {
721  /*
722  Copy this tile to the composite.
723  */
724  image=CloneImage(image_list[tile],0,0,MagickTrue,exception);
725  if (image == (Image *) NULL)
726  ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
727  progress_monitor=SetImageProgressMonitor(image,
728  (MagickProgressMonitor) NULL,image->client_data);
729  width=concatenate != MagickFalse ? image->columns : extract_info.width;
730  if (image->rows > max_height)
731  max_height=image->rows;
732  height=concatenate != MagickFalse ? max_height : extract_info.height;
733  if (border_width != 0)
734  {
735  Image
736  *border_image;
737 
739  border_info;
740 
741  /*
742  Put a border around the image.
743  */
744  border_info.width=border_width;
745  border_info.height=border_width;
746  if (montage_info->frame != (char *) NULL)
747  {
748  border_info.width=(width-image->columns+1)/2;
749  border_info.height=(height-image->rows+1)/2;
750  }
751  border_image=BorderImage(image,&border_info,image->compose,exception);
752  if (border_image != (Image *) NULL)
753  {
754  image=DestroyImage(image);
755  image=border_image;
756  }
757  if ((montage_info->frame != (char *) NULL) &&
758  (image->compose == DstOutCompositeOp))
759  {
760  (void) SetPixelChannelMask(image,AlphaChannel);
761  (void) NegateImage(image,MagickFalse,exception);
762  (void) SetPixelChannelMask(image,DefaultChannels);
763  }
764  }
765  /*
766  Gravitate as specified by the tile gravity.
767  */
768  tile_image->columns=width;
769  tile_image->rows=height;
770  tile_image->gravity=montage_info->gravity;
771  if (image->gravity != UndefinedGravity)
772  tile_image->gravity=image->gravity;
773  (void) FormatLocaleString(tile_geometry,MagickPathExtent,
774  "%.20gx%.20g+0+0",(double) image->columns,(double) image->rows);
775  flags=ParseGravityGeometry(tile_image,tile_geometry,&geometry,exception);
776  x=geometry.x+(ssize_t) border_width;
777  y=geometry.y+(ssize_t) border_width;
778  if ((montage_info->frame != (char *) NULL) && (bevel_width > 0))
779  {
780  FrameInfo
781  frame_clone;
782 
783  Image
784  *frame_image;
785 
786  /*
787  Put an ornamental border around this tile.
788  */
789  frame_clone=frame_info;
790  frame_clone.width=width+2*frame_info.width;
791  frame_clone.height=height+2*frame_info.height;
792  value=GetImageProperty(image,"label",exception);
793  if (value != (const char *) NULL)
794  frame_clone.height+=(size_t) ((metrics.ascent-metrics.descent+4)*
795  MultilineCensus(value));
796  frame_image=FrameImage(image,&frame_clone,image->compose,exception);
797  if (frame_image != (Image *) NULL)
798  {
799  image=DestroyImage(image);
800  image=frame_image;
801  }
802  x=0;
803  y=0;
804  }
805  if (LocaleCompare(image->magick,"NULL") != 0)
806  {
807  /*
808  Composite background with tile.
809  */
810  if (montage_info->shadow != MagickFalse)
811  {
812  Image
813  *shadow_image;
814 
815  /*
816  Shadow image.
817  */
818  (void) QueryColorCompliance("#0000",AllCompliance,
819  &image->background_color,exception);
820  shadow_image=ShadowImage(image,30.0,5.0,5,5,exception);
821  if (shadow_image != (Image *) NULL)
822  {
823  (void) CompositeImage(shadow_image,image,OverCompositeOp,
824  MagickTrue,0,0,exception);
825  image=DestroyImage(image);
826  image=shadow_image;
827  }
828  }
829  (void) CompositeImage(montage,image,image->compose,MagickTrue,
830  x_offset+x,y_offset+y,exception);
831  value=GetImageProperty(image,"label",exception);
832  if (value != (const char *) NULL)
833  {
834  /*
835  Annotate composite tile with label.
836  */
837  (void) FormatLocaleString(tile_geometry,MagickPathExtent,
838  "%.20gx%.20g%+.20g%+.20g",(double) ((montage_info->frame ?
839  image->columns : width)-2*border_width),(double)
840  (metrics.ascent-metrics.descent+4)*MultilineCensus(value),
841  (double) (x_offset+(ssize_t) border_width),(double)
842  ((montage_info->frame ? y_offset+(ssize_t) height+(ssize_t)
843  border_width+4 : y_offset+(ssize_t) extract_info.height+
844  (ssize_t) border_width+
845  (montage_info->shadow != MagickFalse ? 4 : 0))+bevel_width));
846  (void) CloneString(&draw_info->geometry,tile_geometry);
847  (void) CloneString(&draw_info->text,value);
848  (void) AnnotateImage(montage,draw_info,exception);
849  }
850  }
851  x_offset+=(ssize_t) width+2*(extract_info.x+(ssize_t) border_width);
852  if (((tile+1) == (ssize_t) tiles_per_page) ||
853  (((tile+1) % (ssize_t) tiles_per_row) == 0))
854  {
855  x_offset=extract_info.x;
856  y_offset+=((ssize_t) height+(extract_info.y+(ssize_t) border_width)*2+
857  (metrics.ascent-metrics.descent+4)*number_lines+
858  (montage_info->shadow != MagickFalse ? 4 : 0));
859  max_height=0;
860  }
861  if (images->progress_monitor != (MagickProgressMonitor) NULL)
862  {
863  proceed=SetImageProgress(image,MontageImageTag,tiles,total_tiles);
864  if (proceed == MagickFalse)
865  status=MagickFalse;
866  }
867  image_list[tile]=DestroyImage(image_list[tile]);
868  image=DestroyImage(image);
869  tiles++;
870  }
871  (void) status;
872  if ((i+1) < (ssize_t) images_per_page)
873  {
874  /*
875  Allocate next image structure.
876  */
877  AcquireNextImage(clone_info,montage,exception);
878  if (GetNextImageInList(montage) == (Image *) NULL)
879  {
880  montage=DestroyImageList(montage);
881  return((Image *) NULL);
882  }
883  montage=GetNextImageInList(montage);
884  montage->background_color=montage_info->background_color;
885  image_list+=tiles_per_page;
886  number_images-=tiles_per_page;
887  }
888  }
889  tile_image=DestroyImage(tile_image);
890  if (texture != (Image *) NULL)
891  texture=DestroyImage(texture);
892  title=DestroyString(title);
893  primary_list=(Image **) RelinquishMagickMemory(primary_list);
894  draw_info=DestroyDrawInfo(draw_info);
895  clone_info=DestroyImageInfo(clone_info);
896  return(GetFirstImageInList(montage));
897 }
_FrameInfo
Definition: decorate.h:27
_MontageInfo
Definition: montage.h:33
_RectangleInfo
Definition: geometry.h:129
_Image
Definition: image.h:131
_ImageInfo
Definition: image.h:358
_TypeMetric
Definition: draw.h:380
_ExceptionInfo
Definition: exception.h:101
_DrawInfo
Definition: draw.h:209