------------------------------------------------------------------------------- To see how something is done from command line look at the source "wand/mogrify.c" and search for the second entry for the 'option' you are interested in, and follow the function names (search ".h" export files) into the "magick/" sub-directory where the core library functions are all defined. My own work in the past centered on "lists.c" "layers.c", and "composite.c", "distort.c" "resample.c" "morphology.c" "resize.c" Also look at "core.c" in the installed IM documention "www/source/core.c" for a example of using the magick core without the command line. I have not needed this myself, as Chrisy added the appropriate command line options I needed for testing. The architecture web document on the IM web site is also a must read, though at the time of writing a little dated. That is the start point. The Commandline, MagickWand, and other API interfaces then maps its calls to the C MagickCore functions. See "wand/mogrify.c" as previously mentioned. Remember you don't have to understand everything, just copy something that does what you want and create new functions. Try to keep future expandsion in mind. ------------------------------------------------------------------------------- Notes on iterating over pixels in Core Library... * Get...() gets the actual values from the image Queue...() just returns an empty array of values to fill * ....AuthenticPixels() must have a SyncAuthenticPixels() to write values back into the image and release the lock. * ...VirtualPixels() is faster but can not write values. But it can return 'virtual pixels' beyond the normal scope of the image * ...VirtualPixels() does not need any 'free' or 'finished' requirement as memory will be freed when the pixel cache is freed. * ...Views...Pixels() allow you get multiple cached views of the image and must be used when using OpenMP support. * All the above goes for '...IndexQueue()' functions as for ...Pixels() functions. Question: Does that mean that if some routine gets a really large GetVirtualPixels() that memory will remain used until the actual image (and thus its cache) is destroyed? Answer from Cristy: There are two solutions. One, use a cache view and destroy the view or if you use a large GetVirtualPixels() follow it with a small GetVirtualPixels() to free most of the reserved memory (e.g. GetVirtualPixels(...4096,4096,...) followed by GetVirtualPixels(...1,1,...)). My recommendation is to always use a cache view except for the read or writer coders (e.g. ReadJPEGImage(), WriteJPEGImage()). ------------------------------------------------------------------------------- Read pixels only ssize_t y; for (y=0; y < (ssize_t) image->rows; y++) { register const IndexPacket *restrict indexes; register const PixelPacket *restrict p; register ssize_t x; p=GetVirtualPixels(image,0,y,image->columns,1,exception); if (p == (const PixelPacket *) NULL) break; indexes=GetVirtualIndexQueue(image); for (x=0; x < (ssize_t) image->columns; x++) { ...get info from p->color and indexes... } p++; indexes++; } if (y < (ssize_t) image->rows) { /* an exception was thrown */ } } ------------------------------------------------------------------------------- Normal (complete Image pixel loop) Can do neighbourhood reading too by expanding the Virtual pixel read of an input image. image --> new_image Image *new_image; new_image=CloneImage(image,image->columns,image->rows,MagickTrue,exception if (new_image == (Image *) NULL) return((Image *) NULL); if (SetImageStorageClass(new_image,DirectClass) == MagickFalse) { /* if image is ColorMapped - change it to DirectClass */ InheritException(exception,&new_image->exception); new_image=DestroyImage(new_image); return((Image *) NULL); } { /* ----- MAIN CODE ----- */ CacheView *image_view, *new_view; MagickBooleanType status; MagickOffsetType progress; ssize_t y; status=MagickTrue; progress=0; #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp parallel for schedule(dynamic,4) shared(progress,status) #endif for (y=0; y < (ssize_t) image->rows; y++) { register const IndexPacket *restrict p_indexes; register IndexPacket *restrict q_indexes; register ssize_t x; register const PixelPacket *restrict p; register PixelPacket *restrict q; p=GetCacheViewVirtualPixels(image_view,0,j,image->columns,1,exception); q=QueueCacheViewAuthenticPixels(new_view,0,j,new_image->columns, 1,exception); if ((p == (const PixelPacket *) NULL) || (q == (PixelPacket *) NULL)) { status=MagickFalse; continue; } p_indexes=GetCacheViewAuthenticIndexQueue(image_view); q_indexes=GetCacheViewAuthenticIndexQueue(new_view); GetMagickPixelPacket(new_image,&pixel); for (x=0; x < (ssize_t) image->columns; x++) { SetMagickPixelPacket(image,p,p_indexes,&pixel); ...do whatever... SetPixelPacket(new_image,&pixel,q,q_indexes); p++; q++; p_indexes++; q_indexes++; } if ( SyncCacheViewAuthenticPixels(new_view,exception) == MagickFalse ) status=MagickFalse; if (image->progress_monitor != (MagickProgressMonitor) NULL) { MagickBooleanType proceed; #if defined(MAGICKCORE_OPENMP_SUPPORT) #pragma omp critical (MagickCore_SparseColorImage) #endif proceed=SetImageProgress(image,SparseColorTag,progress++,image->rows); if (proceed == MagickFalse) status=MagickFalse; } } new_view=DestroyCacheView(new_view); if (status == MagickFalse) new_image=DestroyImage(new_image); } return(new_image); ------------------------------------------------------------------------------- Read from and modify pixels from same image... Take a look at ContrastImage() in magick.enhance.c You get the pixels, modify them then sync them back to the pixel cache: You can not do this with virtual pixel involvement. for (y=0; y < work_image->rows; y++0 ... q=GetAuthenticPixels(work_image,0,y,image->columns,1,exception); if (q == (PixelPacket *) NULL) { status=MagickFalse; continue; } for (x=0; x < (ssize_t) image->columns; x++) { Contrast(sign,&q->red,&q->green,&q->blue); q++; } if (SyncAuthenticPixels(work_image,exception) == MagickFalse) status=MagickFalse if (image->progress_monitor != (MagickProgressMonitor) NULL) if ( SetImageProgress(image,???Tag,progress++,work_image->rows) == MagickFalse ) status=MagickFalse; Notes... * CloneImage() with zero width and height creates a full clone of the image including the image data. * CloneImage() with non-zero width and height functions will create a new blank image, to be filled with Queue...() and Sync...() functions. The data contains no values so modifying values directly will not work. * You may not be able to use OpenMP, if you are using previously set pixel values for determining the next set of pixel values. That is because rows may be processed out of order! * If you also need Virtual-Pixels you will need to read the image twice! Once for the virtural pixel lookup, and one for the authenticate pixels that you want to modify. However while changing a row of authentic pixels, virtual pixels will not change to match. As such you may need to do special handling within the actual row to handle end of row conditions. -------------------------------------------------------------------------------