1 /**
2  * Copyright: Mike Wey 2011
3  * License:   zlib (See accompanying LICENSE file)
4  * Authors:   Mike Wey
5  */
6 
7 module dmagick.Image;
8 
9 import std.algorithm : min;
10 import std.array;
11 import std.conv;
12 import std.math;
13 import std..string;
14 import std.typecons : Tuple;
15 import std.uni;
16 import core.memory;
17 import core.runtime;
18 import core.time;
19 import core.stdc..string;
20 
21 import dmagick.Color;
22 import dmagick.Exception;
23 import dmagick.Geometry;
24 import dmagick.ImageView;
25 import dmagick.Options;
26 import dmagick.Utils;
27 
28 version(DMagick_No_Display)
29 {
30 }
31 else
32 {
33 	version(Windows) import dmagick.internal.Windows;
34 }
35 
36 //Import all translated c headers.
37 import dmagick.c.MagickCore;
38 
39 /// See_Also: $(CXREF geometry, _AffineMatrix)
40 public alias dmagick.c.geometry.AffineMatrix AffineMatrix;
41 /// See_Also: $(CXREF image, _AlphaChannelType)
42 public alias dmagick.c.image.AlphaChannelType AlphaChannelType;
43 /// See_Also: $(CXREF magickType, _ChannelType)
44 public alias dmagick.c.magickType.ChannelType ChannelType;
45 /// See_Also: $(CXREF image, _ChromaticityInfo)
46 public alias dmagick.c.image.ChromaticityInfo ChromaticityInfo;
47 /// See_Also: $(CXREF magickType, _ClassType)
48 public alias dmagick.c.magickType.ClassType ClassType;
49 /// See_Also: $(CXREF colorspace, _ColorspaceType)
50 public alias dmagick.c.colorspace.ColorspaceType ColorspaceType;
51 /// See_Also: $(CXREF composite, _CompositeOperator)
52 public alias dmagick.c.composite.CompositeOperator CompositeOperator;
53 /// See_Also: $(CXREF compress, _CompressionType)
54 public alias dmagick.c.compress.CompressionType CompressionType;
55 /// See_Also: $(CXREF layer, _DisposeType)
56 public alias dmagick.c.layer.DisposeType DisposeType;
57 /// See_Also: $(CXREF distort, _DistortImageMethod)
58 public alias dmagick.c.distort.DistortImageMethod DistortImageMethod;
59 /// See_Also: $(CXREF quantum, _EndianType)
60 public alias dmagick.c.quantum.EndianType EndianType;
61 /// See_Also: $(CXREF resample, _FilterTypes)
62 public alias dmagick.c.resample.FilterTypes FilterTypes;
63 /// See_Also: $(CXREF geometry, _GravityType)
64 public alias dmagick.c.geometry.GravityType GravityType;
65 /// See_Also: $(CXREF image, _ImageType)
66 public alias dmagick.c.image.ImageType ImageType;
67 /// See_Also: $(CXREF image, _InterlaceType)
68 public alias dmagick.c.image.InterlaceType InterlaceType;
69 /// See_Also: $(CXREF pixel, _InterpolatePixelMethod)
70 public alias dmagick.c.pixel.InterpolatePixelMethod InterpolatePixelMethod;
71 /// See_Also: $(CXREF statistic, _MagickEvaluateOperator)
72 public alias dmagick.c.statistic.MagickEvaluateOperator MagickEvaluateOperator;
73 /// See_Also: $(CXREF statistic, _MagickFunction)
74 public alias dmagick.c.statistic.MagickFunction MagickFunction;
75 /// See_Also: $(CXREF fx, _NoiseType)
76 public alias dmagick.c.fx.NoiseType NoiseType;
77 /// See_Also: $(CXREF image, _OrientationType)
78 public alias dmagick.c.image.OrientationType OrientationType;
79 /// See_Also: $(CXREF effect, _PreviewType)
80 public alias dmagick.c.effect.PreviewType PreviewType;
81 /// See_Also: $(CXREF magickType, _Quantum)
82 public alias dmagick.c.magickType.Quantum Quantum;
83 /// See_Also: $(CXREF profile, _RenderingIntent)
84 public alias dmagick.c.profile.RenderingIntent RenderingIntent;
85 /// See_Also: $(CXREF image, _ResolutionType)
86 public alias dmagick.c.image.ResolutionType ResolutionType;
87 /// See_Also: $(CXREF distort, _SparseColorMethod)
88 public alias dmagick.c.distort.SparseColorMethod SparseColorMethod;
89 /// See_Also: $(CXREF effect, _StatisticType)
90 public alias dmagick.c.statistic.StatisticType StatisticType;
91 /// See_Also: $(CXREF constitute, _StorageType)
92 public alias dmagick.c.constitute.StorageType StorageType;
93 /// See_Also: $(CXREF draw, _TypeMetric)
94 public alias dmagick.c.draw.TypeMetric TypeMetric;
95 /// See_Also: $(CXREF cacheView, _VirtualPixelMethod)
96 public alias dmagick.c.cacheView.VirtualPixelMethod VirtualPixelMethod;
97 
98 alias ptrdiff_t ssize_t;
99 
100 /**
101  * The image
102  */
103 class Image
104 {
105 	alias dmagick.c.image.Image MagickCoreImage;
106 	alias RefCounted!( DestroyImage, MagickCoreImage ) ImageRef;
107 
108 	ImageRef imageRef;
109 	Options options;  ///The options for this image.
110 
111 	private bool delegate(string, long, ulong) progressMonitor;
112 	
113 	///
114 	this()
115 	{
116 		options = new Options();
117 		imageRef = ImageRef(AcquireImage(options.imageInfo));
118 	}
119 
120 	this(MagickCoreImage* image, Options options = null)
121 	{
122 		this(ImageRef(image), options);
123 	}
124 
125 	this(ImageRef image, Options options = null)
126 	{
127 		if ( options is null )
128 			this.options = new Options();
129 		else
130 			this.options = options;
131 
132 		imageRef = image;
133 	}
134 
135 	/**
136 	 * Construct an Image by reading from the file or
137 	 * URL specified by filename.
138 	 */
139 	this(string filename)
140 	{
141 		options = new Options();
142 		read(filename);
143 	}
144 
145 	/**
146 	 * Construct a blank image with the specified color.
147 	 */
148 	this(Geometry size, Color color)
149 	{
150 		options = new Options();
151 		options.size = size;
152 
153 		//Use read to create a cnavas with the spacified color.
154 		read( "canvas:"~ color.toString() );
155 	}
156 
157 	/**
158 	 * Construct an image from an in-memory blob.
159 	 * The Blob size, depth and magick format may also be specified.
160 	 *
161 	 * Some image formats require size to be specified,
162 	 * the default depth Imagemagick uses is the Quantum size
163 	 * it's compiled with. If it doesn't match the depth of the image
164 	 * it may need to be specified.
165 	 *
166 	 * Imagemagick can usualy detect the image format, when the
167 	 * format can't be detected a magick format must be specified.
168 	 */
169 	this(void[] blob)
170 	{
171 		options = new Options();
172 
173 		read(blob);
174 	}
175 
176 	///ditto
177 	this(void[] blob, Geometry size)
178 	{
179 		options = new Options();
180 
181 		read(blob, size);
182 	}
183 
184 	///ditto
185 	this(void[] blob, Geometry size, size_t depth)
186 	{
187 		options = new Options();
188 
189 		read(blob, size, depth);
190 	}
191 
192 	///ditto
193 	this(void[] blob, Geometry size, size_t depth, string magick)
194 	{
195 		options = new Options();
196 
197 		read(blob, size, depth, magick);
198 	}
199 	
200 	///ditto
201 	this(void[] blob, Geometry size, string magick)
202 	{
203 		options = new Options();
204 
205 		read(blob, size, magick);
206 	}
207 
208 	/**
209 	 * Constructs an image from an array of pixels.
210 	 *
211 	 * Params:
212 	 *     columns =  The number of columns in the image.
213 	 *     rows    =  The number of rows in the image.
214 	 *     map     =  A string describing the expected ordering
215 	 *                of the pixel array. It can be any combination
216 	 *                or order of R = red, G = green, B = blue, A = alpha
217 	 *                , C = cyan, Y = yellow, M = magenta, K = black,
218 	 *                or I = intensity (for grayscale).
219 	 *     storage  = The pixel Staroage type (CharPixel,
220 	 *                ShortPixel, IntegerPixel, FloatPixel, or DoublePixel).
221 	 *     pixels   = The pixel data.
222 	 */
223 	this(size_t columns, size_t rows, string map, StorageType storage, void[] pixels)
224 	{
225 		options = new Options();
226 
227 		MagickCoreImage* image = 
228 			ConstituteImage(columns, rows, toStringz(map), storage, pixels.ptr, DMagickExceptionInfo());
229 
230 		imageRef = ImageRef(image);
231 	}
232 
233 	/**
234 	 * Constructs a description of the image as a string.
235 	 * The string contains some or all of the following fields:
236 	 * $(LIST
237 	 *     $(B filename) The current filename.,
238 	 *     $(B [scene]) The scene number if the image is part of a secuence.,
239 	 *     $(B format) The image format.,
240 	 *     $(B width x height),
241 	 *     $(B page width x height + xOffset + yOffset),
242 	 *     $(B classType) DirectClass or PseudoClass,
243 	 *     $(B N-bit) bit depth.,
244 	 *     $(B blob size) if present.
245 	 * )
246 	 */
247 	override string toString()
248 	{
249 		string result;
250 
251 		result = filename;
252 
253 		//Scene number.
254 		ssize_t index = GetImageIndexInList(imageRef);
255 		if ( index > 0 )
256 		{
257 			result ~= std..string.format("[%s]", index);
258 		}
259 
260 		result ~= std..string.format(" %s ", format);
261 		result ~= std..string.format("%sx%s ", columns, rows);
262 
263 		//Page size
264 		if ( imageRef.page.width > 0 || imageRef.page.height > 0 
265 			|| imageRef.page.x != 0 || imageRef.page.y != 0 )
266 		{
267 			result ~= std..string.format("%sx%s%+s%+s ",
268 				imageRef.page.width, imageRef.page.height,
269 				imageRef.page.x,     imageRef.page.y);
270 		}
271 
272 		if ( classType == ClassType.DirectClass )
273 			result ~= "DirectClass ";
274 		else
275 			result ~= "PseudoClass ";
276 
277 		result ~= std..string.format("%s-bit ", GetImageQuantumDepth(imageRef, true));
278 
279 		//Size of the image.
280 		MagickSizeType size = GetBlobSize(imageRef);
281 		if ( size > 0 )
282 		{
283 			if ( size > 2*1024*1024 )
284 				result ~= std..string.format("%s MiB", size/1024/1024);
285 			else if ( size > 2*1024 )
286 				result ~= std..string.format("%s KiB", size/1024);
287 			else
288 				result ~= std..string.format("%s bytes", size);
289 		}
290 
291 		return result;
292 	}
293 
294 	/**
295 	 * Adaptively blurs the image by blurring more intensely near
296 	 * image edges and less intensely far from edges.
297 	 * The adaptiveBlur method blurs the image with a Gaussian operator
298 	 * of the given radius and standard deviation (sigma).
299 	 * For reasonable results, radius should be larger than sigma.
300 	 * Use a radius of 0 and adaptiveBlur selects a suitable radius for you.
301 	 *
302 	 * Params:
303 	 *     radius  = The radius of the Gaussian in pixels,
304 	 *               not counting the center pixel.
305 	 *     sigma   = The standard deviation of the Laplacian, in pixels.
306 	 *     channel = The channels to blur.
307 	 */
308 	void adaptiveBlur(double radius = 0, double sigma = 1, ChannelType channel = ChannelType.DefaultChannels)
309 	{
310 		MagickCoreImage* image =
311 			AdaptiveBlurImageChannel(imageRef, channel, radius, sigma, DMagickExceptionInfo());
312 
313 		imageRef = ImageRef(image);
314 	}
315 
316 	/**
317 	 * adaptiveResize uses the special Mesh Interpolation method
318 	 * to resize images. Basically adaptiveResize avoids the excessive
319 	 * blurring that resize can produce with sharp color changes.
320 	 * This works well for slight image size adjustments and in
321 	 * particularly for magnification, And especially with images
322 	 * with sharp color changes. But when images are enlarged or reduced
323 	 * by more than 50% it will start to produce aliasing,
324 	 * and Moiré effects in the results.
325 	 */
326 	void adaptiveResize(Geometry size)
327 	{
328 		size = size.toAbsolute(columns, rows);
329 
330 		MagickCoreImage* image =
331 			AdaptiveResizeImage(imageRef, size.width, size.height, DMagickExceptionInfo());
332 
333 		imageRef = ImageRef(image);
334 	}
335 
336 	/**
337 	 * Adaptively sharpens the image by sharpening more intensely near
338 	 * image edges and less intensely far from edges. The adaptiveSharpen
339 	 * method sharpens the image with a Gaussian operator of the given
340 	 * radius and standard deviation (sigma). For reasonable results,
341 	 * radius should be larger than sigma. Use a radius of 0 and
342 	 * adaptiveSharpen selects a suitable radius for you.
343 	 *
344 	 * Params:
345 	 *     radius  = The radius of the Gaussian in pixels,
346 	 *               not counting the center pixel.
347 	 *     sigma   = The standard deviation of the Laplacian, in pixels.
348 	 *     channel = If no channels are specified, sharpens all the channels.
349 	 */
350 	void adaptiveSharpen(double radius = 0, double sigma = 1, ChannelType channel = ChannelType.DefaultChannels)
351 	{
352 		MagickCoreImage* image =
353 			AdaptiveSharpenImageChannel(imageRef, channel, radius, sigma, DMagickExceptionInfo());
354 
355 		imageRef = ImageRef(image);
356 	}
357 
358 	/**
359 	 * Selects an individual threshold for each pixel based on the range
360 	 * of intensity values in its local neighborhood. This allows for
361 	 * thresholding of an image whose global intensity histogram doesn't
362 	 * contain distinctive peaks.
363 	 *
364 	 * Params:
365 	 *     width  = define the width of the local neighborhood.
366 	 *     heigth = define the height of the local neighborhood.
367 	 *     offset = constant to subtract from pixel neighborhood mean.
368 	 */
369 	void adaptiveThreshold(size_t width = 3, size_t height = 3, ssize_t offset = 0)
370 	{
371 		MagickCoreImage* image =
372 			AdaptiveThresholdImage(imageRef, width, height, offset, DMagickExceptionInfo());
373 
374 		imageRef = ImageRef(image);
375 	}
376 
377 	/**
378 	 * Adds random noise to the specified channel or channels in the image.
379 	 * The amount of time addNoise requires depends on the NoiseType argument.
380 	 *
381 	 * Params:
382 	 *     type    = A NoiseType value.
383 	 *     channel = 0 or more ChannelType arguments. If no channels are
384 	 *               specified, adds noise to all the channels
385 	 */
386 	void addNoise(NoiseType type, ChannelType channel = ChannelType.DefaultChannels)
387 	{
388 		MagickCoreImage* image =
389 			AddNoiseImageChannel(imageRef, channel, type, DMagickExceptionInfo());
390 
391 		imageRef = ImageRef(image);
392 	}
393 
394 	/**
395 	 * Transforms the image as specified by the affine matrix.
396 	 */
397 	void affineTransform(AffineMatrix affine)
398 	{
399 		MagickCoreImage* image =
400 			AffineTransformImage(imageRef, &affine, DMagickExceptionInfo());
401 
402 		imageRef = ImageRef(image);
403 	}
404 
405 	/**
406 	 * Annotates an image with text.  Optionally you can include any 
407 	 * of the following bits of information about the image by embedding
408 	 * the appropriate special characters:
409 	 * --------------------
410 	 *     %b   file size in bytes.
411 	 *     %c   comment.
412 	 *     %d   directory in which the image resides.
413 	 *     %e   extension of the image file.
414 	 *     %f   original filename of the image.
415 	 *     %h   height of image.
416 	 *     %i   filename of the image.
417 	 *     %k   number of unique colors.
418 	 *     %l   image label.
419 	 *     %m   image file format.
420 	 *     %n   number of images in a image sequence.
421 	 *     %o   output image filename.
422 	 *     %p   page number of the image.
423 	 *     %q   image depth (8 or 16).
424 	 *     %q   image depth (8 or 16).
425 	 *     %s   image scene number.
426 	 *     %t   image filename without any extension.
427 	 *     %u   a unique temporary filename.
428 	 *     %w   image width.
429 	 *     %x   x resolution of the image.
430 	 *     %y   y resolution of the image.
431 	 *--------------------
432 	 * Params:
433 	 *     text    = The text.
434 	 *     xOffset = The x coordinate.
435 	 *     yOffset = The y coordinate.
436 	 *     gravity = Placement gravity.
437 	 *     degrees = The angle of the Text.
438 	 */
439 	void annotate(
440 		string text,
441 		size_t xOffset,
442 		size_t yOffset,
443 		GravityType gravity = GravityType.NorthWestGravity,
444 		double degrees = 0.0)
445 	{
446 		annotate(text, Geometry(size_t.max, size_t.max, xOffset, yOffset), gravity, degrees);
447 	}
448 
449 	/**
450 	 * Ditto, but word wraps the text so it stays withing the
451 	 * boundingArea. if the height and width are 0 the height and
452 	 * with of the image are used to calculate the bounding area.
453 	 */
454 	void annotate(
455 		string text,
456 		Geometry boundingArea = Geometry.init,
457 		GravityType gravity = GravityType.NorthWestGravity,
458 		double degrees = 0.0)
459 	{
460 		if ( boundingArea.width == 0 )
461 			boundingArea.width = columns;
462 
463 		if ( boundingArea.height == 0 )
464 			boundingArea.height = rows;
465 
466 		if ( boundingArea.width > 0 )
467 			text = wordWrap(text, boundingArea);
468 
469 		DrawInfo* drawInfo = options.drawInfo;
470 		AffineMatrix oldAffine = options.affine;
471 
472 		copyString(drawInfo.text, text);
473 		copyString(drawInfo.geometry, boundingArea.toString());
474 
475 		drawInfo.gravity = gravity;
476 		options.transformRotation(degrees);
477 
478 		scope(exit)
479 		{
480 			copyString(drawInfo.text, null);
481 			copyString(drawInfo.geometry, null);
482 
483 			drawInfo.gravity = GravityType.NorthWestGravity;
484 			options.affine = oldAffine;
485 		}
486 
487 		AnnotateImage(imageRef, drawInfo);
488 
489 		DMagickException.throwException(&(imageRef.exception));
490 	}
491 
492 	/**
493 	 * extract the 'mean' from the image and adjust the image
494 	 * to try make set its gamma appropriatally.
495 	 * 
496 	 * Params:
497 	 *     channel = One or more channels to adjust.
498 	 */
499 	void autoGamma(ChannelType channel = ChannelType.DefaultChannels)
500 	{
501 		AutoGammaImageChannel(imageRef, channel);
502 
503 		DMagickException.throwException(&(imageRef.exception));
504 	}
505 
506 	/**
507 	 * adjusts the levels of a particular image channel by scaling
508 	 * the minimum and maximum values to the full quantum range.
509 	 * 
510 	 * Params:
511 	 *     channel = One or more channels to adjust.
512 	 */
513 	void autoLevel(ChannelType channel = ChannelType.DefaultChannels)
514 	{
515 		AutoLevelImageChannel(imageRef, channel);
516 
517 		DMagickException.throwException(&(imageRef.exception));
518 	}
519 
520 	/**
521 	 * Adjusts an image so that its orientation is suitable for viewing
522 	 * (i.e. top-left orientation). Note that only some models of modern
523 	 * digital cameras can tag an image with the orientation.
524 	 */
525 	void autoOrient()
526 	{
527 		final switch( this.orientation )
528 		{
529 			case OrientationType.UndefinedOrientation:
530 			case OrientationType.TopLeftOrientation:
531 				return;
532 
533 			case OrientationType.TopRightOrientation:
534 				flop();
535 				break;
536 
537 			case OrientationType.BottomRightOrientation:
538 				rotate(180);
539 				break;
540 
541 			case OrientationType.BottomLeftOrientation:
542 				flip();
543 				break;
544 
545 			case OrientationType.LeftTopOrientation:
546 				transpose();
547 				break;
548 
549 			case OrientationType.RightTopOrientation:
550 				rotate(90);
551 				break;
552 
553 			case OrientationType.RightBottomOrientation:
554 				transverse();
555 				break;
556 
557 			case OrientationType.LeftBottomOrientation:
558 				rotate(270);
559 				break;
560 		}
561 
562 		orientation = OrientationType.TopLeftOrientation;
563 	}
564 
565 	/**
566 	 * Changes the value of individual pixels based on the intensity
567 	 * of each pixel channel. The result is a high-contrast image.
568 	 *
569 	 * More precisely each channel value of the image is 'thresholded'
570 	 * so that if it is equal to or less than the given value it is set
571 	 * to zero, while any value greater than that give is set to it
572 	 * maximum or QuantumRange.
573 	 * 
574 	 * Params:
575 	 *     threshold = The threshold value.
576 	 *     channel   = One or more channels to adjust.
577 	 */
578 	void bilevel(Quantum threshold, ChannelType channel = ChannelType.DefaultChannels)
579 	{
580 		BilevelImageChannel(imageRef, channel, threshold);
581 
582 		DMagickException.throwException(&(imageRef.exception));
583 	}
584 
585 	/**
586 	 * Forces all pixels below the threshold into black while leaving
587 	 * all pixels above the threshold unchanged.
588 	 * 
589 	 * Params:
590 	 *     threshold = The threshold value for red green and blue.
591 	 *     channel   = One or more channels to adjust.
592 	 */
593 	void blackThreshold(Quantum threshold, ChannelType channel = ChannelType.DefaultChannels)
594 	{
595 		blackThreshold(threshold, threshold, threshold, 0, channel);
596 	}
597 
598 	///ditto
599 	void blackThreshold(
600 		Quantum red,
601 		Quantum green,
602 		Quantum blue,
603 		Quantum opacity = 0,
604 		ChannelType channel = ChannelType.DefaultChannels)
605 	{
606 		string thresholds = std..string.format("%s,%s,%s,%s", red, green, blue, opacity);
607 
608 		BlackThresholdImageChannel(
609 			imageRef, channel, toStringz(thresholds), DMagickExceptionInfo()
610 		);
611 	}
612 
613 	/**
614 	 * Adds the overlay image to the target image according to
615 	 * srcPercent and dstPercent.
616 	 * 
617 	 * This method corresponds to the -blend option of ImageMagick's
618 	 * composite command.
619 	 * 
620 	 * Params:
621 	 *     overlay       = The source image for the composite operation.
622 	 *     srcPercentage = Percentage for the source image.
623 	 *     dstPercentage = Percentage for this image.
624 	 *     xOffset       = The x offset to use for the overlay.
625 	 *     yOffset       = The y offset to use for the overlay.
626 	 *     gravity       = The gravity to use for the overlay.
627 	 */
628 	void blend(
629 		const(Image) overlay,
630 		int srcPercentage,
631 		int dstPercentage,
632 		ssize_t xOffset,
633 		ssize_t yOffset)
634 	{
635 		SetImageArtifact(imageRef, "compose:args",
636 			toStringz(std..string.format("%s,%s", srcPercentage, dstPercentage)));
637 		scope(exit) RemoveImageArtifact(imageRef, "compose:args");
638 
639 		composite(overlay, CompositeOperator.BlendCompositeOp, xOffset, yOffset);
640 	}
641 
642 	///ditto
643 	void blend(
644 		const(Image) overlay,
645 		int srcPercentage,
646 		int dstPercentage,
647 		GravityType gravity = GravityType.NorthWestGravity)
648 	{
649 		RectangleInfo geometry;
650 
651 		SetGeometry(overlay.imageRef, &geometry);
652 		GravityAdjustGeometry(columns, rows, gravity, &geometry);
653 
654 		blend(overlay, srcPercentage, dstPercentage, geometry.x, geometry.y);
655 	}
656 
657 	/**
658 	 * mutes the colors of the image to simulate a scene at
659 	 * nighttime in the moonlight.
660 	 * 
661 	 * Params:
662 	 *     factor = The shift factor, larger values increase the effect.
663 	 */
664 	void blueShift(double factor = 1.5)
665 	{
666 		MagickCoreImage* image =
667 			BlueShiftImage(imageRef, factor, DMagickExceptionInfo());
668 
669 		imageRef = ImageRef(image);
670 	}
671 
672 	/**
673 	 * Blurs the specified channel. We convolve the image with a Gaussian
674 	 * operator of the given radius and standard deviation (sigma).
675 	 * The blur method differs from gaussianBlur in that it uses a
676 	 * separable kernel which is faster but mathematically equivalent
677 	 * to the non-separable kernel.
678 	 *
679 	 * Params:
680 	 *     radius  = The radius of the Gaussian in pixels,
681 	 *               not counting the center pixel.
682 	 *     sigma   = The standard deviation of the Laplacian, in pixels.
683 	 *     channel = The channels to blur.
684 	 */
685 	void blur(double radius = 0, double sigma = 1, ChannelType channel = ChannelType.DefaultChannels)
686 	{
687 		MagickCoreImage* image =
688 			BlurImageChannel(imageRef, channel, radius, sigma, DMagickExceptionInfo());
689 
690 		imageRef = ImageRef(image);
691 	}
692 
693 	/**
694 	 * Surrounds the image with a border of the color defined
695 	 * by the borderColor property.
696 	 *
697 	 * Params:
698 	 *     width  = Border width in pixels.
699 	 *     height = Border height in pixels.
700 	 */
701 	void border(size_t width, size_t height)
702 	{
703 		RectangleInfo borderInfo = RectangleInfo(width, height);
704 
705 		MagickCoreImage* image =
706 			BorderImage(imageRef, &borderInfo, DMagickExceptionInfo());
707 
708 		imageRef = ImageRef(image);
709 	}
710 
711 	/**
712 	 * Extract channel from image. Use this option to extract a
713 	 * particular channel from the image. ChannelType.MatteChannel for
714 	 * example, is useful for extracting the opacity values from an image.
715 	 */
716 	Image channel(ChannelType channel) const
717 	{
718 		MagickCoreImage* image =
719 			SeparateImages(imageRef, channel, DMagickExceptionInfo());
720 
721 		return new Image(image);
722 	}
723 
724 	/**
725 	 * Adds a "charcoal" effect to the image. You can alter the
726 	 * intensity of the effect by changing the radius and sigma arguments.
727 	 *
728 	 * Params:
729 	 *     radius = The radius of the pixel neighborhood.
730 	 *     sigma  = The standard deviation of the Gaussian, in pixels.
731 	 */
732 	void charcoal(double radius = 0, double sigma = 1)
733 	{
734 		MagickCoreImage* image =
735 			CharcoalImage(imageRef, radius, sigma, DMagickExceptionInfo());
736 
737 		imageRef = ImageRef(image);
738 	}
739 
740 	/**
741 	 * Removes the specified rectangle and collapses the rest of
742 	 * the image to fill the removed portion.
743 	 *
744 	 * Params:
745 	 *     geometry = The horizontal and/or vertical subregion to remove.
746 	 */
747 	void chop(Geometry geometry)
748 	{
749 		RectangleInfo rectangle = geometry.rectangleInfo;
750 
751 		MagickCoreImage* image =
752 			ChopImage(imageRef, &rectangle, DMagickExceptionInfo());
753 
754 		imageRef = ImageRef(image);		
755 	}
756 
757 	/**
758 	 * Returns a copy of the image.
759 	 */
760 	Image clone() const
761 	{
762 		MagickCoreImage* image =
763 			CloneImage(imageRef, 0, 0, true, DMagickExceptionInfo());
764 
765 		return new Image(image, options.clone());
766 	}
767 
768 	/**
769 	 * replaces each color value in the given image, by using it as an
770 	 * index to lookup a replacement color value in a Color Look UP Table
771 	 * in the form of an image.  The values are extracted along a diagonal
772 	 * of the CLUT image so either a horizontal or vertial gradient image
773 	 * can be used.
774 	 * 
775 	 * Typically this is used to either re-color a gray-scale image
776 	 * according to a color gradient in the CLUT image, or to perform a
777 	 * freeform histogram (level) adjustment according to the (typically
778 	 * gray-scale) gradient in the CLUT image.
779 	 * 
780 	 * When the 'channel' mask includes the matte/alpha transparency
781 	 * channel but one image has no such channel it is assumed that that
782 	 * image is a simple gray-scale image that will effect the alpha channel
783 	 * values, either for gray-scale coloring (with transparent or
784 	 * semi-transparent colors), or a histogram adjustment of existing alpha
785 	 * channel values. If both images have matte channels, direct and normal
786 	 * indexing is applied, which is rarely used.
787 	 *
788 	 * Params:
789 	 *     clutImage = the color lookup table image for replacement
790 	 *                 color values.
791 	 *     channel   = One or more channels to adjust.
792 	 */
793 	void clut(Image clutImage, ChannelType channel = ChannelType.DefaultChannels)
794 	{
795 		ClutImageChannel(imageRef, channel, clutImage.imageRef);
796 
797 		DMagickException.throwException(&(imageRef.exception));
798 	}
799 
800 	/**
801 	 * Applies a lightweight Color Correction Collection (CCC) file
802 	 * to the image. The file solely contains one or more color corrections.
803 	 * Here is a sample:
804 	 * --------------------
805 	 * <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2">
806 	 *     <ColorCorrection id="cc03345">
807 	 *         <SOPNode>
808 	 *             <Slope> 0.9 1.2 0.5 </Slope>
809 	 *             <Offset> 0.4 -0.5 0.6 </Offset>
810 	 *             <Power> 1.0 0.8 1.5 </Power>
811 	 *         </SOPNode>
812 	 *         <SATNode>
813 	 *             <Saturation> 0.85 </Saturation>
814 	 *         </SATNode>
815 	 *     </ColorCorrection>
816 	 * </ColorCorrectionCollection>
817 	 * --------------------
818 	 * which includes the slop, offset, and power for each of
819 	 * the RGB channels as well as the saturation.
820 	 * 
821 	 * See_Also: $(LINK2 http://en.wikipedia.org/wiki/ASC_CDL,
822 	 *         Wikipedia ASC CDL).
823 	 */
824 	void colorDecisionList(string colorCorrectionCollection)
825 	{
826 		ColorDecisionListImage(imageRef, toStringz(colorCorrectionCollection));
827 
828 		DMagickException.throwException(&(imageRef.exception));
829 	}
830 
831 	/**
832 	 * Blend the fill color with the image pixels. The opacityRed,
833 	 * opacityGreen, opacityBlue and opacityAlpha arguments are the
834 	 * percentage to blend with the red, green, blue and alpha channels.
835 	 */
836 	void colorize(Color fill, uint opacityRed, uint opacityGreen, uint opacityBlue, uint opacityAlpha = 0)
837 	{
838 		string opacity = std..string.format("%s/%s/%s/%s",
839 			opacityRed, opacityGreen, opacityBlue, opacityAlpha);
840 
841 		MagickCoreImage* image =
842 			ColorizeImage(imageRef, toStringz(opacity), fill.pixelPacket, DMagickExceptionInfo());
843 
844 		imageRef = ImageRef(image);
845 	}
846 
847 	/**
848 	 * Applies color transformation to an image. This method permits
849 	 * saturation changes, hue rotation, luminance to alpha, and various
850 	 * other effects.  Although variable-sized transformation matrices can
851 	 * be used, typically one uses a 5x5 matrix for an RGBA image and a 6x6
852 	 * for CMYKA (or RGBA with offsets).  The matrix is similar to those
853 	 * used by Adobe Flash except offsets are in column 6 rather than 5
854 	 * (in support of CMYKA images) and offsets are normalized
855 	 * (divide Flash offset by 255)
856 	 *
857 	 * Params:
858 	 *     matrix = A tranformation matrix, with a maximum size of 6x6.
859 	 */
860 	void colorMatrix(double[][] matrix)
861 	{
862 		if ( matrix.length > 6 || matrix[0].length > 6 )
863 			throw new DMagickException("Matrix must be 6x6 or smaller.");
864 
865 		static if ( is(typeof(ColorMatrixImage)) )
866 		{
867 			KernelInfo* kernelInfo = AcquireKernelInfo("1");
868 			scope(exit) DestroyKernelInfo(kernelInfo);
869 
870 			kernelInfo.width = matrix[0].length;
871 			kernelInfo.height = matrix.length;
872 			kernelInfo.values = cast(double*)AcquireQuantumMemory(kernelInfo.width*kernelInfo.height, double.sizeof);
873 			scope(exit) kernelInfo.values = cast(double*)RelinquishMagickMemory(kernelInfo.values);
874 
875 			foreach ( i, row; matrix )
876 			{
877 				size_t offset = i * row.length;
878 
879 				kernelInfo.values[offset .. offset+row.length] = row;
880 			}
881 
882 			MagickCoreImage* image =
883 				ColorMatrixImage(imageRef, kernelInfo, DMagickExceptionInfo());
884 		}
885 		else
886 		{
887 			double[] values;
888 
889 			foreach ( i, row; matrix )
890 			{
891 				size_t offset = i * row.length;
892 
893 				values[offset .. offset+row.length] = row;
894 			}
895 
896 			MagickCoreImage* image =
897 				RecolorImage(imageRef, matrix.length, values.ptr, DMagickExceptionInfo());
898 		}
899 
900 		imageRef = ImageRef(image);
901 	}
902 
903 	/**
904 	 * Compare current image with another image. Sets meanErrorPerPixel,
905 	 * normalizedMaxError , and normalizedMeanError in the current image.
906 	 * false is returned if the images are identical. An ErrorOption
907 	 * exception is thrown if the reference image columns, rows, colorspace,
908 	 * or matte differ from the current image.
909 	 */
910 	bool compare(const(Image) referenceImage)
911 	{
912 		bool isEqual = IsImagesEqual(imageRef, referenceImage.imageRef) == 1;
913 		DMagickException.throwException(&(imageRef.exception));
914 
915 		return isEqual;
916 	}
917 
918 	/**
919 	 * Composites dest onto this image using the specified composite operator.
920 	 *
921 	 * Params:
922 	 *     overlay     = Image to use in the composite operation.
923 	 *     compositeOp = The composite operation to use.
924 	 *     xOffset     = The x-offset of the composited image,
925 	 *                   measured from the upper-left corner
926 	 *                   of the image.
927 	 *     yOffset     = The y-offset of the composited image,
928 	 *                   measured from the upper-left corner
929 	 *                   of the image.
930 	 *     gravity     = The gravity that defines the location of the
931 	 *                   location of overlay.
932 	 *     channel     = One or more channels to compose.
933 	 */
934 	void composite(
935 		const(Image) overlay,
936 		CompositeOperator compositeOp,
937 		ssize_t xOffset,
938 		ssize_t yOffset,
939 		ChannelType channel = ChannelType.DefaultChannels)
940 	{
941 		CompositeImageChannel(imageRef, channel, compositeOp, overlay.imageRef, xOffset, yOffset);
942 
943 		DMagickException.throwException(&(imageRef.exception));
944 	}
945 
946 	///ditto
947 	void composite(
948 		const(Image) overlay,
949 		CompositeOperator compositeOp,
950 		GravityType gravity = GravityType.NorthWestGravity,
951 		ChannelType channel = ChannelType.DefaultChannels)
952 	{
953 		RectangleInfo geometry;
954 
955 		SetGeometry(overlay.imageRef, &geometry);
956 		GravityAdjustGeometry(columns, rows, gravity, &geometry);
957 
958 		composite(overlay, compositeOp, geometry.x, geometry.y, channel);
959 	}
960 
961 	/**
962 	 * Merge the source and destination images according to the
963 	 * formula a*Sc*Dc + b*Sc + c*Dc + d where Sc is the source
964 	 * pixel and Dc is the destination pixel.
965 	 *
966 	 * Params:
967 	 *     overlay = Image to use in to composite operation.
968 	 *     xOffset = The x-offset of the composited image,
969 	 *               measured from the upper-left corner
970 	 *               of the image.
971 	 *     yOffset = The y-offset of the composited image,
972 	 *               measured from the upper-left corner
973 	 *               of the image.
974 	 *     gravity = The gravity that defines the location of the
975 	 *               location of overlay.
976 	 *     channel = One or more channels to compose.
977 	 */
978 	void composite(
979 		const(Image) overlay,
980 		double a,
981 		double b,
982 		double c,
983 		double d,
984 		ssize_t xOffset,
985 		ssize_t yOffset,
986 		ChannelType channel = ChannelType.DefaultChannels)
987 	{
988 		SetImageArtifact(imageRef, "compose:args",
989 			toStringz(std..string.format("%s,%s,%s,%s", a, b, c, d)));
990 		scope(exit) RemoveImageArtifact(imageRef, "compose:args");
991 
992 		composite(overlay, CompositeOperator.MathematicsCompositeOp, xOffset, yOffset, channel);
993 	}
994 
995 	///ditto
996 	void composite(
997 		const(Image) overlay,
998 		double a,
999 		double b,
1000 		double c,
1001 		double d,
1002 		GravityType gravity = GravityType.NorthWestGravity,
1003 		ChannelType channel = ChannelType.DefaultChannels)
1004 	{
1005 		RectangleInfo geometry;
1006 
1007 		SetGeometry(overlay.imageRef, &geometry);
1008 		GravityAdjustGeometry(columns, rows, gravity, &geometry);
1009 
1010 		composite(overlay, a, b, c, d, geometry.x, geometry.y, channel);
1011 	}
1012 
1013 	/**
1014 	 * Composites multiple copies of the source image across and down
1015 	 * the image, producing the same results as ImageMagick's composite
1016 	 * command with the -tile option.
1017 	 *
1018 	 * Params:
1019 	 *     overlay     = Image to use in to composite operation.
1020 	 *     compositeOp = The composite operation to use.
1021 	 *     channel     = One or more channels to compose.
1022 	 */
1023 	void compositeTiled(
1024 		const(Image) overlay,
1025 		CompositeOperator compositeOp,
1026 		ChannelType channel = ChannelType.DefaultChannels)
1027 	{
1028 		SetImageArtifact(imageRef, "compose:outside-overlay", "false");
1029 		scope(exit) RemoveImageArtifact(imageRef, "compose:outside-overlay");
1030 
1031 		for ( size_t y = 0; y < rows; y += overlay.rows )
1032 			for ( size_t x = 0; x < columns; x += overlay.columns )
1033 				composite(overlay, compositeOp, x, y, channel);
1034 	}
1035 
1036 	/**
1037 	 * enhances the intensity differences between the lighter and
1038 	 * darker elements of the image.
1039 	 *
1040 	 * Params:
1041 	 *     sharpen = If true increases the image contrast otherwise
1042 	 *               the contrast is reduced.
1043 	 */
1044 	void contrast(bool sharpen = false)
1045 	{
1046 		ContrastImage(imageRef, sharpen);
1047 		DMagickException.throwException(&(imageRef.exception));
1048 	}
1049 
1050 	/**
1051 	 * This is a simple image enhancement technique that attempts to
1052 	 * improve the contrast in an image by `stretching' the range of
1053 	 * intensity values it contains to span a desired range of values.
1054 	 * It differs from the more sophisticated histogram equalization in
1055 	 * that it can only apply a linear scaling function to the image pixel
1056 	 * values. As a result the `enhancement' is less harsh.
1057 	 *
1058 	 * Params:
1059 	 *     blackPoint = Black out at most this many pixels.
1060 	 *                  Specify an apsulute number of pixels or an
1061 	 *                  percentage by passing a value between 1 and 0
1062 	 *     whitePoint = Burn at most this many pixels.
1063 	 *                  Specify an apsulute number of pixels or an
1064 	 *                  percentage by passing a value between 1 and 0
1065 	 *     channel    = One or more channels to adjust.
1066 	 */
1067 	void contrastStretch(double blackPoint, double whitePoint, ChannelType channel = ChannelType.DefaultChannels)
1068 	{
1069 		if ( blackPoint < 1 )
1070 			blackPoint *= QuantumRange;
1071 		if ( whitePoint < 1 )
1072 			whitePoint *= QuantumRange;
1073 
1074 		ContrastStretchImageChannel(imageRef, channel, blackPoint, whitePoint);
1075 		DMagickException.throwException(&(imageRef.exception));
1076 	}
1077 
1078 	/**
1079 	 * Applies a custom convolution kernel to the image.
1080 	 * See_Also: $(LINK2 http://www.dai.ed.ac.uk/HIPR2/convolve.htm,
1081 	 *        Convolution in the Hypermedia Image Processing Reference).
1082 	 */
1083 	void convolve(double[][] matrix, ChannelType channel = ChannelType.DefaultChannels)
1084 	{
1085 		double[] kernel = new double[matrix.length * matrix[0].length];
1086 
1087 		foreach ( i, row; matrix )
1088 		{
1089 			size_t offset = i * row.length;
1090 
1091 			kernel[offset .. offset+row.length] = row;
1092 		}
1093 
1094 		MagickCoreImage* image =
1095 			ConvolveImageChannel(imageRef, channel, matrix.length, kernel.ptr,  DMagickExceptionInfo());
1096 
1097 		imageRef = ImageRef(image);
1098 	}
1099 
1100 	/**
1101 	 * Extract a region of the image starting at the offset defined by
1102 	 * geometry.  Region must be fully defined, and no special handling
1103 	 * of geometry flags is performed.
1104 	 */
1105 	void crop(Geometry geometry)
1106 	{
1107 		RectangleInfo rectangle = geometry.rectangleInfo;
1108 
1109 		MagickCoreImage* image =
1110 			CropImage(imageRef, &rectangle, DMagickExceptionInfo());
1111 
1112 		imageRef = ImageRef(image);
1113 	}
1114 
1115 	/**
1116 	 * displaces an image's colormap by a given number of positions.
1117 	 * If you cycle the colormap a number of times you can produce
1118 	 * a psychodelic effect.
1119 	 */
1120 	void cycleColormap(ssize_t amount)
1121 	{
1122 		CycleColormapImage(imageRef, amount);
1123 		DMagickException.throwException(&(imageRef.exception));
1124 	}
1125 
1126 	/**
1127 	 * Decipher an enciphered image.
1128 	 */
1129 	void decipher(string passphrase)
1130 	{
1131 		DecipherImage(imageRef, toStringz(passphrase), DMagickExceptionInfo());
1132 	}
1133 
1134 	/**
1135 	 * Straightens an image. A threshold of 40% works for most images.
1136 	 * 
1137 	 * Skew is an artifact that occurs in scanned images because of the
1138 	 * camera being misaligned, imperfections in the scanning or surface,
1139 	 * or simply because the paper was not placed completely flat when
1140 	 * scanned.
1141 	 * 
1142 	 * Params:
1143 	 *     threshold     = Specify an apsulute number of pixels or an
1144 	 *                     percentage by passing a value between 1 and 0.
1145 	 *     autoCropWidth = Specify a value for this argument to cause the
1146 	 *                     deskewed image to be auto-cropped.
1147 	 *                     The argument is the pixel width of the
1148 	 *                     image background (e.g. 40).
1149 	 *                     A width of 0 disables auto cropping.
1150 	 */
1151 	void deskew(double threshold = 0.4, size_t autoCropWidth = 0)
1152 	{
1153 		if ( autoCropWidth > 0 )
1154 		{
1155 			SetImageArtifact(imageRef, "deskew:auto-crop", toStringz(to!(string)(autoCropWidth)) );
1156 			scope(exit) RemoveImageArtifact(imageRef, "deskew:auto-crop");
1157 		}
1158 
1159 		if ( threshold < 1 )
1160 			threshold *= QuantumRange;
1161 
1162 		MagickCoreImage* image =
1163 			DeskewImage(imageRef, threshold, DMagickExceptionInfo());
1164 
1165 		imageRef = ImageRef(image);
1166 	}
1167 
1168 	/**
1169 	 * Reduces the speckle noise in an image while perserving
1170 	 * the edges of the original image.
1171 	 */
1172 	void despeckle()
1173 	{
1174 		MagickCoreImage* image =
1175 			DespeckleImage(imageRef, DMagickExceptionInfo());
1176 
1177 		imageRef = ImageRef(image);
1178 	}
1179 
1180 	/**
1181 	 * Uses displacementMap to move color from img to the output image.
1182 	 * 
1183 	 * This method corresponds to the -displace option of ImageMagick's
1184 	 * composite command.
1185 	 * 
1186 	 * Params:
1187 	 *     displacementMap = 
1188 	 *                  The source image for the composite operation.
1189 	 *     xAmplitude = The maximum displacement on the x-axis.
1190 	 *     yAmplitude = The maximum displacement on the y-axis.
1191 	 *     xOffset    = The x offset to use.
1192 	 *     yOffset    = The y offset to use.
1193 	 *     gravity    = The gravity to use.
1194 	 */
1195 	void displace(
1196 		const(Image) displacementMap,
1197 		int xAmplitude,
1198 		int yAmplitude,
1199 		ssize_t xOffset,
1200 		ssize_t yOffset)
1201 	{
1202 		SetImageArtifact(imageRef, "compose:args",
1203 			toStringz(std..string.format("%s,%s", xAmplitude, yAmplitude)));
1204 		scope(exit) RemoveImageArtifact(imageRef, "compose:args");
1205 
1206 		composite(displacementMap, CompositeOperator.DisplaceCompositeOp, xOffset, yOffset);
1207 	}
1208 
1209 	///ditto
1210 	void displace(
1211 		const(Image) overlay,
1212 		int srcPercentage,
1213 		int dstPercentage,
1214 		GravityType gravity = GravityType.NorthWestGravity)
1215 	{
1216 		RectangleInfo geometry;
1217 
1218 		SetGeometry(overlay.imageRef, &geometry);
1219 		GravityAdjustGeometry(columns, rows, gravity, &geometry);
1220 
1221 		displace(overlay, srcPercentage, dstPercentage, geometry.x, geometry.y);
1222 	}	
1223 
1224 	/**
1225 	 * Display image on screen.
1226 	 * 
1227 	 * $(RED Caution:) if an image format is is not compatible with
1228 	 * the display visual (e.g. JPEG on a colormapped display)
1229 	 * then the original image will be altered. Use a copy of the
1230 	 * original if this is a problem.
1231 	 */
1232 	void display()
1233 	{
1234 		version(DMagick_No_Display)
1235 		{
1236 		}
1237 		else
1238 		{
1239 			version(Windows)
1240 			{
1241 				Window win = new Window(this);
1242 				win.display();
1243 			}
1244 			else
1245 			{
1246 				DisplayImages(options.imageInfo, imageRef);
1247 
1248 				DMagickException.throwException(&(imageRef.exception));
1249 			}
1250 		}
1251 	}
1252 
1253 	/**
1254 	 * Composites the overlay image onto this image. The opacity
1255 	 * of this image is multiplied by dstPercentage and opacity of
1256 	 * overlay is multiplied by srcPercentage.
1257 	 * 
1258 	 * This method corresponds to the -dissolve option
1259 	 * of ImageMagick's composite command.
1260 	 * 
1261 	 * Params:
1262 	 *     overlay       = The source image for the composite operation.
1263 	 *     srcPercentage = Percentage for the source image.
1264 	 *     dstPercentage = Percentage for this image.
1265 	 *     xOffset       = The x offset to use for the overlay.
1266 	 *     yOffset       = The y offset to use for the overlay.
1267 	 *     gravity       = The gravity to use for the overlay.
1268 	 */
1269 	void dissolve(
1270 		const(Image) overlay,
1271 		int srcPercentage,
1272 		int dstPercentage,
1273 		ssize_t xOffset,
1274 		ssize_t yOffset)
1275 	{
1276 		SetImageArtifact(imageRef, "compose:args",
1277 			toStringz(std..string.format("%s,%s", srcPercentage, dstPercentage)));
1278 		scope(exit) RemoveImageArtifact(imageRef, "compose:args");
1279 
1280 		composite(overlay, CompositeOperator.DissolveCompositeOp, xOffset, yOffset);
1281 	}
1282 
1283 	///ditto
1284 	void dissolve(
1285 		const(Image) overlay,
1286 		int srcPercentage,
1287 		int dstPercentage,
1288 		GravityType gravity = GravityType.NorthWestGravity)
1289 	{
1290 		RectangleInfo geometry;
1291 
1292 		SetGeometry(overlay.imageRef, &geometry);
1293 		GravityAdjustGeometry(columns, rows, gravity, &geometry);
1294 
1295 		dissolve(overlay, srcPercentage, dstPercentage, geometry.x, geometry.y);
1296 	}	
1297 
1298 	/**
1299 	 * Distort an image using the specified distortion type and its
1300 	 * required arguments. This method is equivalent to ImageMagick's
1301 	 * -distort option.
1302 	 * 
1303 	 * Params:
1304 	 *     method    = Distortion method to use.
1305 	 *     arguments = An array of numbers. The size of the array
1306 	 *                 depends on the distortion type.
1307 	 *     bestfit   = If enabled, and the distortion allows it,
1308 	 *                 the destination image is adjusted to ensure 
1309 	 *                 the whole source image will just fit within
1310 	 *                 the final destination image, which will be
1311 	 *                 sized and offset accordingly.
1312 	 */
1313 	void distort(DistortImageMethod method, double[] arguments, bool bestfit = false)
1314 	{
1315 		MagickCoreImage* image =
1316 			DistortImage(imageRef, method, arguments.length, arguments.ptr, bestfit, DMagickExceptionInfo());
1317 
1318 		imageRef = ImageRef(image);
1319 	}
1320 
1321 	/**
1322 	 * Finds edges in an image.
1323 	 * 
1324 	 * Params:
1325 	 *     radius = the radius of the convolution filter.
1326 	 *              If 0 a suitable default is selected.
1327 	 */
1328 	void edge(double radius = 0)
1329 	{
1330 		MagickCoreImage* image =
1331 			EdgeImage(imageRef, radius, DMagickExceptionInfo());
1332 
1333 		imageRef = ImageRef(image);
1334 	}
1335 
1336 	/**
1337 	 * Emboss image (hilight edges with 3D effect).
1338 	 * 
1339 	 * Params:
1340 	 *     radius = The radius of the Gaussian, in pixels,
1341 	 *              not counting the center pixel.
1342 	 *     sigma  = The standard deviation of the Laplacian, in pixels.
1343 	 */
1344 	void emboss(double radius = 0, double sigma = 1)
1345 	{
1346 		MagickCoreImage* image =
1347 			EmbossImage(imageRef, radius, sigma, DMagickExceptionInfo());
1348 
1349 		imageRef = ImageRef(image);
1350 	}
1351 
1352 	/**
1353 	 * Encipher an image.
1354 	 */
1355 	void encipher(string passphrase)
1356 	{
1357 		EncipherImage(imageRef, toStringz(passphrase), DMagickExceptionInfo());
1358 	}
1359 
1360 	/**
1361 	 * Applies a digital filter that improves the quality of a noisy image.
1362 	 */
1363 	void enhance()
1364 	{
1365 		MagickCoreImage* image =
1366 			EnhanceImage(imageRef, DMagickExceptionInfo());
1367 
1368 		imageRef = ImageRef(image);
1369 	}
1370 
1371 	/**
1372 	 * Applies a histogram equalization to the image.
1373 	 */
1374 	void equalize(ChannelType channel = ChannelType.DefaultChannels)
1375 	{
1376 		EqualizeImageChannel(imageRef, channel);
1377 
1378 		DMagickException.throwException(&(imageRef.exception));
1379 	}
1380 
1381 	/**
1382 	 * Initializes the image pixels to the image background color.
1383 	 */
1384 	void erase()
1385 	{
1386 		SetImageBackgroundColor(imageRef);
1387 
1388 		DMagickException.throwException(&(imageRef.exception));
1389 	}
1390 
1391 	/**
1392 	 * Applies a value to the image with an arithmetic, relational, or
1393 	 * logical operator to an image. Use these operations to lighten or
1394 	 * darken an image, to increase or decrease contrast in an image, or
1395 	 * to produce the "negative" of an image.
1396 	 *
1397 	 * See_Also: $(LINK2 http://www.imagemagick.org/script/command-line-options.php#evaluate,
1398 	 *     ImageMagick's -_evaluate option).
1399 	 */
1400 	void evaluate(MagickEvaluateOperator op, double value, ChannelType channel = ChannelType.DefaultChannels)
1401 	{
1402 		EvaluateImageChannel(imageRef, channel, op, value,  DMagickExceptionInfo());
1403 	}
1404 
1405 	/**
1406 	 * This method is very similar to crop. It extracts the rectangle
1407 	 * specified by its arguments from the image and returns it as a new
1408 	 * image. However, excerpt does not respect the virtual page offset and
1409 	 * does not update the page offset and is more efficient than cropping.
1410 	 * 
1411 	 * It is the caller's responsibility to ensure that the rectangle lies
1412 	 * entirely within the original image.
1413 	 */
1414 	void excerpt(Geometry geometry)
1415 	{
1416 		RectangleInfo rectangle = geometry.rectangleInfo;
1417 
1418 		MagickCoreImage* image =
1419 			ExcerptImage(imageRef, &rectangle, DMagickExceptionInfo());
1420 
1421 		imageRef = ImageRef(image);
1422 	}
1423 
1424 	/**
1425 	 * Extracts the pixel data from the specified rectangle.
1426 	 *
1427 	 * Params:
1428 	 *     area = Area to extract.
1429 	 *     map  = This character string can be any combination
1430 	 *            or order of R = red, G = green, B = blue, A = 
1431 	 *            alpha, C = cyan, Y = yellow, M = magenta, and K = black.
1432 	 *            The ordering reflects the order of the pixels in
1433 	 *            the supplied pixel array.
1434 	 * 
1435 	 * Returns: An array of values containing the pixel components as
1436 	 *          defined by the map parameter and the Type.
1437 	 */
1438 	T[] exportPixels(T)(Geometry area, string map = "RGBA") const
1439 	{
1440 		T[] pixels = new T[(area.width * area.height) * map.length];
1441 
1442 		exportPixels(area, pixels, map);
1443 
1444 		return pixels;
1445 	}
1446 
1447 	/**
1448 	 * Ditto, but takes an existing pixel buffer.
1449 	 *
1450 	 * Throws: An ImageException if the buffer length is insufficient.
1451 	 */
1452 	void exportPixels(T)(Geometry area, T[] pixels, string map = "RGBA") const
1453 	{
1454 		if ( pixels.length < (area.width * area.height) * map.length )
1455 			throw new ImageException(std..string.format("Pixel buffer needs more storage for %s channels.", map));
1456 
1457 		StorageType storage = getStorageType!(T);
1458 
1459 		ExportImagePixels(
1460 			imageRef,
1461 			area.xOffset, 
1462 			area.yOffset,
1463 			area.width,   
1464 			area.height,
1465 			toStringz(map), 
1466 			storage, 
1467 			pixels.ptr, 
1468 			DMagickExceptionInfo());
1469 	}
1470 
1471 	unittest
1472 	{
1473 		Image image = new Image(Geometry(100, 100), new Color("red"));
1474 		byte[] bytes = image.exportPixels!(byte)(Geometry(10,10,10,10));
1475 
1476 		assert(bytes.length == 10 * 10 * 4);
1477 	}
1478 
1479 	/**
1480 	 * If the Geometry is larger than this Image, extends the image to
1481 	 * the specified geometry. And the new pixels are set to the
1482 	 * background color. If the Geometry is smaller than this image
1483 	 * crops the image.
1484 	 * 
1485 	 * The new image is composed over the background using
1486 	 * the composite operator specified by the compose property.
1487 	 */
1488 	void extent(Geometry geometry)
1489 	{
1490 		RectangleInfo rectangle = geometry.rectangleInfo;
1491 
1492 		MagickCoreImage* image =
1493 			ExtentImage(imageRef, &rectangle, DMagickExceptionInfo());
1494 
1495 		imageRef = ImageRef(image);
1496 	}
1497 
1498 	/**
1499 	 * This interesting method searches for a rectangle in the image that
1500 	 * is similar to the target. For the rectangle to be similar each pixel
1501 	 * in the rectangle must match the corresponding pixel in the target
1502 	 * image within the range specified by the fuzz property of this image
1503 	 * and the target image.
1504 	 *
1505 	 * Params:
1506 	 *     target  = An image that forms the target of the search.
1507 	 *     xOffset = The starting x position to search for a match.
1508 	 *     yOffset = The starting y position to search for a match.
1509 	 * 
1510 	 * Returns: The size and location of the match.
1511 	 */
1512 	Geometry findSimilarRegion(Image target, ssize_t xOffset, ssize_t yOffset)
1513 	{
1514 		IsImageSimilar(imageRef, target.imageRef, &xOffset, &yOffset, DMagickExceptionInfo());
1515 
1516 		return Geometry(target.columns, target.rows, xOffset, yOffset);
1517 	}
1518 
1519 	/**
1520 	 * creates a vertical mirror image by reflecting the pixels
1521 	 * around the central x-axis.
1522 	 */
1523 	void flip()
1524 	{
1525 		FlipImage(imageRef, DMagickExceptionInfo());
1526 	}
1527 
1528 	/**
1529 	 * Changes the color value of any pixel that matches target and is an
1530 	 * immediate neighbor. To the fillColor or fillPattern set for this
1531 	 * image. If fillToBorder is true, the color value is changed
1532 	 * for any neighbor pixel that does not match the borderColor.
1533 	 * 
1534 	 * By default target must match a particular pixel color exactly.
1535 	 * However, in many cases two colors may differ by a small amount.
1536 	 * The fuzz property of image defines how much tolerance is acceptable
1537 	 * to consider two colors as the same. For example, set fuzz to 10 and
1538 	 * the color red at intensities of 100 and 102 respectively are now
1539 	 * interpreted as the same color for the purposes of the floodfill.
1540 	 * 
1541 	 * Params:
1542 	 *     xOffset      = Starting x location for the operation.
1543 	 *     xOffset      = Starting y location for the operation.
1544 	 *     fillToBorder = If true fill untill the borderColor, else only
1545 	 *                    the target color if affected.
1546 	 *     channel      = The affected channels.
1547 	 */
1548 	void floodFill(
1549 		ssize_t xOffset,
1550 		ssize_t yOffset,
1551 		bool fillToBorder = false,
1552 		ChannelType channel = ChannelType.DefaultChannels)
1553 	{
1554 		MagickPixelPacket target;
1555 
1556 		GetMagickPixelPacket(imageRef, &target);
1557 
1558 		if ( fillToBorder )
1559 		{
1560 			setMagickPixelPacket(&target, borderColor);
1561 		}
1562 		else
1563 		{
1564 			PixelPacket packet;
1565 			GetOneAuthenticPixel(imageRef, xOffset, yOffset, &packet, DMagickExceptionInfo());
1566 
1567 			setMagickPixelPacket(&target, new Color(packet));
1568 		}
1569 
1570 		FloodfillPaintImage(imageRef, channel, options.drawInfo, &target, xOffset, yOffset, fillToBorder);
1571 
1572 		DMagickException.throwException(&(imageRef.exception));
1573 	}
1574 
1575 	/**
1576 	 * Fill the image like floodFill but use the specified colors.
1577 	 *
1578 	 * Params:
1579 	 *     xOffset     = Starting x location for the operation.
1580 	 *     xOffset     = Starting y location for the operation.
1581 	 *     fillColor   = Fill color to use.
1582 	 *     borderColor = borderColor to use.
1583 	 *     channel     = The affected channels.
1584 	 */
1585 	void floodFillColor(
1586 		ssize_t xOffset,
1587 		ssize_t yOffset,
1588 		Color fillColor,
1589 		Color borderColor = null,
1590 		ChannelType channel = ChannelType.DefaultChannels)
1591 	{
1592 		Color oldFillColor = options.fillColor;
1593 		options.fillColor = fillColor;
1594 		scope(exit) options.fillColor = oldFillColor;
1595 
1596 		floodFillPattern(xOffset, yOffset, null, borderColor, channel);
1597 	}
1598 
1599 	/**
1600 	 * Fill the image like floodFill but use the specified
1601 	 * pattern an borderColor.
1602 	 *
1603 	 * Params:
1604 	 *     xOffset     = Starting x location for the operation.
1605 	 *     xOffset     = Starting y location for the operation.
1606 	 *     fillPattern = Fill pattern to use.
1607 	 *     borderColor = borderColor to use.
1608 	 *     channel     = The affected channels.
1609 	 */
1610 	void floodFillPattern(
1611 		ssize_t xOffset,
1612 		ssize_t yOffset,
1613 		Image fillPattern,
1614 		Color borderColor = null,
1615 		ChannelType channel = ChannelType.DefaultChannels)
1616 	{
1617 		// Cast away const, so we can temporarily hold
1618 		// The image and asign it back to the fillPattern. 
1619 		Image oldFillPattern = cast(Image)options.fillPattern;
1620 		options.fillPattern = fillPattern;
1621 		scope(exit) options.fillPattern = oldFillPattern;
1622 
1623 		Color oldBorderColor = this.borderColor;
1624 		this.borderColor = borderColor;
1625 		scope(exit) this.borderColor = oldBorderColor;
1626 
1627 		// If the borderColor !is null, set fillToBorder to true.
1628 		floodFill(xOffset, yOffset, borderColor !is null, channel);
1629 	}
1630 
1631 	/**
1632 	 * creates a horizontal mirror image by reflecting the pixels
1633 	 * around the central y-axis.
1634 	 */
1635 	void flop()
1636 	{
1637 		FlopImage(imageRef, DMagickExceptionInfo());
1638 	}
1639 
1640 	/**
1641 	 * Adds a simulated 3D border.
1642 	 * The matteColor is used to draw the frame.
1643 	 * 
1644 	 * Params:
1645 	 *     geometry = The size portion indicates the width and height of
1646 	 *                the frame. If no offsets are given then the border
1647 	 *                added is a solid color. Offsets x and y, if present,
1648 	 *                specify that the width and height of the border is
1649 	 *                partitioned to form an outer bevel of thickness x
1650 	 *                pixels and an inner bevel of thickness y pixels.
1651 	 *                Negative offsets make no sense as frame arguments.
1652 	 */
1653 	void frame(Geometry geometry)
1654 	{
1655 		FrameInfo frameInfo;
1656 
1657 		frameInfo.width       = columns + ( 2 * geometry.width  );
1658 		frameInfo.height      = rows    + ( 2 * geometry.height );
1659 		frameInfo.x           = geometry.width;
1660 		frameInfo.y           = geometry.height;
1661 		frameInfo.inner_bevel = geometry.yOffset;
1662 		frameInfo.outer_bevel = geometry.xOffset;
1663 
1664 		MagickCoreImage* image =
1665 			FrameImage(imageRef, &frameInfo, DMagickExceptionInfo());
1666 
1667 		imageRef = ImageRef(image);
1668 	}
1669 
1670 	/**
1671 	 * Applies a value to the image with an arithmetic, relational, or
1672 	 * logical operator to an image. Use these operations to lighten or
1673 	 * darken an image, to increase or decrease contrast in an image, or
1674 	 * to produce the "negative" of an image.
1675 	 *
1676 	 * This method is equivalent to the 
1677 	 * $(LINK2 http://www.imagemagick.org/script/command-line-options.php#function,
1678 	 *       convert -function) option.
1679 	 *
1680 	 * Params:
1681 	 *     function = The MagickFunction to use.
1682 	 *     params   = 
1683 	 *         An array of values to be used by the function.
1684 	 *         $(LIST $(COMMA $(B PolynomialFunction:)
1685 	 *             The Polynomial function takes an arbitrary number of
1686 	 *             parameters, these being the coefficients of a polynomial,
1687 	 *             in decreasing order of degree. That is, entering
1688 	 *             [aₙ, aₙ₋₁, ... a₁, a₀] will invoke a polynomial function
1689 	 *             given by: aₙ uⁿ + aₙ₋₁ uⁿ⁻¹ + ··· a₁ u + a₀, where where
1690 	 *             u is pixel's original normalized channel value.),
1691 	 *         $(COMMA $(B SinusoidFunction:)
1692 	 *             These values are given as one to four parameters, as
1693 	 *             follows, [freq, phase, amp, bias] if omitted the default
1694 	 *             values will be used: [1.0, 0.0, 0.5, 0.5].),
1695 	 *         $(COMMA $(B ArcsinFunction:)
1696 	 *             These values are given as one to four parameters, as
1697 	 *             follows, [width, center, range, bias] if omitted the
1698 	 *             default values will be used: [1.0, 0.5, 1.0, 0.5].),
1699 	 *         $(COMMA $(B ArctanFunction:)
1700 	 *             These values are given as one to four parameters, as
1701 	 *             follows, [slope, center, range, bias] if omitted the
1702 	 *             default values will be used: [1.0, 0.5, 1.0, 0.5].))
1703 	 *     channel  = The channels this funtion aplies to.
1704 	 */
1705 	void functionImage(MagickFunction funct, double[] params, ChannelType channel = ChannelType.DefaultChannels)
1706 	{
1707 		FunctionImageChannel(imageRef, channel, funct, params.length, params.ptr, DMagickExceptionInfo());
1708 	}
1709 
1710 	/**
1711 	 * Applies a mathematical expression to the specified image.
1712 	 * 
1713 	 * See_Aso:
1714 	 *     $(LINK2 http://www.imagemagick.org/script/fx.php,
1715 	 *     FX, The Special Effects Image Operator) for a detailed
1716 	 *     discussion of this option.
1717 	 */
1718 	void fx(string expression, ChannelType channel = ChannelType.DefaultChannels)
1719 	{
1720 		MagickCoreImage* image =
1721 			FxImageChannel(imageRef, channel, toStringz(expression), DMagickExceptionInfo());
1722 
1723 		imageRef = ImageRef(image);
1724 	}
1725 
1726 	/**
1727 	 * gamma gamma-corrects a particular image channel.
1728 	 * The same image viewed on different devices will have perceptual
1729 	 * differences in the way the image's intensities are represented
1730 	 * on the screen.  Specify individual gamma levels for the red,
1731 	 * green, and blue channels, or adjust all three with the gamma
1732 	 * function. Values typically range from 0.8 to 2.3.
1733 	 * 
1734 	 * You can also reduce the influence of a particular channel
1735 	 * with a gamma value of 0.
1736 	 */
1737 	void gamma(double value, ChannelType channel = ChannelType.DefaultChannels)
1738 	{
1739 		GammaImageChannel(imageRef, channel, value);
1740 
1741 		DMagickException.throwException(&(imageRef.exception));
1742 	}
1743 
1744 	///ditto
1745 	void gamma(double red, double green, double blue)
1746 	{
1747 		GammaImageChannel(imageRef, ChannelType.RedChannel, red);
1748 		GammaImageChannel(imageRef, ChannelType.GreenChannel, green);
1749 		GammaImageChannel(imageRef, ChannelType.BlueChannel, blue);
1750 
1751 		DMagickException.throwException(&(imageRef.exception));
1752 	}
1753 
1754 	/**
1755 	 * Blurs an image. We convolve the image with a Gaussian operator
1756 	 * of the given radius and standard deviation (sigma).
1757 	 * For reasonable results, the radius should be larger than sigma.
1758 	 * 
1759 	 * Params:
1760 	 *     radius  = The radius of the Gaussian, in pixels,
1761 	 *               not counting the center pixel.
1762 	 *     sigma   = the standard deviation of the Gaussian, in pixels.
1763 	 *     channel = The channels to blur.
1764 	 */
1765 	void gaussianBlur(double radius = 0, double sigma = 1, ChannelType channel = ChannelType.DefaultChannels)
1766 	{
1767 		MagickCoreImage* image =
1768 			GaussianBlurImageChannel(imageRef, channel, radius, sigma, DMagickExceptionInfo());
1769 
1770 		imageRef = ImageRef(image);
1771 	}
1772 
1773 	/**
1774 	 * Returns the TypeMetric class witch provides the information
1775 	 * regarding font metrics such as ascent, descent, text width,
1776 	 * text height, and maximum horizontal advance. The units of
1777 	 * these font metrics are in pixels, and that the metrics are
1778 	 * dependent on the current Image font (default Ghostscript's
1779 	 * "Helvetica"), pointsize (default 12 points), and x/y resolution
1780 	 * (default 72 DPI) settings.
1781 	 * 
1782 	 * The pixel units may be converted to points (the standard
1783 	 * resolution-independent measure used by the typesetting industry)
1784 	 * via the following equation:
1785 	 * ----------------------------------
1786 	 * sizePoints = (sizePixels * 72)/resolution
1787 	 * ----------------------------------
1788 	 * where resolution is in dots-per-inch (DPI). This means that at the
1789 	 * default image resolution, there is one pixel per point.
1790 	 * See_Also:
1791 	 *     $(LINK2 http://freetype.sourceforge.net/freetype2/docs/glyphs/index.html,
1792 	 *         FreeType Glyph Conventions) for a detailed description of
1793 	 *     font metrics related issues.
1794 	 */
1795 	TypeMetric getTypeMetrics(string text)
1796 	{
1797 		TypeMetric metric;
1798 		DrawInfo* drawInfo = options.drawInfo;
1799 
1800 		copyString(drawInfo.text, text);
1801 		scope(exit) copyString(drawInfo.text, null);
1802 
1803 		GetMultilineTypeMetrics(imageRef, drawInfo, &metric);
1804 		DMagickException.throwException(&(imageRef.exception));
1805 
1806 		return metric;
1807 	}
1808 
1809 	/**
1810 	 * applies a Hald color lookup table to the image. A Hald color lookup
1811 	 * table is a 3-dimensional color cube mapped to 2 dimensions. Create
1812 	 * it with the HALD coder. You can apply any color transformation to
1813 	 * the Hald image and then use this method to apply the transform to
1814 	 * the image.
1815 	 * 
1816 	 * Params:
1817 	 *     haldImage = The image, which is replaced by indexed CLUT values.
1818 	 *     channel   = The channels to aply the CLUT to.
1819 	 * 
1820 	 * See_Also:
1821 	 *     $(XREF Image, clut) which provides color value replacement of
1822 	 *     the individual color channels, usally involving a simplier
1823 	 *     gray-scale image. E.g: gray-scale to color replacement, or
1824 	 *     modification by a histogram mapping.
1825 	 */
1826 	void haldClut(Image haldImage, ChannelType channel = ChannelType.DefaultChannels)
1827 	{
1828 		HaldClutImageChannel(imageRef, channel, haldImage.imageRef);
1829 
1830 		DMagickException.throwException(&(imageRef.exception));
1831 	}
1832 
1833 	/**
1834 	 * A funhouse mirror effect.
1835 	 * 
1836 	 * Params:
1837 	 *     amount = Defines the extend of the effect.
1838 	 *              The value may be positive for implosion,
1839 	 *              or negative for explosion.
1840 	 */
1841 	void implode(double amount = 0.5)
1842 	{
1843 		MagickCoreImage* image =
1844 			ImplodeImage(imageRef, amount, DMagickExceptionInfo());
1845 
1846 		imageRef = ImageRef(image);
1847 	}
1848 
1849 	/**
1850 	 * Replaces the pixels in the specified area with pixel data
1851 	 * from the supplied array.
1852 	 * 
1853 	 * Params:
1854 	 *     area   = Location in the image to store the pixels. 
1855 	 *     pixels = An array of pixels defined by map.
1856 	 *     map    = This character string can be any combination
1857 	 *              or order of R = red, G = green, B = blue, A = 
1858 	 *              alpha, C = cyan, Y = yellow, M = magenta, and K = black.
1859 	 *              The ordering reflects the order of the pixels in
1860 	 *              the supplied pixel array.
1861 	 */
1862 	void importPixels(T)(Geometry area, T[] pixels, string map = "RGBA")
1863 	{
1864 		StorageType storage = getStorageType!(T);
1865 
1866 		ImportImagePixels(imageRef,
1867 			area.xOffset, area.yOffset,
1868 			area.width,   area.height,
1869 			toStringz(map), storage, pixels.ptr);
1870 
1871 		DMagickException.throwException(&(imageRef.exception));
1872 	}
1873 
1874 	/**
1875 	 * Adjusts the levels of an image by scaling the colors falling between
1876 	 * specified white and black points to the full available quantum range.
1877 	 * The parameters provided represent the black, mid, and white points.
1878 	 * Colors darker than the black point are set to zero. Colors brighter
1879 	 * than the white point are set to the maximum quantum value.
1880 	 * 
1881 	 * It is typically used to improve image contrast, or to provide a
1882 	 * controlled linear threshold for the image. If the black and white
1883 	 * points are set to the minimum and maximum values found in the image,
1884 	 * the image can be normalized. or by swapping black and white values,
1885 	 * negate the image.
1886 	 * 
1887 	 * Params: 
1888 	 *     blackPoint = Specifies the darkest color in the image.
1889 	 *     whitePoint = Specifies the lightest color in the image.
1890 	 *     gamma      = Specifies the gamma correction to apply to the image.
1891 	 *     channel    = The channels to level.
1892 	 */
1893 	void level(
1894 		Quantum blackPoint = 0,
1895 		Quantum whitePoint = QuantumRange,
1896 		double gamma = 1,
1897 		ChannelType channel = ChannelType.DefaultChannels)
1898 	{
1899 		LevelImageChannel(imageRef, channel, blackPoint, whitePoint, gamma);
1900 
1901 		DMagickException.throwException(&(imageRef.exception));
1902 	}
1903 
1904 	/**
1905 	 * applies the reversed level operation to just the channels specified.
1906 	 * It compresses the full range of color values, so that they lie between
1907 	 * the given black and white points. Gamma is applied before the values
1908 	 * are mapped.
1909 	 * 
1910 	 * It can be used for example de-contrast a greyscale image to the exact
1911 	 * levels specified. Or by using specific levels for each channel of an
1912 	 * image you can convert a gray-scale image to any linear color gradient,
1913 	 * according to those levels.
1914 	 * 
1915 	 * Params: 
1916 	 *     blackPoint = Specifies the darkest color in the image.
1917 	 *     whitePoint = Specifies the lightest color in the image.
1918 	 *     gamma      = Specifies the gamma correction to apply to the image.
1919 	 *     channel    = The channels to level.
1920 	 */
1921 	void levelize(
1922 		Quantum blackPoint = 0,
1923 		Quantum whitePoint = QuantumRange,
1924 		double gamma = 1,
1925 		ChannelType channel = ChannelType.DefaultChannels)
1926 	{
1927 		LevelizeImageChannel(imageRef, channel, blackPoint, whitePoint, gamma);
1928 
1929 		DMagickException.throwException(&(imageRef.exception));
1930 	}
1931 
1932 	/**
1933 	 * Discards any pixels below the black point and above the white
1934 	 * point and levels the remaining pixels.
1935 	 * 
1936 	 * Params: 
1937 	 *     blackPoint = Specifies the darkest color in the image.
1938 	 *     whitePoint = Specifies the lightest color in the image.
1939 	 */
1940 	void linearStretch(Quantum blackPoint, Quantum whitePoint)
1941 	{
1942 		LinearStretchImage(imageRef, blackPoint, whitePoint);
1943 
1944 		DMagickException.throwException(&(imageRef.exception));
1945 	}
1946 
1947 	/**
1948 	 * Rescale image with seam carving. To use this method, you must
1949 	 * have installed and configured ImageMagick to use the
1950 	 * $(LINK2 http://liblqr.wikidot.com/, Liquid Rescale Library).
1951 	 *
1952 	 * Params:
1953 	 *     columns  = The desired width.
1954 	 *     rows     = The desired height.
1955 	 *     deltaX   = Maximum seam transversal step (0 means straight seams).
1956 	 *     rigidity = Introduce a bias for non-straight seams (typically 0).
1957 	 */
1958 	void liquidRescale(Geometry size, size_t rows, double deltaX = 0, double rigidity = 0)
1959 	{
1960 		size = size.toAbsolute(columns, rows);
1961 
1962 		MagickCoreImage* image =
1963 			LiquidRescaleImage(imageRef, size.width, size.height, deltaX, rigidity, DMagickExceptionInfo());
1964 
1965 		imageRef = ImageRef(image);
1966 	}
1967 
1968 	/**
1969 	 * A convenience method that scales an image proportionally to
1970 	 * twice its size.
1971 	 */
1972 	void magnify()
1973 	{
1974 		MagickCoreImage* image = MagnifyImage(imageRef, DMagickExceptionInfo());
1975 
1976 		imageRef = ImageRef(image);
1977 	}
1978 
1979 	/**
1980 	 * Applies a digital filter that improves the quality of a noisy image.
1981 	 * Each pixel is replaced by the median in a set of neighboring pixels
1982 	 * as defined by radius.
1983 	 * 
1984 	 * Params:
1985 	 *     radius = The filter radius. Values larger than 8 or 9 may take
1986 	 *              longer than you want to wait, and will not have
1987 	 *              significantly better results than much smaller values.
1988 	 */
1989 	void medianFilter(size_t radius = 0)
1990 	{
1991 		static if ( is(typeof(StatisticImage)) )
1992 		{
1993 			MagickCoreImage* image = 
1994 				StatisticImage(imageRef, StatisticType.MedianStatistic, radius, radius, DMagickExceptionInfo());
1995 		}
1996 		else
1997 		{
1998 			MagickCoreImage* image = 
1999 				MedianFilterImage(imageRef, radius, DMagickExceptionInfo());
2000 		}
2001 
2002 		imageRef = ImageRef(image);
2003 	}
2004 
2005 	/**
2006 	 * A convenience method that scales an image proportionally to 
2007 	 * half its size.
2008 	 */
2009 	void minify()
2010 	{
2011 		MagickCoreImage* image = MinifyImage(imageRef, DMagickExceptionInfo());
2012 
2013 		imageRef = ImageRef(image);
2014 	}
2015 
2016 	/**
2017 	 * Modulate percent hue, saturation, and brightness of an image.
2018 	 * Modulation of saturation and brightness is as a ratio of the current
2019 	 * value (1 ( == 100% ) for no change).
2020 	 * 
2021 	 * Params:
2022 	 *     brightness = The percentage of change in the brightness.
2023 	 *     saturation = The percentage of change in the saturation.
2024 	 *     hue        = The percentage of change in the hue.
2025 	 */
2026 	void modulate(double brightness = 1, double saturation = 1, double hue = 1)
2027 	{
2028 		string args = std..string.format("%s,%s,%s", brightness*100, saturation*100, hue*100);
2029 		ModulateImage(imageRef, toStringz(args));
2030 
2031 		DMagickException.throwException(&(imageRef.exception));
2032 	}
2033 
2034 	/**
2035 	 * Simulates motion blur. We convolve the image with a Gaussian operator
2036 	 * of the given radius and standard deviation (sigma). Use a radius of 0
2037 	 * and motion_blur selects a suitable radius for you. Angle gives the
2038 	 * angle of the blurring motion.
2039 	 * 
2040 	 * Params:
2041 	 *     radius  = The radius of the Gaussian operator.
2042 	 *     sigma   = The standard deviation of the Gaussian operator.
2043 	 *               Must be non-0.
2044 	 *     angle   = The angle (in degrees) of the blurring motion.
2045 	 *     channel = The affected channels.
2046 	 */
2047 	void motionBlur(
2048 		double radius = 0,
2049 		double sigma = 1,
2050 		double angle = 0,
2051 		ChannelType channel = ChannelType.DefaultChannels)
2052 	{
2053 		MagickCoreImage* image = 
2054 			MotionBlurImageChannel(imageRef, channel, radius, sigma, angle, DMagickExceptionInfo());
2055 
2056 		imageRef = ImageRef(image);
2057 	}
2058 
2059 	/**
2060 	 * Negates the colors in the reference image.
2061 	 * 
2062 	 * Params:
2063 	 *     grayscale = If true, only negate grayscale pixels
2064 	 *                 within the image.
2065 	 *     channel   = The affected channels.
2066 	 */
2067 	void negate(bool grayscale = false, ChannelType channel = ChannelType.DefaultChannels)
2068 	{
2069 		NegateImageChannel(imageRef, channel, grayscale);
2070 
2071 		DMagickException.throwException(&(imageRef.exception));
2072 	}
2073 
2074 	/**
2075 	 * Enhances the contrast of a color image by adjusting the pixel
2076 	 * color to span the entire range of colors available.
2077 	 */
2078 	void normalize(ChannelType channel = ChannelType.DefaultChannels)
2079 	{
2080 		NormalizeImageChannel(imageRef, channel);
2081 
2082 		DMagickException.throwException(&(imageRef.exception));
2083 	}
2084 
2085 	/**
2086 	 * Applies a special effect filter that simulates an oil painting.
2087 	 * Each pixel is replaced by the most frequent color occurring in a
2088 	 * circular region defined by radius.
2089 	 */
2090 	void oilPaint(double radius = 3)
2091 	{
2092 		MagickCoreImage* image = 
2093 			OilPaintImage(imageRef, radius, DMagickExceptionInfo());
2094 
2095 		imageRef = ImageRef(image);
2096 	}
2097 
2098 	/**
2099 	 * Set or attenuate the opacity channel in the image.
2100 	 * If the image pixels are opaque then they are set to the specified
2101 	 * opacity value, otherwise they are blended with the supplied opacity
2102 	 * value.
2103 	 */
2104 	void opacity(Quantum value)
2105 	{
2106 		SetImageOpacity(imageRef, value);
2107 
2108 		DMagickException.throwException(&(imageRef.exception));
2109 	}
2110 
2111 	/**
2112 	 * Changes all pixels having the target color to the fill color.
2113 	 * 
2114 	 * Params:
2115 	 *     target  = The color to be replaced.
2116 	 *     fill    = The replacement color.
2117 	 *     invert  = If true, the target pixels are all the pixels
2118 	 *               that are not the target color.
2119 	 *     channel = The affected channels.
2120 	 */
2121 	void opaque(Color target, Color fill, bool invert = false, ChannelType channel = ChannelType.CompositeChannels)
2122 	{
2123 		MagickPixelPacket magickTarget;
2124 		MagickPixelPacket magickFill;
2125 		
2126 		GetMagickPixelPacket(imageRef, &magickTarget);
2127 		GetMagickPixelPacket(imageRef, &magickFill);
2128 
2129 		setMagickPixelPacket(&magickTarget, target);
2130 		setMagickPixelPacket(&magickFill, fill);
2131 
2132 		OpaquePaintImageChannel(imageRef, channel, &magickTarget, &magickFill, invert);
2133 		DMagickException.throwException(&(imageRef.exception));
2134 	}
2135 
2136 	/**
2137 	 * Dithers the image to a predefined pattern.
2138 	 * 
2139 	 * Params:
2140 	 *     map = The map argument can be any of the strings
2141 	 *           listed by this command:
2142 	 *           --------------------
2143 	 *           convert -list Threshold
2144 	 *           --------------------
2145 	 * See_Also: $(LINK2 http://www.imagemagick.org/script/command-line-options.php#ordered-dither,
2146 	 *     ImageMagick's -ordered-dither option).
2147 	 */
2148 	void orderedDither(string map)
2149 	{
2150 		OrderedPosterizeImage(imageRef, toStringz(map), DMagickExceptionInfo());
2151 	}
2152 
2153 	/**
2154 	 * Ping is similar to read except only enough of the image is read to
2155 	 * determine the image columns, rows, and filesize. The columns, rows,
2156 	 * and fileSize attributes are valid after invoking ping.
2157 	 * The image data is not valid after calling ping.
2158 	 */
2159 	void ping(string filename)
2160 	{
2161 		options.filename = filename;
2162 
2163 		MagickCoreImage* image = PingImages(options.imageInfo, DMagickExceptionInfo());
2164 
2165 		//Make sure a single image (frame) is read.
2166 		if ( image.next !is null )
2167 		{
2168 			MagickCoreImage* nextImage;
2169 
2170 			nextImage = image.next;
2171 			image.next = null;
2172 			nextImage.previous = null;
2173 
2174 			DestroyImageList(nextImage);
2175 		}
2176 
2177 		imageRef = ImageRef(image);
2178 	}
2179 
2180 	///ditto
2181 	void ping(void[] blob)
2182 	{
2183 		MagickCoreImage* image = 
2184 			PingBlob(options.imageInfo, blob.ptr, blob.length, DMagickExceptionInfo());
2185 
2186 		imageRef = ImageRef(image);
2187 	}
2188 
2189 	/**
2190 	 * Produce an image that looks like a Polaroid® instant picture.
2191 	 * If the image has a "Caption" property, the value is used as a caption.
2192 	 * 
2193 	 * Params:
2194 	 *     angle = The resulting image is rotated by this amount,
2195 	 *             measured in degrees.
2196 	 */
2197 	void polaroid(double angle)
2198 	{
2199 		MagickCoreImage* image = 
2200 			PolaroidImage(imageRef, options.drawInfo, angle, DMagickExceptionInfo());
2201 
2202 		imageRef = ImageRef(image);
2203 	}
2204 
2205 	/**
2206 	 * Reduces the image to a limited number of colors for a "poster" effect.
2207 	 * 
2208 	 * Params:
2209 	 *     levels = Number of color levels allowed in each channel.
2210 	 *              Very low values (2, 3, or 4) have the most
2211 	 *              visible effect.
2212 	 *     dither = If true, dither the image.
2213 	 */
2214 	void posterize(size_t levels = 4, bool dither = false)
2215 	{
2216 		PosterizeImage(imageRef, levels, dither);
2217 		DMagickException.throwException(&(imageRef.exception));
2218 	}
2219 
2220 	/**
2221 	 * Creates an image that contains 9 small versions of the receiver
2222 	 * image. The center image is the unchanged receiver. The other 8
2223 	 * images are variations created by transforming the receiver according
2224 	 * to the specified preview type with varying parameters.
2225 	 *
2226 	 * A preview image is an easy way to "try out" a transformation method.
2227 	 */
2228 	Image preview(PreviewType preview)
2229 	{
2230 		MagickCoreImage* image = 
2231 			PreviewImage(imageRef, preview, DMagickExceptionInfo());
2232 
2233 		return new Image(image, options.clone());
2234 	}
2235 
2236 	/**
2237 	 * Execute the named process module, passing any arguments arguments.
2238 	 * An exception is thrown if the requested process module does not exist,
2239 	 * fails to load, or fails during execution.
2240 	 * 
2241 	 * Params:
2242 	 *     name      = The name of a module.
2243 	 *     arguments = The arguments to pass to the module.
2244 	 */
2245 	void process(string name, string[] arguments)
2246 	{
2247 		MagickCoreImage* image = imageRef;
2248 		const(char)*[] args = new const(char)*[arguments.length];
2249 
2250 		foreach( i, arg; arguments )
2251 			args[i] = toStringz(arg);
2252 
2253 		InvokeDynamicImageFilter(toStringz(name), &image, cast(int)args.length, args.ptr, DMagickExceptionInfo());
2254 
2255 		imageRef = ImageRef(image);
2256 	}
2257 
2258 	/**
2259 	 * Analyzes the colors within a reference image and chooses a fixed
2260 	 * number of colors to represent the image. The goal of the algorithm
2261 	 * is to minimize the difference between the input and output image
2262 	 * while minimizing the processing time.
2263 	 * 
2264 	 * Params:
2265 	 *     measureError = Set to true to calculate quantization errors
2266 	 *                    when quantizing the image. These can be accessed
2267 	 *                    with: normalizedMeanError, normalizedMaxError
2268 	 *                    and meanErrorPerPixel.
2269 	 */
2270 	void quantize(bool measureError = false)
2271 	{
2272 		options.quantizeInfo.measure_error = measureError;
2273 
2274 		QuantizeImage(options.quantizeInfo, imageRef);
2275 		DMagickException.throwException(&(imageRef.exception));
2276 	}
2277 
2278 	/**
2279 	 * Creates a simulated three-dimensional button-like effect by
2280 	 * lightening and darkening the edges of the image.
2281 	 * 
2282 	 * Params:
2283 	 *     width  = The width of the raised edge in pixels.
2284 	 *     height = The height of the raised edge in pixels.
2285 	 *     raised = If true, the image is raised, otherwise lowered.
2286 	 */
2287 	void raise(size_t width, size_t height, bool raised = true)
2288 	{
2289 		RectangleInfo raiseInfo;
2290 
2291 		raiseInfo.width  = width;
2292 		raiseInfo.height = height;
2293 
2294 		RaiseImage(imageRef, &raiseInfo, raised);
2295 		DMagickException.throwException(&(imageRef.exception));
2296 	}
2297 
2298 	/**
2299 	 * Changes the value of individual pixels based on the intensity of
2300 	 * each pixel compared to a random threshold. The result is a
2301 	 * low-contrast, two color image.
2302 	 * 
2303 	 * Params:
2304 	 *     thresholds = A geometry string containing LOWxHIGH thresholds.
2305 	 *                  The string is in the form `XxY'. The Y value may be
2306 	 *                  omitted, in which case it is assigned the value
2307 	 *                  QuantumRange-X. If an % appears in the string then
2308 	 *                  the values are assumed to be percentages of
2309 	 *                  QuantumRange. If the string contains 2x2, 3x3, or
2310 	 *                  4x4, then an ordered dither of order 2, 3, or 4
2311 	 *                  will be performed instead.
2312 	 *     channel    = The affected channels.
2313 	 */
2314 	void randomThreshold(Geometry thresholds, ChannelType channel = ChannelType.DefaultChannels)
2315 	{
2316 		RandomThresholdImageChannel(imageRef, channel, toStringz(thresholds.toString()), DMagickExceptionInfo());
2317 	}
2318 
2319 	/**
2320 	 * Read an Image by reading from the file or
2321 	 * URL specified by filename.
2322 	 */
2323 	void read(string filename)
2324 	{
2325 		options.filename = filename;
2326 
2327 		MagickCoreImage* image = ReadImage(options.imageInfo, DMagickExceptionInfo());
2328 
2329 		//Make sure a single image (frame) is read.
2330 		if ( image.next !is null )
2331 		{
2332 			MagickCoreImage* nextImage;
2333 
2334 			nextImage = image.next;
2335 			image.next = null;
2336 			nextImage.previous = null;
2337 
2338 			DestroyImageList(nextImage);
2339 		}
2340 
2341 		imageRef = ImageRef(image);
2342 	}
2343 
2344 	/**
2345 	 * Read an Image by reading from the file or
2346 	 * URL specified by filename with the specified size.
2347 	 * Usefull for images that don't specify their size.
2348 	 */
2349 	void read(string filename, Geometry size)
2350 	{
2351 		options.size = size;
2352 		read(filename);
2353 	}
2354 
2355 	/**
2356 	 * Reads an image from an in-memory blob.
2357 	 * The Blob size, depth and magick format may also be specified.
2358 	 *
2359 	 * Some image formats require size to be specified,
2360 	 * the default depth Imagemagick uses is the Quantum size
2361 	 * it's compiled with. If it doesn't match the depth of the image
2362 	 * it may need to be specified.
2363 	 *
2364 	 * Imagemagick can usualy detect the image format, when the
2365 	 * format can't be detected a magick format must be specified.
2366 	 */
2367 	void read(void[] blob)
2368 	{
2369 		MagickCoreImage* image = 
2370 			BlobToImage(options.imageInfo, blob.ptr, blob.length, DMagickExceptionInfo());
2371 
2372 		//Make sure a single image (frame) is read.
2373 		if ( image.next !is null )
2374 		{
2375 			MagickCoreImage* nextImage;
2376 
2377 			nextImage = image.next;
2378 			image.next = null;
2379 			nextImage.previous = null;
2380 
2381 			DestroyImageList(nextImage);
2382 		}
2383 
2384 		imageRef = ImageRef(image);
2385 	}
2386 
2387 	///ditto
2388 	void read(void[] blob, Geometry size)
2389 	{
2390 		options.size = size;
2391 
2392 		read(blob);
2393 	}
2394 
2395 	///ditto
2396 	void read(void[] blob, Geometry size, size_t depth)
2397 	{
2398 		options.size = size;
2399 		options.depth = depth;
2400 
2401 		read(blob);
2402 	}
2403 
2404 	///ditto
2405 	void read(void[] blob, Geometry size, size_t depth, string magick)
2406 	{
2407 		options.size = size;
2408 		options.depth = depth;
2409 		options.magick = magick;
2410 		//Also set the filename to the image format
2411 		options.filename = magick ~":";
2412 
2413 		read(blob);
2414 	}
2415 
2416 	///ditto
2417 	void read(void[] blob, Geometry size, string magick)
2418 	{
2419 		options.size = size;
2420 		options.magick = magick;
2421 		//Also set the filename to the image format
2422 		options.filename = magick ~":";
2423 
2424 		read(blob);
2425 	}
2426 
2427 	/**
2428 	 * Reads an image from an array of pixels.
2429 	 *
2430 	 * Params:
2431 	 *     width  =  The number of columns in the image.
2432 	 *     height =  The number of rows in the image.
2433 	 *     map    =  A string describing the expected ordering
2434 	 *               of the pixel array. It can be any combination
2435 	 *               or order of R = red, G = green, B = blue, A = alpha
2436 	 *               , C = cyan, Y = yellow, M = magenta, K = black,
2437 	 *               or I = intensity (for grayscale).
2438 	 *     storage = The pixel Staroage type (CharPixel,
2439 	 *               ShortPixel, IntegerPixel, FloatPixel, or DoublePixel).
2440 	 *     pixels  = The pixel data.
2441 	 */
2442 	void read(T)(size_t width, size_t height, string map, T[] pixels)
2443 	{
2444 		StorageType storage = getStorageType!(T);
2445 
2446 		MagickCoreImage* image = 
2447 			ConstituteImage(width, height, toStringz(map), storage, pixels.ptr, DMagickExceptionInfo());
2448 
2449 		imageRef = ImageRef(image);
2450 	}
2451 
2452 	/**
2453 	 * Smooths the contours of an image while still preserving edge
2454 	 * information. The algorithm works by replacing each pixel with its
2455 	 * neighbor closest in value.
2456 	 * 
2457 	 * Params:
2458 	 *     radius = A neighbor is defined by radius. Use a radius of 0
2459 	 *              and reduceNoise selects a suitable radius for you.
2460 	 */
2461 	void reduceNoise(size_t radius = 0)
2462 	{
2463 		static if ( is(typeof(StatisticImage)) )
2464 		{
2465 			MagickCoreImage* image = 
2466 				StatisticImage(imageRef, StatisticType.NonpeakStatistic, radius, radius, DMagickExceptionInfo());
2467 		}
2468 		else
2469 		{
2470 			MagickCoreImage* image = 
2471 				ReduceNoiseImage(imageRef, radius, DMagickExceptionInfo());
2472 		}
2473 
2474 		imageRef = ImageRef(image);
2475 	}
2476 
2477 	/**
2478 	 * Reduce the number of colors in img to the colors used by reference.
2479 	 * If a dither method is set then the given colors are dithered over
2480 	 * the image as necessary, otherwise the closest color
2481 	 * (in RGB colorspace) is selected to replace that pixel in the image.
2482 	 */
2483 	void remap(Image reference)
2484 	{
2485 		RemapImage(options.quantizeInfo, imageRef, reference.imageRef);
2486 
2487 		DMagickException.throwException(&(imageRef.exception));
2488 	}
2489 
2490 	/**
2491 	 * Resize image in terms of its pixel size, so that when displayed at
2492 	 * the given resolution it will be the same size in terms of real world
2493 	 * units as the original image at the original resolution.
2494 	 * 
2495 	 * Params:
2496 	 *     xResolution = the target horizontal resolution
2497 	 *     yResolution = the target vertical resolution
2498 	 *     filter      = The filter to use when resizing.
2499 	 *     blur        = Values > 1 increase the blurriness.
2500 	 *                   Values < 1 increase the sharpness.
2501 	 */
2502 	void resample(
2503 		double xResolution,
2504 		double yResolution,
2505 		FilterTypes filter = FilterTypes.LanczosFilter,
2506 		double blur = 1)
2507 	{
2508 		MagickCoreImage* image = 
2509 			ResampleImage(imageRef, xResolution, yResolution, filter, blur, DMagickExceptionInfo());
2510 
2511 		imageRef = ImageRef(image);
2512 	}
2513 
2514 	/**
2515 	 * scales an image to the desired dimensions, using the given filter.
2516 	 * 
2517 	 * Params:
2518 	 *     size   = The desired width and height.
2519 	 *     filter = The filter to use when resizing.
2520 	 *     blur   = Values > 1 increase the blurriness.
2521 	 *              Values < 1 increase the sharpness.
2522 	 */
2523 	void resize(Geometry size, FilterTypes filter = FilterTypes.LanczosFilter, double blur = 1)
2524 	{
2525 		size = size.toAbsolute(columns, rows);
2526 
2527 		MagickCoreImage* image = 
2528 			ResizeImage(imageRef, size.width, size.height, filter, blur, DMagickExceptionInfo());
2529 
2530 		imageRef = ImageRef(image);
2531 	}
2532 
2533 	/**
2534 	 * Offsets an image as defined by xOffset and yOffset.
2535 	 */
2536 	void roll(ssize_t xOffset, ssize_t yOffset)
2537 	{
2538 		MagickCoreImage* image = 
2539 			RollImage(imageRef, xOffset, yOffset, DMagickExceptionInfo());
2540 
2541 		imageRef = ImageRef(image);
2542 	}
2543 
2544 	/**
2545 	 * Rotate the image by specified number of degrees. Rotated images are
2546 	 * usually larger than the originals and have 'empty' triangular corners.
2547 	 * Empty triangles left over from shearing the image are filled with the
2548 	 * background color defined by the 'backgroundColor' property
2549 	 * of the image.
2550 	 * 
2551 	 * Params:
2552 	 *     degrees = The number of degrees to rotate the image. Positive
2553 	 *               angles rotate counter-clockwise (right-hand rule),
2554 	 *               while negative angles rotate clockwise.
2555 	 */
2556 	void rotate(double degrees)
2557 	{
2558 		MagickCoreImage* image = 
2559 			RotateImage(imageRef, degrees, DMagickExceptionInfo());
2560 
2561 		imageRef = ImageRef(image);
2562 	}
2563 
2564 	/**
2565 	 * Applies a rotational blur to the image.
2566 	 * 
2567 	 * Params:
2568 	 *     angle   = The angle of the rotational blur, in degrees.
2569 	 *     channel = If no channels are specified, blurs all the channels.
2570 	 */
2571 	void rotationalBlur(double angle, ChannelType channel = ChannelType.DefaultChannels)
2572 	{
2573 		static if ( is(typeof(RotationalBlurImage)) )
2574 		{
2575 			MagickCoreImage* image = 
2576 				RotationalBlurImageChannel(imageRef, channel, angle, DMagickExceptionInfo());
2577 		}
2578 		else
2579 		{
2580 			MagickCoreImage* image = 
2581 				RadialBlurImageChannel(imageRef, channel, angle, DMagickExceptionInfo());
2582 		}
2583 
2584 		imageRef = ImageRef(image);
2585 	}
2586 	/** ditto */
2587 	alias rotationalBlur radialBlur;
2588 
2589 	/**
2590 	 * scales an image to the desired dimensions with pixel sampling.
2591 	 * Unlike other scaling methods, this method does not introduce any
2592 	 * additional color into the scaled image.
2593 	 */
2594 	void sample(Geometry size)
2595 	{
2596 		size = size.toAbsolute(columns, rows);
2597 
2598 		MagickCoreImage* image = 
2599 			SampleImage(imageRef, size.width, size.height, DMagickExceptionInfo());
2600 
2601 		imageRef = ImageRef(image);
2602 	}
2603 
2604 	/**
2605 	 * Resize image by using simple ratio algorithm.
2606 	 */
2607 	void scale(Geometry size)
2608 	{
2609 		size = size.toAbsolute(columns, rows);
2610 
2611 		MagickCoreImage* image = 
2612 			ScaleImage(imageRef, size.width, size.height, DMagickExceptionInfo());
2613 
2614 		imageRef = ImageRef(image);
2615 	}
2616 
2617 	/**
2618 	 * Segments an image by analyzing the histograms of the color
2619 	 * components and identifying units that are homogeneous with the
2620 	 * fuzzy c-means technique. Also uses quantizeColorSpace and
2621 	 * verbose image properties. 
2622 	 * 
2623 	 * Params:
2624 	 *     clusterThreshold   = 
2625 	 *          The number of pixels in each cluster must exceed the
2626 	 *          the cluster threshold to be considered valid.
2627 	 *     smoothingThreshold = 
2628 	 *          The smoothing threshold eliminates noise in the second
2629 	 *          derivative of the histogram. As the value is increased,
2630 	 *          you can expect a smoother second derivative.
2631 	 */
2632 	void segment(double clusterThreshold = 1, double smoothingThreshold = 1.5)
2633 	{
2634 		SegmentImage(imageRef, options.quantizeColorSpace, options.verbose, clusterThreshold, smoothingThreshold);
2635 		DMagickException.throwException(&(imageRef.exception));
2636 	}
2637 
2638 	/**
2639 	 * Selectively blur pixels within a contrast threshold.
2640 	 * 
2641 	 * Params:
2642 	 *     radius    = The radius of the Gaussian in pixels,
2643 	 *                 not counting the center pixel.
2644 	 *     sigma     = The standard deviation of the Laplacian, in pixels.
2645 	 *     threshold = Threshold level represented as a percentage
2646 	 *                 of the quantum range.
2647 	 *     channel   = The channels to blur.
2648 	 */
2649 	void selectiveBlur(
2650 		double radius,
2651 		double sigma,
2652 		double threshold,
2653 		ChannelType channel = ChannelType.DefaultChannels)
2654 	{
2655 		MagickCoreImage* image = 
2656 			SelectiveBlurImageChannel(imageRef, channel, radius, sigma, threshold, DMagickExceptionInfo());
2657 
2658 		imageRef = ImageRef(image);
2659 	}
2660 
2661 	/**
2662 	 * applies a special effect to the image, similar to the effect achieved
2663 	 * in a photo darkroom by sepia toning. A threshold of 80% is a good
2664 	 * starting point for a reasonable tone.
2665 	 * 
2666 	 * Params:
2667 	 *     threshold = Threshold ranges from 0 to QuantumRange and is
2668 	 *                 a measure of the extent of the sepia toning.
2669 	 *                 A value lower than 1 is treated as a percentage.
2670 	 */
2671 	void sepiatone(double threshold = QuantumRange)
2672 	{
2673 		if ( threshold < 1 )
2674 			threshold *= QuantumRange;
2675 
2676 		MagickCoreImage* image = 
2677 			SepiaToneImage(imageRef, threshold, DMagickExceptionInfo());
2678 
2679 		imageRef = ImageRef(image);
2680 	}
2681 
2682 	/**
2683 	 * shines a distant light on an image to create a three-dimensional
2684 	 * effect. You control the positioning of the light with azimuth and
2685 	 * elevation.
2686 	 * 
2687 	 * Params:
2688 	 *     azimuth   = The amount of degrees off the X axis.
2689 	 *     elevation = The amount of pixels above the Z axis.
2690 	 *     shading   = If true, shade shades the intensity of each pixel.
2691 	 */
2692 	void shade(double azimuth = 30, double elevation = 30, bool shading = false)
2693 	{
2694 		MagickCoreImage* image = 
2695 			ShadeImage(imageRef, shading, azimuth, elevation, DMagickExceptionInfo());
2696 
2697 		imageRef = ImageRef(image);
2698 	}
2699 
2700 	/**
2701 	 * Simulates a shadow from the specified image and returns it.
2702 	 * This method only works when the image has opaque parts and
2703 	 * transparent parts. Note that the resulting image is just the shadow.
2704 	 * 
2705 	 * Params:
2706 	 *     xOffset = The shadow x offset.
2707 	 *     yOffset = The shadow y offset.
2708 	 *     sigma   = The standard deviation of the Gaussian operator used
2709 	 *               to produce the shadow. The higher the number, the
2710 	 *               "blurrier" the shadow, but the longer it takes to
2711 	 *               produce the shadow.
2712 	 *     opacity = The percent opacity of the shadow.
2713 	 *               A number between 0.1 and 1.0
2714 	 * Returns: The shadows for this image.
2715 	 */
2716 	Image shadowImage(ssize_t xOffset, ssize_t yOffset, double sigma = 4, double opacity = 1)
2717 	{
2718 		MagickCoreImage* image = 
2719 			ShadowImage(imageRef, opacity, sigma, xOffset, yOffset, DMagickExceptionInfo());
2720 
2721 		return new Image(image);
2722 	}
2723 
2724 	/**
2725 	 * Sharpens an image. We convolve the image with a Gaussian operator
2726 	 * of the given radius and standard deviation (sigma). For reasonable
2727 	 * results, radius should be larger than sigma. Use a radius of 0 and
2728 	 * sharpen selects a suitable radius for you.
2729 	 * 
2730 	 * Params:
2731 	 *     radius  = The radius of the Gaussian in pixels,
2732 	 *               not counting the center pixel.
2733 	 *     sigma   = The standard deviation of the Laplacian, in pixels.
2734 	 *     channel = If no channels are specified, sharpens all the channels.
2735 	 */
2736 	void sharpen(double radius = 0, double sigma = 1, ChannelType channel = ChannelType.DefaultChannels)
2737 	{
2738 		MagickCoreImage* image = 
2739 			SharpenImageChannel(imageRef, channel, radius, sigma, DMagickExceptionInfo());
2740 
2741 		imageRef = ImageRef(image);
2742 	}
2743 
2744 	/**
2745 	 * Removes pixels from the edges of the image,
2746 	 * leaving the center rectangle.
2747 	 * 
2748 	 * Params:
2749 	 *     geometry = The region of the image to crop.
2750 	 */
2751 	void shave(Geometry geometry)
2752 	{
2753 		RectangleInfo rectangle = geometry.rectangleInfo;
2754 		MagickCoreImage* image = ShaveImage(imageRef, &rectangle, DMagickExceptionInfo());
2755 
2756 		imageRef = ImageRef(image);
2757 	}
2758 
2759 	/**
2760 	 * Shearing slides one edge of an image along the X or Y axis, creating
2761 	 * a parallelogram. An X direction shear slides an edge along the X axis,
2762 	 * while a Y direction shear slides an edge along the Y axis. The amount
2763 	 * of the shear is controlled by a shear angle. For X direction shears,
2764 	 * xShearAngle is measured relative to the Y axis, and similarly, for Y
2765 	 * direction shears yShearAngle is measured relative to the X axis.
2766 	 * Empty triangles left over from shearing the image are filled with
2767 	 * the background color.
2768 	 */
2769 	void shear(double xShearAngle, double yShearAngle)
2770 	{
2771 		MagickCoreImage* image = 
2772 			ShearImage(imageRef, xShearAngle, yShearAngle, DMagickExceptionInfo());
2773 
2774 		imageRef = ImageRef(image);
2775 	}
2776 
2777 	/**
2778 	 * Adjusts the contrast of an image channel with a non-linear sigmoidal
2779 	 * contrast algorithm. Increases the contrast of the image using a
2780 	 * sigmoidal transfer function without saturating highlights or shadows.
2781 	 * 
2782 	 * Params:
2783 	 *     contrast = indicates how much to increase the contrast
2784 	 *                (0 is none; 3 is typical; 20 is pushing it)
2785 	 *     midpoint = indicates where midtones fall in the resultant
2786 	 *                image (0 is white; 50% is middle-gray; 100% is black).
2787 	 *                Specify an apsulute number of pixels or an
2788 	 *                percentage by passing a value between 1 and 0
2789 	 *     sharpen  = Increase or decrease image contrast.
2790 	 *     channel  = The channels to adjust.
2791 	 */
2792 	void sigmoidalContrast(
2793 		double contrast = 3,
2794 		double midpoint = 50,
2795 		bool sharpen = false,
2796 		ChannelType channel = ChannelType.DefaultChannels)
2797 	{
2798 		if ( midpoint < 1 )
2799 			midpoint *= QuantumRange;
2800 
2801 		SigmoidalContrastImageChannel(imageRef, channel, sharpen, contrast, midpoint);
2802 		DMagickException.throwException(&(imageRef.exception));
2803 	}
2804 
2805 	/**
2806 	 * Simulates a pencil sketch. For best results start with
2807 	 * a grayscale image.
2808 	 * 
2809 	 * Params:
2810 	 *     radius = The radius of the Gaussian, in pixels, not counting
2811 	 *              the center pixel.
2812 	 *     sigma  = The standard deviation of the Gaussian, in pixels.
2813 	 *     angle  = The angle toward which the image is sketched.
2814 	 */
2815 	void sketch(double radius = 0, double sigma = 1, double angle = 0)
2816 	{
2817 		MagickCoreImage* image = 
2818 			SketchImage(imageRef, radius, sigma, angle, DMagickExceptionInfo());
2819 
2820 		imageRef = ImageRef(image);
2821 	}
2822 
2823 	/**
2824 	 * Applies a special effect to the image similar to the effect achieved
2825 	 * in a photo darkroom by selectively exposing areas of photo sensitive
2826 	 * paper to light.
2827 	 * 
2828 	 * Params:
2829 	 *     threshold = The extent of the solarization.
2830 	 *     channel   = The channels to adjust. Anything other than
2831 	 *                 ChannelType.DefaultChannels requires ImageMagick 6.8.0
2832 	 *                 ot higher.
2833 	 */
2834 	void solarize(Quantum threshold, ChannelType channel = ChannelType.DefaultChannels)
2835 	{
2836 		static if ( is(typeof(SolarizeImageChannel)) )
2837 		{
2838 			SolarizeImageChannel(imageRef, channel, threshold, DMagickExceptionInfo());
2839 		}
2840 		else
2841 		{
2842 			SolarizeImage(imageRef, threshold);
2843 			DMagickException.throwException(&(imageRef.exception));
2844 		}
2845 	}
2846 
2847 	/**
2848 	 * Fills the image with the specified color or colors, starting at
2849 	 * the x,y coordinates associated with the color and using the specified
2850 	 * interpolation method.
2851 	 * 
2852 	 * Params:
2853 	 *     method = The method to fill in the gradient between the
2854 	 *              control points.
2855 	 *     args   = A series of control points, and there Color.
2856 	 * 
2857 	 * See_Also: $(LINK2 http://www.imagemagick.org/Usage/canvas/#sparse-color,
2858 	 *     Sparse Points of Color) at Examples of ImageMagick Usage.
2859 	 */
2860 	void sparseColor(SparseColorMethod method, Tuple!(size_t, "x", size_t, "y", Color, "color")[] args ...)
2861 	{
2862 		double[] argv = new double[args.length * 6];
2863 
2864 		foreach( i, arg; args )
2865 		{
2866 			double[] values = argv[i*6 .. i*6+6];
2867 
2868 			values[0] = arg.x;
2869 			values[1] = arg.y;
2870 
2871 			values[2] = arg.color.redQuantum / QuantumRange;
2872 			values[3] = arg.color.greenQuantum / QuantumRange;
2873 			values[4] = arg.color.blueQuantum / QuantumRange;
2874 			values[5] = arg.color.opacityQuantum / QuantumRange;
2875 		}
2876 
2877 		MagickCoreImage* image = 
2878 			SparseColorImage(imageRef,
2879 				ChannelType.DefaultChannels,
2880 				method,   argv.length,
2881 				argv.ptr, DMagickExceptionInfo());
2882 
2883 		imageRef = ImageRef(image);
2884 	}
2885 
2886 	/**
2887 	 * Splice the background color into the image as defined by the geometry.
2888 	 * This method is the opposite of chop.
2889 	 */
2890 	void splice(Geometry geometry)
2891 	{
2892 		RectangleInfo rectangle = geometry.rectangleInfo;
2893 		MagickCoreImage* image = SpliceImage(imageRef, &rectangle, DMagickExceptionInfo());
2894 
2895 		imageRef = ImageRef(image);
2896 	}
2897 
2898 	/**
2899 	 * Randomly displaces each pixel in a block defined by the
2900 	 * radius parameter.
2901 	 */
2902 	void spread(double radius = 3)
2903 	{
2904 		MagickCoreImage* image =
2905 			SpreadImage(imageRef, radius, DMagickExceptionInfo());
2906 
2907 		imageRef = ImageRef(image);
2908 	}
2909 
2910 	/**
2911 	 * Makes each pixel the min / max / median / mode / etc. of the
2912 	 * neighborhood of the specified width and height.
2913 	 * 
2914 	 * Params:
2915 	 *     type   = The type pf statistic to apply.
2916 	 *     width  = The width of the pixel neighborhood.
2917 	 *     height = The height of the pixel neighborhood.
2918 	 */
2919 	void statistic()(StatisticType type, size_t width, size_t height)
2920 	{
2921 		static if ( is(typeof(StatisticImage)) )
2922 		{
2923 			MagickCoreImage* image =
2924 				StatisticImage(imageRef, type, width, height, DMagickExceptionInfo());
2925 
2926 			imageRef = ImageRef(image);
2927 		}
2928 		else
2929 		{
2930 			static assert(0, "dmagick.Image.Image.statistic requires MagickCore version >= 6.6.9");
2931 		}
2932 	}
2933 
2934 	/**
2935 	 * Hides a digital watermark in the receiver. You can retrieve the
2936 	 * watermark by reading the file with the stegano: prefix, thereby
2937 	 * proving the authenticity of the file.
2938 	 * 
2939 	 * The watermarked image must be saved in a lossless RGB format such
2940 	 * as MIFF, or PNG. You cannot save a watermarked image in a lossy
2941 	 * format such as JPEG or a pseudocolor format such as GIF. Once
2942 	 * written, the file must not be modified or processed in any way.
2943 	 * 
2944 	 * Params:
2945 	 *     watermark = An image or imagelist to be used as the watermark.
2946 	 *                 The watermark must be grayscale and should be
2947 	 *                 substantially smaller than the receiver. The recovery
2948 	 *                 time is proportional to the size of the watermark.
2949 	 *     offset    = The starting position within the receiver at which
2950 	 *                 the watermark will be hidden. When you retrieve the
2951 	 *                 watermark from the file, you must supply this value,
2952 	 *                 along with the width and height of the watermark, in
2953 	 *                 the size optional parameter to the read method.
2954 	 */
2955 	void stegano(Image watermark, ssize_t offset)
2956 	{
2957 		imageRef.offset = offset;
2958 
2959 		MagickCoreImage* image =
2960 			SteganoImage(imageRef, watermark.imageRef, DMagickExceptionInfo());
2961 
2962 		imageRef = ImageRef(image);
2963 	}
2964 
2965 	/**
2966 	 * Combines two images and produces a single image that is the composite
2967 	 * of a left and right image of a stereo pair. Special red-green stereo
2968 	 * glasses are required to view this effect.
2969 	 */
2970 	void stereo(Image rightImage)
2971 	{
2972 		MagickCoreImage* image =
2973 			StereoImage(imageRef, rightImage.imageRef, DMagickExceptionInfo());
2974 
2975 		imageRef = ImageRef(image);
2976 	}
2977 
2978 	/**
2979 	 * Strips an image of all profiles and comments.
2980 	 */
2981 	void strip()
2982 	{
2983 		StripImage(imageRef);
2984 		DMagickException.throwException(&(imageRef.exception));
2985 	}
2986 
2987 	/**
2988 	 * synchronizes image properties with the image profiles. Currently
2989 	 * we only support updating the EXIF resolution and orientation.
2990 	 */
2991 	void syncProfiles()
2992 	{
2993 		SyncImageProfiles(imageRef);
2994 		DMagickException.throwException(&(imageRef.exception));
2995 	}
2996 
2997 	/**
2998 	 * Swirls the pixels about the center of the image, where degrees
2999 	 * indicates the sweep of the arc through which each pixel is moved.
3000 	 * You get a more dramatic effect as the degrees move from 1 to 360.
3001 	 */
3002 	void swirl(double degrees)
3003 	{
3004 		MagickCoreImage* image =
3005 			SwirlImage(imageRef, degrees, DMagickExceptionInfo());
3006 
3007 		imageRef = ImageRef(image);
3008 	}
3009 
3010 	/**
3011 	 * Changes the value of individual pixels based on the intensity of
3012 	 * each pixel compared to threshold. The result is a high-contrast,
3013 	 * two color image.
3014 	 * 
3015 	 * See_Also: $(XREF Image, bilevel).
3016 	 */
3017 	//TODO: deprecated ?
3018 	void threshold(Quantum value)
3019 	{
3020 		bilevel(value);
3021 	}
3022 
3023 	/**
3024 	 * changes the size of an image to the given dimensions and removes
3025 	 * any associated profiles. The goal is to produce small low cost
3026 	 * thumbnail images suited for display on the Web.
3027 	 */
3028 	void thumbnail(Geometry size)
3029 	{
3030 		size = size.toAbsolute(columns, rows);
3031 
3032 		MagickCoreImage* image =
3033 			ThumbnailImage(imageRef, size.width, size.height, DMagickExceptionInfo());
3034 
3035 		imageRef = ImageRef(image);
3036 	}
3037 
3038 	/**
3039 	 * Creates a Binary Large OBject, a direct-to-memory
3040 	 * version of the image.
3041 	 *
3042 	 * if an image format is selected which is capable of supporting
3043 	 * fewer colors than the original image or quantization has been
3044 	 * requested, the original image will be quantized to fewer colors.
3045 	 * Use a copy of the original if this is a problem.
3046 	 * 
3047 	 * Params:
3048 	 *     magick = specifies the image format to write.
3049 	 *     depth  = specifies the image depth.
3050 	 */
3051 	void[] toBlob(string magick = null, size_t depth = 0)
3052 	{
3053 		size_t length;
3054 		ExceptionInfo* exceptionInfo = AcquireExceptionInfo();
3055 
3056 		if ( magick !is null )
3057 			this.magick = magick;
3058 		if ( depth != 0 )
3059 			this.depth = depth;
3060 
3061 		string originalFilename = filename;
3062 		filename = this.magick ~ ":";
3063 		scope(exit) filename = originalFilename;
3064 
3065 		void* blob = ImageToBlob(options.imageInfo, imageRef, &length, exceptionInfo);
3066 
3067 		DMagickException.throwException(exceptionInfo);
3068 
3069 		void[] dBlob = blob[0 .. length].dup;
3070 		RelinquishMagickMemory(blob);
3071 
3072 		return dBlob;	
3073 	}
3074 
3075 	unittest
3076 	{
3077 		Image example = new Image(Geometry(100, 100), new Color("green"));
3078 		example.toBlob("jpg");
3079 	}
3080 
3081 	/**
3082 	 * Changes the opacity value of all the pixels that match color to
3083 	 * the value specified by opacity. By default the pixel must match
3084 	 * exactly, but you can specify a tolerance level by setting the fuzz
3085 	 * attribute on the image.
3086 	 * 
3087 	 * Params:
3088 	 *     target  = The target color.
3089 	 *     opacity = The desired opacity.
3090 	 *     invert  = If true, all pixels outside the range
3091 	 *               are set to opacity.
3092 	 */
3093 	void transparent(Color color, Quantum opacity = TransparentOpacity, bool invert = false)
3094 	{
3095 		MagickPixelPacket target;
3096 
3097 		GetMagickPixelPacket(imageRef, &target);
3098 		setMagickPixelPacket(&target, color);
3099 
3100 		TransparentPaintImage(imageRef, &target, opacity, invert);
3101 		DMagickException.throwException(&(imageRef.exception));
3102 	}
3103 
3104 	/**
3105 	 * Changes the opacity value associated with any pixel between low and
3106 	 * high to the value defined by opacity.
3107 	 * 
3108 	 * As there is one fuzz value for the all the channels, the transparent
3109 	 * method is not suitable for the operations like chroma, where the
3110 	 * tolerance for similarity of two color components (RGB) can be
3111 	 * different, Thus we define this method take two target pixels (one
3112 	 * low and one high) and all the pixels of an image which are lying
3113 	 * between these two pixels are made transparent.
3114 	 * 
3115 	 * Params:
3116 	 *     low     = The low end of the pixel range.
3117 	 *     high    = The high end of the pixel range.
3118 	 *     opacity = The desired opacity.
3119 	 *     invert  = If true, all pixels outside the range
3120 	 *               are set to opacity.
3121 	 */
3122 	void transparentChroma(Color low, Color high, Quantum opacity = TransparentOpacity, bool invert = false)
3123 	{
3124 		MagickPixelPacket lowTarget;
3125 		MagickPixelPacket highTarget;
3126 
3127 		GetMagickPixelPacket(imageRef, &lowTarget);
3128 		setMagickPixelPacket(&lowTarget, low);
3129 
3130 		GetMagickPixelPacket(imageRef, &highTarget);
3131 		setMagickPixelPacket(&highTarget, high);
3132 
3133 		TransparentPaintImageChroma(imageRef, &lowTarget, &highTarget, opacity, invert);
3134 		DMagickException.throwException(&(imageRef.exception));
3135 	}
3136 
3137 	/**
3138 	 * Creates a horizontal mirror image by reflecting the pixels around
3139 	 * the central y-axis while rotating them by 90 degrees.
3140 	 */
3141 	void transpose()
3142 	{
3143 		MagickCoreImage* image = TransposeImage(imageRef, DMagickExceptionInfo());
3144 
3145 		imageRef = ImageRef(image);
3146 	}
3147 
3148 	/**
3149 	 * Creates a vertical mirror image by reflecting the pixels around
3150 	 * the central x-axis while rotating them by 270 degrees
3151 	 */
3152 	void transverse()
3153 	{
3154 		MagickCoreImage* image = TransverseImage(imageRef, DMagickExceptionInfo());
3155 
3156 		imageRef = ImageRef(image);
3157 	}
3158 
3159 	/**
3160 	 * Removes the edges that are exactly the same color as the corner
3161 	 * pixels. Use the fuzz property to make trim remove edges that are
3162 	 * nearly the same color as the corner pixels.
3163 	 */
3164 	void trim()
3165 	{
3166 		MagickCoreImage* image = TrimImage(imageRef, DMagickExceptionInfo());
3167 
3168 		imageRef = ImageRef(image);
3169 	}
3170 
3171 	/**
3172 	 * Constructs a new image with one pixel for each unique color in the
3173 	 * image. The new image has 1 row. The row has 1 column for each unique
3174 	 * pixel in the image.
3175 	 */
3176 	Image uniqueColors()
3177 	{
3178 		MagickCoreImage* image = UniqueImageColors(imageRef, DMagickExceptionInfo());
3179 
3180 		return new Image(image);
3181 	}
3182 
3183 	/**
3184 	 * Sharpens an image. We convolve the image with a Gaussian operator
3185 	 * of the given radius and standard deviation (sigma). For reasonable
3186 	 * results, radius should be larger than sigma. Use a radius of 0 and
3187 	 * unsharpMask selects a suitable radius for you.
3188 	 * 
3189 	 * Params:
3190 	 *     radius    = The radius of the Gaussian operator.
3191 	 *     sigma     = The standard deviation of the Gaussian operator.
3192 	 *     amount    = The percentage of the blurred image to be added
3193 	 *                 to the receiver, specified as a fraction between 0
3194 	 *                 and 1.0. A good starting value is 1.0
3195 	 *     threshold = The threshold needed to apply the amount, specified
3196 	 *                 as a fraction between 0 and 1.0.
3197 	 *     channel   = The channels to sharpen.
3198 	 */
3199 	void unsharpMask(
3200 		double radius = 0,
3201 		double sigma = 1,
3202 		double amount = 1,
3203 		double threshold = 0.05,
3204 		ChannelType channel = ChannelType.DefaultChannels)
3205 	{
3206 		MagickCoreImage* image =
3207 			UnsharpMaskImageChannel(imageRef, channel, radius, sigma, amount, threshold, DMagickExceptionInfo());
3208 
3209 		imageRef = ImageRef(image);
3210 	}
3211 
3212 	/**
3213 	 * Get a view into the image. The ImageView can be used to modify
3214 	 * individual pixels of the image.
3215 	 * 
3216 	 * Params:
3217 	 *     area = The area accessible through the view. 
3218 	 */
3219 	dmagick.ImageView.ImageView view(Geometry area = Geometry.init )
3220 	{
3221 		if ( area == Geometry.init )
3222 		{
3223 			area.width = columns;
3224 			area.height = rows;
3225 		}
3226 
3227 		return new dmagick.ImageView.ImageView(this, area);
3228 	}
3229 
3230 	/**
3231 	 * Gradually shades the edges of the image by transforming the pixels
3232 	 * into the background color.
3233 	 * 
3234 	 * Larger values of sigma increase the blurring at the expense of
3235 	 * increased execution time. In general, radius should be larger than
3236 	 * sigma, although if radius is 0 then ImageMagick will choose a suitable
3237 	 * value. Sigma must be non-zero. Choose a very small value for sigma to
3238 	 * produce a "hard" edge.
3239 3240 	 * Params:
3241 	 *     xOffset = Influences the amount of background color in the
3242 	 *               horizontal dimension.
3243 	 *     yOffset = Influences the amount of background color in the
3244 	 *               vertical dimension.
3245 	 *     radius  = The radius of the pixel neighborhood.
3246 	 *     sigma   = The standard deviation of the Gaussian, in pixels.
3247 	 */
3248 	void vignette(ssize_t xOffset, ssize_t yOffset, double radius = 0, double sigma = 10)
3249 	{
3250 		MagickCoreImage* image =
3251 			VignetteImage(imageRef, radius, sigma, xOffset, yOffset, DMagickExceptionInfo());
3252 
3253 		imageRef = ImageRef(image);
3254 	}
3255 
3256 	/**
3257 	 * Creates a "ripple" effect in the image by shifting the pixels
3258 	 * vertically along a sine wave whose amplitude and wavelength is
3259 	 * specified by the given parameters.Creates a "ripple" effect in the
3260 	 * image by shifting the pixels vertically along a sine wave whose
3261 	 * amplitude and wavelength is specified by the given parameters.
3262 	 */
3263 	void wave(double amplitude = 25, double wavelength = 150)
3264 	{
3265 		MagickCoreImage* image =
3266 			WaveImage(imageRef, amplitude, wavelength, DMagickExceptionInfo());
3267 
3268 		imageRef = ImageRef(image);
3269 	}
3270 
3271 	/**
3272 	 * Forces all pixels above the threshold into white while leaving
3273 	 * all pixels below the threshold unchanged.
3274 	 * 
3275 	 * Params:
3276 	 *     threshold = The threshold value for red green and blue.
3277 	 *     channel   = One or more channels to adjust.
3278 	 */
3279 	void whiteThreshold(Quantum threshold, ChannelType channel = ChannelType.DefaultChannels)
3280 	{
3281 		whiteThreshold(threshold, threshold, threshold, 0, channel);
3282 	}
3283 
3284 	///ditto
3285 	void whiteThreshold(
3286 		Quantum red,
3287 		Quantum green,
3288 		Quantum blue,
3289 		Quantum opacity = 0,
3290 		ChannelType channel = ChannelType.DefaultChannels)
3291 	{
3292 		string thresholds = std..string.format("%s,%s,%s,%s", red, green, blue, opacity);
3293 
3294 		WhiteThresholdImageChannel(
3295 			imageRef, channel, toStringz(thresholds), DMagickExceptionInfo()
3296 		);
3297 	}
3298 
3299 	/**
3300 	 * Writes the image to the specified file. ImageMagick
3301 	 * determines image format from the prefix or extension.
3302 	 * 
3303 	 * if an image format is selected which is capable of supporting
3304 	 * fewer colors than the original image or quantization has been
3305 	 * requested, the original image will be quantized to fewer colors.
3306 	 * Use a copy of the original if this is a problem.
3307 	 */
3308 	void write(string filename)
3309 	{
3310 		this.filename = filename;
3311 		WriteImage(options.imageInfo, imageRef);
3312 
3313 		DMagickException.throwException(&(imageRef.exception));
3314 	}
3315 
3316 	/**
3317 	 * Set a flag to indicate whether or not to use alpha channel data.
3318 	 */
3319 	void alpha(AlphaChannelType type)
3320 	{
3321 		SetImageAlphaChannel(imageRef, type);
3322 	}
3323 	///ditto
3324 	bool alpha() const
3325 	{
3326 		return GetImageAlphaChannel(imageRef) != 0;
3327 	}
3328 
3329 	/**
3330 	 * Number time which must expire before displaying the
3331 	 * next image in an animated sequence.
3332 	 */
3333 	void animationDelay(Duration delay)
3334 	{
3335 		imageRef.delay = cast(size_t)(delay.total!"msecs"() * imageRef.ticks_per_second) / 1000;
3336 	}
3337 	///ditto
3338 	Duration animationDelay() const
3339 	{
3340 		return dur!"msecs"((imageRef.delay * 1000) / imageRef.ticks_per_second);
3341 	}
3342 
3343 	/**
3344 	 * Number of iterations to loop an animation.
3345 	 */
3346 	void animationIterations(size_t iterations)
3347 	{
3348 		imageRef.iterations = iterations;
3349 	}
3350 	///ditto
3351 	size_t animationIterations() const
3352 	{
3353 		return imageRef.iterations;
3354 	}
3355 
3356 	/**
3357 	 * Set the image background color. The default is "white".
3358 	 */
3359 	void backgroundColor(string color)
3360 	{
3361 		backgroundColor = new Color(color);
3362 	}
3363 	///ditto	
3364 	void backgroundColor(Color color)
3365 	{
3366 		options.backgroundColor(color);
3367 
3368 		imageRef.background_color = color.pixelPacket;
3369 	}
3370 	///ditto
3371 	Color backgroundColor() const
3372 	{
3373 		return options.backgroundColor;
3374 	}
3375 
3376 	/**
3377 	 * Set the image border color. The default is "#dfdfdf".
3378 	 */
3379 	void borderColor(string color)
3380 	{
3381 		borderColor = new Color(color);
3382 	}
3383 	///ditto
3384 	void borderColor(Color color)
3385 	{
3386 		options.borderColor = color;
3387 
3388 		imageRef.border_color = color.pixelPacket;
3389 	}
3390 	///ditto
3391 	Color borderColor() const
3392 	{
3393 		return options.borderColor;
3394 	}
3395 
3396 	/**
3397 	 * Return smallest bounding box enclosing non-border pixels.
3398 	 * The current fuzz value is used when discriminating between pixels.
3399 	 */
3400 	Geometry boundingBox() const
3401 	{
3402 		RectangleInfo box = GetImageBoundingBox(imageRef, DMagickExceptionInfo());
3403 
3404 		return Geometry(box);
3405 	}
3406 
3407 	/**
3408 	 * Pixel cache threshold in megabytes. Once this threshold is exceeded,
3409 	 * all subsequent pixels cache operations are to/from disk.
3410 	 * This is a static method and the attribute it sets is shared
3411 	 * by all Image objects
3412 	 */
3413 	static void cacheThreshold(size_t threshold)
3414 	{
3415 		SetMagickResourceLimit(ResourceType.MemoryResource, threshold);
3416 	}
3417 
3418 	/**
3419 	 * returns true if any pixel in the image has been altered
3420 	 * since it was first constituted.
3421 	 */
3422 	bool changed() const
3423 	{
3424 		return IsTaintImage(imageRef) != 0;
3425 	}
3426 
3427 	/**
3428 	 * Channel modulus depth. The channel modulus depth represents
3429 	 * the minimum number of bits required to support the channel without loss.
3430 	 * Setting the channel's modulus depth modifies the channel (i.e. discards
3431 	 * resolution) if the requested modulus depth is less than the current
3432 	 * modulus depth, otherwise the channel is not altered. There is no
3433 	 * attribute associated with the modulus depth so the current modulus
3434 	 * depth is obtained by inspecting the pixels. As a result, the depth
3435 	 * returned may be less than the most recently set channel depth.
3436 	 * Subsequent image processing may result in increasing the channel depth.
3437 	 */
3438 	//TODO: Is this a property?
3439 	void channelDepth(ChannelType channel, size_t depth)
3440 	{
3441 		SetImageChannelDepth(imageRef, channel, depth);
3442 	}
3443 	///ditto
3444 	size_t channelDepth(ChannelType channel) const
3445 	{
3446 		size_t depth = GetImageChannelDepth(imageRef, channel, DMagickExceptionInfo());
3447 
3448 		return depth;
3449 	}
3450 
3451 	/**
3452 	 * The red, green, blue, and white-point chromaticity values.
3453 	 */
3454 	void chromaticity(ChromaticityInfo chroma)
3455 	{
3456 		imageRef.chromaticity = chroma;
3457 	}
3458 	///ditto
3459 	ChromaticityInfo chromaticity() const
3460 	{
3461 		return imageRef.chromaticity;
3462 	}
3463 
3464 	/**
3465 	 * The image's storage class. If DirectClass then the pixels
3466 	 * contain valid RGB or CMYK colors. If PseudoClass then the
3467 	 * image has a colormap referenced by the pixel's index member.
3468 	 */
3469 	void classType(ClassType type)
3470 	{
3471 		if ( imageRef.storage_class == ClassType.PseudoClass && type == ClassType.DirectClass )
3472 		{
3473 			SyncImage(imageRef);
3474 			colormap() = null;
3475 		}
3476 		else if ( imageRef.storage_class == ClassType.DirectClass && type == ClassType.PseudoClass )
3477 		{
3478 			options.quantizeColors = MaxColormapSize;
3479 			quantize();
3480 		}
3481 
3482 		imageRef.storage_class = type;
3483 	}
3484 	///ditto
3485 	ClassType classType() const
3486 	{
3487 		return imageRef.storage_class;
3488 	}
3489 
3490 	/**
3491 	 * Associate a clip mask image with the current image.
3492 	 * The clip mask image must have the same dimensions as the current
3493 	 * image or an exception is thrown. Clipping occurs wherever pixels are
3494 	 * transparent in the clip mask image. Clipping Pass an invalid image
3495 	 * to unset an existing clip mask.
3496 	 */
3497 	void clipMask(const(Image) image)
3498 	{
3499 		if ( image is null )
3500 		{
3501 			SetImageClipMask(imageRef, null);
3502 			return;
3503 		}
3504 
3505 		//Throw a chatchable exception when the size differs.
3506 		if ( image.columns != columns || image.rows != rows )
3507 			throw new ImageException("image size differs");
3508 
3509 		SetImageClipMask(imageRef, image.imageRef);
3510 	}
3511 	///ditto
3512 	Image clipMask() const
3513 	{
3514 		MagickCoreImage* image = CloneImage(imageRef.clip_mask, 0, 0, true, DMagickExceptionInfo());
3515 
3516 		return new Image(image);
3517 	}
3518 
3519 	/**
3520 	 * Access the image color map.
3521 	 * Only ClassType.PsseudoClass images have a colormap.
3522 	 * ----------------------------------
3523 	 * Color color = image.colormap[2];
3524 	 * image.colormap()[2] = color;
3525 	 * ----------------------------------
3526 	 * To asign the complete colormap at once:
3527 	 * ----------------------------------
3528 	 * Color[] colors = new Colors[255];
3529 	 * image.colormap() = colors;
3530 	 * //Or
3531 	 * image.colormap.size = 255;
3532 	 * foreach(i, color; colors)
3533 	 *     image.colormap()[i] = color;
3534 	 * ----------------------------------
3535 	 * Bugs: because of dmd bug 2152 the parentheses are needed when assigning;
3536 	 */
3537 	auto colormap()
3538 	{
3539 		struct Colormap
3540 		{
3541 			Image img;
3542 
3543 			this(Image img)
3544 			{
3545 				this.img = img;
3546 			}
3547 
3548 			Color opIndex(uint index)
3549 			{
3550 				if ( index >= img.colormapSize )
3551 					throw new Exception("Index out of bounds");
3552 
3553 				return new Color(img.imageRef.colormap[index]);
3554 			}
3555 
3556 			void opIndexAssign(Color value, size_t index)
3557 			{
3558 				if ( index >= img.colormapSize )
3559 					throw new Exception("Index out of bounds");
3560 
3561 				img.imageRef.colormap[index] = value.pixelPacket;
3562 			}
3563 
3564 			void opAssign(Color[] colors)
3565 			{
3566 				img.colormapSize = colors.length;
3567 
3568 				if ( colors.length == 0 )
3569 					return;
3570 
3571 				foreach(i, color; colors)
3572 					this[i] = color;
3573 			}
3574 
3575 			void opOpAssign(string op)(Color color) if ( op == "~" )
3576 			{
3577 				img.colormapSize = img.colormapSize + 1;
3578 
3579 				this[img.colormapSize] = color;
3580 			}
3581 
3582 			void opOpAssign(string op)(Color[] colors) if ( op == "~" )
3583 			{
3584 				uint oldSize = img.colormapSize;
3585 
3586 				img.colormapSize = oldSize + colors.length;
3587 
3588 				foreach ( i; oldSize..img.colormapSize)
3589 					this[i] = colors[i];
3590 			}
3591 
3592 			/**
3593 			 * compresses the colormap by removing any
3594 			 * duplicate or unused color entries.
3595 			 */
3596 			void compress()
3597 			{
3598 				CompressImageColormap(img.imageRef);
3599 				DMagickException.throwException(&(img.imageRef.exception));
3600 			}
3601 
3602 			size_t size()
3603 			{
3604 				return img.colormapSize;
3605 			}
3606 			void size(size_t s)
3607 			{
3608 				img.colormapSize = s;
3609 			}
3610 		}
3611 
3612 		return Colormap(this);
3613 	}
3614 
3615 	/**
3616 	 * The number of colors in the colormap. Only meaningful for PseudoClass images.
3617 	 * 
3618 	 * Setting the colormap size may extend or truncate the colormap.
3619 	 * The maximum number of supported entries is specified by the
3620 	 * MaxColormapSize constant, and is dependent on the value of
3621 	 * QuantumDepth when ImageMagick is compiled. An exception is thrown
3622 	 * if more entries are requested than may be supported.
3623 	 * Care should be taken when truncating the colormap to ensure that
3624 	 * the image colormap indexes reference valid colormap entries.
3625 	 */
3626 	void colormapSize(size_t size)
3627 	{
3628 		if ( size > MaxColormapSize )
3629 			throw new OptionException(
3630 				"the size of the colormap can't exceed MaxColormapSize");
3631 
3632 		if ( size == 0 && imageRef.colors > 0 )
3633 		{
3634 			imageRef.colormap = cast(PixelPacket*)RelinquishMagickMemory( imageRef.colormap );
3635 			imageRef.colors = 0;
3636 
3637 			return;
3638 		}
3639 
3640 		if ( imageRef.colormap is null )
3641 		{
3642 			AcquireImageColormap(imageRef, size);
3643 			imageRef.colors = 0;
3644 		}
3645 		else
3646 		{
3647 			imageRef.colormap = cast(PixelPacket*)
3648 				ResizeMagickMemory(imageRef.colormap, size * PixelPacket.sizeof);
3649 		}
3650 
3651 		//Initialize the colors as black.
3652 		foreach ( i; imageRef.colors .. size )
3653 		{
3654 			imageRef.colormap[i].blue    = 0;
3655 			imageRef.colormap[i].green   = 0;
3656 			imageRef.colormap[i].red     = 0;
3657 			imageRef.colormap[i].opacity = 0;
3658 		}
3659 
3660 		imageRef.colors = size;
3661 	}
3662 	///ditto
3663 	size_t colormapSize() const
3664 	{
3665 		return imageRef.colors;
3666 	}
3667 
3668 	/**
3669 	 * The colorspace used to represent the image pixel colors.
3670 	 * Image pixels are always stored as RGB(A) except for the case of CMY(K).
3671 	 */
3672 	void colorspace(ColorspaceType type)
3673 	{
3674 		TransformImageColorspace(imageRef, type);
3675 
3676 		options.colorspace = type;
3677 	}
3678 	///ditto
3679 	ColorspaceType colorspace() const
3680 	{
3681 		return imageRef.colorspace;
3682 	}
3683 
3684 	/**
3685 	 * The width of the image in pixels.
3686 	 */
3687 	size_t columns() const
3688 	{
3689 		return imageRef.columns;
3690 	}
3691 
3692 	/**
3693 	 * Composition operator to be used when composition is
3694 	 * implicitly used (such as for image flattening).
3695 	 */
3696 	void compose(CompositeOperator op)
3697 	{
3698 		imageRef.compose = op;
3699 	}
3700 	///ditto
3701 	CompositeOperator compose() const
3702 	{
3703 		return imageRef.compose;
3704 	}
3705 
3706 	/**
3707 	 * The image compression type. The default is the
3708 	 * compression type of the specified image file.
3709 	 */
3710 	void compression(CompressionType type)
3711 	{
3712 		imageRef.compression = type;
3713 		options.compression = type;
3714 	}
3715 	///ditto
3716 	CompressionType compression() const
3717 	{
3718 		return imageRef.compression;
3719 	}
3720 
3721 	/**
3722 	 * The vertical and horizontal resolution in pixels of the image.
3723 	 * This option specifies an image density when decoding
3724 	 * a Postscript or Portable Document page.
3725 	 * 
3726 	 * The default is "72x72".
3727 	 */
3728 	void density(Geometry value)
3729 	{
3730 		options.density = value;
3731 
3732 		imageRef.x_resolution = value.width;
3733 		imageRef.y_resolution = ( value.width != 0 ) ? value.width : value.height;
3734 	}
3735 	///ditto
3736 	Geometry density() const
3737 	{
3738 		ssize_t width  = cast(ssize_t)rndtol(imageRef.x_resolution);
3739 		ssize_t height = cast(ssize_t)rndtol(imageRef.y_resolution);
3740 
3741 		return Geometry(width, height);
3742 	}
3743 
3744 	/**
3745 	 * Image depth. Used to specify the bit depth when reading or writing
3746 	 * raw images or when the output format supports multiple depths.
3747 	 * Defaults to the quantum depth that ImageMagick is compiled with.
3748 	 */
3749 	void depth(size_t value)
3750 	{
3751 		if ( value > MagickQuantumDepth)
3752 			value = MagickQuantumDepth;
3753 
3754 		imageRef.depth = value;
3755 		options.depth = value;
3756 	}
3757 	///ditto
3758 	size_t depth() const
3759 	{
3760 		return imageRef.depth;
3761 	}
3762 
3763 	/**
3764 	 * Tile names from within an image montage.
3765 	 * Only valid after calling montage or reading a MIFF file
3766 	 * which contains a directory.
3767 	 */
3768 	string directory() const
3769 	{
3770 		return to!(string)(imageRef.directory);
3771 	}
3772 
3773 	/**
3774 	 * Specify (or obtain) endian option for formats which support it.
3775 	 */
3776 	void endian(EndianType type)
3777 	{
3778 		imageRef.endian = type;
3779 		options.endian = type;
3780 	}
3781 	///ditto
3782 	EndianType endian() const
3783 	{
3784 		return imageRef.endian;
3785 	}
3786 
3787 	/**
3788 	 * The EXIF profile.
3789 	 */
3790 	void exifProfile(void[] blob)
3791 	{
3792 		StringInfo* profile = AcquireStringInfo(blob.length);
3793 		SetStringInfoDatum(profile, cast(ubyte*)blob.ptr);
3794 
3795 		SetImageProfile(imageRef, "exif", profile);
3796 
3797 		DestroyStringInfo(profile);		
3798 	}
3799 	///ditto
3800 	void[] exifProfile() const
3801 	{
3802 		const(StringInfo)* profile = GetImageProfile(imageRef, "exif");
3803 
3804 		if ( profile is null )
3805 			return null;
3806 
3807 		return GetStringInfoDatum(profile)[0 .. GetStringInfoLength(profile)].dup;
3808 	}
3809 
3810 	/**
3811 	 * The image filename.
3812 	 */
3813 	void filename(string str)
3814 	{
3815 		copyString(imageRef.filename, str);
3816 		options.filename = str;
3817 	}
3818 	///ditto
3819 	string filename() const
3820 	{
3821 		return imageRef.filename[0 .. strlen(imageRef.filename.ptr)].idup;
3822 	}
3823 
3824 	/**
3825 	 * The image filesize in bytes.
3826 	 */
3827 	MagickSizeType fileSize() const
3828 	{
3829 		return GetBlobSize(imageRef);
3830 	}
3831 
3832 	/**
3833 	 * Filter to use when resizing image. The reduction filter employed
3834 	 * has a significant effect on the time required to resize an image
3835 	 * and the resulting quality. The default filter is Lanczos which has
3836 	 * been shown to produce high quality results when reducing most images.
3837 	 */
3838 	void filter(FilterTypes type)
3839 	{
3840 		imageRef.filter = type;
3841 	}
3842 	///ditto
3843 	FilterTypes filter() const
3844 	{
3845 		return imageRef.filter;
3846 	}
3847 
3848 	/**
3849 	 * The image encoding format. For example, "GIF" or "PNG".
3850 	 */
3851 	string format() const
3852 	{
3853 		const(MagickInfo)* info = GetMagickInfo(imageRef.magick.ptr, DMagickExceptionInfo());
3854 
3855 		return to!(string)( info.description );
3856 	}
3857 
3858 	/**
3859 	 * Colors within this distance are considered equal. 
3860 	 * A number of algorithms search for a target  color.
3861 	 * By default the color must be exact. Use this option to match
3862 	 * colors that are close to the target color in RGB space.
3863 	 */
3864 	void fuzz(double f)
3865 	{
3866 		options.fuzz = f;
3867 		imageRef.fuzz = f;
3868 	}
3869 	///ditto
3870 	double fuzz() const
3871 	{
3872 		return options.fuzz;
3873 	}
3874 
3875 	/**
3876 	 * Gamma level of the image. The same color image displayed on
3877 	 * two different workstations may look different due to differences
3878 	 * in the display monitor. Use gamma correction to adjust for this
3879 	 * color difference.
3880 	 */
3881 	double gamma() const
3882 	{
3883 		return imageRef.gamma;
3884 	}
3885 
3886 	/**
3887 	 * Preferred size of the image when encoding.
3888 	 */
3889 	void geometry(string str)
3890 	{
3891 		copyString(imageRef.geometry, str);
3892 	}
3893 	///ditto
3894 	void geometry(Geometry value)
3895 	{
3896 		geometry(value.toString());
3897 	}
3898 	///ditto
3899 	Geometry geometry() const
3900 	{
3901 		return Geometry( to!(string)(imageRef.geometry) );
3902 	}
3903 
3904 	/**
3905 	 * GIF disposal method. This attribute is used to control how
3906 	 * successive images are rendered (how the preceding image
3907 	 * is disposed of) when creating a GIF animation.
3908 	 */
3909 	void gifDisposeMethod(DisposeType type)
3910 	{
3911 		imageRef.dispose = type;
3912 	}
3913 	///ditto
3914 	DisposeType gifDisposeMethod() const
3915 	{
3916 		return imageRef.dispose;
3917 	}
3918 
3919 	/**
3920 	 * Returns true if all the pixels in the image have the same red,
3921 	 * green, and blue intensities.
3922 	 */
3923 	bool gray()
3924 	{
3925 		return dmagick.c.attribute.IsGrayImage(imageRef, DMagickExceptionInfo()) == 1;
3926 	}
3927 
3928 	/**
3929 	 * Computes the number of times each unique color appears in the image.
3930 	 * You may want to quantize the image before using this property.
3931 	 * 
3932 	 * Returns: A associative array. Each key reprecents a color in the Image.
3933 	 *     The value is the number of times the color apears in the image.
3934 	 */
3935 	MagickSizeType[Color] histogram() const
3936 	{
3937 		size_t count;
3938 		MagickSizeType[Color] hashMap;
3939 		ColorPacket* colorPackets;
3940 
3941 		colorPackets = GetImageHistogram(imageRef, &count, DMagickExceptionInfo());
3942 
3943 		foreach ( packet; colorPackets[0 .. count] )
3944 		{
3945 			hashMap[new Color(packet.pixel)] = packet.count;
3946 		}
3947 
3948 		RelinquishMagickMemory(colorPackets);
3949 
3950 		return hashMap;
3951 	}
3952 
3953 	/**
3954 	 * ICC color profile.
3955 	 */
3956 	void iccColorProfile(void[] blob)
3957 	{
3958 		profile("icm", blob);
3959 	}
3960 	///ditto
3961 	void[] iccColorProfile() const
3962 	{
3963 		return profile("icm");
3964 	}
3965 
3966 	/**
3967 	 * Specify the _type of interlacing scheme for raw image formats
3968 	 * such as RGB or YUV. NoInterlace means do not _interlace,
3969 	 * LineInterlace uses scanline interlacing, and PlaneInterlace
3970 	 * uses plane interlacing. PartitionInterlace is like PlaneInterlace
3971 	 * except the different planes are saved to individual files
3972 	 * (e.g. image.R, image.G, and image.B). Use LineInterlace or
3973 	 * PlaneInterlace to create an interlaced GIF or
3974 	 * progressive JPEG image. The default is NoInterlace.
3975 	 */
3976 	void interlace(InterlaceType type)
3977 	{
3978 		imageRef.interlace = type;
3979 		options.interlace = type;
3980 	}
3981 	///ditto
3982 	InterlaceType interlace() const
3983 	{
3984 		return imageRef.interlace;
3985 	}
3986 
3987 	/**
3988 	 * The International Press Telecommunications Council profile.
3989 	 */
3990 	void iptcProfile(void[] blob)
3991 	{
3992 		profile("iptc", blob);
3993 	}
3994 	///ditto
3995 	void[] iptcProfile() const
3996 	{
3997 		return profile("iptc");
3998 	}
3999 
4000 	/**
4001 	 * Image format (e.g. "GIF")
4002 	 */
4003 	void magick(string str)
4004 	{
4005 		copyString(imageRef.magick, str);
4006 		options.magick = str;
4007 	}
4008 	///ditto
4009 	string magick() const
4010 	{
4011 		if ( imageRef.magick[0] !is '\0' )
4012 		{
4013 			return imageRef.magick[0 .. strlen(imageRef.magick.ptr)].idup;
4014 		}
4015 		return options.magick;
4016 	}
4017 
4018 	/**
4019 	 * Set the image transparent color. The default is "#bdbdbd".
4020 	 */
4021 	void matteColor(string color)
4022 	{
4023 		matteColor = new Color(color);
4024 	}
4025 	///ditto
4026 	void matteColor(Color color)
4027 	{
4028 		imageRef.matte_color = color.pixelPacket;
4029 		options.matteColor = color;
4030 	}
4031 	///ditto
4032 	Color matteColor() const
4033 	{
4034 		return new Color(imageRef.matte_color);
4035 	}
4036 
4037 	/**
4038 	 * The mean error per pixel computed when an image is color reduced.
4039 	 * This parameter is only valid if verbose is set to true and the
4040 	 * image has just been quantized.
4041 	 */
4042 	double meanErrorPerPixel() const
4043 	{
4044 		return imageRef.error.mean_error_per_pixel;
4045 	}
4046 
4047 	/**
4048 	 * Image modulus depth (minimum number of bits required to
4049 	 * support red/green/blue components without loss of accuracy).
4050 	 * The pixel modulus depth may be decreased by supplying a value
4051 	 * which is less than the current value, updating the pixels
4052 	 * (reducing accuracy) to the new depth. The pixel modulus depth
4053 	 * can not be increased over the current value using this method.
4054 	 */
4055 	void modulusDepth(size_t depth)
4056 	{
4057 		SetImageDepth(imageRef, depth);
4058 		options.depth = depth;
4059 	}
4060 	///ditto
4061 	size_t modulusDepth() const
4062 	{
4063 		size_t depth = GetImageDepth(imageRef, DMagickExceptionInfo());
4064 
4065 		return depth;
4066 	}
4067 
4068 	/**
4069 	 * Establish a progress monitor. Most Image and ImageList methods
4070 	 * will periodically call the monitor with arguments indicating the
4071 	 * progress of the method.
4072 	 *
4073 	 * The delegate receves the folowing $(B parameters): $(BR)
4074 	 * $(TABLE 
4075 	 *     $(ROW string $(I methodName), The name of the monitored method.)
4076 	 *     $(ROW long   $(I offset    ), A number between 0 and extent that
4077 	 *          identifies how much of the operation has been completed
4078 	 *          (or, in some cases, remains to be completed).)
4079 	 *     $(ROW ulong  $(I extent    ), The number of quanta needed to
4080 	 *                                   complete the operation.)
4081 	 * )
4082 	 */
4083 	void monitor(bool delegate(string methodName, long offset, ulong extent) progressMonitor)
4084 	{
4085 		if ( this.progressMonitor is null )
4086 			SetImageProgressMonitor(imageRef, cast(MagickProgressMonitor)&ImageProgressMonitor, cast(void*)this);
4087 
4088 		this.progressMonitor = progressMonitor;
4089 
4090 		if ( progressMonitor is null )
4091 			SetImageProgressMonitor(imageRef, null, null);		
4092 	}
4093 	///ditto
4094 	bool delegate(string, long, ulong) monitor()
4095 	{
4096 		return progressMonitor;
4097 	}
4098 
4099 	static extern(C) MagickBooleanType ImageProgressMonitor(
4100 		const(char)* methodName,
4101 		MagickOffsetType offset,
4102 		MagickSizeType extend,
4103 		Image image)
4104 	{
4105 		return image.progressMonitor(to!(string)(methodName), offset, extend);
4106 	}
4107 
4108 	/**
4109 	 * Tile size and offset within an image montage.
4110 	 * Only valid for images produced by montage.
4111 	 */
4112 	Geometry montageGeometry() const
4113 	{
4114 		return Geometry( to!(string)(imageRef.geometry) );
4115 	}
4116 
4117 	/**
4118 	 * The normalized max error per pixel computed when
4119 	 * an image is color reduced. This parameter is only
4120 	 * valid if verbose is set to true and the image
4121 	 * has just been quantized.
4122 	 */
4123 	double normalizedMaxError() const
4124 	{
4125 		return imageRef.error.normalized_maximum_error;
4126 	}
4127 
4128 	/**
4129 	 * The normalized mean error per pixel computed when
4130 	 * an image is color reduced. This parameter is only
4131 	 * valid if verbose is set to true and the image
4132 	 * has just been quantized.
4133 	 */
4134 	double normalizedMeanError() const
4135 	{
4136 		return imageRef.error.normalized_mean_error;
4137 	}
4138 
4139 	/**
4140 	 * Sets the value of the image property. An image may have any number
4141 	 * of properties. ImageMagick predefines some properties, including
4142 	 * attribute, label, caption, comment, signature, and in some cases EXIF.
4143 	 */
4144 	void opDispatch(string property)(string value)
4145 		 if ( property != "popFront" )
4146 	{
4147 		SetImageProperty(imageRef, toStringz(property), toStringz(value));
4148 
4149 		return;
4150 	}
4151 
4152 	/**
4153 	 * Returns the value of the image property.
4154 	 */
4155 	auto opDispatch(string property)()
4156 		 if ( property != "popFront" )
4157 	{
4158 		return to!(string)(GetImageProperty(imageRef, toStringz(property)));
4159 	}
4160 
4161 	unittest
4162 	{
4163 		Image image = new Image();
4164 
4165 		image.comment = "unittest";
4166 		assert(image.comment == "unittest");
4167 	}
4168 
4169 	/**
4170 	 * Image orientation.  Supported by some file formats
4171 	 * such as DPX and TIFF. Useful for turning the right way up.
4172 	 */
4173 	void orientation(OrientationType orientation)
4174 	{
4175 		imageRef.orientation = orientation;
4176 	}
4177 	///ditto
4178 	OrientationType orientation() const
4179 	{
4180 		return imageRef.orientation;
4181 	}
4182 
4183 	/**
4184 	 * When compositing, this attribute describes the position
4185 	 * of this image with respect to the underlying image.
4186 	 * 
4187 	 * Use this option to specify the dimensions and position of
4188 	 * the Postscript page in dots per inch or a TEXT page in pixels.
4189 	 * This option is typically used in concert with density.
4190 	 * 
4191 	 * Page may also be used to position a GIF image
4192 	 * (such as for a scene in an animation).
4193 	 */
4194 	void page(Geometry geometry)
4195 	{
4196 		options.page = geometry;
4197 		imageRef.page = geometry.rectangleInfo;
4198 	}
4199 	///ditto
4200 	Geometry page() const
4201 	{
4202 		return Geometry(imageRef.page);
4203 	}
4204 
4205 	/**
4206 	 * The pixel color interpolation method. Some methods (such
4207 	 * as wave, swirl, implode, and composite) use the pixel color
4208 	 * interpolation method to determine how to blend adjacent pixels.
4209 	 */
4210 	void pixelInterpolationMethod(InterpolatePixelMethod method)
4211 	{
4212 		imageRef.interpolate = method;
4213 	}
4214 	///ditto
4215 	InterpolatePixelMethod pixelInterpolationMethod() const
4216 	{
4217 		return imageRef.interpolate;
4218 	}
4219 
4220 	/**
4221 	 * Get/set/remove a named profile. Valid names include "*",
4222 	 * "8BIM", "ICM", "IPTC", or a user/format-defined profile name. 
4223 	 */
4224 	void profile(string name, void[] blob)
4225 	{
4226 		ProfileImage(imageRef, toStringz(name), blob.ptr, blob.length, false);
4227 	}
4228 	///ditto
4229 	void[] profile(string name) const
4230 	{
4231 		const(StringInfo)* profile = GetImageProfile(imageRef, toStringz(name));
4232 
4233 		if ( profile is null )
4234 			return null;
4235 
4236 		return GetStringInfoDatum(profile)[0 .. GetStringInfoLength(profile)].dup;
4237 	}
4238 
4239 	/**
4240 	 * JPEG/MIFF/PNG compression level (default 75).
4241 	 */
4242 	void quality(size_t quality)
4243 	{
4244 		imageRef.quality = quality;
4245 		options.quality = quality;
4246 	}
4247 	///ditto
4248 	size_t quality() const
4249 	{
4250 		return imageRef.quality;
4251 	}
4252 
4253 	/**
4254 	 * The type of rendering intent.
4255 	 * See_Also: 
4256 	 * $(LINK http://www.cambridgeincolour.com/tutorials/color-space-conversion.htm)
4257 	 */
4258 	void renderingIntent(RenderingIntent intent)
4259 	{
4260 		imageRef.rendering_intent = intent;
4261 	}
4262 	///ditto
4263 	RenderingIntent renderingIntent() const
4264 	{
4265 		return imageRef.rendering_intent;
4266 	}
4267 
4268 	/**
4269 	 * Units of image resolution
4270 	 */
4271 	void resolutionUnits(ResolutionType type)
4272 	{
4273 		imageRef.units = type;
4274 		options.resolutionUnits = type;
4275 	}
4276 	///ditto
4277 	ResolutionType resolutionUnits() const
4278 	{
4279 		return options.resolutionUnits;
4280 	}
4281 
4282 	/**
4283 	 * The scene number assigned to the image the last
4284 	 * time the image was written to a multi-image image file.
4285 	 */
4286 	void scene(size_t value)
4287 	{
4288 		imageRef.scene = value;
4289 	}
4290 	///ditto
4291 	size_t scene() const
4292 	{
4293 		return imageRef.scene;
4294 	}
4295 
4296 	/**
4297 	 * The height of the image in pixels.
4298 	 */
4299 	size_t rows() const
4300 	{
4301 		return imageRef.rows;
4302 	}
4303 
4304 	/**
4305 	 * Width and height of a image.
4306 	 */
4307 	Geometry size() const
4308 	{
4309 		return Geometry(imageRef.columns, imageRef.rows);
4310 	}
4311 
4312 	/**
4313 	 * Number of colors in the image.
4314 	 */
4315 	size_t totalColors() const
4316 	{
4317 		size_t colors = GetNumberColors(imageRef, null, DMagickExceptionInfo());
4318 
4319 		return colors;
4320 	}
4321 
4322 	/**
4323 	 * Image type.
4324 	 */
4325 	void type(ImageType imageType)
4326 	{
4327 		options.type = imageType;
4328 		SetImageType(imageRef, imageType);
4329 	}
4330 	///ditto
4331 	ImageType type() const
4332 	{
4333 		if (options.type != ImageType.UndefinedType )
4334 			return options.type;
4335 
4336 		ImageType imageType = GetImageType(imageRef, DMagickExceptionInfo());
4337 
4338 		return imageType;
4339 	}
4340 
4341 	/**
4342 	 * Specify how "virtual pixels" behave. Virtual pixels are
4343 	 * pixels that are outside the boundaries of the image.
4344 	 * Methods such as blurImage, sharpen, and wave use virtual pixels.
4345 	 */
4346 	void virtualPixelMethod(VirtualPixelMethod method)
4347 	{
4348 		options.virtualPixelMethod = method;
4349 		SetImageVirtualPixelMethod(imageRef, method);
4350 	}
4351 	///ditto
4352 	VirtualPixelMethod virtualPixelMethod() const
4353 	{
4354 		return GetImageVirtualPixelMethod(imageRef);
4355 	}
4356 
4357 	/**
4358 	 * Horizontal resolution of the image.
4359 	 */
4360 	double xResolution() const
4361 	{
4362 		return imageRef.x_resolution;
4363 	}
4364 
4365 	/**
4366 	 * Vertical resolution of the image.
4367 	 */
4368 	double yResolution() const
4369 	{
4370 		return imageRef.y_resolution;
4371 	}
4372 
4373 	private void setMagickPixelPacket(MagickPixelPacket* magick, Color color)
4374 	{
4375 		magick.red     = color.redQuantum;
4376 		magick.green   = color.greenQuantum;
4377 		magick.blue    = color.blueQuantum;
4378 		magick.opacity = color.opacityQuantum;
4379 	}
4380 
4381 	private string wordWrap(string text, Geometry boundingBox)
4382 	{
4383 		size_t pos;
4384 		string[] lines;
4385 
4386 		if ( text.empty )
4387 			return text;
4388 
4389 		double lineHeight = getTypeMetrics([text[0]]).height;
4390 		size_t maxLines = cast(size_t)(boundingBox.height / lineHeight);
4391 
4392 		while ( !text.empty )
4393 		{
4394 			for ( size_t i; i < text.length; i++ )
4395 			{
4396 				if ( isWhite(text[i]) || i == text.length-1 )
4397 				{
4398 					TypeMetric metric = getTypeMetrics(text[0..i]);
4399 
4400 					if ( metric.width >  boundingBox.width )
4401 					{
4402 						if ( pos == 0 )
4403 							pos = i;
4404 
4405 						break;
4406 					}
4407 
4408 					pos = i;
4409 
4410 					if ( text[i] == '\n' )
4411 						break;
4412 
4413 					if ( i == text.length-1 )
4414 						pos++;
4415 				}
4416 			}
4417 
4418 			lines ~= text[0 .. pos].strip();
4419 			text = text[min(pos+1, text.length) .. $];
4420 			pos = 0;
4421 
4422 			if ( lines.length == maxLines )
4423 				break;
4424 		}
4425 
4426 		return join(lines, "\n");	
4427 	}
4428 
4429 	unittest
4430 	{
4431 		Image img = new Image(Geometry(200, 200), new Color());
4432 		img.options.font = "/usr/share/fonts/TTF/DejaVuSans.ttf";
4433 		string wraped = img.wordWrap("Lorem ipsum dolor sit amet.", Geometry(100, 200));
4434 
4435 		assert(wraped == "Lorem ipsum\ndolor sit amet.");
4436 	}
4437 
4438 	private template getStorageType(T)
4439 	{
4440 		static if ( is( T == byte) )
4441 		{
4442 			enum getStorageType = StorageType.CharPixel;
4443 		}
4444 		else static if ( is( T == short) )
4445 		{
4446 			enum getStorageType  = StorageType.ShortPixel;
4447 		}
4448 		else static if ( is( T == int) )
4449 		{
4450 			enum getStorageType = StorageType.IntegerPixel;
4451 		}
4452 		else static if ( is( T == long) )
4453 		{
4454 			enum getStorageType = StorageType.LongPixel;
4455 		}
4456 		else static if ( is( T == float) )
4457 		{
4458 			enum getStorageType = StorageType.FloatPixel;
4459 		}
4460 		else static if ( is( T == double) )
4461 		{
4462 			enum getStorageType = StorageType.DoublePixel;
4463 		}
4464 		else
4465 		{
4466 			static assert(false, "Unsupported type");
4467 		}
4468 	}
4469 
4470 	unittest
4471 	{
4472 		StorageType storage = getStorageType!(int);
4473 
4474 		assert( storage == StorageType.IntegerPixel );
4475 	}
4476 }
4477 
4478 /*
4479  * Initialize ImageMagick, causes an access violation on Windows.
4480  */
4481 version (Posix)
4482 {
4483 	shared static this()
4484 	{
4485 			MagickCoreGenesis(null, false);
4486 	}
4487 
4488 	shared static ~this()
4489 	{
4490 			MagickCoreTerminus();
4491 	}
4492 }