PageRenderTime 199ms CodeModel.GetById 44ms app.highlight 137ms RepoModel.GetById 1ms app.codeStats 1ms

/dmagick/Image.d

http://github.com/MikeWey/DMagick
D | 4492 lines | 2101 code | 547 blank | 1844 comment | 125 complexity | 04d00e165654ff064104a41ec065a457 MD5 | raw file

Large files files are truncated, but you can click here to view the full file

   1/**
   2 * Copyright: Mike Wey 2011
   3 * License:   zlib (See accompanying LICENSE file)
   4 * Authors:   Mike Wey
   5 */
   6
   7module dmagick.Image;
   8
   9import std.algorithm : min;
  10import std.array;
  11import std.conv;
  12import std.math;
  13import std.string;
  14import std.typecons : Tuple;
  15import std.uni;
  16import core.memory;
  17import core.runtime;
  18import core.time;
  19import core.stdc.string;
  20
  21import dmagick.Color;
  22import dmagick.Exception;
  23import dmagick.Geometry;
  24import dmagick.ImageView;
  25import dmagick.Options;
  26import dmagick.Utils;
  27
  28version(DMagick_No_Display)
  29{
  30}
  31else
  32{
  33	version(Windows) import dmagick.internal.Windows;
  34}
  35
  36//Import all translated c headers.
  37import dmagick.c.MagickCore;
  38
  39/// See_Also: $(CXREF geometry, _AffineMatrix)
  40public alias dmagick.c.geometry.AffineMatrix AffineMatrix;
  41/// See_Also: $(CXREF image, _AlphaChannelType)
  42public alias dmagick.c.image.AlphaChannelType AlphaChannelType;
  43/// See_Also: $(CXREF magickType, _ChannelType)
  44public alias dmagick.c.magickType.ChannelType ChannelType;
  45/// See_Also: $(CXREF image, _ChromaticityInfo)
  46public alias dmagick.c.image.ChromaticityInfo ChromaticityInfo;
  47/// See_Also: $(CXREF magickType, _ClassType)
  48public alias dmagick.c.magickType.ClassType ClassType;
  49/// See_Also: $(CXREF colorspace, _ColorspaceType)
  50public alias dmagick.c.colorspace.ColorspaceType ColorspaceType;
  51/// See_Also: $(CXREF composite, _CompositeOperator)
  52public alias dmagick.c.composite.CompositeOperator CompositeOperator;
  53/// See_Also: $(CXREF compress, _CompressionType)
  54public alias dmagick.c.compress.CompressionType CompressionType;
  55/// See_Also: $(CXREF layer, _DisposeType)
  56public alias dmagick.c.layer.DisposeType DisposeType;
  57/// See_Also: $(CXREF distort, _DistortImageMethod)
  58public alias dmagick.c.distort.DistortImageMethod DistortImageMethod;
  59/// See_Also: $(CXREF quantum, _EndianType)
  60public alias dmagick.c.quantum.EndianType EndianType;
  61/// See_Also: $(CXREF resample, _FilterTypes)
  62public alias dmagick.c.resample.FilterTypes FilterTypes;
  63/// See_Also: $(CXREF geometry, _GravityType)
  64public alias dmagick.c.geometry.GravityType GravityType;
  65/// See_Also: $(CXREF image, _ImageType)
  66public alias dmagick.c.image.ImageType ImageType;
  67/// See_Also: $(CXREF image, _InterlaceType)
  68public alias dmagick.c.image.InterlaceType InterlaceType;
  69/// See_Also: $(CXREF pixel, _InterpolatePixelMethod)
  70public alias dmagick.c.pixel.InterpolatePixelMethod InterpolatePixelMethod;
  71/// See_Also: $(CXREF statistic, _MagickEvaluateOperator)
  72public alias dmagick.c.statistic.MagickEvaluateOperator MagickEvaluateOperator;
  73/// See_Also: $(CXREF statistic, _MagickFunction)
  74public alias dmagick.c.statistic.MagickFunction MagickFunction;
  75/// See_Also: $(CXREF fx, _NoiseType)
  76public alias dmagick.c.fx.NoiseType NoiseType;
  77/// See_Also: $(CXREF image, _OrientationType)
  78public alias dmagick.c.image.OrientationType OrientationType;
  79/// See_Also: $(CXREF effect, _PreviewType)
  80public alias dmagick.c.effect.PreviewType PreviewType;
  81/// See_Also: $(CXREF magickType, _Quantum)
  82public alias dmagick.c.magickType.Quantum Quantum;
  83/// See_Also: $(CXREF profile, _RenderingIntent)
  84public alias dmagick.c.profile.RenderingIntent RenderingIntent;
  85/// See_Also: $(CXREF image, _ResolutionType)
  86public alias dmagick.c.image.ResolutionType ResolutionType;
  87/// See_Also: $(CXREF distort, _SparseColorMethod)
  88public alias dmagick.c.distort.SparseColorMethod SparseColorMethod;
  89/// See_Also: $(CXREF effect, _StatisticType)
  90public alias dmagick.c.statistic.StatisticType StatisticType;
  91/// See_Also: $(CXREF constitute, _StorageType)
  92public alias dmagick.c.constitute.StorageType StorageType;
  93/// See_Also: $(CXREF draw, _TypeMetric)
  94public alias dmagick.c.draw.TypeMetric TypeMetric;
  95/// See_Also: $(CXREF cacheView, _VirtualPixelMethod)
  96public alias dmagick.c.cacheView.VirtualPixelMethod VirtualPixelMethod;
  97
  98alias ptrdiff_t ssize_t;
  99
 100/**
 101 * The image
 102 */
 103class Image
 104{
 105	alias dmagick.c.image.Image MagickCoreImage;
 106	alias RefCounted!( DestroyImage, MagickCoreImage ) ImageRef;
 107
 108	ImageRef imageRef;
 109	Options options;  ///The options for this image.
 110
 111	private bool delegate(string, long, ulong) progressMonitor;
 112	
 113	///
 114	this()
 115	{
 116		options = new Options();
 117		imageRef = ImageRef(AcquireImage(options.imageInfo));
 118	}
 119
 120	this(MagickCoreImage* image, Options options = null)
 121	{
 122		this(ImageRef(image), options);
 123	}
 124
 125	this(ImageRef image, Options options = null)
 126	{
 127		if ( options is null )
 128			this.options = new Options();
 129		else
 130			this.options = options;
 131
 132		imageRef = image;
 133	}
 134
 135	/**
 136	 * Construct an Image by reading from the file or
 137	 * URL specified by filename.
 138	 */
 139	this(string filename)
 140	{
 141		options = new Options();
 142		read(filename);
 143	}
 144
 145	/**
 146	 * Construct a blank image with the specified color.
 147	 */
 148	this(Geometry size, Color color)
 149	{
 150		options = new Options();
 151		options.size = size;
 152
 153		//Use read to create a cnavas with the spacified color.
 154		read( "canvas:"~ color.toString() );
 155	}
 156
 157	/**
 158	 * Construct an image from an in-memory blob.
 159	 * The Blob size, depth and magick format may also be specified.
 160	 *
 161	 * Some image formats require size to be specified,
 162	 * the default depth Imagemagick uses is the Quantum size
 163	 * it's compiled with. If it doesn't match the depth of the image
 164	 * it may need to be specified.
 165	 *
 166	 * Imagemagick can usualy detect the image format, when the
 167	 * format can't be detected a magick format must be specified.
 168	 */
 169	this(void[] blob)
 170	{
 171		options = new Options();
 172
 173		read(blob);
 174	}
 175
 176	///ditto
 177	this(void[] blob, Geometry size)
 178	{
 179		options = new Options();
 180
 181		read(blob, size);
 182	}
 183
 184	///ditto
 185	this(void[] blob, Geometry size, size_t depth)
 186	{
 187		options = new Options();
 188
 189		read(blob, size, depth);
 190	}
 191
 192	///ditto
 193	this(void[] blob, Geometry size, size_t depth, string magick)
 194	{
 195		options = new Options();
 196
 197		read(blob, size, depth, magick);
 198	}
 199	
 200	///ditto
 201	this(void[] blob, Geometry size, string magick)
 202	{
 203		options = new Options();
 204
 205		read(blob, size, magick);
 206	}
 207
 208	/**
 209	 * Constructs an image from an array of pixels.
 210	 *
 211	 * Params:
 212	 *     columns =  The number of columns in the image.
 213	 *     rows    =  The number of rows in the image.
 214	 *     map     =  A string describing the expected ordering
 215	 *                of the pixel array. It can be any combination
 216	 *                or order of R = red, G = green, B = blue, A = alpha
 217	 *                , C = cyan, Y = yellow, M = magenta, K = black,
 218	 *                or I = intensity (for grayscale).
 219	 *     storage  = The pixel Staroage type (CharPixel,
 220	 *                ShortPixel, IntegerPixel, FloatPixel, or DoublePixel).
 221	 *     pixels   = The pixel data.
 222	 */
 223	this(size_t columns, size_t rows, string map, StorageType storage, void[] pixels)
 224	{
 225		options = new Options();
 226
 227		MagickCoreImage* image = 
 228			ConstituteImage(columns, rows, toStringz(map), storage, pixels.ptr, DMagickExceptionInfo());
 229
 230		imageRef = ImageRef(image);
 231	}
 232
 233	/**
 234	 * Constructs a description of the image as a string.
 235	 * The string contains some or all of the following fields:
 236	 * $(LIST
 237	 *     $(B filename) The current filename.,
 238	 *     $(B [scene]) The scene number if the image is part of a secuence.,
 239	 *     $(B format) The image format.,
 240	 *     $(B width x height),
 241	 *     $(B page width x height + xOffset + yOffset),
 242	 *     $(B classType) DirectClass or PseudoClass,
 243	 *     $(B N-bit) bit depth.,
 244	 *     $(B blob size) if present.
 245	 * )
 246	 */
 247	override string toString()
 248	{
 249		string result;
 250
 251		result = filename;
 252
 253		//Scene number.
 254		ssize_t index = GetImageIndexInList(imageRef);
 255		if ( index > 0 )
 256		{
 257			result ~= std.string.format("[%s]", index);
 258		}
 259
 260		result ~= std.string.format(" %s ", format);
 261		result ~= std.string.format("%sx%s ", columns, rows);
 262
 263		//Page size
 264		if ( imageRef.page.width > 0 || imageRef.page.height > 0 
 265			|| imageRef.page.x != 0 || imageRef.page.y != 0 )
 266		{
 267			result ~= std.string.format("%sx%s%+s%+s ",
 268				imageRef.page.width, imageRef.page.height,
 269				imageRef.page.x,     imageRef.page.y);
 270		}
 271
 272		if ( classType == ClassType.DirectClass )
 273			result ~= "DirectClass ";
 274		else
 275			result ~= "PseudoClass ";
 276
 277		result ~= std.string.format("%s-bit ", GetImageQuantumDepth(imageRef, true));
 278
 279		//Size of the image.
 280		MagickSizeType size = GetBlobSize(imageRef);
 281		if ( size > 0 )
 282		{
 283			if ( size > 2*1024*1024 )
 284				result ~= std.string.format("%s MiB", size/1024/1024);
 285			else if ( size > 2*1024 )
 286				result ~= std.string.format("%s KiB", size/1024);
 287			else
 288				result ~= std.string.format("%s bytes", size);
 289		}
 290
 291		return result;
 292	}
 293
 294	/**
 295	 * Adaptively blurs the image by blurring more intensely near
 296	 * image edges and less intensely far from edges.
 297	 * The adaptiveBlur method blurs the image with a Gaussian operator
 298	 * of the given radius and standard deviation (sigma).
 299	 * For reasonable results, radius should be larger than sigma.
 300	 * Use a radius of 0 and adaptiveBlur selects a suitable radius for you.
 301	 *
 302	 * Params:
 303	 *     radius  = The radius of the Gaussian in pixels,
 304	 *               not counting the center pixel.
 305	 *     sigma   = The standard deviation of the Laplacian, in pixels.
 306	 *     channel = The channels to blur.
 307	 */
 308	void adaptiveBlur(double radius = 0, double sigma = 1, ChannelType channel = ChannelType.DefaultChannels)
 309	{
 310		MagickCoreImage* image =
 311			AdaptiveBlurImageChannel(imageRef, channel, radius, sigma, DMagickExceptionInfo());
 312
 313		imageRef = ImageRef(image);
 314	}
 315
 316	/**
 317	 * adaptiveResize uses the special Mesh Interpolation method
 318	 * to resize images. Basically adaptiveResize avoids the excessive
 319	 * blurring that resize can produce with sharp color changes.
 320	 * This works well for slight image size adjustments and in
 321	 * particularly for magnification, And especially with images
 322	 * with sharp color changes. But when images are enlarged or reduced
 323	 * by more than 50% it will start to produce aliasing,
 324	 * and Moiré effects in the results.
 325	 */
 326	void adaptiveResize(Geometry size)
 327	{
 328		size = size.toAbsolute(columns, rows);
 329
 330		MagickCoreImage* image =
 331			AdaptiveResizeImage(imageRef, size.width, size.height, DMagickExceptionInfo());
 332
 333		imageRef = ImageRef(image);
 334	}
 335
 336	/**
 337	 * Adaptively sharpens the image by sharpening more intensely near
 338	 * image edges and less intensely far from edges. The adaptiveSharpen
 339	 * method sharpens the image with a Gaussian operator of the given
 340	 * radius and standard deviation (sigma). For reasonable results,
 341	 * radius should be larger than sigma. Use a radius of 0 and
 342	 * adaptiveSharpen selects a suitable radius for you.
 343	 *
 344	 * Params:
 345	 *     radius  = The radius of the Gaussian in pixels,
 346	 *               not counting the center pixel.
 347	 *     sigma   = The standard deviation of the Laplacian, in pixels.
 348	 *     channel = If no channels are specified, sharpens all the channels.
 349	 */
 350	void adaptiveSharpen(double radius = 0, double sigma = 1, ChannelType channel = ChannelType.DefaultChannels)
 351	{
 352		MagickCoreImage* image =
 353			AdaptiveSharpenImageChannel(imageRef, channel, radius, sigma, DMagickExceptionInfo());
 354
 355		imageRef = ImageRef(image);
 356	}
 357
 358	/**
 359	 * Selects an individual threshold for each pixel based on the range
 360	 * of intensity values in its local neighborhood. This allows for
 361	 * thresholding of an image whose global intensity histogram doesn't
 362	 * contain distinctive peaks.
 363	 *
 364	 * Params:
 365	 *     width  = define the width of the local neighborhood.
 366	 *     heigth = define the height of the local neighborhood.
 367	 *     offset = constant to subtract from pixel neighborhood mean.
 368	 */
 369	void adaptiveThreshold(size_t width = 3, size_t height = 3, ssize_t offset = 0)
 370	{
 371		MagickCoreImage* image =
 372			AdaptiveThresholdImage(imageRef, width, height, offset, DMagickExceptionInfo());
 373
 374		imageRef = ImageRef(image);
 375	}
 376
 377	/**
 378	 * Adds random noise to the specified channel or channels in the image.
 379	 * The amount of time addNoise requires depends on the NoiseType argument.
 380	 *
 381	 * Params:
 382	 *     type    = A NoiseType value.
 383	 *     channel = 0 or more ChannelType arguments. If no channels are
 384	 *               specified, adds noise to all the channels
 385	 */
 386	void addNoise(NoiseType type, ChannelType channel = ChannelType.DefaultChannels)
 387	{
 388		MagickCoreImage* image =
 389			AddNoiseImageChannel(imageRef, channel, type, DMagickExceptionInfo());
 390
 391		imageRef = ImageRef(image);
 392	}
 393
 394	/**
 395	 * Transforms the image as specified by the affine matrix.
 396	 */
 397	void affineTransform(AffineMatrix affine)
 398	{
 399		MagickCoreImage* image =
 400			AffineTransformImage(imageRef, &affine, DMagickExceptionInfo());
 401
 402		imageRef = ImageRef(image);
 403	}
 404
 405	/**
 406	 * Annotates an image with text.  Optionally you can include any 
 407	 * of the following bits of information about the image by embedding
 408	 * the appropriate special characters:
 409	 * --------------------
 410	 *     %b   file size in bytes.
 411	 *     %c   comment.
 412	 *     %d   directory in which the image resides.
 413	 *     %e   extension of the image file.
 414	 *     %f   original filename of the image.
 415	 *     %h   height of image.
 416	 *     %i   filename of the image.
 417	 *     %k   number of unique colors.
 418	 *     %l   image label.
 419	 *     %m   image file format.
 420	 *     %n   number of images in a image sequence.
 421	 *     %o   output image filename.
 422	 *     %p   page number of the image.
 423	 *     %q   image depth (8 or 16).
 424	 *     %q   image depth (8 or 16).
 425	 *     %s   image scene number.
 426	 *     %t   image filename without any extension.
 427	 *     %u   a unique temporary filename.
 428	 *     %w   image width.
 429	 *     %x   x resolution of the image.
 430	 *     %y   y resolution of the image.
 431	 *--------------------
 432	 * Params:
 433	 *     text    = The text.
 434	 *     xOffset = The x coordinate.
 435	 *     yOffset = The y coordinate.
 436	 *     gravity = Placement gravity.
 437	 *     degrees = The angle of the Text.
 438	 */
 439	void annotate(
 440		string text,
 441		size_t xOffset,
 442		size_t yOffset,
 443		GravityType gravity = GravityType.NorthWestGravity,
 444		double degrees = 0.0)
 445	{
 446		annotate(text, Geometry(size_t.max, size_t.max, xOffset, yOffset), gravity, degrees);
 447	}
 448
 449	/**
 450	 * Ditto, but word wraps the text so it stays withing the
 451	 * boundingArea. if the height and width are 0 the height and
 452	 * with of the image are used to calculate the bounding area.
 453	 */
 454	void annotate(
 455		string text,
 456		Geometry boundingArea = Geometry.init,
 457		GravityType gravity = GravityType.NorthWestGravity,
 458		double degrees = 0.0)
 459	{
 460		if ( boundingArea.width == 0 )
 461			boundingArea.width = columns;
 462
 463		if ( boundingArea.height == 0 )
 464			boundingArea.height = rows;
 465
 466		if ( boundingArea.width > 0 )
 467			text = wordWrap(text, boundingArea);
 468
 469		DrawInfo* drawInfo = options.drawInfo;
 470		AffineMatrix oldAffine = options.affine;
 471
 472		copyString(drawInfo.text, text);
 473		copyString(drawInfo.geometry, boundingArea.toString());
 474
 475		drawInfo.gravity = gravity;
 476		options.transformRotation(degrees);
 477
 478		scope(exit)
 479		{
 480			copyString(drawInfo.text, null);
 481			copyString(drawInfo.geometry, null);
 482
 483			drawInfo.gravity = GravityType.NorthWestGravity;
 484			options.affine = oldAffine;
 485		}
 486
 487		AnnotateImage(imageRef, drawInfo);
 488
 489		DMagickException.throwException(&(imageRef.exception));
 490	}
 491
 492	/**
 493	 * extract the 'mean' from the image and adjust the image
 494	 * to try make set its gamma appropriatally.
 495	 * 
 496	 * Params:
 497	 *     channel = One or more channels to adjust.
 498	 */
 499	void autoGamma(ChannelType channel = ChannelType.DefaultChannels)
 500	{
 501		AutoGammaImageChannel(imageRef, channel);
 502
 503		DMagickException.throwException(&(imageRef.exception));
 504	}
 505
 506	/**
 507	 * adjusts the levels of a particular image channel by scaling
 508	 * the minimum and maximum values to the full quantum range.
 509	 * 
 510	 * Params:
 511	 *     channel = One or more channels to adjust.
 512	 */
 513	void autoLevel(ChannelType channel = ChannelType.DefaultChannels)
 514	{
 515		AutoLevelImageChannel(imageRef, channel);
 516
 517		DMagickException.throwException(&(imageRef.exception));
 518	}
 519
 520	/**
 521	 * Adjusts an image so that its orientation is suitable for viewing
 522	 * (i.e. top-left orientation). Note that only some models of modern
 523	 * digital cameras can tag an image with the orientation.
 524	 */
 525	void autoOrient()
 526	{
 527		final switch( this.orientation )
 528		{
 529			case OrientationType.UndefinedOrientation:
 530			case OrientationType.TopLeftOrientation:
 531				return;
 532
 533			case OrientationType.TopRightOrientation:
 534				flop();
 535				break;
 536
 537			case OrientationType.BottomRightOrientation:
 538				rotate(180);
 539				break;
 540
 541			case OrientationType.BottomLeftOrientation:
 542				flip();
 543				break;
 544
 545			case OrientationType.LeftTopOrientation:
 546				transpose();
 547				break;
 548
 549			case OrientationType.RightTopOrientation:
 550				rotate(90);
 551				break;
 552
 553			case OrientationType.RightBottomOrientation:
 554				transverse();
 555				break;
 556
 557			case OrientationType.LeftBottomOrientation:
 558				rotate(270);
 559				break;
 560		}
 561
 562		orientation = OrientationType.TopLeftOrientation;
 563	}
 564
 565	/**
 566	 * Changes the value of individual pixels based on the intensity
 567	 * of each pixel channel. The result is a high-contrast image.
 568	 *
 569	 * More precisely each channel value of the image is 'thresholded'
 570	 * so that if it is equal to or less than the given value it is set
 571	 * to zero, while any value greater than that give is set to it
 572	 * maximum or QuantumRange.
 573	 * 
 574	 * Params:
 575	 *     threshold = The threshold value.
 576	 *     channel   = One or more channels to adjust.
 577	 */
 578	void bilevel(Quantum threshold, ChannelType channel = ChannelType.DefaultChannels)
 579	{
 580		BilevelImageChannel(imageRef, channel, threshold);
 581
 582		DMagickException.throwException(&(imageRef.exception));
 583	}
 584
 585	/**
 586	 * Forces all pixels below the threshold into black while leaving
 587	 * all pixels above the threshold unchanged.
 588	 * 
 589	 * Params:
 590	 *     threshold = The threshold value for red green and blue.
 591	 *     channel   = One or more channels to adjust.
 592	 */
 593	void blackThreshold(Quantum threshold, ChannelType channel = ChannelType.DefaultChannels)
 594	{
 595		blackThreshold(threshold, threshold, threshold, 0, channel);
 596	}
 597
 598	///ditto
 599	void blackThreshold(
 600		Quantum red,
 601		Quantum green,
 602		Quantum blue,
 603		Quantum opacity = 0,
 604		ChannelType channel = ChannelType.DefaultChannels)
 605	{
 606		string thresholds = std.string.format("%s,%s,%s,%s", red, green, blue, opacity);
 607
 608		BlackThresholdImageChannel(
 609			imageRef, channel, toStringz(thresholds), DMagickExceptionInfo()
 610		);
 611	}
 612
 613	/**
 614	 * Adds the overlay image to the target image according to
 615	 * srcPercent and dstPercent.
 616	 * 
 617	 * This method corresponds to the -blend option of ImageMagick's
 618	 * composite command.
 619	 * 
 620	 * Params:
 621	 *     overlay       = The source image for the composite operation.
 622	 *     srcPercentage = Percentage for the source image.
 623	 *     dstPercentage = Percentage for this image.
 624	 *     xOffset       = The x offset to use for the overlay.
 625	 *     yOffset       = The y offset to use for the overlay.
 626	 *     gravity       = The gravity to use for the overlay.
 627	 */
 628	void blend(
 629		const(Image) overlay,
 630		int srcPercentage,
 631		int dstPercentage,
 632		ssize_t xOffset,
 633		ssize_t yOffset)
 634	{
 635		SetImageArtifact(imageRef, "compose:args",
 636			toStringz(std.string.format("%s,%s", srcPercentage, dstPercentage)));
 637		scope(exit) RemoveImageArtifact(imageRef, "compose:args");
 638
 639		composite(overlay, CompositeOperator.BlendCompositeOp, xOffset, yOffset);
 640	}
 641
 642	///ditto
 643	void blend(
 644		const(Image) overlay,
 645		int srcPercentage,
 646		int dstPercentage,
 647		GravityType gravity = GravityType.NorthWestGravity)
 648	{
 649		RectangleInfo geometry;
 650
 651		SetGeometry(overlay.imageRef, &geometry);
 652		GravityAdjustGeometry(columns, rows, gravity, &geometry);
 653
 654		blend(overlay, srcPercentage, dstPercentage, geometry.x, geometry.y);
 655	}
 656
 657	/**
 658	 * mutes the colors of the image to simulate a scene at
 659	 * nighttime in the moonlight.
 660	 * 
 661	 * Params:
 662	 *     factor = The shift factor, larger values increase the effect.
 663	 */
 664	void blueShift(double factor = 1.5)
 665	{
 666		MagickCoreImage* image =
 667			BlueShiftImage(imageRef, factor, DMagickExceptionInfo());
 668
 669		imageRef = ImageRef(image);
 670	}
 671
 672	/**
 673	 * Blurs the specified channel. We convolve the image with a Gaussian
 674	 * operator of the given radius and standard deviation (sigma).
 675	 * The blur method differs from gaussianBlur in that it uses a
 676	 * separable kernel which is faster but mathematically equivalent
 677	 * to the non-separable kernel.
 678	 *
 679	 * Params:
 680	 *     radius  = The radius of the Gaussian in pixels,
 681	 *               not counting the center pixel.
 682	 *     sigma   = The standard deviation of the Laplacian, in pixels.
 683	 *     channel = The channels to blur.
 684	 */
 685	void blur(double radius = 0, double sigma = 1, ChannelType channel = ChannelType.DefaultChannels)
 686	{
 687		MagickCoreImage* image =
 688			BlurImageChannel(imageRef, channel, radius, sigma, DMagickExceptionInfo());
 689
 690		imageRef = ImageRef(image);
 691	}
 692
 693	/**
 694	 * Surrounds the image with a border of the color defined
 695	 * by the borderColor property.
 696	 *
 697	 * Params:
 698	 *     width  = Border width in pixels.
 699	 *     height = Border height in pixels.
 700	 */
 701	void border(size_t width, size_t height)
 702	{
 703		RectangleInfo borderInfo = RectangleInfo(width, height);
 704
 705		MagickCoreImage* image =
 706			BorderImage(imageRef, &borderInfo, DMagickExceptionInfo());
 707
 708		imageRef = ImageRef(image);
 709	}
 710
 711	/**
 712	 * Extract channel from image. Use this option to extract a
 713	 * particular channel from the image. ChannelType.MatteChannel for
 714	 * example, is useful for extracting the opacity values from an image.
 715	 */
 716	Image channel(ChannelType channel) const
 717	{
 718		MagickCoreImage* image =
 719			SeparateImages(imageRef, channel, DMagickExceptionInfo());
 720
 721		return new Image(image);
 722	}
 723
 724	/**
 725	 * Adds a "charcoal" effect to the image. You can alter the
 726	 * intensity of the effect by changing the radius and sigma arguments.
 727	 *
 728	 * Params:
 729	 *     radius = The radius of the pixel neighborhood.
 730	 *     sigma  = The standard deviation of the Gaussian, in pixels.
 731	 */
 732	void charcoal(double radius = 0, double sigma = 1)
 733	{
 734		MagickCoreImage* image =
 735			CharcoalImage(imageRef, radius, sigma, DMagickExceptionInfo());
 736
 737		imageRef = ImageRef(image);
 738	}
 739
 740	/**
 741	 * Removes the specified rectangle and collapses the rest of
 742	 * the image to fill the removed portion.
 743	 *
 744	 * Params:
 745	 *     geometry = The horizontal and/or vertical subregion to remove.
 746	 */
 747	void chop(Geometry geometry)
 748	{
 749		RectangleInfo rectangle = geometry.rectangleInfo;
 750
 751		MagickCoreImage* image =
 752			ChopImage(imageRef, &rectangle, DMagickExceptionInfo());
 753
 754		imageRef = ImageRef(image);		
 755	}
 756
 757	/**
 758	 * Returns a copy of the image.
 759	 */
 760	Image clone() const
 761	{
 762		MagickCoreImage* image =
 763			CloneImage(imageRef, 0, 0, true, DMagickExceptionInfo());
 764
 765		return new Image(image, options.clone());
 766	}
 767
 768	/**
 769	 * replaces each color value in the given image, by using it as an
 770	 * index to lookup a replacement color value in a Color Look UP Table
 771	 * in the form of an image.  The values are extracted along a diagonal
 772	 * of the CLUT image so either a horizontal or vertial gradient image
 773	 * can be used.
 774	 * 
 775	 * Typically this is used to either re-color a gray-scale image
 776	 * according to a color gradient in the CLUT image, or to perform a
 777	 * freeform histogram (level) adjustment according to the (typically
 778	 * gray-scale) gradient in the CLUT image.
 779	 * 
 780	 * When the 'channel' mask includes the matte/alpha transparency
 781	 * channel but one image has no such channel it is assumed that that
 782	 * image is a simple gray-scale image that will effect the alpha channel
 783	 * values, either for gray-scale coloring (with transparent or
 784	 * semi-transparent colors), or a histogram adjustment of existing alpha
 785	 * channel values. If both images have matte channels, direct and normal
 786	 * indexing is applied, which is rarely used.
 787	 *
 788	 * Params:
 789	 *     clutImage = the color lookup table image for replacement
 790	 *                 color values.
 791	 *     channel   = One or more channels to adjust.
 792	 */
 793	void clut(Image clutImage, ChannelType channel = ChannelType.DefaultChannels)
 794	{
 795		ClutImageChannel(imageRef, channel, clutImage.imageRef);
 796
 797		DMagickException.throwException(&(imageRef.exception));
 798	}
 799
 800	/**
 801	 * Applies a lightweight Color Correction Collection (CCC) file
 802	 * to the image. The file solely contains one or more color corrections.
 803	 * Here is a sample:
 804	 * --------------------
 805	 * <ColorCorrectionCollection xmlns="urn:ASC:CDL:v1.2">
 806	 *     <ColorCorrection id="cc03345">
 807	 *         <SOPNode>
 808	 *             <Slope> 0.9 1.2 0.5 </Slope>
 809	 *             <Offset> 0.4 -0.5 0.6 </Offset>
 810	 *             <Power> 1.0 0.8 1.5 </Power>
 811	 *         </SOPNode>
 812	 *         <SATNode>
 813	 *             <Saturation> 0.85 </Saturation>
 814	 *         </SATNode>
 815	 *     </ColorCorrection>
 816	 * </ColorCorrectionCollection>
 817	 * --------------------
 818	 * which includes the slop, offset, and power for each of
 819	 * the RGB channels as well as the saturation.
 820	 * 
 821	 * See_Also: $(LINK2 http://en.wikipedia.org/wiki/ASC_CDL,
 822	 *         Wikipedia ASC CDL).
 823	 */
 824	void colorDecisionList(string colorCorrectionCollection)
 825	{
 826		ColorDecisionListImage(imageRef, toStringz(colorCorrectionCollection));
 827
 828		DMagickException.throwException(&(imageRef.exception));
 829	}
 830
 831	/**
 832	 * Blend the fill color with the image pixels. The opacityRed,
 833	 * opacityGreen, opacityBlue and opacityAlpha arguments are the
 834	 * percentage to blend with the red, green, blue and alpha channels.
 835	 */
 836	void colorize(Color fill, uint opacityRed, uint opacityGreen, uint opacityBlue, uint opacityAlpha = 0)
 837	{
 838		string opacity = std.string.format("%s/%s/%s/%s",
 839			opacityRed, opacityGreen, opacityBlue, opacityAlpha);
 840
 841		MagickCoreImage* image =
 842			ColorizeImage(imageRef, toStringz(opacity), fill.pixelPacket, DMagickExceptionInfo());
 843
 844		imageRef = ImageRef(image);
 845	}
 846
 847	/**
 848	 * Applies color transformation to an image. This method permits
 849	 * saturation changes, hue rotation, luminance to alpha, and various
 850	 * other effects.  Although variable-sized transformation matrices can
 851	 * be used, typically one uses a 5x5 matrix for an RGBA image and a 6x6
 852	 * for CMYKA (or RGBA with offsets).  The matrix is similar to those
 853	 * used by Adobe Flash except offsets are in column 6 rather than 5
 854	 * (in support of CMYKA images) and offsets are normalized
 855	 * (divide Flash offset by 255)
 856	 *
 857	 * Params:
 858	 *     matrix = A tranformation matrix, with a maximum size of 6x6.
 859	 */
 860	void colorMatrix(double[][] matrix)
 861	{
 862		if ( matrix.length > 6 || matrix[0].length > 6 )
 863			throw new DMagickException("Matrix must be 6x6 or smaller.");
 864
 865		static if ( is(typeof(ColorMatrixImage)) )
 866		{
 867			KernelInfo* kernelInfo = AcquireKernelInfo("1");
 868			scope(exit) DestroyKernelInfo(kernelInfo);
 869
 870			kernelInfo.width = matrix[0].length;
 871			kernelInfo.height = matrix.length;
 872			kernelInfo.values = cast(double*)AcquireQuantumMemory(kernelInfo.width*kernelInfo.height, double.sizeof);
 873			scope(exit) kernelInfo.values = cast(double*)RelinquishMagickMemory(kernelInfo.values);
 874
 875			foreach ( i, row; matrix )
 876			{
 877				size_t offset = i * row.length;
 878
 879				kernelInfo.values[offset .. offset+row.length] = row;
 880			}
 881
 882			MagickCoreImage* image =
 883				ColorMatrixImage(imageRef, kernelInfo, DMagickExceptionInfo());
 884		}
 885		else
 886		{
 887			double[] values;
 888
 889			foreach ( i, row; matrix )
 890			{
 891				size_t offset = i * row.length;
 892
 893				values[offset .. offset+row.length] = row;
 894			}
 895
 896			MagickCoreImage* image =
 897				RecolorImage(imageRef, matrix.length, values.ptr, DMagickExceptionInfo());
 898		}
 899
 900		imageRef = ImageRef(image);
 901	}
 902
 903	/**
 904	 * Compare current image with another image. Sets meanErrorPerPixel,
 905	 * normalizedMaxError , and normalizedMeanError in the current image.
 906	 * false is returned if the images are identical. An ErrorOption
 907	 * exception is thrown if the reference image columns, rows, colorspace,
 908	 * or matte differ from the current image.
 909	 */
 910	bool compare(const(Image) referenceImage)
 911	{
 912		bool isEqual = IsImagesEqual(imageRef, referenceImage.imageRef) == 1;
 913		DMagickException.throwException(&(imageRef.exception));
 914
 915		return isEqual;
 916	}
 917
 918	/**
 919	 * Composites dest onto this image using the specified composite operator.
 920	 *
 921	 * Params:
 922	 *     overlay     = Image to use in the composite operation.
 923	 *     compositeOp = The composite operation to use.
 924	 *     xOffset     = The x-offset of the composited image,
 925	 *                   measured from the upper-left corner
 926	 *                   of the image.
 927	 *     yOffset     = The y-offset of the composited image,
 928	 *                   measured from the upper-left corner
 929	 *                   of the image.
 930	 *     gravity     = The gravity that defines the location of the
 931	 *                   location of overlay.
 932	 *     channel     = One or more channels to compose.
 933	 */
 934	void composite(
 935		const(Image) overlay,
 936		CompositeOperator compositeOp,
 937		ssize_t xOffset,
 938		ssize_t yOffset,
 939		ChannelType channel = ChannelType.DefaultChannels)
 940	{
 941		CompositeImageChannel(imageRef, channel, compositeOp, overlay.imageRef, xOffset, yOffset);
 942
 943		DMagickException.throwException(&(imageRef.exception));
 944	}
 945
 946	///ditto
 947	void composite(
 948		const(Image) overlay,
 949		CompositeOperator compositeOp,
 950		GravityType gravity = GravityType.NorthWestGravity,
 951		ChannelType channel = ChannelType.DefaultChannels)
 952	{
 953		RectangleInfo geometry;
 954
 955		SetGeometry(overlay.imageRef, &geometry);
 956		GravityAdjustGeometry(columns, rows, gravity, &geometry);
 957
 958		composite(overlay, compositeOp, geometry.x, geometry.y, channel);
 959	}
 960
 961	/**
 962	 * Merge the source and destination images according to the
 963	 * formula a*Sc*Dc + b*Sc + c*Dc + d where Sc is the source
 964	 * pixel and Dc is the destination pixel.
 965	 *
 966	 * Params:
 967	 *     overlay = Image to use in to composite operation.
 968	 *     xOffset = The x-offset of the composited image,
 969	 *               measured from the upper-left corner
 970	 *               of the image.
 971	 *     yOffset = The y-offset of the composited image,
 972	 *               measured from the upper-left corner
 973	 *               of the image.
 974	 *     gravity = The gravity that defines the location of the
 975	 *               location of overlay.
 976	 *     channel = One or more channels to compose.
 977	 */
 978	void composite(
 979		const(Image) overlay,
 980		double a,
 981		double b,
 982		double c,
 983		double d,
 984		ssize_t xOffset,
 985		ssize_t yOffset,
 986		ChannelType channel = ChannelType.DefaultChannels)
 987	{
 988		SetImageArtifact(imageRef, "compose:args",
 989			toStringz(std.string.format("%s,%s,%s,%s", a, b, c, d)));
 990		scope(exit) RemoveImageArtifact(imageRef, "compose:args");
 991
 992		composite(overlay, CompositeOperator.MathematicsCompositeOp, xOffset, yOffset, channel);
 993	}
 994
 995	///ditto
 996	void composite(
 997		const(Image) overlay,
 998		double a,
 999		double b,
1000		double c,
1001		double d,
1002		GravityType gravity = GravityType.NorthWestGravity,
1003		ChannelType channel = ChannelType.DefaultChannels)
1004	{
1005		RectangleInfo geometry;
1006
1007		SetGeometry(overlay.imageRef, &geometry);
1008		GravityAdjustGeometry(columns, rows, gravity, &geometry);
1009
1010		composite(overlay, a, b, c, d, geometry.x, geometry.y, channel);
1011	}
1012
1013	/**
1014	 * Composites multiple copies of the source image across and down
1015	 * the image, producing the same results as ImageMagick's composite
1016	 * command with the -tile option.
1017	 *
1018	 * Params:
1019	 *     overlay     = Image to use in to composite operation.
1020	 *     compositeOp = The composite operation to use.
1021	 *     channel     = One or more channels to compose.
1022	 */
1023	void compositeTiled(
1024		const(Image) overlay,
1025		CompositeOperator compositeOp,
1026		ChannelType channel = ChannelType.DefaultChannels)
1027	{
1028		SetImageArtifact(imageRef, "compose:outside-overlay", "false");
1029		scope(exit) RemoveImageArtifact(imageRef, "compose:outside-overlay");
1030
1031		for ( size_t y = 0; y < rows; y += overlay.rows )
1032			for ( size_t x = 0; x < columns; x += overlay.columns )
1033				composite(overlay, compositeOp, x, y, channel);
1034	}
1035
1036	/**
1037	 * enhances the intensity differences between the lighter and
1038	 * darker elements of the image.
1039	 *
1040	 * Params:
1041	 *     sharpen = If true increases the image contrast otherwise
1042	 *               the contrast is reduced.
1043	 */
1044	void contrast(bool sharpen = false)
1045	{
1046		ContrastImage(imageRef, sharpen);
1047		DMagickException.throwException(&(imageRef.exception));
1048	}
1049
1050	/**
1051	 * This is a simple image enhancement technique that attempts to
1052	 * improve the contrast in an image by `stretching' the range of
1053	 * intensity values it contains to span a desired range of values.
1054	 * It differs from the more sophisticated histogram equalization in
1055	 * that it can only apply a linear scaling function to the image pixel
1056	 * values. As a result the `enhancement' is less harsh.
1057	 *
1058	 * Params:
1059	 *     blackPoint = Black out at most this many pixels.
1060	 *                  Specify an apsulute number of pixels or an
1061	 *                  percentage by passing a value between 1 and 0
1062	 *     whitePoint = Burn at most this many pixels.
1063	 *                  Specify an apsulute number of pixels or an
1064	 *                  percentage by passing a value between 1 and 0
1065	 *     channel    = One or more channels to adjust.
1066	 */
1067	void contrastStretch(double blackPoint, double whitePoint, ChannelType channel = ChannelType.DefaultChannels)
1068	{
1069		if ( blackPoint < 1 )
1070			blackPoint *= QuantumRange;
1071		if ( whitePoint < 1 )
1072			whitePoint *= QuantumRange;
1073
1074		ContrastStretchImageChannel(imageRef, channel, blackPoint, whitePoint);
1075		DMagickException.throwException(&(imageRef.exception));
1076	}
1077
1078	/**
1079	 * Applies a custom convolution kernel to the image.
1080	 * See_Also: $(LINK2 http://www.dai.ed.ac.uk/HIPR2/convolve.htm,
1081	 *        Convolution in the Hypermedia Image Processing Reference).
1082	 */
1083	void convolve(double[][] matrix, ChannelType channel = ChannelType.DefaultChannels)
1084	{
1085		double[] kernel = new double[matrix.length * matrix[0].length];
1086
1087		foreach ( i, row; matrix )
1088		{
1089			size_t offset = i * row.length;
1090
1091			kernel[offset .. offset+row.length] = row;
1092		}
1093
1094		MagickCoreImage* image =
1095			ConvolveImageChannel(imageRef, channel, matrix.length, kernel.ptr,  DMagickExceptionInfo());
1096
1097		imageRef = ImageRef(image);
1098	}
1099
1100	/**
1101	 * Extract a region of the image starting at the offset defined by
1102	 * geometry.  Region must be fully defined, and no special handling
1103	 * of geometry flags is performed.
1104	 */
1105	void crop(Geometry geometry)
1106	{
1107		RectangleInfo rectangle = geometry.rectangleInfo;
1108
1109		MagickCoreImage* image =
1110			CropImage(imageRef, &rectangle, DMagickExceptionInfo());
1111
1112		imageRef = ImageRef(image);
1113	}
1114
1115	/**
1116	 * displaces an image's colormap by a given number of positions.
1117	 * If you cycle the colormap a number of times you can produce
1118	 * a psychodelic effect.
1119	 */
1120	void cycleColormap(ssize_t amount)
1121	{
1122		CycleColormapImage(imageRef, amount);
1123		DMagickException.throwException(&(imageRef.exception));
1124	}
1125
1126	/**
1127	 * Decipher an enciphered image.
1128	 */
1129	void decipher(string passphrase)
1130	{
1131		DecipherImage(imageRef, toStringz(passphrase), DMagickExceptionInfo());
1132	}
1133
1134	/**
1135	 * Straightens an image. A threshold of 40% works for most images.
1136	 * 
1137	 * Skew is an artifact that occurs in scanned images because of the
1138	 * camera being misaligned, imperfections in the scanning or surface,
1139	 * or simply because the paper was not placed completely flat when
1140	 * scanned.
1141	 * 
1142	 * Params:
1143	 *     threshold     = Specify an apsulute number of pixels or an
1144	 *                     percentage by passing a value between 1 and 0.
1145	 *     autoCropWidth = Specify a value for this argument to cause the
1146	 *                     deskewed image to be auto-cropped.
1147	 *                     The argument is the pixel width of the
1148	 *                     image background (e.g. 40).
1149	 *                     A width of 0 disables auto cropping.
1150	 */
1151	void deskew(double threshold = 0.4, size_t autoCropWidth = 0)
1152	{
1153		if ( autoCropWidth > 0 )
1154		{
1155			SetImageArtifact(imageRef, "deskew:auto-crop", toStringz(to!(string)(autoCropWidth)) );
1156			scope(exit) RemoveImageArtifact(imageRef, "deskew:auto-crop");
1157		}
1158
1159		if ( threshold < 1 )
1160			threshold *= QuantumRange;
1161
1162		MagickCoreImage* image =
1163			DeskewImage(imageRef, threshold, DMagickExceptionInfo());
1164
1165		imageRef = ImageRef(image);
1166	}
1167
1168	/**
1169	 * Reduces the speckle noise in an image while perserving
1170	 * the edges of the original image.
1171	 */
1172	void despeckle()
1173	{
1174		MagickCoreImage* image =
1175			DespeckleImage(imageRef, DMagickExceptionInfo());
1176
1177		imageRef = ImageRef(image);
1178	}
1179
1180	/**
1181	 * Uses displacementMap to move color from img to the output image.
1182	 * 
1183	 * This method corresponds to the -displace option of ImageMagick's
1184	 * composite command.
1185	 * 
1186	 * Params:
1187	 *     displacementMap = 
1188	 *                  The source image for the composite operation.
1189	 *     xAmplitude = The maximum displacement on the x-axis.
1190	 *     yAmplitude = The maximum displacement on the y-axis.
1191	 *     xOffset    = The x offset to use.
1192	 *     yOffset    = The y offset to use.
1193	 *     gravity    = The gravity to use.
1194	 */
1195	void displace(
1196		const(Image) displacementMap,
1197		int xAmplitude,
1198		int yAmplitude,
1199		ssize_t xOffset,
1200		ssize_t yOffset)
1201	{
1202		SetImageArtifact(imageRef, "compose:args",
1203			toStringz(std.string.format("%s,%s", xAmplitude, yAmplitude)));
1204		scope(exit) RemoveImageArtifact(imageRef, "compose:args");
1205
1206		composite(displacementMap, CompositeOperator.DisplaceCompositeOp, xOffset, yOffset);
1207	}
1208
1209	///ditto
1210	void displace(
1211		const(Image) overlay,
1212		int srcPercentage,
1213		int dstPercentage,
1214		GravityType gravity = GravityType.NorthWestGravity)
1215	{
1216		RectangleInfo geometry;
1217
1218		SetGeometry(overlay.imageRef, &geometry);
1219		GravityAdjustGeometry(columns, rows, gravity, &geometry);
1220
1221		displace(overlay, srcPercentage, dstPercentage, geometry.x, geometry.y);
1222	}	
1223
1224	/**
1225	 * Display image on screen.
1226	 * 
1227	 * $(RED Caution:) if an image format is is not compatible with
1228	 * the display visual (e.g. JPEG on a colormapped display)
1229	 * then the original image will be altered. Use a copy of the
1230	 * original if this is a problem.
1231	 */
1232	void display()
1233	{
1234		version(DMagick_No_Display)
1235		{
1236		}
1237		else
1238		{
1239			version(Windows)
1240			{
1241				Window win = new Window(this);
1242				win.display();
1243			}
1244			else
1245			{
1246				DisplayImages(options.imageInfo, imageRef);
1247
1248				DMagickException.throwException(&(imageRef.exception));
1249			}
1250		}
1251	}
1252
1253	/**
1254	 * Composites the overlay image onto this image. The opacity
1255	 * of this image is multiplied by dstPercentage and opacity of
1256	 * overlay is multiplied by srcPercentage.
1257	 * 
1258	 * This method corresponds to the -dissolve option
1259	 * of ImageMagick's composite command.
1260	 * 
1261	 * Params:
1262	 *     overlay       = The source image for the composite operation.
1263	 *     srcPercentage = Percentage for the source image.
1264	 *     dstPercentage = Percentage for this image.
1265	 *     xOffset       = The x offset to use for the overlay.
1266	 *     yOffset       = The y offset to use for the overlay.
1267	 *     gravity       = The gravity to use for the overlay.
1268	 */
1269	void dissolve(
1270		const(Image) overlay,
1271		int srcPercentage,
1272		int dstPercentage,
1273		ssize_t xOffset,
1274		ssize_t yOffset)
1275	{
1276		SetImageArtifact(imageRef, "compose:args",
1277			toStringz(std.string.format("%s,%s", srcPercentage, dstPercentage)));
1278		scope(exit) RemoveImageArtifact(imageRef, "compose:args");
1279
1280		composite(overlay, CompositeOperator.DissolveCompositeOp, xOffset, yOffset);
1281	}
1282
1283	///ditto
1284	void dissolve(
1285		const(Image) overlay,
1286		int srcPercentage,
1287		int dstPercentage,
1288		GravityType gravity = GravityType.NorthWestGravity)
1289	{
1290		RectangleInfo geometry;
1291
1292		SetGeometry(overlay.imageRef, &geometry);
1293		GravityAdjustGeometry(columns, rows, gravity, &geometry);
1294
1295		dissolve(overlay, srcPercentage, dstPercentage, geometry.x, geometry.y);
1296	}	
1297
1298	/**
1299	 * Distort an image using the specified distortion type and its
1300	 * required arguments. This method is equivalent to ImageMagick's
1301	 * -distort option.
1302	 * 
1303	 * Params:
1304	 *     method    = Distortion method to use.
1305	 *     arguments = An array of numbers. The size of the array
1306	 *                 depends on the distortion type.
1307	 *     bestfit   = If enabled, and the distortion allows it,
1308	 *                 the destination image is adjusted to ensure 
1309	 *                 the whole source image will just fit within
1310	 *                 the final destination image, which will be
1311	 *                 sized and offset accordingly.
1312	 */
1313	void distort(DistortImageMethod method, double[] arguments, bool bestfit = false)
1314	{
1315		MagickCoreImage* image =
1316			DistortImage(imageRef, method, arguments.length, arguments.ptr, bestfit, DMagickExceptionInfo());
1317
1318		imageRef = ImageRef(image);
1319	}
1320
1321	/**
1322	 * Finds edges in an image.
1323	 * 
1324	 * Params:
1325	 *     radius = the radius of the convolution filter.
1326	 *              If 0 a suitable default is selected.
1327	 */
1328	void edge(double radius = 0)
1329	{
1330		MagickCoreImage* image =
1331			EdgeImage(imageRef, radius, DMagickExceptionInfo());
1332
1333		imageRef = ImageRef(image);
1334	}
1335
1336	/**
1337	 * Emboss image (hilight edges with 3D effect).
1338	 * 
1339	 * Params:
1340	 *     radius = The radius of the Gaussian, in pixels,
1341	 *              not counting the center pixel.
1342	 *     sigma  = The standard deviation of the Laplacian, in pixels.
1343	 */
1344	void emboss(double radius = 0, double sigma = 1)
1345	{
1346		MagickCoreImage* image =
1347			EmbossImage(imageRef, radius, sigma, DMagickExceptionInfo());
1348
1349		imageRef = ImageRef(image);
1350	}
1351
1352	/**
1353	 * Encipher an image.
1354	 */
1355	void encipher(string passphrase)
1356	{
1357		EncipherImage(imageRef, toStringz(passphrase), DMagickExceptionInfo());
1358	}
1359
1360	/**
1361	 * Applies a digital filter that improves the quality of a noisy image.
1362	 */
1363	void enhance()
1364	{
1365		MagickCoreImage* image =
1366			EnhanceImage(imageRef, DMagickExceptionInfo());
1367
1368		imageRef = ImageRef(image);
1369	}
1370
1371	/**
1372	 * Applies a histogram equalization to the image.
1373	 */
1374	void equalize(ChannelType channel = ChannelType.DefaultChannels)
1375	{
1376		EqualizeImageChannel(imageRef, channel);
1377
1378		DMagickException.throwException(&(imageRef.exception));
1379	}
1380
1381	/**
1382	 * Initializes the image pixels to the image background color.
1383	 */
1384	void erase()
1385	{
1386		SetImageBackgroundColor(imageRef);
1387
1388		DMagickException.throwException(&(imageRef.exception));
1389	}
1390
1391	/**
1392	 * Applies a value to the image with an arithmetic, relational, or
1393	 * logical operator to an image. Use these operations to lighten or
1394	 * darken an image, to increase or decrease contrast in an image, or
1395	 * to produce the "negative" of an image.
1396	 *
1397	 * See_Also: $(LINK2 http://www.imagemagick.org/script/command-line-options.php#evaluate,
1398	 *     ImageMagick's -_evaluate option).
1399	 */
1400	void evaluate(MagickEvaluateOperator op, double value, ChannelType channel = ChannelType.DefaultChannels)
1401	{
1402		EvaluateImageChannel(imageRef, channel, op, value,  DMagickExceptionInfo());
1403	}
1404
1405	/**
1406	 * This method is very similar to crop. It extracts the rectangle
1407	 * specified by its arguments from the image and returns it as a new
1408	 * image. However, excerpt does not respect the virtual page offset and
1409	 * does not update the page offset and is more efficient than cropping.
1410	 * 
1411	 * It is the caller's responsibility to ensure that the rectangle lies
1412	 * entirely within the original image.
1413	 */
1414	void excerpt(Geometry geometry)
1415	{
1416		RectangleInfo rectangle = geometry.rectangleInfo;
1417
1418		MagickCoreImage* image =
1419			ExcerptImage(imageRef, &rectangle, DMagickExceptionInfo());
1420
1421		imageRef = ImageRef(image);
1422	}
1423
1424	/**
1425	 * Extracts the pixel data from the specified rectangle.
1426	 *
1427	 * Params:
1428	 *     area = Area to extract.
1429	 *     map  = This character string can be any combination
1430	 *            or order of R = red, G = green, B = blue, A = 
1431	 *            alpha, C = cyan, Y = yellow, M = magenta, and K = black.
1432	 *            The ordering reflects the order of the pixels in
1433	 *            the supplied pixel array.
1434	 * 
1435	 * Returns: An array of values containing the pixel components as
1436	 *          defined by the map parameter and the Type.
1437	 */
1438	T[] exportPixels(T)(Geometry area, string map = "RGBA") const
1439	{
1440		T[] pixels = new T[(area.width * area.height) * map.length];
1441
1442		exportPixels(area, pixels, map);
1443
1444		return pixels;
1445	}
1446
1447	/**
1448	 * Ditto, but takes an existing pixel buffer.
1449	 *
1450	 * Throws: An ImageException if the buffer length is insufficient.
1451	 */
1452	void exportPixels(T)(Geometry area, T[] pixels, string map = "RGBA") const
1453	{
1454		if ( pixels.length < (area.width * area.height) * map.length )
1455			throw new ImageException(std.string.format("Pixel buffer needs more storage for %s channels.", map));
1456
1457		StorageType storage = getStorageType!(T);
1458
1459		ExportImagePixels(
1460			imageRef,
1461			area.xOffset, 
1462			area.yOffset,
1463			area.width,   
1464			area.height,
1465			toStringz(map), 
1466			storage, 
1467			pixels.ptr, 
1468			DMagickExceptionInfo());
1469	}
1470
1471	unittest
1472	{
1473		Image image = new Image(Geometry(100, 100), new Color("red"));
1474		byte[] bytes = image.exportPixels!(byte)(Geometry(10,10,10,10));
1475
1476		assert(bytes.length == 10 * 10 * 4);
1477	}
1478
1479	/**
1480	 * If the Geometry is larger than this Image, extends the image to
1481	 * the specified geometry. And the new pixels are set to the
1482	 * background color. If the Geometry is smaller than this image
1483	 * crops the image.
1484	 * 
1485	 * The new image is composed over the background using
1486	 * the composite operator specified by the compose property.
1487	 */
1488	void extent(Geometry geometry)
1489	{
1490		RectangleInfo rectangle = geometry.rectangleInfo;
1491
1492		MagickCoreImage* image =
1493			ExtentImage(imageRef, &rectangle, DMagickExceptionInfo());
1494
1495		imageRef = ImageRef(image);
1496	}
1497
1498	/**
1499	 * This interesting method searches for a rectangle in the image that
1500	 * is similar to the target. For the rectangle to be similar each pixel
1501	 * in the rectangle must match the corresponding pixel in the target
1502	 * image within the range specified by the fuzz property of this image
1503	 * and the target image.
1504	 *
1505	 * Params:
1506	 *     target  = An image that forms the target of the search.
1507	 *     xOffset = The starting x position to search for a match.
1508	 *     yOffset = The starting y position to search for a match.
1509	 * 
1510	 * Returns: The size and location of the match.
1511	 */
1512	Geometry findSimilarRegion(Image target, ssize_t xOffset, ssize_t yOffset)
1513	{
1514		IsImageSimilar(imageRef, target.imageRef, &xOffset, &yOffset, DMagickExceptionInfo());
1515
1516		return Geometry(target.columns, target.rows, xOffset, yOffset);
1517	}
1518
1519	/**
1520	 * creates a vertical mirror image by reflecting the pixels
1521	 * around the central x-axis.
1522	 */
1523	void flip()
1524	{
1525		FlipImage(imageRef, DMagickExceptionInfo());
1526	}
1527
1528	/**
1529	 * Changes the color value of any pixel that matches target and is an
1530	 * immediate neighbor. To the fillColor or fillPattern set for this
1531	 * image. If fillToBorder is true, the color value is changed
1532	 * for any neighbor pixel that does not match the borderColor.
1533	 * 
1534	 * By default target must match a particular pixel color exactly.
1535	 * However, in many cases two colors may differ by a small amount.
1536	 * The fuzz property of image defines how much tolerance is acceptable
1537	 * to consider two colors as the same. For example, set fuzz to 10 and
1538	 * the color red at intensities of 100 and 102 respectively are now
1539	 * interpreted as the same color for the purposes of the floodfill.
1540	 * 
1541	 * Params:
1542	 *     xOffset      = Starting x location for the operation.
1543	 *     xOffset      = Starting y location for the operation.
1544	 *     fillToBorder = If true fill untill the borderColor, else only
1545	 *                    the target color if affected.
1546	 *     channel      = The affected channels.
1547	 */
1548	void floodFill(
1549		ssize_t xOffset,
1550		ssize_t yOffset,
1551		bool fillToBorder = false,
1552		ChannelType channel = ChannelType.DefaultChannels)
1553	{
1554		MagickPixelPacket target;
1555
1556		GetMagickPixelPacket(imageRef, &target);
1557
1558		if ( fillToBorder )
1559		{
1560			setMagickPixelPacket(&target, borderColor);
1561		}
1562		else
1563		{
1564			PixelPacket packet;
1565			GetOneAuthenticPixel(imageRef, xOffset, yOffset, &packet, DMagickExceptionInfo());
1566
1567			setMagickPixelPacket(&target, new Color(packet));
1568		}
1569
1570		FloodfillPaintImage(imageRef, channel, options.drawInfo, &target, xOffset, yOffset, fillToBorder);
1571
1572		DMagickException.throwException(&(imageRef.exception));
1573	}
1574
1575	/**
1576	 * Fill the image like floodFill but use the specified colors.
1577	 *
1578	 * Params:
1579	 *     xOffset     = Starting x location for the operation.
1580	 *     xOffset     = Starting y location for the operation.
1581	 *     fillColor   = Fill color to use.
1582	 *     borderColor = borderColor to use.
1583	 *     channel     = The affected channels.
1584	 */
1585	void floodFillColor(
1586		ssize_t xOffset,
1587		ssize_t yOffset,
1588		Color fillColor,
1589		Color borderColor = null,
1590		ChannelType channel = ChannelType.DefaultChannels)
1591	{
1592		Color oldFillColor = options.fillColor;
1593		options.fillColor = fillColor;
1594		scope(exit) options.fillColor = oldFillColor;
1595
1596		floodFillPattern(xOffset, yOffset, null, borderColor, channel);
1597	}
1598
1599	/**
1600	 * Fill the image like floodFill but use the specified
1601	 * pattern an borderColor.
1602	 *
1603	 * Params:
1604	 *     xOffset     = Starting x location for the operation.
1605	 *     xOffset     = Starting y location for the operation.
1606	 *     fillPattern = Fill pattern to use.
1607	 *     borderColor = borderColor to use.
1608	 *     channel     = The affected channels.
1609	 */
1610	void floodFillPattern(
1611		ssize_t xOffset,
1612		ssize_t yOffset,
1613		Image fillPattern,
1614		Color borderColor = null,
1615		ChannelType channel = ChannelType.DefaultChannels)
1616	{
1617		// Cast away const, so we can temporarily hold
1618		// The image and asign it back to the fillPattern. 
1619		Image oldFillPattern = cast(Image)options.fillPattern;
1620		options.fillPattern = fillPattern;
1621		scope(exit) options.fillPattern = oldFillPattern;
1622
1623		Color oldBorderColor = this.borderColor;
1624		this.borderColor = borderColor;
1625		scope(exit) this.borderColor = oldBorderColor;
1626
1627		// If the borderColor !is null, set fillToBorder to true.
1628		floodFill(xOffset, yOffset, borderColor !is null, channel);
1629	}
1630
1631	/**
1632	 * creates a horizontal mirror image by reflecting the pixels
1633	 * around the central y-axis.
1634	 */
1635	void flop()
1636	{
1637		FlopImage(imageRef, DMagickExceptionInfo());
1638	}
1639
1640	/**
1641	 * Adds a simulated 3D border.
1642	 * The matteColor is used to draw the frame.
1643	 * 
1644	 * Params:
1645	 *     geometry = The size portion indicates the width and height of
1646	 *                the frame. If no offsets are given then the border
1647	 *                added is a solid color. Offsets x and y, if present,
1648	 *                specify that the width and height of the border is
1649	 *  

Large files files are truncated, but you can click here to view the full file