cache.c

Go to the documentation of this file.
00001 /*
00002 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00003 %                                                                             %
00004 %                                                                             %
00005 %                                                                             %
00006 %                      CCCC   AAA    CCCC  H   H  EEEEE                       %
00007 %                     C      A   A  C      H   H  E                           %
00008 %                     C      AAAAA  C      HHHHH  EEE                         %
00009 %                     C      A   A  C      H   H  E                           %
00010 %                      CCCC  A   A   CCCC  H   H  EEEEE                       %
00011 %                                                                             %
00012 %                                                                             %
00013 %                       MagickCore Pixel Cache Methods                        %
00014 %                                                                             %
00015 %                              Software Design                                %
00016 %                                John Cristy                                  %
00017 %                                 July 1999                                   %
00018 %                                                                             %
00019 %                                                                             %
00020 %  Copyright 1999-2009 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 "magick/studio.h"
00044 #include "magick/blob.h"
00045 #include "magick/blob-private.h"
00046 #include "magick/cache.h"
00047 #include "magick/cache-private.h"
00048 #include "magick/color-private.h"
00049 #include "magick/composite-private.h"
00050 #include "magick/exception.h"
00051 #include "magick/exception-private.h"
00052 #include "magick/list.h"
00053 #include "magick/log.h"
00054 #include "magick/magick.h"
00055 #include "magick/memory_.h"
00056 #include "magick/pixel-private.h"
00057 #include "magick/quantum.h"
00058 #include "magick/random_.h"
00059 #include "magick/resource_.h"
00060 #include "magick/semaphore.h"
00061 #include "magick/splay-tree.h"
00062 #include "magick/string_.h"
00063 #include "magick/utility.h"
00064 #if defined(MAGICKCORE_ZLIB_DELEGATE)
00065 #include "zlib.h"
00066 #endif
00067 #if defined(MAGICKCORE_HAVE_PTHREAD)
00068 #include <pthread.h>
00069 #endif
00070 #if defined(__WINDOWS__)
00071 #include <windows.h>
00072 #endif
00073 
00074 /*
00075   Typedef declarations.
00076 */
00077 struct _NexusInfo
00078 {
00079   MagickBooleanType
00080     mapped;
00081 
00082   RectangleInfo
00083     region;
00084 
00085   MagickSizeType
00086     length;
00087 
00088   PixelPacket
00089     *cache,
00090     *pixels;
00091 
00092   IndexPacket
00093     *indexes;
00094 
00095   unsigned long
00096     signature;
00097 };
00098 
00099 /*
00100   Forward declarations.
00101 */
00102 #if defined(__cplusplus) || defined(c_plusplus)
00103 extern "C" {
00104 #endif
00105 
00106 static const IndexPacket
00107   *GetVirtualIndexesFromCache(const Image *);
00108 
00109 static const PixelPacket
00110   *GetVirtualPixelCache(const Image *,const VirtualPixelMethod,const long,
00111     const long,const unsigned long,const unsigned long,ExceptionInfo *),
00112   *GetVirtualPixelsCache(const Image *);
00113 
00114 static MagickBooleanType
00115   GetOneAuthenticPixelFromCache(Image *,const long,const long,PixelPacket *,
00116     ExceptionInfo *),
00117   GetOneVirtualPixelFromCache(const Image *,const VirtualPixelMethod,
00118     const long,const long,PixelPacket *,ExceptionInfo *),
00119   OpenPixelCache(Image *,const MapMode,ExceptionInfo *),
00120   ReadPixelCacheIndexes(CacheInfo *,NexusInfo *,ExceptionInfo *),
00121   ReadPixelCachePixels(CacheInfo *,NexusInfo *,ExceptionInfo *),
00122   SyncAuthenticPixelsCache(Image *,ExceptionInfo *),
00123   WritePixelCacheIndexes(CacheInfo *,NexusInfo *,ExceptionInfo *),
00124   WritePixelCachePixels(CacheInfo *,NexusInfo *,ExceptionInfo *);
00125 
00126 static PixelPacket
00127   *GetAuthenticPixelsCache(Image *,const long,const long,const unsigned long,
00128     const unsigned long,ExceptionInfo *),
00129   *QueueAuthenticPixelsCache(Image *,const long,const long,const unsigned long,
00130     const unsigned long,ExceptionInfo *),
00131   *SetPixelCacheNexusPixels(const Image *,const RectangleInfo *,NexusInfo *);
00132 
00133 #if defined(__cplusplus) || defined(c_plusplus)
00134 }
00135 #endif
00136 
00137 /*
00138   Global declarations.
00139 */
00140 static volatile MagickBooleanType
00141   instantiate_cache = MagickFalse;
00142 
00143 static SemaphoreInfo
00144   *cache_semaphore = (SemaphoreInfo *) NULL;
00145 
00146 static SplayTreeInfo
00147   *cache_resources = (SplayTreeInfo *) NULL;
00148 
00149 /*
00150   Lightweight OpenMP methods.
00151 */
00152 MagickExport unsigned long GetPixelCacheMaximumThreads(void)
00153 {
00154   static unsigned long
00155     maximum_threads = 0;
00156 
00157 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00158   if (omp_get_max_threads() > (long) maximum_threads)
00159     maximum_threads=omp_get_max_threads();
00160 #else
00161   maximum_threads=1;
00162 #endif
00163   return(maximum_threads);
00164 }
00165 
00166 MagickExport long GetPixelCacheThreadId(void)
00167 {
00168 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00169   return(omp_get_thread_num());
00170 #else
00171   return(0);
00172 #endif
00173 }
00174 
00175 MagickExport void SetPixelCacheMaximumThreads(const unsigned long threads)
00176 {
00177 #if defined(MAGICKCORE_OPENMP_SUPPORT)
00178   omp_set_num_threads(threads);
00179 #else
00180   (void) threads;
00181 #endif
00182 }
00183 
00184 /*
00185 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00186 %                                                                             %
00187 %                                                                             %
00188 %                                                                             %
00189 +   A c q u i r e P i x e l C a c h e I n f o                                 %
00190 %                                                                             %
00191 %                                                                             %
00192 %                                                                             %
00193 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00194 %
00195 %  AcquirePixelCacheInfo() acquires a pixel cache.
00196 %
00197 %  The format of the AcquirePixelCacheInfo() method is:
00198 %
00199 %      Cache AcquirePixelCacheInfo(const unsigned long number_threads)
00200 %
00201 %  A description of each parameter follows:
00202 %
00203 %    o number_threads: the number of nexus threads.
00204 %
00205 */
00206 MagickExport Cache AcquirePixelCacheInfo(const unsigned long number_threads)
00207 {
00208   CacheInfo
00209     *cache_info;
00210 
00211   cache_info=(CacheInfo *) AcquireMagickMemory(sizeof(*cache_info));
00212   if (cache_info == (CacheInfo *) NULL)
00213     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00214   (void) ResetMagickMemory(cache_info,0,sizeof(*cache_info));
00215   cache_info->type=UndefinedCache;
00216   cache_info->colorspace=RGBColorspace;
00217   cache_info->file=(-1);
00218 #if defined(MAGICKCORE_HAVE_PTHREAD)
00219   cache_info->id=pthread_self();
00220 #elif defined(__WINDOWS__)
00221   cache_info->id=GetCurrentThreadId();
00222 #else
00223   cache_info->id=getpid();
00224 #endif
00225   cache_info->number_threads=number_threads;
00226   if (number_threads == 0)
00227     cache_info->number_threads=GetPixelCacheMaximumThreads();
00228   cache_info->nexus_info=AcquirePixelCacheNexus(cache_info->number_threads);
00229   if (cache_info->nexus_info == (NexusInfo **) NULL)
00230     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00231   GetPixelCacheMethods(&cache_info->methods);
00232   cache_info->reference_count=1;
00233   cache_info->semaphore=AllocateSemaphoreInfo();
00234   cache_info->disk_semaphore=AllocateSemaphoreInfo();
00235   cache_info->debug=IsEventLogging();
00236   cache_info->signature=MagickSignature;
00237   if ((cache_resources == (SplayTreeInfo *) NULL) &&
00238       (instantiate_cache == MagickFalse))
00239     {
00240       AcquireSemaphoreInfo(&cache_semaphore);
00241       if ((cache_resources == (SplayTreeInfo *) NULL) &&
00242           (instantiate_cache == MagickFalse))
00243         {
00244           cache_resources=NewSplayTree((int (*)(const void *,const void *))
00245             NULL,(void *(*)(void *)) NULL,(void *(*)(void *)) NULL);
00246           instantiate_cache=MagickTrue;
00247         }
00248       RelinquishSemaphoreInfo(cache_semaphore);
00249     }
00250   (void) AddValueToSplayTree(cache_resources,cache_info,cache_info);
00251   return((Cache ) cache_info);
00252 }
00253 
00254 /*
00255 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00256 %                                                                             %
00257 %                                                                             %
00258 %                                                                             %
00259 %   A c q u i r e P i x e l C a c h e N e x u s                               %
00260 %                                                                             %
00261 %                                                                             %
00262 %                                                                             %
00263 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00264 %
00265 %  AcquirePixelCacheNexus() allocates the NexusInfo structure.
00266 %
00267 %  The format of the AcquirePixelCacheNexus method is:
00268 %
00269 %      NexusInfo **AcquirePixelCacheNexus(const unsigned long number_threads)
00270 %
00271 %  A description of each parameter follows:
00272 %
00273 %    o number_threads: the number of nexus threads.
00274 %
00275 */
00276 MagickExport NexusInfo **AcquirePixelCacheNexus(
00277   const unsigned long number_threads)
00278 {
00279   register long
00280     i;
00281 
00282   NexusInfo
00283     **nexus_info;
00284 
00285   nexus_info=(NexusInfo **) AcquireQuantumMemory(number_threads,
00286     sizeof(*nexus_info));
00287   if (nexus_info == (NexusInfo **) NULL)
00288     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00289   for (i=0; i < (long) number_threads; i++)
00290   {
00291     nexus_info[i]=(NexusInfo *) AcquireMagickMemory(sizeof(**nexus_info));
00292     if (nexus_info[i] == (NexusInfo *) NULL)
00293       ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00294     (void) ResetMagickMemory(nexus_info[i],0,sizeof(*nexus_info[i]));
00295     nexus_info[i]->signature=MagickSignature;
00296   }
00297   return(nexus_info);
00298 }
00299 
00300 /*
00301 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00302 %                                                                             %
00303 %                                                                             %
00304 %                                                                             %
00305 +   C l i p P i x e l C a c h e N e x u s                                     %
00306 %                                                                             %
00307 %                                                                             %
00308 %                                                                             %
00309 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00310 %
00311 %  ClipPixelCacheNexus() clips the cache nexus as defined by the image clip
00312 %  mask.  The method returns MagickTrue if the pixel region is clipped,
00313 %  otherwise MagickFalse.
00314 %
00315 %  The format of the ClipPixelCacheNexus() method is:
00316 %
00317 %      MagickBooleanType ClipPixelCacheNexus(Image *image,NexusInfo *nexus_info,
00318 %        ExceptionInfo *exception)
00319 %
00320 %  A description of each parameter follows:
00321 %
00322 %    o image: the image.
00323 %
00324 %    o nexus_info: the cache nexus to clip.
00325 %
00326 %    o exception: return any errors or warnings in this structure.
00327 %
00328 */
00329 static MagickBooleanType ClipPixelCacheNexus(Image *image,
00330   NexusInfo *nexus_info,ExceptionInfo *exception)
00331 {
00332   CacheInfo
00333     *cache_info;
00334 
00335   MagickSizeType
00336     number_pixels;
00337 
00338   NexusInfo
00339     **clip_nexus,
00340     **image_nexus;
00341 
00342   register const PixelPacket
00343     *r;
00344 
00345   register IndexPacket
00346     *nexus_indexes,
00347     *indexes;
00348 
00349   register long
00350     i;
00351 
00352   register PixelPacket
00353     *p,
00354     *q;
00355 
00356   /*
00357     Apply clip mask.
00358   */
00359   if (image->debug != MagickFalse)
00360     (void) LogMagickEvent(TraceEvent,GetMagickModule(),"%s",image->filename);
00361   if (image->clip_mask == (Image *) NULL)
00362     return(MagickFalse);
00363   cache_info=GetImagePixelCache(image,MagickTrue,exception);
00364   if (cache_info == (Cache) NULL)
00365     return(MagickFalse);
00366   image_nexus=AcquirePixelCacheNexus(1);
00367   clip_nexus=AcquirePixelCacheNexus(1);
00368   if ((image_nexus == (NexusInfo **) NULL) ||
00369       (clip_nexus == (NexusInfo **) NULL))
00370     ThrowBinaryException(CacheError,"UnableToGetCacheNexus",image->filename);
00371   p=GetAuthenticPixelCacheNexus(image,nexus_info->region.x,nexus_info->region.y,
00372     nexus_info->region.width,nexus_info->region.height,image_nexus[0],
00373     exception);
00374   indexes=GetPixelCacheNexusIndexes(image->cache,image_nexus[0]);
00375   q=nexus_info->pixels;
00376   nexus_indexes=nexus_info->indexes;
00377   r=GetVirtualPixelsFromNexus(image->clip_mask,MaskVirtualPixelMethod,
00378     nexus_info->region.x,nexus_info->region.y,nexus_info->region.width,
00379     nexus_info->region.height,clip_nexus[0],exception);
00380   number_pixels=(MagickSizeType) nexus_info->region.width*
00381     nexus_info->region.height;
00382   for (i=0; i < (long) number_pixels; i++)
00383   {
00384     if ((p == (PixelPacket *) NULL) || (r == (const PixelPacket *) NULL))
00385       break;
00386     if (PixelIntensityToQuantum(r) > ((Quantum) QuantumRange/2))
00387       {
00388         q->red=p->red;
00389         q->green=p->green;
00390         q->blue=p->blue;
00391         q->opacity=p->opacity;
00392         if (cache_info->active_index_channel != MagickFalse)
00393           nexus_indexes[i]=indexes[i];
00394       }
00395     p++;
00396     q++;
00397     r++;
00398   }
00399   clip_nexus=DestroyPixelCacheNexus(clip_nexus,1);
00400   image_nexus=DestroyPixelCacheNexus(image_nexus,1);
00401   if (i < (long) number_pixels)
00402     return(MagickFalse);
00403   return(MagickTrue);
00404 }
00405 
00406 /*
00407 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00408 %                                                                             %
00409 %                                                                             %
00410 %                                                                             %
00411 +   C l o n e P i x e l C a c h e N e x u s                                   %
00412 %                                                                             %
00413 %                                                                             %
00414 %                                                                             %
00415 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00416 %
00417 %  ClonePixelCacheNexus() clones the source cache nexus to the destination
00418 %  nexus.
00419 %
00420 %  The format of the ClonePixelCacheNexus() method is:
00421 %
00422 %      MagickBooleanType ClonePixelCacheNexus(CacheInfo *destination,
00423 %        CacheInfo *source)
00424 %
00425 %  A description of each parameter follows:
00426 %
00427 %    o destination: the destination cache nexus.
00428 %
00429 %    o source: the source cache nexus.
00430 %
00431 */
00432 
00433 static inline MagickBooleanType GetVirtualPixelsFromNexusPixels(
00434   NexusInfo *nexus_info)
00435 {
00436   if (nexus_info->length != (MagickSizeType) ((size_t) nexus_info->length))
00437     return(MagickFalse);
00438   nexus_info->mapped=MagickFalse;
00439   nexus_info->cache=(PixelPacket *) AcquireMagickMemory((size_t)
00440     nexus_info->length);
00441   if (nexus_info->cache == (PixelPacket *) NULL)
00442     {
00443       nexus_info->mapped=MagickTrue;
00444       nexus_info->cache=(PixelPacket *) MapBlob(-1,IOMode,0,(size_t)
00445         nexus_info->length);
00446     }
00447   return(nexus_info->cache == (PixelPacket *) NULL ? MagickFalse : MagickTrue);
00448 }
00449 
00450 static MagickBooleanType ClonePixelCacheNexus(CacheInfo *destination,
00451   CacheInfo *source)
00452 {
00453   MagickBooleanType
00454     status;
00455 
00456   MagickSizeType
00457     number_pixels;
00458 
00459   register long
00460     i;
00461 
00462   register NexusInfo
00463     *p;
00464 
00465   register NexusInfo
00466     *q;
00467 
00468   destination->nexus_info=AcquirePixelCacheNexus(source->number_threads);
00469   if (destination->nexus_info == (NexusInfo **) NULL)
00470     ThrowFatalException(ResourceLimitFatalError,"MemoryAllocationFailed");
00471   status=MagickTrue;
00472   for (i=0; i < (long) source->number_threads; i++)
00473   {
00474     p=source->nexus_info[i];
00475     q=destination->nexus_info[i];
00476     q->mapped=p->mapped;
00477     q->region=p->region;
00478     q->length=p->length;
00479     q->cache=p->cache;
00480     q->pixels=p->pixels;
00481     q->indexes=p->indexes;
00482     if (p->cache != (PixelPacket *) NULL)
00483       {
00484         status=GetVirtualPixelsFromNexusPixels(q);
00485         if (status != MagickFalse)
00486           {
00487             (void) CopyMagickMemory(q->cache,p->cache,(size_t) p->length);
00488             q->pixels=q->cache;
00489             q->indexes=(IndexPacket *) NULL;
00490             number_pixels=(MagickSizeType) q->region.width*q->region.height;
00491             if (p->indexes != (IndexPacket *) NULL)
00492               q->indexes=(IndexPacket *) (q->pixels+number_pixels);
00493           }
00494       }
00495   }
00496   return(status);
00497 }
00498 
00499 /*
00500 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
00501 %                                                                             %
00502 %                                                                             %
00503 %                                                                             %
00504 +   C l o n e P i x e l C a c h e                                             %
00505 %                                                                             %
00506 %                                                                             %
00507 %                                                                             %
00508 %%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% %
00509 %  ClonePixelCache() clones the source pixel cache to the destination cache.
00510 %
00511 %  The format of the ClonePixelCache() method is:
00512 %
00513 %      MagickBooleanType ClonePixelCache(CacheInfo *cache_info,
00514 %        CacheInfo *source_info,ExceptionInfo *exception)
00515 %
00516 %  A description of each parameter follows:
00517 %
00518 %    o cache_info: the pixel cache.
00519 %
00520 %    o source_info: the source pixel cache.
00521 %
00522 %    o exception: return any errors or warnings in this structure.
00523 %
00524 */
00525 
00526 static MagickBooleanType ClosePixelCacheOnDisk(CacheInfo *cache_info)
00527 {
00528   int
00529     status;
00530 
00531   AcquireSemaphoreInfo(&cache_info->disk_semaphore);
00532   status=close(cache_info->file);
00533   cache_info->file=(-1);
00534   RelinquishMagickResource(FileResource,1);
00535   RelinquishSemaphoreInfo(cache_info->disk_semaphore);
00536   return(status == -1 ? MagickFalse : MagickTrue);
00537 }
00538 
00539 static void LimitPixelCacheDescriptors(void)
00540 {
00541   register CacheInfo
00542     *p,
00543     *q;
00544 
00545   /*
00546     Limit # of open file descriptors.
00547   */
00548   if (GetMagickResource(FileResource) < GetMagickResourceLimit(FileResource))
00549     return;
00550   AcquireSemaphoreInfo(&cache_semaphore);
00551   if (cache_resources == (SplayTreeInfo *) NULL)
00552     {
00553       RelinquishSemaphoreInfo(cache_semaphore);
00554       return;
00555     }
00556   ResetSplayTreeIterator(cache_resources);
00557   p=(CacheInfo *) GetNextKeyInSplayTree(cache_resources);
00558   while (p != (CacheInfo *) NULL)
00559   {
00560     if ((p->type == DiskCache) && (p->file != -1))
00561       {
00562 #if defined(MAGICKCORE_HAVE_PTHREAD)
00563         if (pthread_equal(p->id,pthread_self()) != 0)
00564 #elif defined(__WINDOWS__)
00565         if (p->id == GetCurrentThreadId())
00566 #else
00567         if (p->id == getpid())
00568 #endif
00569           break;
00570       }
00571     p=(CacheInfo *) GetNextKeyInSplayTree(cache_resources);
00572   }
00573   for (q=p; p != (CacheInfo *) NULL; )
00574   {
00575     if ((p->type == DiskCache) && (p->file != -1) &&
00576         (p->timestamp < q->timestamp))
00577       {
00578 #if defined(MAGICKCORE_HAVE_PTHREAD)
00579         if (pthread_equal(p->id,pthread_self()) != 0)
00580 #elif defined(__WINDOWS__)
00581         if (p->id == GetCurrentThreadId())
00582 #else
00583         if (p->id == getpid())
00584 #endif
00585           q=p;
00586       }
00587     p=(CacheInfo *) GetNextKeyInSplayTree(cache_resources);
00588   }
00589   if (q != (CacheInfo *) NULL)
00590     (void) ClosePixelCacheOnDisk(q);  /* relinquish least recently used cache */
00591   RelinquishSemaphoreInfo(cache_semaphore);
00592 }
00593 
00594 static inline MagickSizeType MagickMax(const MagickSizeType x,
00595   const MagickSizeType y)
00596 {
00597   if (x > y)
00598     return(x);
00599   return(y);
00600 }
00601 
00602 static inline MagickSizeType MagickMin(const MagickSizeType x,
00603   const MagickSizeType y)
00604 {
00605   if (x < y)
00606     return(x);
00607   return(y);
00608 }
00609 
00610 static MagickBooleanType OpenPixelCacheOnDisk(CacheInfo *cache_info,
00611   const MapMode mode)
00612 {
00613   int
00614     file;
00615 
00616   /*
00617     Open pixel cache on disk.
00618   */
00619   AcquireSemaphoreInfo(&cache_info->disk_semaphore);
00620   if (cache_info->file != -1)
00621     {
00622       RelinquishSemaphoreInfo(cache_info->disk_semaphore);
00623       return(MagickTrue);  /* cache already open */
00624     }
00625   LimitPixelCacheDescriptors();
00626   if (*cache_info->cache_filename == '\0')
00627     file=AcquireUniqueFileResource(cache_info->cache_filename);
00628   else
00629     switch (mode)
00630     {
00631       case ReadMode:
00632       {
00633         file=open(cache_info->cache_filename,O_RDONLY | O_BINARY);
00634         break;
00635       }
00636       case WriteMode:
00637       {
00638         file=open(cache_info->cache_filename,O_WRONLY | O_CREAT | O_BINARY |
00639           O_EXCL,S_MODE);
00640         if (file == -1)
00641           file=open(cache_info->cache_filename,O_WRONLY | O_BINARY,S_MODE);
00642         break;
00643       }
00644       case IOMode:
00645       default:
00646       {
00647         file=open(cache_info->cache_filename,O_RDWR | O_CREAT | O_BINARY |
00648           O_EXCL,S_MODE);
00649         if (file == -1)
00650           file=open(cache_info->cache_filename,O_RDWR | O_BINARY,S_MODE);
00651         break;
00652       }
00653     }
00654   if (file == -1)
00655     {
00656       RelinquishSemaphoreInfo(cache_info->disk_semaphore);
00657       return(MagickFalse);
00658     }
00659   (void) AcquireMagickResource(FileResource,1);
00660   cache_info->file=file;
00661   cache_info->timestamp=time(0);
00662   RelinquishSemaphoreInfo(cache_info->disk_semaphore);
00663   return(MagickTrue);
00664 }
00665 
00666 static inline MagickOffsetType ReadPixelCacheRegion(CacheInfo *cache_info,
00667   const MagickOffsetType offset,const MagickSizeType length,
00668   unsigned char *buffer)
00669 {
00670   register MagickOffsetType
00671     i;
00672 
00673   ssize_t
00674     count;
00675 
00676 #if !defined(MAGICKCORE_HAVE_PREAD)
00677   (void) LockSemaphoreInfo(cache_info->disk_semaphore);
00678   cache_info->timestamp=time(0);
00679   if (MagickSeek(cache_info->file,offset,SEEK_SET) < 0)
00680     {
00681       (void) UnlockSemaphoreInfo(cache_info->disk_semaphore);
00682       return((MagickOffsetType) -1);
00683     }
00684 #endif
00685   count=0;
00686   for (i=0; i < (MagickOffsetType) length; i+=count)
00687   {
00688 #if !defined(MAGICKCORE_HAVE_PREAD)
00689     count=read(cache_info->file,buffer+i,(size_t) MagickMin(length-i,
00690       (MagickSizeType) SSIZE_MAX));
00691 #else
00692     count=pread(cache_info->file,buffer+i,(size_t) MagickMin(length-i,
00693       (MagickSizeType) SSIZE_MAX),(off_t) (offset+i));
00694 #endif
00695     if (count > 0)
00696       continue;
00697     count=0;
00698     if (errno != EINTR)
00699       {
00700         i=(-1);
00701         break;
00702       }
00703   }
00704 #if !defined(MAGICKCORE_HAVE_PREAD)
00705   (void) UnlockSemaphoreInfo(cache_info->disk_semaphore);
00706 #endif
00707   return(i);
00708 }
00709 
00710 static inline MagickOffsetType WritePixelCacheRegion(CacheInfo *cache_info,
00711   const MagickOffsetType offset,const MagickSizeType length,
00712   const unsigned char *buffer)
00713 {
00714   register MagickOffsetType
00715     i;
00716 
00717   ssize_t
00718     count;
00719 
00720 #if !defined(MAGICKCORE_HAVE_PWRITE)
00721   (void) LockSemaphoreInfo(cache_info->disk_semaphore);
00722   cache_info->timestamp=time(0);
00723   if (MagickSeek(cache_info->file,offset,SEEK_SET) < 0)
00724     {
00725       (void) UnlockSemaphoreInfo(cache_info->disk_semaphore);
00726       return((MagickOffsetType) -1);
00727     }
00728 #endif
00729   count=0;
00730   for (i=0; i < (MagickOffsetType) length; i+=count)
00731   {
00732 #if !defined(MAGICKCORE_HAVE_PWRITE)
00733     count=write(cache_info->file,buffer+i,(size_t) MagickMin(length-i,
00734       (MagickSizeType) SSIZE_MAX));
00735 #else
00736     count=pwrite(cache_info->file,buffer+i,(size_t) MagickMin(length-i,
00737       (MagickSizeType) SSIZE_MAX),(off_t) (offset+i));
00738 #endif
00739     if (count > 0)
00740       continue;
00741     count=0;
00742     if (errno != EINTR)
00743       {
00744         i=(-1);
00745         break;
00746       }
00747   }
00748 #if !defined(MAGICKCORE_HAVE_PWRITE)
00749   (void) UnlockSemaphoreInfo(cache_info->disk_semaphore);
00750 #endif
00751   return(i);
00752 }
00753 
00754 static MagickBooleanType CloneDiskToDiskPixelCache(CacheInfo *clone_info,
00755   CacheInfo *cache_info,ExceptionInfo *exception)
00756 {
00757   MagickOffsetType
00758     count,
00759     offset,
00760     source_offset;
00761 
00762   MagickSizeType
00763     length;
00764 
00765   register long
00766     y;
00767 
00768   register PixelPacket
00769     *pixels;
00770 
00771   unsigned long
00772     columns,
00773     rows;
00774 
00775   if (cache_info->debug != MagickFalse)
00776     (void) LogMagickEvent(CacheEvent,GetMagickModule(),"disk => disk");
00777   if (OpenPixelCacheOnDisk(clone_info,IOMode) == MagickFalse)
00778     {
00779       ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
00780         clone_info->cache_filename);
00781       return(MagickFalse);
00782     }
00783   if (OpenPixelCacheOnDisk(cache_info,IOMode) == MagickFalse)
00784     {
00785       ThrowFileException(exception,FileOpenError,"UnableToOpenFile",
00786         cache_info->cache_filename);
00787       return(MagickFalse);
00788     }
00789   columns=(unsigned long) MagickMin(clone_info->columns,cache_info->columns);
00790   rows=(unsigned long) MagickMin(clone_info->rows,cache_info->rows);
00791   if ((clone_info->active_index_channel != MagickFalse) &&
00792       (cache_info->active_index_channel != MagickFalse))
00793     {
00794       register IndexPacket
00795         *indexes;
00796 
00797       /*
00798         Clone cache indexes.
00799       */
00800       length=MagickMax(clone_info->columns,cache_info->columns)*
00801         sizeof(*indexes);
00802       indexes=(IndexPacket *) AcquireMagickMemory((size_t) length);
00803       if (indexes == (IndexPacket *) NULL)
00804         {
00805           (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
00806             "MemoryAllocationFailed","`%s'",cache_info->cache_filename);
00807           return(MagickFalse);
00808         }
00809       (void) ResetMagickMemory(indexes,0,(size_t) length);
00810       length=columns*sizeof(*indexes);
00811       source_offset=(MagickOffsetType) cache_info->columns*cache_info->rows*
00812         sizeof(*pixels)+cache_info->columns*rows*sizeof(*indexes);
00813       offset=(MagickOffsetType) clone_info->columns*clone_info->rows*
00814         sizeof(*pixels)+clone_info->columns*rows*sizeof(*indexes);
00815       for (y=0; y < (long) rows; y++)
00816       {
00817         source_offset-=cache_info->columns*sizeof(*indexes);
00818         count=ReadPixelCacheRegion(cache_info,cache_info->offset+
00819           source_offset,length,(unsigned char *) indexes);
00820         if ((MagickSizeType) count != length)
00821           break;
00822         offset-=clone_info->columns*sizeof(*indexes);
00823         count=WritePixelCacheRegion(clone_info,clone_info->offset+offset,length,
00824           (unsigned char *) indexes);
00825         if ((MagickSizeType) count != length)
00826           break;
00827       }
00828       if (y < (long) rows)
00829         {
00830           indexes=(IndexPacket *) RelinquishMagickMemory(indexes);
00831           ThrowFileException(exception,CacheError,"UnableToCloneCache",
00832             cache_info->cache_filename);
00833           return(MagickFalse);
00834         }
00835       if (clone_info->columns > cache_info->columns)
00836         {
00837           length=(clone_info->columns-cache_info->columns)*sizeof(*indexes);
00838           (void) ResetMagickMemory(indexes,0,(size_t) length);
00839           offset=(MagickOffsetType) clone_info->columns*clone_info->rows*
00840             sizeof(*pixels)+(clone_info->columns*rows+columns)*sizeof(*indexes);
00841           for (y=0; y < (long) rows; y++)
00842           {
00843             offset-=clone_info->columns*sizeof(*indexes);
00844             count=WritePixelCacheRegion(clone_info,clone_info->offset+offset,
00845               length,(unsigned char *) indexes);
00846             if ((MagickSizeType) count != length)
00847               break;
00848           }
00849           if (y < (long) rows)
00850             {
00851               indexes=(IndexPacket *) RelinquishMagickMemory(indexes);
00852               ThrowFileException(exception,CacheError,"UnableToCloneCache",
00853                 cache_info->cache_filename);
00854               return(MagickFalse);
00855             }
00856         }
00857       indexes=(IndexPacket *) RelinquishMagickMemory(indexes);
00858     }
00859   /*
00860     Clone cache pixels.
00861   */
00862   length=MagickMax(clone_info->columns,cache_info->columns)*sizeof(*pixels);
00863   pixels=(PixelPacket *) AcquireMagickMemory((size_t) length);
00864   if (pixels == (PixelPacket *) NULL)
00865     {
00866       (void) ThrowMagickException(exception,GetMagickModule(),CacheError,
00867         "MemoryAllocationFailed","`%s'",cache_info->cache_filename);
00868       return(MagickFalse);
00869     }
00870   (void) ResetMagickMemory(pixels,0,(size_t) length);
00871   length=columns*sizeof(*pixels);
00872   source_offset=(MagickOffsetType) cache_info->columns*rows*sizeof(*pixels);
00873   offset=(MagickOffsetType) clone_info->columns*rows*sizeof(*pixels);
00874   for (y=0; y < (long) rows; y++)
00875   {
00876     source_offset-=cache_info->columns*sizeof(*pixels);
00877     count=ReadPixelCacheRegion(cache_info,cache_info->offset+source_offset,
00878       length,(unsigned char *) pixels);
00879     if ((MagickSizeType) count != length)
00880       break;
00881     offset-=clone_info->columns*sizeof(*pixels);
00882     count=WritePixelCacheRegion(clone_info,clone_info->offset+offset,length,
00883       (unsigned char *) pixels);
00884     if ((MagickSizeType) count != length)
00885       break;
00886   }
00887   if (y < (long) rows)
00888     {
00889       pixels=(PixelPacket *) RelinquishMagickMemory(pixels);
00890       ThrowFileException(exception,CacheError,"UnableToCloneCache",
00891         cache_info->cache_filename);
00892       return(MagickFalse);
00893     }
00894   if (clone_info->columns > cache_info->columns)
00895     {
00896       offset=(MagickOffsetType) (clone_info->columns*rows+columns)*
00897         sizeof(*pixels);
00898       length=(clone_info->columns-cache_info->columns)*sizeof(*pixels);
00899       (void) ResetMagickMemory(pixels,0,(size_t) length);
00900       for (y=0; y < (long) rows; y++)
00901       {
00902         offset-=clone_info->columns*sizeof(*pixels);
00903         count=WritePixelCacheRegion(clone_info,clone_info->offset+offset,length,
00904           (unsigned char *) pixels);
00905         if ((MagickSizeType) count != length)
00906           break;
00907       }
00908       if (y < (long) rows)
00909         {
00910           pixels=(PixelPacket *) RelinquishMagickMemory(pixels);
00911           ThrowFileException(exception,CacheError,"UnableToCloneCache",
00912             cache_info->cache_filename);
00913           return(MagickFalse);
00914         }
00915     }
00916   pixels=(PixelPacket *) RelinquishMagickMemory(pixels);
00917   return(MagickTrue);
00918 }
00919 
00920 static MagickBooleanType CloneDiskToMemoryPixelCache(CacheInfo *clone_info,
00921   CacheInfo *cache_info,ExceptionInfo *exception)
00922 {
00923   MagickOffsetType
00924     count,
00925     offset;
00926 
00927   MagickSizeType
00928     length;
00929 
00930   register long