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