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