X-Git-Url: https://granicus.if.org/sourcecode?a=blobdiff_plain;f=www%2Farchitecture.html;h=34e7c9e91288f2cc5c3c1d25f4d5b3a9ecb9563a;hb=7e41fe84a841d7b9d7b36b245b65e9dcb3314943;hp=4ccdeadeb54be4586547e68288f9677048da9672;hpb=73fd1cd895f109af8b9e15ebe45b3a4699b8668c;p=imagemagick diff --git a/www/architecture.html b/www/architecture.html index 4ccdeadeb..34e7c9e91 100644 --- a/www/architecture.html +++ b/www/architecture.html @@ -19,7 +19,7 @@ - + @@ -127,7 +127,10 @@
+
@@ -221,7 +221,7 @@

The Pixel Cache

-

The ImageMagick pixel cache is a repository for image pixels with up to 5 channels. The first 4 channels are stored contiguously and an optional second area follows with 1 channel. The channels are at the depth specified when ImageMagick was built. The channel depths are 8 bits-per-pixel component for the Q8 version of ImageMagick, 16 bits-per-pixel component for the Q16 version, and 32 bits-per-pixel component for the Q32 version. By default pixel components are unsigned quantities, however, if you use the high dynamic-range version of ImageMagick, the components are 32-bit floating point. The primary 4 channels can hold any value but typically contain red, green, blue, and alpha intensities or cyan, magenta, yellow, and alpha intensities. The optional fifth channel contains the colormap indexes for colormapped images or the black channel for CMYK images. The pixel cache storage may be heap memory, anonymous memory mapped memory, disk-backed memory mapped, or on disk. The pixel cache is reference-counted. Only the cache properties are copied when the cache is cloned. The cache pixels are subsequently copied when you signal your intention to update any of the pixels.

+

The ImageMagick pixel cache is a repository for image pixels with up to 5 channels. The first 4 channels are stored contiguously and an optional second area follows with 1 channel. The channels are at the depth specified when ImageMagick was built. The channel depths are 8 bits-per-pixel component for the Q8 version of ImageMagick, 16 bits-per-pixel component for the Q16 version, and 32 bits-per-pixel component for the Q32 version. By default pixel components are unsigned quantities, however, if you use the high dynamic-range version of ImageMagick, the components are 32-bit floating point. The primary 4 channels can hold any value but typically contain red, green, blue, and alpha intensities or cyan, magenta, yellow, and alpha intensities. The optional fifth channel contains the colormap indexes for colormapped images or the black channel for CMYK images. The pixel cache storage may be heap memory, anonymous memory mapped memory, disk-backed memory mapped, or on disk. The pixel cache is reference-counted. Only the cache properties are copied when the cache is cloned. The cache pixels are subsequently copied only when you signal your intention to update any of the pixels.

Create the Pixel Cache

@@ -254,7 +254,7 @@

When the pixel cache is initialized, pixels are scaled from whatever bit depth they originated from to that required by the pixel cache. For example, a 1-channel 1-bit monochrome PBM image is scaled to a 4 channel 8-bit RGBA image, if you are using the Q8 version of ImageMagick, and 16-bit RGBA for the Q16 version. You can determine which version you have with the ‑version option:

-

$magick> identify -versionVersion: ImageMagick 6.6.4-1 2010-19-14 Q16 http://www.imagemagick.org

+

$magick> identify -versionVersion: ImageMagick 6.6.6-3 2010-22-21 Q16 http://www.imagemagick.org

As you can see, the convenience of the pixel cache sometimes comes with a trade-off in storage (e.g. storing a 1-bit monochrome image as 16-bit RGBA is wasteful) and speed (i.e. storing the entire image in memory is generally slower than accessing one scanline of pixels at a time). In most cases, the benefits of the pixel cache typically outweigh any disadvantages.

@@ -263,7 +263,7 @@

Once the pixel cache is associated with an image, you typically want to get, update, or put pixels into it. We refer to pixels inside the image region as authentic pixels and outside the region as virtual pixels. Use these methods to access the pixels in the cache:

@@ -427,6 +429,7 @@

Recall that each image format is decoded by ImageMagick and the pixels are deposited in the pixel cache. If you write an image, the pixels are read from the pixel cache and encoded as required by the format you are writing (e.g. GIF, PNG, etc.). The Magick Persistent Cache (MPC) format is designed to eliminate the overhead of decoding and encoding pixels to and from an image format. MPC writes two files. One, with the extension .mpc, retains all the properties associated with the image or image sequence (e.g. width, height, colorspace, etc.) and the second, with the extension .cache, is the pixel cache in the native raw format. When reading an MPC image file, ImageMagick reads the image properties and memory maps the pixel cache on disk eliminating the need for decoding the image pixels. The tradeoff is in disk space. MPC is generally larger in file size than most other image formats.

+

The most efficient use of MPC image files is a write-once, read-many-times pattern. For example, your workflow requires extracting random blocks of pixels from the source image. Rather than re-reading and possibly decompressing the source image each time, we use MPC and map the image directly to memory.

Best Practices

@@ -505,7 +508,7 @@ image=ReadStream(image_info,&StreamHandler,exception); (void) printf("Image comment: %s\n",comment); -

ImageMagick supports artifacts with the GetImageArtifact() and SetImageArtifact() methods. Artifacts are stealth properties that are not exported to image formats (e.g. PNG) and they do not display when identifying an image.

+

ImageMagick supports artifacts with the GetImageArtifact() and SetImageArtifact() methods. Artifacts are stealth properties that are not exported to image formats (e.g. PNG).

Image profiles are handled with GetImageProfile(), SetImageProfile(), and ProfileImage() methods. Here we set a profile and fetch it right back:

@@ -545,7 +548,7 @@ image=ReadStream(image_info,&StreamHandler,exception);

Threads of Execution

-

Many of ImageMagick's internal algorithms are threaded to take advantage of speed-ups offered by the multicore processor chips. However, you are welcome to use ImageMagick algorithms in your threads of execution with the exception of the MagickCore's GetVirtualPixels(), GetAuthenticPixels(), QueueAuthenticPixels(), or SyncAuthenticPixels() pixel cache methods. These methods are intended for one thread of execution only. To access the pixel cache with more than one thread of execution, use a cache view. We do this for the CompositeImage() method, for example. Suppose we want to composite a single image over a different image in each thread of execution. If we use GetVirtualPixels(), the results are unpredictable because multiple threads would likely be asking for different areas of the pixel cache simultaneously. Instead we use GetCacheViewVirtualPixels() which creates a unique view for each thread of execution ensuring our program behaves properly regardless of how many threads are invoked. The other program interfaces, such as the MagickWand API, are completely thread safe so there are no special precautions for threads of execution.

+

Many of ImageMagick's internal algorithms are threaded to take advantage of speed-ups offered by the multicore processor chips. However, you are welcome to use ImageMagick algorithms in your threads of execution with the exception of the MagickCore's GetVirtualPixels(), GetAuthenticPixels(), QueueAuthenticPixels(), or SyncAuthenticPixels() pixel cache methods. These methods are intended for one thread of execution only with the exception of an OpenMP parallel section. To access the pixel cache with more than one thread of execution, use a cache view. We do this for the CompositeImage() method, for example. Suppose we want to composite a single image over a different image in each thread of execution. If we use GetVirtualPixels(), the results are unpredictable because multiple threads would likely be asking for different areas of the pixel cache simultaneously. Instead we use GetCacheViewVirtualPixels() which creates a unique view for each thread of execution ensuring our program behaves properly regardless of how many threads are invoked. The other program interfaces, such as the MagickWand API, are completely thread safe so there are no special precautions for threads of execution.

Here is an example of how ImageMagick can take advantage of threads of execution with the OpenMP programming paradigm:

@@ -555,26 +558,26 @@ image=ReadStream(image_info,&StreamHandler,exception); CacheView *image_view; - long - y; - MagickBooleanType status; + ssize_t + y; + status=MagickTrue; image_view=AcquireCacheView(image); #pragma omp parallel for schedule(dynamic,4) shared(status) - for (y=0; y < (long) image->rows; y++) + for (y=0; y < (ssize_t) image->rows; y++) { register IndexPacket *indexes; - register long - x; - register PixelPacket *q; + register ssize_t + x; + if (status == MagickFalse) continue; q=GetCacheViewAuthenticPixels(image_view,0,y,image->columns,1,exception); @@ -584,7 +587,7 @@ image=ReadStream(image_info,&StreamHandler,exception); continue; } indexes=GetCacheViewAuthenticIndexQueue(image_view); - for (x=0; x < (long) image->columns; x++) + for (x=0; x < (ssize_t) image->columns; x++) { q->red= ... q->green= ... @@ -607,9 +610,22 @@ image=ReadStream(image_info,&StreamHandler,exception);

If you call the ImageMagick API from your OpenMP-enabled application and you intend to dynamically increase the number of threads available in subsequent parallel regions, be sure to perform the increase before you call the API otherwise ImageMagick may fault.

-

MagickWand support wand views. A view iterates over the entire, or portion, of the image in parallel and for each row of pixels, it invokes a callback method you provide. This limits most of your parallel programming activity to just that one module. There are similar methods in MagickCore. For an example, see the same sigmoidal contrast algorithm implemented in both MagickWand and MagickCore.

+

MagickWand supports wand views. A view iterates over the entire, or portion, of the image in parallel and for each row of pixels, it invokes a callback method you provide. This limits most of your parallel programming activity to just that one module. There are similar methods in MagickCore. For an example, see the same sigmoidal contrast algorithm implemented in both MagickWand and MagickCore.

+ +

In most circumstances, the default number of threads is set to the number of processor cores on your system for optimal performance. However, if your system is hyperthreaded or if you are running on a virtual host and only a subset of the processors are available to your server instance, you might get an increase in performance by setting the thread policy or the MAGICK_THREAD_LIMIT environment variable. For example, your virtual host has 8 processors but only 2 are assigned to your server instance. The default of 8 threads can cause severe performance problems. One solution is to limit the number of threads to the available processors in your policy.xml configuration file:

+ +
+  <policy domain="resource" name="thread" value="2"/>
+
+ +

Or suppose your 12 core hyperthreaded computer defaults to 24 threads. Set the MAGICK_THREAD_LIMIT environment variable and you will likely get improved performance:

+ +
+  export MAGICK_THREAD_LIMIT=12
+

The OpenMP committee has not defined the behavior of mixing OpenMP with other threading models such as Posix threads. However, using modern releases of Linux, OpenMP and Posix threads appear to interoperate without complaint. If you want to use Posix threads from a program module that calls one of the ImageMagick application programming interfaces (e.g. MagickCore, MagickWand, Magick++, etc.) from Mac OS X or an older Linux release, you may need to disable OpenMP support within ImageMagick. Add the --disable-openmp option to the configure script command line and rebuild and reinstall ImageMagick.

+

Heterogeneous Distributed Processing

@@ -797,23 +813,21 @@ static Image *ReadMGKImage(const ImageInfo *image_info, Image *image; - long - y; - MagickBooleanType status; - register long - x; - register PixelPacket *q; + register size_t + x; + register unsigned char *p; ssize_t - count; + count, + y; unsigned char *pixels; @@ -870,7 +884,7 @@ static Image *ReadMGKImage(const ImageInfo *image_info, pixels=(unsigned char *) AcquireQuantumMemory((size_t) image->columns,3UL*sizeof(*pixels)); if (pixels == (unsigned char *) NULL) ThrowReaderException(ResourceLimitError,"MemoryAllocationFailed"); - for (y=0; y < (long) image->rows; y++) + for (y=0; y < (ssize_t) image->rows; y++) { count=(ssize_t) ReadBlob(image,(size_t) (3*image->columns),pixels); if (count != (ssize_t) (3*image->columns)) @@ -879,7 +893,7 @@ static Image *ReadMGKImage(const ImageInfo *image_info, q=QueueAuthenticPixels(image,0,y,image->columns,1,exception); if (q == (PixelPacket *) NULL) break; - for (x=0; x < (long) image->columns; x++) + for (x=0; x < (ssize_t) image->columns; x++) { q->red=ScaleCharToQuantum(*p++); q->green=ScaleCharToQuantum(*p++); @@ -1020,9 +1034,6 @@ static MagickBooleanType WriteMGKImage(const ImageInfo *image_info,Image *image) char buffer[MaxTextExtent]; - long - y; - MagickBooleanType status; @@ -1032,12 +1043,15 @@ static MagickBooleanType WriteMGKImage(const ImageInfo *image_info,Image *image) register const PixelPacket *p; - register long + register ssize_t x; register unsigned char *q; + ssize_t + y; + unsigned char *pixels; @@ -1072,13 +1086,13 @@ static MagickBooleanType WriteMGKImage(const ImageInfo *image_info,Image *image) (void) FormatMagickString(buffer,MaxTextExtent,"%lu %lu\n", image->columns,image->rows); (void) WriteBlobString(image,buffer); - for (y=0; y < (long) image->rows; y++) + for (y=0; y < (ssize_t) image->rows; y++) { p=GetVirtualPixels(image,0,y,image->columns,1,&image->exception); if (p == (const PixelPacket *) NULL) break; q=pixels; - for (x=0; x < (long) image->columns; x++) + for (x=0; x < (ssize_t) image->columns; x++) { *q++=ScaleQuantumToChar(p->red); *q++=ScaleQuantumToChar(p->green); @@ -1205,12 +1219,12 @@ ModuleExport unsigned long analyzeImage(Image **images,const int argc, CacheView *image_view; - long - y; - MagickBooleanType status; + ssize_t + y; + brightness_sum_x=0.0; brightness_sum_x2=0.0; brightness_sum_x3=0.0; @@ -1233,12 +1247,12 @@ ModuleExport unsigned long analyzeImage(Image **images,const int argc, #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp parallel for schedule(dynamic,4) shared(status) #endif - for (y=0; y < (long) image->rows; y++) + for (y=0; y < (ssize_t) image->rows; y++) { register const PixelPacket *p; - register long + register ssize_t x; if (status == MagickFalse) @@ -1249,7 +1263,7 @@ ModuleExport unsigned long analyzeImage(Image **images,const int argc, status=MagickFalse; continue; } - for (x=0; x < (long) image->columns; x++) + for (x=0; x < (ssize_t) image->columns; x++) { ConvertRGBToHSB(p->red,p->green,p->blue,&hue,&saturation,&brightness); brightness*=QuantumRange; @@ -1354,7 +1368,7 @@ ModuleExport unsigned long analyzeImage(Image **images,const int argc,