|
MagickCore
6.7.5
|
00001 /* 00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00003 % % 00004 % % 00005 % % 00006 % M M OOO N N TTTTT AAA GGGG EEEEE % 00007 % MM MM O O NN N T A A G E % 00008 % M M M O O N N N T AAAAA G GG EEE % 00009 % M M O O N NN T A A G G E % 00010 % M M OOO N N T A A GGG EEEEE % 00011 % % 00012 % % 00013 % MagickCore Methods to Create Image Thumbnails % 00014 % % 00015 % Software Design % 00016 % John Cristy % 00017 % July 1992 % 00018 % % 00019 % % 00020 % Copyright 1999-2012 ImageMagick Studio LLC, a non-profit organization % 00021 % dedicated to making software imaging solutions freely available. % 00022 % % 00023 % You may not use this file except in compliance with the License. You may % 00024 % obtain a copy of the License at % 00025 % % 00026 % http://www.imagemagick.org/script/license.php % 00027 % % 00028 % Unless required by applicable law or agreed to in writing, software % 00029 % distributed under the License is distributed on an "AS IS" BASIS, % 00030 % WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. % 00031 % See the License for the specific language governing permissions and % 00032 % limitations under the License. % 00033 % % 00034 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00035 % 00036 % 00037 % 00038 */ 00039 00040 /* 00041 Include declarations. 00042 */ 00043 #include "MagickCore/studio.h" 00044 #include "MagickCore/annotate.h" 00045 #include "MagickCore/client.h" 00046 #include "MagickCore/color.h" 00047 #include "MagickCore/composite.h" 00048 #include "MagickCore/constitute.h" 00049 #include "MagickCore/decorate.h" 00050 #include "MagickCore/draw.h" 00051 #include "MagickCore/effect.h" 00052 #include "MagickCore/enhance.h" 00053 #include "MagickCore/exception.h" 00054 #include "MagickCore/exception-private.h" 00055 #include "MagickCore/fx.h" 00056 #include "MagickCore/gem.h" 00057 #include "MagickCore/geometry.h" 00058 #include "MagickCore/image.h" 00059 #include "MagickCore/image-private.h" 00060 #include "MagickCore/list.h" 00061 #include "MagickCore/memory_.h" 00062 #include "MagickCore/monitor.h" 00063 #include "MagickCore/monitor-private.h" 00064 #include "MagickCore/montage.h" 00065 #include "MagickCore/option.h" 00066 #include "MagickCore/pixel.h" 00067 #include "MagickCore/quantize.h" 00068 #include "MagickCore/property.h" 00069 #include "MagickCore/resize.h" 00070 #include "MagickCore/resource_.h" 00071 #include "MagickCore/string_.h" 00072 #include "MagickCore/utility.h" 00073 #include "MagickCore/utility-private.h" 00074 #include "MagickCore/version.h" 00075 00076 /* 00077 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00078 % % 00079 % % 00080 % % 00081 % C l o n e M o n t a g e I n f o % 00082 % % 00083 % % 00084 % % 00085 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00086 % 00087 % CloneMontageInfo() makes a copy of the given montage info structure. If 00088 % NULL is specified, a new image info structure is created initialized to 00089 % default values. 00090 % 00091 % The format of the CloneMontageInfo method is: 00092 % 00093 % MontageInfo *CloneMontageInfo(const ImageInfo *image_info, 00094 % const MontageInfo *montage_info) 00095 % 00096 % A description of each parameter follows: 00097 % 00098 % o image_info: the image info. 00099 % 00100 % o montage_info: the montage info. 00101 % 00102 */ 00103 MagickExport MontageInfo *CloneMontageInfo(const ImageInfo *image_info, 00104 const MontageInfo *montage_info) 00105 { 00106 MontageInfo 00107 *clone_info; 00108 00109 clone_info=(MontageInfo *) AcquireMagickMemory(sizeof(*clone_info)); 00110 if (clone_info == (MontageInfo *) NULL) 00111 ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed"); 00112 GetMontageInfo(image_info,clone_info); 00113 if (montage_info == (MontageInfo *) NULL) 00114 return(clone_info); 00115 if (montage_info->geometry != (char *) NULL) 00116 clone_info->geometry=AcquireString(montage_info->geometry); 00117 if (montage_info->tile != (char *) NULL) 00118 clone_info->tile=AcquireString(montage_info->tile); 00119 if (montage_info->title != (char *) NULL) 00120 clone_info->title=AcquireString(montage_info->title); 00121 if (montage_info->frame != (char *) NULL) 00122 clone_info->frame=AcquireString(montage_info->frame); 00123 if (montage_info->texture != (char *) NULL) 00124 clone_info->texture=AcquireString(montage_info->texture); 00125 if (montage_info->font != (char *) NULL) 00126 clone_info->font=AcquireString(montage_info->font); 00127 clone_info->pointsize=montage_info->pointsize; 00128 clone_info->border_width=montage_info->border_width; 00129 clone_info->shadow=montage_info->shadow; 00130 clone_info->fill=montage_info->fill; 00131 clone_info->stroke=montage_info->stroke; 00132 clone_info->background_color=montage_info->background_color; 00133 clone_info->border_color=montage_info->border_color; 00134 clone_info->matte_color=montage_info->matte_color; 00135 clone_info->gravity=montage_info->gravity; 00136 (void) CopyMagickString(clone_info->filename,montage_info->filename, 00137 MaxTextExtent); 00138 clone_info->debug=IsEventLogging(); 00139 return(clone_info); 00140 } 00141 00142 /* 00143 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00144 % % 00145 % % 00146 % % 00147 % D e s t r o y M o n t a g e I n f o % 00148 % % 00149 % % 00150 % % 00151 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00152 % 00153 % DestroyMontageInfo() deallocates memory associated with montage_info. 00154 % 00155 % The format of the DestroyMontageInfo method is: 00156 % 00157 % MontageInfo *DestroyMontageInfo(MontageInfo *montage_info) 00158 % 00159 % A description of each parameter follows: 00160 % 00161 % o montage_info: Specifies a pointer to an MontageInfo structure. 00162 % 00163 % 00164 */ 00165 MagickExport MontageInfo *DestroyMontageInfo(MontageInfo *montage_info) 00166 { 00167 if (montage_info->debug != MagickFalse) 00168 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"..."); 00169 assert(montage_info != (MontageInfo *) NULL); 00170 assert(montage_info->signature == MagickSignature); 00171 if (montage_info->geometry != (char *) NULL) 00172 montage_info->geometry=(char *) 00173 RelinquishMagickMemory(montage_info->geometry); 00174 if (montage_info->tile != (char *) NULL) 00175 montage_info->tile=DestroyString(montage_info->tile); 00176 if (montage_info->title != (char *) NULL) 00177 montage_info->title=DestroyString(montage_info->title); 00178 if (montage_info->frame != (char *) NULL) 00179 montage_info->frame=DestroyString(montage_info->frame); 00180 if (montage_info->texture != (char *) NULL) 00181 montage_info->texture=(char *) RelinquishMagickMemory( 00182 montage_info->texture); 00183 if (montage_info->font != (char *) NULL) 00184 montage_info->font=DestroyString(montage_info->font); 00185 montage_info->signature=(~MagickSignature); 00186 montage_info=(MontageInfo *) RelinquishMagickMemory(montage_info); 00187 return(montage_info); 00188 } 00189 00190 /* 00191 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00192 % % 00193 % % 00194 % % 00195 % G e t M o n t a g e I n f o % 00196 % % 00197 % % 00198 % % 00199 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00200 % 00201 % GetMontageInfo() initializes montage_info to default values. 00202 % 00203 % The format of the GetMontageInfo method is: 00204 % 00205 % void GetMontageInfo(const ImageInfo *image_info, 00206 % MontageInfo *montage_info) 00207 % 00208 % A description of each parameter follows: 00209 % 00210 % o image_info: a structure of type ImageInfo. 00211 % 00212 % o montage_info: Specifies a pointer to a MontageInfo structure. 00213 % 00214 */ 00215 MagickExport void GetMontageInfo(const ImageInfo *image_info, 00216 MontageInfo *montage_info) 00217 { 00218 assert(image_info != (const ImageInfo *) NULL); 00219 assert(image_info->signature == MagickSignature); 00220 if (image_info->debug != MagickFalse) 00221 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s", 00222 image_info->filename); 00223 assert(montage_info != (MontageInfo *) NULL); 00224 (void) ResetMagickMemory(montage_info,0,sizeof(*montage_info)); 00225 (void) CopyMagickString(montage_info->filename,image_info->filename, 00226 MaxTextExtent); 00227 montage_info->geometry=AcquireString(DefaultTileGeometry); 00228 if (image_info->font != (char *) NULL) 00229 montage_info->font=AcquireString(image_info->font); 00230 montage_info->gravity=CenterGravity; 00231 montage_info->pointsize=image_info->pointsize; 00232 montage_info->fill.alpha=OpaqueAlpha; 00233 montage_info->stroke.alpha=(Quantum) TransparentAlpha; 00234 montage_info->background_color=image_info->background_color; 00235 montage_info->border_color=image_info->border_color; 00236 montage_info->matte_color=image_info->matte_color; 00237 montage_info->debug=IsEventLogging(); 00238 montage_info->signature=MagickSignature; 00239 } 00240 00241 /* 00242 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00243 % % 00244 % % 00245 % % 00246 % M o n t a g e I m a g e L i s t % 00247 % % 00248 % % 00249 % % 00250 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% 00251 % 00252 % MontageImageList() is a layout manager that lets you tile one or more 00253 % thumbnails across an image canvas. 00254 % 00255 % The format of the MontageImageList method is: 00256 % 00257 % Image *MontageImageList(const ImageInfo *image_info, 00258 % const MontageInfo *montage_info,Image *images, 00259 % ExceptionInfo *exception) 00260 % 00261 % A description of each parameter follows: 00262 % 00263 % o image_info: the image info. 00264 % 00265 % o montage_info: Specifies a pointer to a MontageInfo structure. 00266 % 00267 % o images: Specifies a pointer to an array of Image structures. 00268 % 00269 % o exception: return any errors or warnings in this structure. 00270 % 00271 */ 00272 00273 static void GetMontageGeometry(char *geometry,const size_t number_images, 00274 ssize_t *x_offset,ssize_t *y_offset,size_t *tiles_per_column, 00275 size_t *tiles_per_row) 00276 { 00277 *tiles_per_column=0; 00278 *tiles_per_row=0; 00279 (void) GetGeometry(geometry,x_offset,y_offset,tiles_per_row,tiles_per_column); 00280 if ((*tiles_per_column == 0) && (*tiles_per_row == 0)) 00281 *tiles_per_column=(size_t) sqrt((double) number_images); 00282 if (*tiles_per_column == 0) 00283 *tiles_per_column=(size_t) 00284 ceil((double) number_images/(*tiles_per_row)); 00285 if (*tiles_per_row == 0) 00286 *tiles_per_row=(size_t) 00287 ceil((double) number_images/(*tiles_per_column)); 00288 } 00289 00290 static inline ssize_t MagickMax(const ssize_t x,const ssize_t y) 00291 { 00292 if (x > y) 00293 return(x); 00294 return(y); 00295 } 00296 00297 static inline ssize_t MagickMin(const ssize_t x,const ssize_t y) 00298 { 00299 if (x < y) 00300 return(x); 00301 return(y); 00302 } 00303 00304 #if defined(__cplusplus) || defined(c_plusplus) 00305 extern "C" { 00306 #endif 00307 00308 static int SceneCompare(const void *x,const void *y) 00309 { 00310 Image 00311 **image_1, 00312 **image_2; 00313 00314 image_1=(Image **) x; 00315 image_2=(Image **) y; 00316 return((int) ((*image_1)->scene-(*image_2)->scene)); 00317 } 00318 00319 #if defined(__cplusplus) || defined(c_plusplus) 00320 } 00321 #endif 00322 00323 MagickExport Image *MontageImages(const Image *images, 00324 const MontageInfo *montage_info,ExceptionInfo *exception) 00325 { 00326 Image 00327 *montage_image; 00328 00329 ImageInfo 00330 *image_info; 00331 00332 image_info=AcquireImageInfo(); 00333 montage_image=MontageImageList(image_info,montage_info,images,exception); 00334 image_info=DestroyImageInfo(image_info); 00335 return(montage_image); 00336 } 00337 00338 MagickExport Image *MontageImageList(const ImageInfo *image_info, 00339 const MontageInfo *montage_info,const Image *images,ExceptionInfo *exception) 00340 { 00341 #define MontageImageTag "Montage/Image" 00342 #define TileImageTag "Tile/Image" 00343 00344 char 00345 tile_geometry[MaxTextExtent], 00346 *title; 00347 00348 const char 00349 *value; 00350 00351 DrawInfo 00352 *draw_info; 00353 00354 FrameInfo 00355 frame_info; 00356 00357 Image 00358 *image, 00359 **image_list, 00360 **master_list, 00361 *montage, 00362 *texture, 00363 *tile_image, 00364 *thumbnail; 00365 00366 ImageInfo 00367 *clone_info; 00368 00369 MagickBooleanType 00370 concatenate, 00371 proceed, 00372 status; 00373 00374 MagickOffsetType 00375 tiles; 00376 00377 MagickProgressMonitor 00378 progress_monitor; 00379 00380 MagickStatusType 00381 flags; 00382 00383 register ssize_t 00384 i; 00385 00386 RectangleInfo 00387 bounds, 00388 geometry, 00389 extract_info; 00390 00391 size_t 00392 bevel_width, 00393 border_width, 00394 extent, 00395 height, 00396 images_per_page, 00397 max_height, 00398 number_images, 00399 number_lines, 00400 sans, 00401 tiles_per_column, 00402 tiles_per_page, 00403 tiles_per_row, 00404 title_offset, 00405 total_tiles, 00406 width; 00407 00408 ssize_t 00409 tile, 00410 x, 00411 x_offset, 00412 y, 00413 y_offset; 00414 00415 TypeMetric 00416 metrics; 00417 00418 /* 00419 Create image tiles. 00420 */ 00421 assert(images != (Image *) NULL); 00422 assert(images->signature == MagickSignature); 00423 if (images->debug != MagickFalse) 00424 (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",images->filename); 00425 assert(montage_info != (MontageInfo *) NULL); 00426 assert(montage_info->signature == MagickSignature); 00427 assert(exception != (ExceptionInfo *) NULL); 00428 assert(exception->signature == MagickSignature); 00429 number_images=GetImageListLength(images); 00430 master_list=ImageListToArray(images,exception); 00431 image_list=master_list; 00432 image=image_list[0]; 00433 if (master_list == (Image **) NULL) 00434 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); 00435 thumbnail=NewImageList(); 00436 for (i=0; i < (ssize_t) number_images; i++) 00437 { 00438 image=CloneImage(image_list[i],0,0,MagickTrue,exception); 00439 if (image == (Image *) NULL) 00440 break; 00441 (void) ParseAbsoluteGeometry("0x0+0+0",&image->page); 00442 progress_monitor=SetImageProgressMonitor(image,(MagickProgressMonitor) NULL, 00443 image->client_data); 00444 flags=ParseRegionGeometry(image,montage_info->geometry,&geometry,exception); 00445 thumbnail=ThumbnailImage(image,geometry.width,geometry.height,exception); 00446 if (thumbnail == (Image *) NULL) 00447 break; 00448 image_list[i]=thumbnail; 00449 (void) SetImageProgressMonitor(image,progress_monitor,image->client_data); 00450 proceed=SetImageProgress(image,TileImageTag,(MagickOffsetType) i, 00451 number_images); 00452 if (proceed == MagickFalse) 00453 break; 00454 image=DestroyImage(image); 00455 } 00456 if (i < (ssize_t) number_images) 00457 { 00458 if (thumbnail == (Image *) NULL) 00459 i--; 00460 for (tile=0; (ssize_t) tile <= i; tile++) 00461 if (image_list[tile] != (Image *) NULL) 00462 image_list[tile]=DestroyImage(image_list[tile]); 00463 master_list=(Image **) RelinquishMagickMemory(master_list); 00464 return((Image *) NULL); 00465 } 00466 /* 00467 Sort image list by increasing tile number. 00468 */ 00469 for (i=0; i < (ssize_t) number_images; i++) 00470 if (image_list[i]->scene == 0) 00471 break; 00472 if (i == (ssize_t) number_images) 00473 qsort((void *) image_list,(size_t) number_images,sizeof(*image_list), 00474 SceneCompare); 00475 /* 00476 Determine tiles per row and column. 00477 */ 00478 tiles_per_column=(size_t) sqrt((double) number_images); 00479 tiles_per_row=(size_t) ceil((double) number_images/tiles_per_column); 00480 x_offset=0; 00481 y_offset=0; 00482 if (montage_info->tile != (char *) NULL) 00483 GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset, 00484 &tiles_per_column,&tiles_per_row); 00485 /* 00486 Determine tile sizes. 00487 */ 00488 concatenate=MagickFalse; 00489 SetGeometry(image_list[0],&extract_info); 00490 extract_info.x=(ssize_t) montage_info->border_width; 00491 extract_info.y=(ssize_t) montage_info->border_width; 00492 if (montage_info->geometry != (char *) NULL) 00493 { 00494 /* 00495 Initialize tile geometry. 00496 */ 00497 flags=GetGeometry(montage_info->geometry,&extract_info.x,&extract_info.y, 00498 &extract_info.width,&extract_info.height); 00499 if ((extract_info.x == 0) && (extract_info.y == 0)) 00500 concatenate=((flags & RhoValue) == 0) && ((flags & SigmaValue) == 0) ? 00501 MagickTrue : MagickFalse; 00502 } 00503 border_width=montage_info->border_width; 00504 bevel_width=0; 00505 if (montage_info->frame != (char *) NULL) 00506 { 00507 char 00508 absolute_geometry[MaxTextExtent]; 00509 00510 (void) ResetMagickMemory(&frame_info,0,sizeof(frame_info)); 00511 frame_info.width=extract_info.width; 00512 frame_info.height=extract_info.height; 00513 (void) FormatLocaleString(absolute_geometry,MaxTextExtent,"%s!", 00514 montage_info->frame); 00515 flags=ParseMetaGeometry(absolute_geometry,&frame_info.outer_bevel, 00516 &frame_info.inner_bevel,&frame_info.width,&frame_info.height); 00517 if ((flags & HeightValue) == 0) 00518 frame_info.height=frame_info.width; 00519 if ((flags & XiValue) == 0) 00520 frame_info.outer_bevel=(ssize_t) frame_info.width/2; 00521 if ((flags & PsiValue) == 0) 00522 frame_info.inner_bevel=frame_info.outer_bevel; 00523 frame_info.x=(ssize_t) frame_info.width; 00524 frame_info.y=(ssize_t) frame_info.height; 00525 bevel_width=(size_t) MagickMax(frame_info.inner_bevel, 00526 frame_info.outer_bevel); 00527 border_width=(size_t) MagickMax((ssize_t) frame_info.width, 00528 (ssize_t) frame_info.height); 00529 } 00530 for (i=0; i < (ssize_t) number_images; i++) 00531 { 00532 if (image_list[i]->columns > extract_info.width) 00533 extract_info.width=image_list[i]->columns; 00534 if (image_list[i]->rows > extract_info.height) 00535 extract_info.height=image_list[i]->rows; 00536 } 00537 /* 00538 Initialize draw attributes. 00539 */ 00540 clone_info=CloneImageInfo(image_info); 00541 clone_info->background_color=montage_info->background_color; 00542 clone_info->border_color=montage_info->border_color; 00543 draw_info=CloneDrawInfo(clone_info,(DrawInfo *) NULL); 00544 if (montage_info->font != (char *) NULL) 00545 (void) CloneString(&draw_info->font,montage_info->font); 00546 if (montage_info->pointsize != 0.0) 00547 draw_info->pointsize=montage_info->pointsize; 00548 draw_info->gravity=CenterGravity; 00549 draw_info->stroke=montage_info->stroke; 00550 draw_info->fill=montage_info->fill; 00551 draw_info->text=AcquireString(""); 00552 (void) GetTypeMetrics(image_list[0],draw_info,&metrics,exception); 00553 texture=NewImageList(); 00554 if (montage_info->texture != (char *) NULL) 00555 { 00556 (void) CopyMagickString(clone_info->filename,montage_info->texture, 00557 MaxTextExtent); 00558 texture=ReadImage(clone_info,exception); 00559 } 00560 /* 00561 Determine the number of lines in an next label. 00562 */ 00563 title=InterpretImageProperties(clone_info,image_list[0],montage_info->title, 00564 exception); 00565 title_offset=0; 00566 if (montage_info->title != (char *) NULL) 00567 title_offset=(size_t) (2*(metrics.ascent-metrics.descent)* 00568 MultilineCensus(title)+2*extract_info.y); 00569 number_lines=0; 00570 for (i=0; i < (ssize_t) number_images; i++) 00571 { 00572 value=GetImageProperty(image_list[i],"label",exception); 00573 if (value == (const char *) NULL) 00574 continue; 00575 if (MultilineCensus(value) > number_lines) 00576 number_lines=MultilineCensus(value); 00577 } 00578 /* 00579 Allocate next structure. 00580 */ 00581 tile_image=AcquireImage((ImageInfo *) NULL,exception); 00582 montage=AcquireImage(clone_info,exception); 00583 montage->background_color=montage_info->background_color; 00584 montage->scene=0; 00585 images_per_page=(number_images-1)/(tiles_per_row*tiles_per_column)+1; 00586 tiles=0; 00587 total_tiles=(size_t) number_images; 00588 for (i=0; i < (ssize_t) images_per_page; i++) 00589 { 00590 /* 00591 Determine bounding box. 00592 */ 00593 tiles_per_page=tiles_per_row*tiles_per_column; 00594 x_offset=0; 00595 y_offset=0; 00596 if (montage_info->tile != (char *) NULL) 00597 GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset, 00598 &sans,&sans); 00599 tiles_per_page=tiles_per_row*tiles_per_column; 00600 y_offset+=(ssize_t) title_offset; 00601 max_height=0; 00602 bounds.width=0; 00603 bounds.height=0; 00604 width=0; 00605 for (tile=0; tile < (ssize_t) tiles_per_page; tile++) 00606 { 00607 if (tile < (ssize_t) number_images) 00608 { 00609 width=concatenate != MagickFalse ? image_list[tile]->columns : 00610 extract_info.width; 00611 if (image_list[tile]->rows > max_height) 00612 max_height=image_list[tile]->rows; 00613 } 00614 x_offset+=(ssize_t) (width+2*(extract_info.x+border_width)); 00615 if (x_offset > (ssize_t) bounds.width) 00616 bounds.width=(size_t) x_offset; 00617 if (((tile+1) == (ssize_t) tiles_per_page) || 00618 (((tile+1) % tiles_per_row) == 0)) 00619 { 00620 x_offset=0; 00621 if (montage_info->tile != (char *) NULL) 00622 GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y, 00623 &sans,&sans); 00624 height=concatenate != MagickFalse ? max_height : extract_info.height; 00625 y_offset+=(ssize_t) (height+(extract_info.y+(ssize_t) border_width)*2+ 00626 (metrics.ascent-metrics.descent+4)*number_lines+ 00627 (montage_info->shadow != MagickFalse ? 4 : 0)); 00628 if (y_offset > (ssize_t) bounds.height) 00629 bounds.height=(size_t) y_offset; 00630 max_height=0; 00631 } 00632 } 00633 if (montage_info->shadow != MagickFalse) 00634 bounds.width+=4; 00635 /* 00636 Initialize montage image. 00637 */ 00638 (void) CopyMagickString(montage->filename,montage_info->filename, 00639 MaxTextExtent); 00640 montage->columns=(size_t) MagickMax((ssize_t) bounds.width,1); 00641 montage->rows=(size_t) MagickMax((ssize_t) bounds.height,1); 00642 (void) SetImageBackgroundColor(montage,exception); 00643 /* 00644 Set montage geometry. 00645 */ 00646 montage->montage=AcquireString((char *) NULL); 00647 tile=0; 00648 extent=1; 00649 while (tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images)) 00650 { 00651 extent+=strlen(image_list[tile]->filename)+1; 00652 tile++; 00653 } 00654 montage->directory=(char *) AcquireQuantumMemory(extent, 00655 sizeof(*montage->directory)); 00656 if ((montage->montage == (char *) NULL) || 00657 (montage->directory == (char *) NULL)) 00658 ThrowImageException(ResourceLimitError,"MemoryAllocationFailed"); 00659 x_offset=0; 00660 y_offset=0; 00661 if (montage_info->tile != (char *) NULL) 00662 GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset, 00663 &sans,&sans); 00664 y_offset+=(ssize_t) title_offset; 00665 (void) FormatLocaleString(montage->montage,MaxTextExtent, 00666 "%.20gx%.20g%+.20g%+.20g",(double) (extract_info.width+ 00667 (extract_info.x+border_width)*2),(double) (extract_info.height+ 00668 (extract_info.y+border_width)*2+(double) ((metrics.ascent- 00669 metrics.descent+4)*number_lines+(montage_info->shadow != MagickFalse ? 4 : 00670 0))),(double) x_offset,(double) y_offset); 00671 *montage->directory='\0'; 00672 tile=0; 00673 while (tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images)) 00674 { 00675 (void) ConcatenateMagickString(montage->directory, 00676 image_list[tile]->filename,extent); 00677 (void) ConcatenateMagickString(montage->directory,"\n",extent); 00678 tile++; 00679 } 00680 progress_monitor=SetImageProgressMonitor(montage,(MagickProgressMonitor) 00681 NULL,montage->client_data); 00682 if (texture != (Image *) NULL) 00683 (void) TextureImage(montage,texture,exception); 00684 if (montage_info->title != (char *) NULL) 00685 { 00686 char 00687 geometry[MaxTextExtent]; 00688 00689 DrawInfo 00690 *clone_info; 00691 00692 TypeMetric 00693 metrics; 00694 00695 /* 00696 Annotate composite image with title. 00697 */ 00698 clone_info=CloneDrawInfo(image_info,draw_info); 00699 clone_info->gravity=CenterGravity; 00700 clone_info->pointsize*=2.0; 00701 (void) GetTypeMetrics(image_list[0],clone_info,&metrics,exception); 00702 (void) FormatLocaleString(geometry,MaxTextExtent, 00703 "%.20gx%.20g%+.20g%+.20g",(double) montage->columns,(double) 00704 (metrics.ascent-metrics.descent),0.0,(double) extract_info.y+4); 00705 (void) CloneString(&clone_info->geometry,geometry); 00706 (void) CloneString(&clone_info->text,title); 00707 (void) AnnotateImage(montage,clone_info,exception); 00708 clone_info=DestroyDrawInfo(clone_info); 00709 } 00710 (void) SetImageProgressMonitor(montage,progress_monitor, 00711 montage->client_data); 00712 /* 00713 Copy tile to the composite. 00714 */ 00715 x_offset=0; 00716 y_offset=0; 00717 if (montage_info->tile != (char *) NULL) 00718 GetMontageGeometry(montage_info->tile,number_images,&x_offset,&y_offset, 00719 &sans,&sans); 00720 x_offset+=extract_info.x; 00721 y_offset+=(ssize_t) title_offset+extract_info.y; 00722 max_height=0; 00723 status=MagickTrue; 00724 for (tile=0; tile < MagickMin((ssize_t) tiles_per_page,(ssize_t) number_images); tile++) 00725 { 00726 /* 00727 Copy this tile to the composite. 00728 */ 00729 image=CloneImage(image_list[tile],0,0,MagickTrue,exception); 00730 progress_monitor=SetImageProgressMonitor(image, 00731 (MagickProgressMonitor) NULL,image->client_data); 00732 width=concatenate != MagickFalse ? image->columns : extract_info.width; 00733 if (image->rows > max_height) 00734 max_height=image->rows; 00735 height=concatenate != MagickFalse ? max_height : extract_info.height; 00736 if (border_width != 0) 00737 { 00738 Image 00739 *border_image; 00740 00741 RectangleInfo 00742 border_info; 00743 00744 /* 00745 Put a border around the image. 00746 */ 00747 border_info.width=border_width; 00748 border_info.height=border_width; 00749 if (montage_info->frame != (char *) NULL) 00750 { 00751 border_info.width=(width-image->columns+1)/2; 00752 border_info.height=(height-image->rows+1)/2; 00753 } 00754 border_image=BorderImage(image,&border_info,image->compose,exception); 00755 if (border_image != (Image *) NULL) 00756 { 00757 image=DestroyImage(image); 00758 image=border_image; 00759 } 00760 if ((montage_info->frame != (char *) NULL) && 00761 (image->compose == DstOutCompositeOp)) 00762 { 00763 SetPixelChannelMapMask(image,AlphaChannel); 00764 (void) NegateImage(image,MagickFalse,exception); 00765 SetPixelChannelMapMask(image,DefaultChannels); 00766 } 00767 } 00768 /* 00769 Gravitate as specified by the tile gravity. 00770 */ 00771 tile_image->columns=width; 00772 tile_image->rows=height; 00773 tile_image->gravity=montage_info->gravity; 00774 if (image->gravity != UndefinedGravity) 00775 tile_image->gravity=image->gravity; 00776 (void) FormatLocaleString(tile_geometry,MaxTextExtent,"%.20gx%.20g+0+0", 00777 (double) image->columns,(double) image->rows); 00778 flags=ParseGravityGeometry(tile_image,tile_geometry,&geometry,exception); 00779 x=(ssize_t) (geometry.x+border_width); 00780 y=(ssize_t) (geometry.y+border_width); 00781 if ((montage_info->frame != (char *) NULL) && (bevel_width != 0)) 00782 { 00783 FrameInfo 00784 extract_info; 00785 00786 Image 00787 *frame_image; 00788 00789 /* 00790 Put an ornamental border around this tile. 00791 */ 00792 extract_info=frame_info; 00793 extract_info.width=width+2*frame_info.width; 00794 extract_info.height=height+2*frame_info.height; 00795 value=GetImageProperty(image,"label",exception); 00796 if (value != (const char *) NULL) 00797 extract_info.height+=(size_t) ((metrics.ascent- 00798 metrics.descent+4)*MultilineCensus(value)); 00799 frame_image=FrameImage(image,&extract_info,image->compose,exception); 00800 if (frame_image != (Image *) NULL) 00801 { 00802 image=DestroyImage(image); 00803 image=frame_image; 00804 } 00805 x=0; 00806 y=0; 00807 } 00808 if (LocaleCompare(image->magick,"NULL") != 0) 00809 { 00810 /* 00811 Composite background with tile. 00812 */ 00813 if (montage_info->shadow != MagickFalse) 00814 { 00815 Image 00816 *shadow_image; 00817 00818 /* 00819 Shadow image. 00820 */ 00821 (void) QueryColorCompliance("#0000",AllCompliance, 00822 &image->background_color,exception); 00823 shadow_image=ShadowImage(image,80.0,2.0,0.0,5,5,exception); 00824 if (shadow_image != (Image *) NULL) 00825 { 00826 (void) CompositeImage(shadow_image,OverCompositeOp,image,0,0, 00827 exception); 00828 image=DestroyImage(image); 00829 image=shadow_image; 00830 } 00831 } 00832 (void) CompositeImage(montage,image->compose,image,x_offset+x, 00833 y_offset+y,exception); 00834 value=GetImageProperty(image,"label",exception); 00835 if (value != (const char *) NULL) 00836 { 00837 char 00838 geometry[MaxTextExtent]; 00839 00840 /* 00841 Annotate composite tile with label. 00842 */ 00843 (void) FormatLocaleString(geometry,MaxTextExtent, 00844 "%.20gx%.20g%+.20g%+.20g",(double) ((montage_info->frame ? 00845 image->columns : width)-2*border_width),(double) 00846 (metrics.ascent-metrics.descent+4)*MultilineCensus(value), 00847 (double) (x_offset+border_width),(double) 00848 ((montage_info->frame ? y_offset+height+border_width+4 : 00849 y_offset+extract_info.height+border_width+ 00850 (montage_info->shadow != MagickFalse ? 4 : 0))+bevel_width)); 00851 (void) CloneString(&draw_info->geometry,geometry); 00852 (void) CloneString(&draw_info->text,value); 00853 (void) AnnotateImage(montage,draw_info,exception); 00854 } 00855 } 00856 x_offset+=(ssize_t) (width+2*(extract_info.x+border_width)); 00857 if (((tile+1) == (ssize_t) tiles_per_page) || 00858 (((tile+1) % tiles_per_row) == 0)) 00859 { 00860 x_offset=extract_info.x; 00861 y_offset+=(ssize_t) (height+(extract_info.y+border_width)*2+ 00862 (metrics.ascent-metrics.descent+4)*number_lines+ 00863 (montage_info->shadow != MagickFalse ? 4 : 0)); 00864 max_height=0; 00865 } 00866 if (images->progress_monitor != (MagickProgressMonitor) NULL) 00867 { 00868 MagickBooleanType 00869 proceed; 00870 00871 proceed=SetImageProgress(image,MontageImageTag,tiles,total_tiles); 00872 if (proceed == MagickFalse) 00873 status=MagickFalse; 00874 } 00875 image_list[tile]=DestroyImage(image_list[tile]); 00876 image=DestroyImage(image); 00877 tiles++; 00878 } 00879 (void) status; 00880 if ((i+1) < (ssize_t) images_per_page) 00881 { 00882 /* 00883 Allocate next image structure. 00884 */ 00885 AcquireNextImage(clone_info,montage,exception); 00886 if (GetNextImageInList(montage) == (Image *) NULL) 00887 { 00888 montage=DestroyImageList(montage); 00889 return((Image *) NULL); 00890 } 00891 montage=GetNextImageInList(montage); 00892 montage->background_color=montage_info->background_color; 00893 image_list+=tiles_per_page; 00894 number_images-=tiles_per_page; 00895 } 00896 } 00897 tile_image=DestroyImage(tile_image); 00898 if (texture != (Image *) NULL) 00899 texture=DestroyImage(texture); 00900 master_list=(Image **) RelinquishMagickMemory(master_list); 00901 draw_info=DestroyDrawInfo(draw_info); 00902 clone_info=DestroyImageInfo(clone_info); 00903 return(GetFirstImageInList(montage)); 00904 }