/*
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
% %
|
% %
|
% %
|
% M M OOO N N TTTTT AAA GGGG EEEEE %
|
% MM MM O O NN N T A A G E %
|
% M M M O O N N N T AAAAA G GG EEE %
|
% M M O O N NN T A A G G E %
|
% M M OOO N N T A A GGG EEEEE %
|
% %
|
% %
|
% MagickCore Methods to Create Image Thumbnails %
|
% %
|
% Software Design %
|
% Cristy %
|
% July 1992 %
|
% %
|
% %
|
% Copyright 1999-2019 ImageMagick Studio LLC, a non-profit organization %
|
% dedicated to making software imaging solutions freely available. %
|
% %
|
% You may not use this file except in compliance with the License. You may %
|
% obtain a copy of the License at %
|
% %
|
% https://imagemagick.org/script/license.php %
|
% %
|
% Unless required by applicable law or agreed to in writing, software %
|
% distributed under the License is distributed on an "AS IS" BASIS, %
|
% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. %
|
% See the License for the specific language governing permissions and %
|
% limitations under the License. %
|
% %
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%
|
%
|
%
|
*/
|
|
/*
|
Include declarations.
|
*/
|
#include "MagickCore/studio.h"
|
#include "MagickCore/annotate.h"
|
#include "MagickCore/client.h"
|
#include "MagickCore/color.h"
|
#include "MagickCore/composite.h"
|
#include "MagickCore/constitute.h"
|
#include "MagickCore/decorate.h"
|
#include "MagickCore/draw.h"
|
#include "MagickCore/effect.h"
|
#include "MagickCore/enhance.h"
|
#include "MagickCore/exception.h"
|
#include "MagickCore/exception-private.h"
|
#include "MagickCore/fx.h"
|
#include "MagickCore/gem.h"
|
#include "MagickCore/geometry.h"
|
#include "MagickCore/image.h"
|
#include "MagickCore/image-private.h"
|
#include "MagickCore/list.h"
|
#include "MagickCore/memory_.h"
|
#include "MagickCore/memory-private.h"
|
#include "MagickCore/monitor.h"
|
#include "MagickCore/monitor-private.h"
|
#include "MagickCore/montage.h"
|
#include "MagickCore/option.h"
|
#include "MagickCore/pixel.h"
|
#include "MagickCore/quantize.h"
|
#include "MagickCore/property.h"
|
#include "MagickCore/resize.h"
|
#include "MagickCore/resource_.h"
|
#include "MagickCore/string_.h"
|
#include "MagickCore/utility.h"
|
#include "MagickCore/utility-private.h"
|
#include "MagickCore/version.h"
|
|
/*
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
% %
|
% %
|
% %
|
% C l o n e M o n t a g e I n f o %
|
% %
|
% %
|
% %
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%
|
% CloneMontageInfo() makes a copy of the given montage info structure. If
|
% NULL is specified, a new image info structure is created initialized to
|
% default values.
|
%
|
% The format of the CloneMontageInfo method is:
|
%
|
% MontageInfo *CloneMontageInfo(const ImageInfo *image_info,
|
% const MontageInfo *montage_info)
|
%
|
% A description of each parameter follows:
|
%
|
% o image_info: the image info.
|
%
|
% o montage_info: the montage info.
|
%
|
*/
|
MagickExport MontageInfo *CloneMontageInfo(const ImageInfo *image_info,
|
const MontageInfo *montage_info)
|
{
|
MontageInfo
|
*clone_info;
|
|
clone_info=(MontageInfo *) AcquireCriticalMemory(sizeof(*clone_info));
|
GetMontageInfo(image_info,clone_info);
|
if (montage_info == (MontageInfo *) NULL)
|
return(clone_info);
|
if (montage_info->geometry != (char *) NULL)
|
clone_info->geometry=AcquireString(montage_info->geometry);
|
if (montage_info->tile != (char *) NULL)
|
clone_info->tile=AcquireString(montage_info->tile);
|
if (montage_info->title != (char *) NULL)
|
clone_info->title=AcquireString(montage_info->title);
|
if (montage_info->frame != (char *) NULL)
|
clone_info->frame=AcquireString(montage_info->frame);
|
if (montage_info->texture != (char *) NULL)
|
clone_info->texture=AcquireString(montage_info->texture);
|
if (montage_info->font != (char *) NULL)
|
clone_info->font=AcquireString(montage_info->font);
|
clone_info->pointsize=montage_info->pointsize;
|
clone_info->border_width=montage_info->border_width;
|
clone_info->shadow=montage_info->shadow;
|
clone_info->fill=montage_info->fill;
|
clone_info->stroke=montage_info->stroke;
|
clone_info->matte_color=montage_info->matte_color;
|
clone_info->background_color=montage_info->background_color;
|
clone_info->border_color=montage_info->border_color;
|
clone_info->gravity=montage_info->gravity;
|
(void) CopyMagickString(clone_info->filename,montage_info->filename,
|
MagickPathExtent);
|
clone_info->debug=IsEventLogging();
|
return(clone_info);
|
}
|
|
/*
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
% %
|
% %
|
% %
|
% D e s t r o y M o n t a g e I n f o %
|
% %
|
% %
|
% %
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%
|
% DestroyMontageInfo() deallocates memory associated with montage_info.
|
%
|
% The format of the DestroyMontageInfo method is:
|
%
|
% MontageInfo *DestroyMontageInfo(MontageInfo *montage_info)
|
%
|
% A description of each parameter follows:
|
%
|
% o montage_info: Specifies a pointer to an MontageInfo structure.
|
%
|
%
|
*/
|
MagickExport MontageInfo *DestroyMontageInfo(MontageInfo *montage_info)
|
{
|
assert(montage_info != (MontageInfo *) NULL);
|
if (montage_info->debug != MagickFalse)
|
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"...");
|
assert(montage_info->signature == MagickCoreSignature);
|
if (montage_info->geometry != (char *) NULL)
|
montage_info->geometry=(char *)
|
RelinquishMagickMemory(montage_info->geometry);
|
if (montage_info->tile != (char *) NULL)
|
montage_info->tile=DestroyString(montage_info->tile);
|
if (montage_info->title != (char *) NULL)
|
montage_info->title=DestroyString(montage_info->title);
|
if (montage_info->frame != (char *) NULL)
|
montage_info->frame=DestroyString(montage_info->frame);
|
if (montage_info->texture != (char *) NULL)
|
montage_info->texture=(char *) RelinquishMagickMemory(
|
montage_info->texture);
|
if (montage_info->font != (char *) NULL)
|
montage_info->font=DestroyString(montage_info->font);
|
montage_info->signature=(~MagickCoreSignature);
|
montage_info=(MontageInfo *) RelinquishMagickMemory(montage_info);
|
return(montage_info);
|
}
|
|
/*
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
% %
|
% %
|
% %
|
% G e t M o n t a g e I n f o %
|
% %
|
% %
|
% %
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%
|
% GetMontageInfo() initializes montage_info to default values.
|
%
|
% The format of the GetMontageInfo method is:
|
%
|
% void GetMontageInfo(const ImageInfo *image_info,
|
% MontageInfo *montage_info)
|
%
|
% A description of each parameter follows:
|
%
|
% o image_info: a structure of type ImageInfo.
|
%
|
% o montage_info: Specifies a pointer to a MontageInfo structure.
|
%
|
*/
|
MagickExport void GetMontageInfo(const ImageInfo *image_info,
|
MontageInfo *montage_info)
|
{
|
assert(image_info != (const ImageInfo *) NULL);
|
assert(image_info->signature == MagickCoreSignature);
|
if (image_info->debug != MagickFalse)
|
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",
|
image_info->filename);
|
assert(montage_info != (MontageInfo *) NULL);
|
(void) memset(montage_info,0,sizeof(*montage_info));
|
(void) CopyMagickString(montage_info->filename,image_info->filename,
|
MagickPathExtent);
|
montage_info->geometry=AcquireString(DefaultTileGeometry);
|
if (image_info->font != (char *) NULL)
|
montage_info->font=AcquireString(image_info->font);
|
montage_info->gravity=CenterGravity;
|
montage_info->pointsize=image_info->pointsize;
|
montage_info->fill.alpha=(MagickRealType) OpaqueAlpha;
|
montage_info->stroke.alpha=(MagickRealType) TransparentAlpha;
|
montage_info->matte_color=image_info->matte_color;
|
montage_info->background_color=image_info->background_color;
|
montage_info->border_color=image_info->border_color;
|
montage_info->debug=IsEventLogging();
|
montage_info->signature=MagickCoreSignature;
|
}
|
|
/*
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
% %
|
% %
|
% %
|
% M o n t a g e I m a g e L i s t %
|
% %
|
% %
|
% %
|
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
|
%
|
% MontageImageList() is a layout manager that lets you tile one or more
|
% thumbnails across an image canvas.
|
%
|
% The format of the MontageImageList method is:
|
%
|
% Image *MontageImageList(const ImageInfo *image_info,
|
% const MontageInfo *montage_info,Image *images,
|
% ExceptionInfo *exception)
|
%
|
% A description of each parameter follows:
|
%
|
% o image_info: the image info.
|
%
|
% o montage_info: Specifies a pointer to a MontageInfo structure.
|
%
|
% o images: Specifies a pointer to an array of Image structures.
|
%
|
% o exception: return any errors or warnings in this structure.
|
%
|
*/
|
|
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)
|
{
|
*tiles_per_column=0;
|
*tiles_per_row=0;
|
(void) GetGeometry(geometry,x_offset,y_offset,tiles_per_row,tiles_per_column);
|
if ((*tiles_per_column == 0) && (*tiles_per_row == 0))
|
*tiles_per_column=(size_t) sqrt((double) number_images);
|
if ((*tiles_per_column == 0) && (*tiles_per_row != 0))
|
*tiles_per_column=(size_t) ceil((double) number_images/(*tiles_per_row));
|
if ((*tiles_per_row == 0) && (*tiles_per_column != 0))
|
*tiles_per_row=(size_t) ceil((double) number_images/(*tiles_per_column));
|
}
|
|
#if defined(__cplusplus) || defined(c_plusplus)
|
extern "C" {
|
#endif
|
|
static int SceneCompare(const void *x,const void *y)
|
{
|
Image
|
**image_1,
|
**image_2;
|
|
image_1=(Image **) x;
|
image_2=(Image **) y;
|
return((int) ((*image_1)->scene-(*image_2)->scene));
|
}
|
|
#if defined(__cplusplus) || defined(c_plusplus)
|
}
|
#endif
|
|
MagickExport Image *MontageImages(const Image *images,
|
const MontageInfo *montage_info,ExceptionInfo *exception)
|
{
|
Image
|
*montage_image;
|
|
ImageInfo
|
*image_info;
|
|
image_info=AcquireImageInfo();
|
montage_image=MontageImageList(image_info,montage_info,images,exception);
|
image_info=DestroyImageInfo(image_info);
|
return(montage_image);
|
}
|
|
MagickExport Image *MontageImageList(const ImageInfo *image_info,
|
const MontageInfo *montage_info,const Image *images,ExceptionInfo *exception)
|
{
|
#define MontageImageTag "Montage/Image"
|
#define TileImageTag "Tile/Image"
|
|
char
|
tile_geometry[MagickPathExtent],
|
*title;
|
|
const char
|
*value;
|
|
DrawInfo
|
*draw_info;
|
|
FrameInfo
|
frame_info;
|
|
Image
|
*image,
|
**image_list,
|
**master_list,
|
*montage,
|
*texture,
|
*tile_image,
|
*thumbnail;
|
|
ImageInfo
|
*clone_info;
|
|
MagickBooleanType
|
concatenate,
|
proceed,
|
status;
|
|
MagickOffsetType
|
tiles;
|
|
MagickProgressMonitor
|
progress_monitor;
|
|
MagickStatusType
|
flags;
|
|
register ssize_t
|
i;
|
|
RectangleInfo
|
bounds,
|
geometry,
|
extract_info;
|
|
size_t
|
border_width,
|
extent,
|
height,
|
images_per_page,
|
max_height,
|
number_images,
|
number_lines,
|
sans,
|
tiles_per_column,
|
tiles_per_page,
|
tiles_per_row,
|
title_offset,
|
total_tiles,
|
width;
|
|
ssize_t
|
bevel_width,
|
tile,
|
x,
|
x_offset,
|
y,
|
y_offset;
|
|
TypeMetric
|
metrics;
|
|
/*
|
Create image tiles.
|
*/
|
assert(images != (Image *) NULL);
|
assert(images->signature == MagickCoreSignature);
|
if (images->debug != MagickFalse)
|
(void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename);
|
assert(montage_info != (MontageInfo *) NULL);
|
assert(montage_info->signature == MagickCoreSignature);
|
assert(exception != (ExceptionInfo *) NULL);
|
assert(exception->signature == MagickCoreSignature);
|
number_images=GetImageListLength(images);
|
master_list=ImageListToArray(images,exception);
|
if (master_list == (Image **) NULL)
|
return((Image *) NULL);
|
image_list=master_list;
|
image=image_list[0];
|
thumbnail=NewImageList();
|
for (i=0; i < (ssize_t) number_images; i++)
|
{
|
image=CloneImage(image_list[i],0,0,MagickTrue,exception);
|
if (image == (Image *) NULL)
|
break;
|
(void) ParseAbsoluteGeometry("0x0+0+0",&image->page);
|
progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL,
|
image->client_data);
|
flags=ParseRegionGeometry(image,montage_info->geometry,&geometry,exception);
|
thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception);
|
if (thumbnail == (Image *) NULL)
|
break;
|
image_list[i]=thumbnail;
|
(void) SetImageProgressMonitor(image,progress_monitor,image->client_data);
|
proceed=SetImageProgress(image,TileImageTag,(MagickOffsetType) i,
|
number_images);
|
if (proceed == MagickFalse)
|
break;
|
image=DestroyImage(image);
|
}
|
if (i < (ssize_t) number_images)
|
{
|
if (image != (Image *) NULL)
|
image=DestroyImage(image);
|
if (thumbnail == (Image *) NULL)
|
i--;
|
for (tile=0; (ssize_t) tile <= i; tile++)
|
if (image_list[tile] != (Image *) NULL)
|
image_list[tile]=DestroyImage(image_list[tile]);
|
master_list=(Image **) RelinquishMagickMemory(master_list);
|
return((Image *) NULL);
|
}
|
/*
|
Sort image list by increasing tile number.
|
*/
|
for (i=0; i < (ssize_t) number_images; i++)
|
if (image_list[i]->scene == 0)
|
break;
|
if (i == (ssize_t) number_images)
|
qsort((void *) image_list,(size_t) number_images,sizeof(*image_list),
|
SceneCompare);
|
/*
|
Determine tiles per row and column.
|
*/
|
tiles_per_column=(size_t) sqrt((double) number_images);
|
tiles_per_row=(size_t) ceil((double) number_images/tiles_per_column);
|
x_offset=0;
|
y_offset=0;
|
if (montage_info->tile != (char *) NULL)
|
GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
|
&tiles_per_column,&tiles_per_row);
|
/*
|
Determine tile sizes.
|
*/
|
concatenate=MagickFalse;
|
SetGeometry(image_list[0],&extract_info);
|
extract_info.x=(ssize_t) montage_info->border_width;
|
extract_info.y=(ssize_t) montage_info->border_width;
|
if (montage_info->geometry != (char *) NULL)
|
{
|
/*
|
Initialize tile geometry.
|
*/
|
flags=GetGeometry(montage_info->geometry,&extract_info.x,&extract_info.y,
|
&extract_info.width,&extract_info.height);
|
concatenate=((flags & RhoValue) == 0) && ((flags & SigmaValue) == 0) ?
|
MagickTrue : MagickFalse;
|
}
|
border_width=montage_info->border_width;
|
bevel_width=0;
|
(void) memset(&frame_info,0,sizeof(frame_info));
|
if (montage_info->frame != (char *) NULL)
|
{
|
char
|
absolute_geometry[MagickPathExtent];
|
|
frame_info.width=extract_info.width;
|
frame_info.height=extract_info.height;
|
(void) FormatLocaleString(absolute_geometry,MagickPathExtent,"%s!",
|
montage_info->frame);
|
flags=ParseMetaGeometry(absolute_geometry,&frame_info.outer_bevel,
|
&frame_info.inner_bevel,&frame_info.width,&frame_info.height);
|
if ((flags & HeightValue) == 0)
|
frame_info.height=frame_info.width;
|
if ((flags & XiValue) == 0)
|
frame_info.outer_bevel=(ssize_t) frame_info.width/2-1;
|
if ((flags & PsiValue) == 0)
|
frame_info.inner_bevel=frame_info.outer_bevel;
|
frame_info.x=(ssize_t) frame_info.width;
|
frame_info.y=(ssize_t) frame_info.height;
|
bevel_width=(ssize_t) MagickMax(frame_info.inner_bevel,
|
frame_info.outer_bevel);
|
border_width=(size_t) MagickMax((ssize_t) frame_info.width,
|
(ssize_t) frame_info.height);
|
}
|
for (i=0; i < (ssize_t) number_images; i++)
|
{
|
if (image_list[i]->columns > extract_info.width)
|
extract_info.width=image_list[i]->columns;
|
if (image_list[i]->rows > extract_info.height)
|
extract_info.height=image_list[i]->rows;
|
}
|
/*
|
Initialize draw attributes.
|
*/
|
clone_info=CloneImageInfo(image_info);
|
clone_info->background_color=montage_info->background_color;
|
clone_info->border_color=montage_info->border_color;
|
draw_info=CloneDrawInfo(clone_info,(DrawInfo *) NULL);
|
if (montage_info->font != (char *) NULL)
|
(void) CloneString(&draw_info->font,montage_info->font);
|
if (montage_info->pointsize != 0.0)
|
draw_info->pointsize=montage_info->pointsize;
|
draw_info->gravity=CenterGravity;
|
draw_info->stroke=montage_info->stroke;
|
draw_info->fill=montage_info->fill;
|
draw_info->text=AcquireString("");
|
(void) GetTypeMetrics(image_list[0],draw_info,&metrics,exception);
|
texture=NewImageList();
|
if (montage_info->texture != (char *) NULL)
|
{
|
(void) CopyMagickString(clone_info->filename,montage_info->texture,
|
MagickPathExtent);
|
texture=ReadImage(clone_info,exception);
|
}
|
/*
|
Determine the number of lines in an next label.
|
*/
|
title=InterpretImageProperties(clone_info,image_list[0],montage_info->title,
|
exception);
|
title_offset=0;
|
if (montage_info->title != (char *) NULL)
|
title_offset=(size_t) (2*(metrics.ascent-metrics.descent)*
|
MultilineCensus(title)+2*extract_info.y);
|
number_lines=0;
|
for (i=0; i < (ssize_t) number_images; i++)
|
{
|
value=GetImageProperty(image_list[i],"label",exception);
|
if (value == (const char *) NULL)
|
continue;
|
if (MultilineCensus(value) > number_lines)
|
number_lines=MultilineCensus(value);
|
}
|
/*
|
Allocate next structure.
|
*/
|
tile_image=AcquireImage((ImageInfo *) NULL,exception);
|
montage=AcquireImage(clone_info,exception);
|
montage->background_color=montage_info->background_color;
|
montage->scene=0;
|
images_per_page=(number_images-1)/(tiles_per_row*tiles_per_column)+1;
|
tiles=0;
|
total_tiles=(size_t) number_images;
|
for (i=0; i < (ssize_t) images_per_page; i++)
|
{
|
/*
|
Determine bounding box.
|
*/
|
tiles_per_page=tiles_per_row*tiles_per_column;
|
x_offset=0;
|
y_offset=0;
|
if (montage_info->tile != (char *) NULL)
|
GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
|
&sans,&sans);
|
tiles_per_page=tiles_per_row*tiles_per_column;
|
y_offset+=(ssize_t) title_offset;
|
max_height=0;
|
bounds.width=0;
|
bounds.height=0;
|
width=0;
|
for (tile=0; tile < (ssize_t) tiles_per_page; tile++)
|
{
|
if (tile < (ssize_t) number_images)
|
{
|
width=concatenate != MagickFalse ? image_list[tile]->columns :
|
extract_info.width;
|
if (image_list[tile]->rows > max_height)
|
max_height=image_list[tile]->rows;
|
}
|
x_offset+=(ssize_t) (width+2*(extract_info.x+border_width));
|
if (x_offset > (ssize_t) bounds.width)
|
bounds.width=(size_t) x_offset;
|
if (((tile+1) == (ssize_t) tiles_per_page) ||
|
(((tile+1) % tiles_per_row) == 0))
|
{
|
x_offset=0;
|
if (montage_info->tile != (char *) NULL)
|
GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y,
|
&sans,&sans);
|
height=concatenate != MagickFalse ? max_height : extract_info.height;
|
y_offset+=(ssize_t) (height+(extract_info.y+(ssize_t) border_width)*2+
|
(metrics.ascent-metrics.descent+4)*number_lines+
|
(montage_info->shadow != MagickFalse ? 4 : 0));
|
if (y_offset > (ssize_t) bounds.height)
|
bounds.height=(size_t) y_offset;
|
max_height=0;
|
}
|
}
|
if (montage_info->shadow != MagickFalse)
|
bounds.width+=4;
|
/*
|
Initialize montage image.
|
*/
|
(void) CopyMagickString(montage->filename,montage_info->filename,
|
MagickPathExtent);
|
montage->columns=(size_t) MagickMax((ssize_t) bounds.width,1);
|
montage->rows=(size_t) MagickMax((ssize_t) bounds.height,1);
|
(void) SetImageBackgroundColor(montage,exception);
|
/*
|
Set montage geometry.
|
*/
|
montage->montage=AcquireString((char *) NULL);
|
tile=0;
|
extent=1;
|
while (tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images))
|
{
|
extent+=strlen(image_list[tile]->filename)+1;
|
tile++;
|
}
|
montage->directory=(char *) AcquireQuantumMemory(extent,
|
sizeof(*montage->directory));
|
if ((montage->montage == (char *) NULL) ||
|
(montage->directory == (char *) NULL))
|
{
|
if (montage->montage != (char *) NULL)
|
montage->montage=(char *) RelinquishMagickMemory(montage->montage);
|
if (montage->directory != (char *) NULL)
|
montage->directory=(char *) RelinquishMagickMemory(
|
montage->directory);
|
ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
|
}
|
x_offset=0;
|
y_offset=0;
|
if (montage_info->tile != (char *) NULL)
|
GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
|
&sans,&sans);
|
y_offset+=(ssize_t) title_offset;
|
(void) FormatLocaleString(montage->montage,MagickPathExtent,
|
"%.20gx%.20g%+.20g%+.20g",(double) (extract_info.width+
|
(extract_info.x+border_width)*2),(double) (extract_info.height+
|
(extract_info.y+border_width)*2+(double) ((metrics.ascent-
|
metrics.descent+4)*number_lines+(montage_info->shadow != MagickFalse ? 4 :
|
0))),(double) x_offset,(double) y_offset);
|
*montage->directory='\0';
|
tile=0;
|
while (tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images))
|
{
|
(void) ConcatenateMagickString(montage->directory,
|
image_list[tile]->filename,extent);
|
(void) ConcatenateMagickString(montage->directory,"\xff",extent);
|
tile++;
|
}
|
progress_monitor=SetImageProgressMonitor(montage,(MagickProgressMonitor)
|
NULL,montage->client_data);
|
if (texture != (Image *) NULL)
|
(void) TextureImage(montage,texture,exception);
|
if (montage_info->title != (char *) NULL)
|
{
|
DrawInfo
|
*draw_clone_info;
|
|
TypeMetric
|
tile_metrics;
|
|
/*
|
Annotate composite image with title.
|
*/
|
draw_clone_info=CloneDrawInfo(image_info,draw_info);
|
draw_clone_info->gravity=CenterGravity;
|
draw_clone_info->pointsize*=2.0;
|
(void) GetTypeMetrics(image_list[0],draw_clone_info,&tile_metrics,
|
exception);
|
(void) FormatLocaleString(tile_geometry,MagickPathExtent,
|
"%.20gx%.20g%+.20g%+.20g",(double) montage->columns,(double)
|
(tile_metrics.ascent-tile_metrics.descent),0.0,
|
(double) extract_info.y+4);
|
(void) CloneString(&draw_clone_info->geometry,tile_geometry);
|
(void) CloneString(&draw_clone_info->text,title);
|
(void) AnnotateImage(montage,draw_clone_info,exception);
|
draw_clone_info=DestroyDrawInfo(draw_clone_info);
|
}
|
(void) SetImageProgressMonitor(montage,progress_monitor,
|
montage->client_data);
|
/*
|
Copy tile to the composite.
|
*/
|
x_offset=0;
|
y_offset=0;
|
if (montage_info->tile != (char *) NULL)
|
GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset,
|
&sans,&sans);
|
x_offset+=extract_info.x;
|
y_offset+=(ssize_t) title_offset+extract_info.y;
|
max_height=0;
|
status=MagickTrue;
|
for (tile=0; tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images); tile++)
|
{
|
/*
|
Copy this tile to the composite.
|
*/
|
image=CloneImage(image_list[tile],0,0,MagickTrue,exception);
|
if (image == (Image *) NULL)
|
ThrowImageException(ResourceLimitError,"MemoryAllocationFailed");
|
progress_monitor=SetImageProgressMonitor(image,
|
(MagickProgressMonitor) NULL,image->client_data);
|
width=concatenate != MagickFalse ? image->columns : extract_info.width;
|
if (image->rows > max_height)
|
max_height=image->rows;
|
height=concatenate != MagickFalse ? max_height : extract_info.height;
|
if (border_width != 0)
|
{
|
Image
|
*border_image;
|
|
RectangleInfo
|
border_info;
|
|
/*
|
Put a border around the image.
|
*/
|
border_info.width=border_width;
|
border_info.height=border_width;
|
if (montage_info->frame != (char *) NULL)
|
{
|
border_info.width=(width-image->columns+1)/2;
|
border_info.height=(height-image->rows+1)/2;
|
}
|
border_image=BorderImage(image,&border_info,image->compose,exception);
|
if (border_image != (Image *) NULL)
|
{
|
image=DestroyImage(image);
|
image=border_image;
|
}
|
if ((montage_info->frame != (char *) NULL) &&
|
(image->compose == DstOutCompositeOp))
|
{
|
(void) SetPixelChannelMask(image,AlphaChannel);
|
(void) NegateImage(image,MagickFalse,exception);
|
(void) SetPixelChannelMask(image,DefaultChannels);
|
}
|
}
|
/*
|
Gravitate as specified by the tile gravity.
|
*/
|
tile_image->columns=width;
|
tile_image->rows=height;
|
tile_image->gravity=montage_info->gravity;
|
if (image->gravity != UndefinedGravity)
|
tile_image->gravity=image->gravity;
|
(void) FormatLocaleString(tile_geometry,MagickPathExtent,
|
"%.20gx%.20g+0+0",(double) image->columns,(double) image->rows);
|
flags=ParseGravityGeometry(tile_image,tile_geometry,&geometry,exception);
|
x=(ssize_t) (geometry.x+border_width);
|
y=(ssize_t) (geometry.y+border_width);
|
if ((montage_info->frame != (char *) NULL) && (bevel_width > 0))
|
{
|
FrameInfo
|
frame_clone;
|
|
Image
|
*frame_image;
|
|
/*
|
Put an ornamental border around this tile.
|
*/
|
frame_clone=frame_info;
|
frame_clone.width=width+2*frame_info.width;
|
frame_clone.height=height+2*frame_info.height;
|
value=GetImageProperty(image,"label",exception);
|
if (value != (const char *) NULL)
|
frame_clone.height+=(size_t) ((metrics.ascent-metrics.descent+4)*
|
MultilineCensus(value));
|
frame_image=FrameImage(image,&frame_clone,image->compose,exception);
|
if (frame_image != (Image *) NULL)
|
{
|
image=DestroyImage(image);
|
image=frame_image;
|
}
|
x=0;
|
y=0;
|
}
|
if (LocaleCompare(image->magick,"NULL") != 0)
|
{
|
/*
|
Composite background with tile.
|
*/
|
if (montage_info->shadow != MagickFalse)
|
{
|
Image
|
*shadow_image;
|
|
/*
|
Shadow image.
|
*/
|
(void) QueryColorCompliance("#0000",AllCompliance,
|
&image->background_color,exception);
|
shadow_image=ShadowImage(image,80.0,2.0,5,5,exception);
|
if (shadow_image != (Image *) NULL)
|
{
|
(void) CompositeImage(shadow_image,image,OverCompositeOp,
|
MagickTrue,0,0,exception);
|
image=DestroyImage(image);
|
image=shadow_image;
|
}
|
}
|
(void) CompositeImage(montage,image,image->compose,MagickTrue,
|
x_offset+x,y_offset+y,exception);
|
value=GetImageProperty(image,"label",exception);
|
if (value != (const char *) NULL)
|
{
|
/*
|
Annotate composite tile with label.
|
*/
|
(void) FormatLocaleString(tile_geometry,MagickPathExtent,
|
"%.20gx%.20g%+.20g%+.20g",(double) ((montage_info->frame ?
|
image->columns : width)-2*border_width),(double)
|
(metrics.ascent-metrics.descent+4)*MultilineCensus(value),
|
(double) (x_offset+border_width),(double)
|
((montage_info->frame ? y_offset+height+border_width+4 :
|
y_offset+extract_info.height+border_width+
|
(montage_info->shadow != MagickFalse ? 4 : 0))+bevel_width));
|
(void) CloneString(&draw_info->geometry,tile_geometry);
|
(void) CloneString(&draw_info->text,value);
|
(void) AnnotateImage(montage,draw_info,exception);
|
}
|
}
|
x_offset+=(ssize_t) (width+2*(extract_info.x+border_width));
|
if (((tile+1) == (ssize_t) tiles_per_page) ||
|
(((tile+1) % tiles_per_row) == 0))
|
{
|
x_offset=extract_info.x;
|
y_offset+=(ssize_t) (height+(extract_info.y+border_width)*2+
|
(metrics.ascent-metrics.descent+4)*number_lines+
|
(montage_info->shadow != MagickFalse ? 4 : 0));
|
max_height=0;
|
}
|
if (images->progress_monitor != (MagickProgressMonitor) NULL)
|
{
|
proceed=SetImageProgress(image,MontageImageTag,tiles,total_tiles);
|
if (proceed == MagickFalse)
|
status=MagickFalse;
|
}
|
image_list[tile]=DestroyImage(image_list[tile]);
|
image=DestroyImage(image);
|
tiles++;
|
}
|
(void) status;
|
if ((i+1) < (ssize_t) images_per_page)
|
{
|
/*
|
Allocate next image structure.
|
*/
|
AcquireNextImage(clone_info,montage,exception);
|
if (GetNextImageInList(montage) == (Image *) NULL)
|
{
|
montage=DestroyImageList(montage);
|
return((Image *) NULL);
|
}
|
montage=GetNextImageInList(montage);
|
montage->background_color=montage_info->background_color;
|
image_list+=tiles_per_page;
|
number_images-=tiles_per_page;
|
}
|
}
|
tile_image=DestroyImage(tile_image);
|
if (texture != (Image *) NULL)
|
texture=DestroyImage(texture);
|
title=DestroyString(title);
|
master_list=(Image **) RelinquishMagickMemory(master_list);
|
draw_info=DestroyDrawInfo(draw_info);
|
clone_info=DestroyImageInfo(clone_info);
|
return(GetFirstImageInList(montage));
|
}
|