PageRenderTime 71ms CodeModel.GetById 22ms 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
  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. * partitioned to form an outer bevel of thickness x
  1442. * pixels and an inner bevel of thickness y pixels.
  1443. * Negative offsets make no sense as frame arguments.
  1444. */
  1445. void frame(Geometry geometry)
  1446. {
  1447. FrameInfo frameInfo;
  1448. frameInfo.width = columns + ( 2 * geometry.width );
  1449. frameInfo.height = rows + ( 2 * geometry.height );
  1450. frameInfo.x = geometry.width;
  1451. frameInfo.y = geometry.height;
  1452. frameInfo.inner_bevel = geometry.yOffset;
  1453. frameInfo.outer_bevel = geometry.xOffset;
  1454. MagickCoreImage* image =
  1455. FrameImage(imageRef, &frameInfo, DMagickExceptionInfo());
  1456. imageRef = ImageRef(image);
  1457. }
  1458. /**
  1459. * Applies a value to the image with an arithmetic, relational, or
  1460. * logical operator to an image. Use these operations to lighten or
  1461. * darken an image, to increase or decrease contrast in an image, or
  1462. * to produce the "negative" of an image.
  1463. *
  1464. * This method is equivalent to the
  1465. * $(LINK2 http://www.imagemagick.org/script/command-line-options.php#function,
  1466. * convert -function) option.
  1467. *
  1468. * Params:
  1469. * function = The MagickFunction to use.
  1470. * params =
  1471. * An array of values to be used by the function.
  1472. * $(LIST $(COMMA $(B PolynomialFunction:)
  1473. * The Polynomial function takes an arbitrary number of
  1474. * parameters, these being the coefficients of a polynomial,
  1475. * in decreasing order of degree. That is, entering
  1476. * [aₙ, aₙ₋₁, ... a₁, a₀] will invoke a polynomial function
  1477. * given by: aₙ uⁿ + aₙ₋₁ uⁿ⁻¹ + ··· a₁ u + a₀, where where
  1478. * u is pixel's original normalized channel value.),
  1479. * $(COMMA $(B SinusoidFunction:)
  1480. * These values are given as one to four parameters, as
  1481. * follows, [freq, phase, amp, bias] if omitted the default
  1482. * values will be used: [1.0, 0.0, 0.5, 0.5].),
  1483. * $(COMMA $(B ArcsinFunction:)
  1484. * These values are given as one to four parameters, as
  1485. * follows, [width, center, range, bias] if omitted the
  1486. * default values will be used: [1.0, 0.5, 1.0, 0.5].),
  1487. * $(COMMA $(B ArctanFunction:)
  1488. * These values are given as one to four parameters, as
  1489. * follows, [slope, center, range, bias] if omitted the
  1490. * default values will be used: [1.0, 0.5, 1.0, 0.5].))
  1491. * channel = The channels this funtion aplies to.
  1492. */
  1493. void functionImage(MagickFunction funct, double[] params, ChannelType channel = ChannelType.DefaultChannels)
  1494. {
  1495. FunctionImageChannel(imageRef, channel, funct, params.length, params.ptr, DMagickExceptionInfo());
  1496. }
  1497. /**
  1498. * Applies a mathematical expression to the specified image.
  1499. *
  1500. * See_Aso:
  1501. * $(LINK2 http://www.imagemagick.org/script/fx.php,
  1502. * FX, The Special Effects Image Operator) for a detailed
  1503. * discussion of this option.
  1504. */
  1505. void fx(string expression, ChannelType channel = ChannelType.DefaultChannels)
  1506. {
  1507. MagickCoreImage* image =
  1508. FxImageChannel(imageRef, channel, toStringz(expression), DMagickExceptionInfo());
  1509. imageRef = ImageRef(image);
  1510. }
  1511. /**
  1512. * gamma gamma-corrects a particular image channel.
  1513. * The same image viewed on different devices will have perceptual
  1514. * differences in the way the image's intensities are represented
  1515. * on the screen. Specify individual gamma levels for the red,
  1516. * green, and blue channels, or adjust all three with the gamma
  1517. * function. Values typically range from 0.8 to 2.3.
  1518. *
  1519. * You can also reduce the influence of a particular channel
  1520. * with a gamma value of 0.
  1521. */
  1522. void gamma(double value, ChannelType channel = ChannelType.DefaultChannels)
  1523. {
  1524. GammaImageChannel(imageRef, channel, value);
  1525. DMagickException.throwException(&(imageRef.exception));
  1526. }
  1527. ///ditto
  1528. void gamma(double red, double green, double blue)
  1529. {
  1530. GammaImageChannel(imageRef, ChannelType.RedChannel, red);
  1531. GammaImageChannel(imageRef, ChannelType.GreenChannel, green);
  1532. GammaImageChannel(imageRef, ChannelType.BlueChannel, blue);
  1533. DMagickException.throwException(&(imageRef.exception));
  1534. }
  1535. /**
  1536. * Blurs an image. We convolve the image with a Gaussian operator
  1537. * of the given radius and standard deviation (sigma).
  1538. * For reasonable results, the radius should be larger than sigma.
  1539. *
  1540. * Params:
  1541. * radius = The radius of the Gaussian, in pixels,
  1542. * not counting the center pixel.
  1543. * sigma = the standard deviation of the Gaussian, in pixels.
  1544. * channel = The channels to blur.
  1545. */
  1546. void gaussianBlur(double radius = 0, double sigma = 1, ChannelType channel = ChannelType.DefaultChannels)
  1547. {
  1548. MagickCoreImage* image =
  1549. GaussianBlurImageChannel(imageRef, channel, radius, sigma, DMagickExceptionInfo());
  1550. imageRef = ImageRef(image);
  1551. }
  1552. /**
  1553. * Returns the TypeMetric class witch provides the information
  1554. * regarding font metrics such as ascent, descent, text width,
  1555. * text height, and maximum horizontal advance. The units of
  1556. * these font metrics are in pixels, and that the metrics are
  1557. * dependent on the current Image font (default Ghostscript's
  1558. * "Helvetica"), pointsize (default 12 points), and x/y resolution
  1559. * (default 72 DPI) settings.
  1560. *
  1561. * The pixel units may be converted to points (the standard
  1562. * resolution-independent measure used by the typesetting industry)
  1563. * via the following equation:
  1564. * ----------------------------------
  1565. * sizePoints = (sizePixels * 72)/resolution
  1566. * ----------------------------------
  1567. * where resolution is in dots-per-inch (DPI). This means that at the
  1568. * default image resolution, there is one pixel per point.
  1569. * See_Also:
  1570. * $(LINK2 http://freetype.sourceforge.net/freetype2/docs/glyphs/index.html,
  1571. * FreeType Glyph Conventions) for a detailed description of
  1572. * font metrics related issues.
  1573. */
  1574. TypeMetric getTypeMetrics(string text)
  1575. {
  1576. TypeMetric metric;
  1577. DrawInfo* drawInfo = options.drawInfo;
  1578. copyString(drawInfo.text, text);
  1579. scope(exit) copyString(drawInfo.text, null);
  1580. GetMultilineTypeMetrics(imageRef, drawInfo, &metric);
  1581. DMagickException.throwException(&(imageRef.exception));
  1582. return metric;
  1583. }
  1584. /**
  1585. * applies a Hald color lookup table to the image. A Hald color lookup
  1586. * table is a 3-dimensional color cube mapped to 2 dimensions. Create
  1587. * it with the HALD coder. You can apply any color transformation to
  1588. * the Hald image and then use this method to apply the transform to
  1589. * the image.
  1590. *
  1591. * Params:
  1592. * haldImage = The image, which is replaced by indexed CLUT values.
  1593. * channel = The channels to aply the CLUT to.
  1594. *
  1595. * See_Also:
  1596. * $(XREF Image, clut) which provides color value replacement of
  1597. * the individual color channels, usally involving a simplier
  1598. * gray-scale image. E.g: gray-scale to color replacement, or
  1599. * modification by a histogram mapping.
  1600. */
  1601. void haldClut(Image haldImage, ChannelType channel = ChannelType.DefaultChannels)
  1602. {
  1603. HaldClutImageChannel(imageRef, channel, haldImage.imageRef);
  1604. DMagickException.throwException(&(imageRef.exception));
  1605. }
  1606. /**
  1607. * A funhouse mirror effect.
  1608. *
  1609. * Params:
  1610. * amount = Defines the extend of the effect.
  1611. * The value may be positive for implosion,
  1612. * or negative for explosion.
  1613. */
  1614. void implode(double amount = 0.5)
  1615. {
  1616. MagickCoreImage* image =
  1617. ImplodeImage(imageRef, amount, DMagickExceptionInfo());
  1618. imageRef = ImageRef(image);
  1619. }
  1620. /**
  1621. * Replaces the pixels in the specified area with pixel data
  1622. * from the supplied array.
  1623. *
  1624. * Params:
  1625. * area = Location in the image to store the pixels.
  1626. * pixels = An array of pixels defined by map.
  1627. * map = This character string can be any combination
  1628. * or order of R = red, G = green, B = blue, A =
  1629. * alpha, C = cyan, Y = yellow, M = magenta, and K = black.
  1630. * The ordering reflects the order of the pixels in
  1631. * the supplied pixel array.
  1632. */
  1633. void importPixels(T)(Geometry area, T[] pixels, string map = "RGBA")
  1634. {
  1635. StorageType storage = getStorageType!(T);
  1636. ImportImagePixels(imageRef,
  1637. area.xOffset, area.yOffset,
  1638. area.width, area.height,
  1639. toStringz(map), storage, pixels.ptr);
  1640. DMagickException.throwException(&(imageRef.exception));
  1641. }
  1642. /**
  1643. * Adjusts the levels of an image by scaling the colors falling between
  1644. * specified white and black points to the full available quantum range.
  1645. * The parameters provided represent the black, mid, and white points.
  1646. * Colors darker than the black point are set to zero. Colors brighter
  1647. * than the white point are set to the maximum quantum value.
  1648. *
  1649. * It is typically used to improve image contrast, or to provide a
  1650. * controlled linear threshold for the image. If the black and white
  1651. * points are set to the minimum and maximum values found in the image,
  1652. * the image can be normalized. or by swapping black and white values,
  1653. * negate the image.
  1654. *
  1655. * Params:
  1656. * blackPoint = Specifies the darkest color in the image.
  1657. * whitePoint = Specifies the lightest color in the image.
  1658. * gamma = Specifies the gamma correction to apply to the image.
  1659. * channel = The channels to level.
  1660. */
  1661. void level(
  1662. Quantum blackPoint = 0,
  1663. Quantum whitePoint = QuantumRange,
  1664. double gamma = 1,
  1665. ChannelType channel = ChannelType.DefaultChannels)
  1666. {
  1667. LevelImageChannel(imageRef, channel, blackPoint, whitePoint, gamma);
  1668. DMagickException.throwException(&(imageRef.exception));
  1669. }
  1670. /**
  1671. * applies the reversed level operation to just the channels specified.
  1672. * It compresses the full range of color values, so that they lie between
  1673. * the given black and white points. Gamma is applied before the values
  1674. * are mapped.
  1675. *
  1676. * It can be used for example de-contrast a greyscale image to the exact
  1677. * levels specified. Or by using specific levels for each channel of an
  1678. * image you can convert a gray-scale image to any linear color gradient,
  1679. * according to those levels.
  1680. *
  1681. * Params:
  1682. * blackPoint = Specifies the darkest color in the image.
  1683. * whitePoint = Specifies the lightest color in the image.
  1684. * gamma = Specifies the gamma correction to apply to the image.
  1685. * channel = The channels to level.
  1686. */
  1687. void levelize(
  1688. Quantum blackPoint = 0,
  1689. Quantum whitePoint = QuantumRange,
  1690. double gamma = 1,
  1691. ChannelType channel = ChannelType.DefaultChannels)
  1692. {
  1693. LevelizeImageChannel(imageRef, channel, blackPoint, whitePoint, gamma);
  1694. DMagickException.throwException(&(imageRef.exception));
  1695. }
  1696. /**
  1697. * Discards any pixels below the black point and above the white
  1698. * point and levels the remaining pixels.
  1699. *
  1700. * Params:
  1701. * blackPoint = Specifies the darkest color in the image.
  1702. * whitePoint = Specifies the lightest color in the image.
  1703. */
  1704. void linearStretch(Quantum blackPoint, Quantum whitePoint)
  1705. {
  1706. LinearStretchImage(imageRef, blackPoint, whitePoint);
  1707. DMagickException.throwException(&(imageRef.exception));
  1708. }
  1709. /**
  1710. * Rescale image with seam carving. To use this method, you must
  1711. * have installed and configured ImageMagick to use the
  1712. * $(LINK2 http://liblqr.wikidot.com/, Liquid Rescale Library).
  1713. *
  1714. * Params:
  1715. * columns = The desired width.
  1716. * rows = The desired height.
  1717. * deltaX = Maximum seam transversal step (0 means straight seams).
  1718. * rigidity = Introduce a bias for non-straight seams (typically 0).
  1719. */
  1720. void liquidRescale(Geometry size, size_t rows, double deltaX = 0, double rigidity = 0)
  1721. {
  1722. size = size.toAbsolute(columns, rows);
  1723. MagickCoreImage* image =
  1724. LiquidRescaleImage(imageRef, size.width, size.height, deltaX, rigidity, DMagickExceptionInfo());
  1725. imageRef = ImageRef(image);
  1726. }
  1727. /**
  1728. * A convenience method that scales an image proportionally to
  1729. * twice its size.
  1730. */
  1731. void magnify()
  1732. {
  1733. MagickCoreImage* image = MagnifyImage(imageRef, DMagickExceptionInfo());
  1734. imageRef = ImageRef(image);
  1735. }
  1736. /**
  1737. * Applies a digital filter that improves the quality of a noisy image.
  1738. * Each pixel is replaced by the median in a set of neighboring pixels
  1739. * as defined by radius.
  1740. *
  1741. * Params:
  1742. * radius = The filter radius. Values larger than 8 or 9 may take
  1743. * longer than you want to wait, and will not have
  1744. * significantly better results than much smaller values.
  1745. */
  1746. void medianFilter(size_t radius = 0)
  1747. {
  1748. static if ( is(typeof(StatisticImage)) )
  1749. {
  1750. MagickCoreImage* image =
  1751. StatisticImage(imageRef, StatisticType.MedianStatistic, radius, radius, DMagickExceptionInfo());
  1752. }
  1753. else
  1754. {
  1755. MagickCoreImage* image =
  1756. MedianFilterImage(imageRef, radius, DMagickExceptionInfo());
  1757. }
  1758. imageRef = ImageRef(image);
  1759. }
  1760. /**
  1761. * A convenience method that scales an image proportionally to
  1762. * half its size.
  1763. */
  1764. void minify()
  1765. {
  1766. MagickCoreImage* image = MinifyImage(imageRef, DMagickExceptionInfo());
  1767. imageRef = ImageRef(image);
  1768. }
  1769. /**
  1770. * Modulate percent hue, saturation, and brightness of an image.
  1771. * Modulation of saturation and brightness is as a ratio of the current
  1772. * value (1 ( == 100% ) for no change).
  1773. *
  1774. * Params:
  1775. * brightness = The percentage of change in the brightness.
  1776. * saturation = The percentage of change in the saturation.
  1777. * hue = The percentage of change in the hue.
  1778. */
  1779. void modulate(double brightness = 1, double saturation = 1, double hue = 1)
  1780. {
  1781. string args = std.string.format("%s,%s,%s", brightness*100, saturation*100, hue*100);
  1782. ModulateImage(imageRef, toStringz(args));
  1783. DMagickException.throwException(&(imageRef.exception));
  1784. }
  1785. /**
  1786. * Simulates motion blur. We convolve the image with a Gaussian operator
  1787. * of the given radius and standard deviation (sigma). Use a radius of 0
  1788. * and motion_blur selects a suitable radius for you. Angle gives the
  1789. * angle of the blurring motion.
  1790. *
  1791. * Params:
  1792. * radius = The radius of the Gaussian operator.
  1793. * sigma = The standard deviation of the Gaussian operator.
  1794. * Must be non-0.
  1795. * angle = The angle (in degrees) of the blurring motion.
  1796. * channel = The affected channels.
  1797. */
  1798. void motionBlur(
  1799. double radius = 0,
  1800. double sigma = 1,
  1801. double angle = 0,
  1802. ChannelType channel = ChannelType.DefaultChannels)
  1803. {
  1804. MagickCoreImage* image =
  1805. MotionBlurImageChannel(imageRef, channel, radius, sigma, angle, DMagickExceptionInfo());
  1806. imageRef = ImageRef(image);
  1807. }
  1808. /**
  1809. * Negates the colors in the reference image.
  1810. *
  1811. * Params:
  1812. * grayscale = If true, only negate grayscale pixels
  1813. * within the image.
  1814. * channel = The affected channels.
  1815. */
  1816. void negate(bool grayscale = false, ChannelType channel = ChannelType.DefaultChannels)
  1817. {
  1818. NegateImageChannel(imageRef, channel, grayscale);
  1819. DMagickException.throwException(&(imageRef.exception));
  1820. }
  1821. /**
  1822. * Enhances the contrast of a color image by adjusting the pixel
  1823. * color to span the entire range of colors available.
  1824. */
  1825. void normalize(ChannelType channel = ChannelType.DefaultChannels)
  1826. {
  1827. NormalizeImageChannel(imageRef, channel);
  1828. DMagickException.throwException(&(imageRef.exception));
  1829. }
  1830. /**
  1831. * Applies a special effect filter that simulates an oil painting.
  1832. * Each pixel is replaced by the most frequent color occurring in a
  1833. * circular region defined by radius.
  1834. */
  1835. void oilPaint(double radius = 3)
  1836. {
  1837. MagickCoreImage* image =
  1838. OilPaintImage(imageRef, radius, DMagickExceptionInfo());
  1839. imageRef = ImageRef(image);
  1840. }
  1841. /**
  1842. * Set or attenuate the opacity channel in the image.
  1843. * If the image pixels are opaque then they are set to the specified
  1844. * opacity value, otherwise they are blended with the supplied opacity
  1845. * value.
  1846. */
  1847. void opacity(Quantum value)
  1848. {
  1849. SetImageOpacity(imageRef, value);
  1850. DMagickException.throwException(&(imageRef.exception));
  1851. }
  1852. /**
  1853. * Changes all pixels having the target color to the fill color.
  1854. *
  1855. * Params:
  1856. * target = The color to be replaced.
  1857. * fill = The replacement color.
  1858. * invert = If true, the target pixels are all the pixels
  1859. * that are not the target color.
  1860. * channel = The affected channels.
  1861. */
  1862. void opaque(Color target, Color fill, bool invert = false, ChannelType channel = ChannelType.CompositeChannels)
  1863. {
  1864. MagickPixelPacket magickTarget;
  1865. MagickPixelPacket magickFill;
  1866. GetMagickPixelPacket(imageRef, &magickTarget);
  1867. GetMagickPixelPacket(imageRef, &magickFill);
  1868. setMagickPixelPacket(&magickTarget, target);
  1869. setMagickPixelPacket(&magickFill, fill);
  1870. OpaquePaintImageChannel(imageRef, channel, &magickTarget, &magickFill, invert);
  1871. DMagickException.throwException(&(imageRef.exception));
  1872. }
  1873. /**
  1874. * Dithers the image to a predefined pattern.
  1875. *
  1876. * Params:
  1877. * map = The map argument can be any of the strings
  1878. * listed by this command:
  1879. * --------------------
  1880. * convert -list Threshold
  1881. * --------------------
  1882. * See_Also: $(LINK2 http://www.imagemagick.org/script/command-line-options.php#ordered-dither,
  1883. * ImageMagick's -ordered-dither option).
  1884. */
  1885. void orderedDither(string map)
  1886. {
  1887. OrderedPosterizeImage(imageRef, toStringz(map), DMagickExceptionInfo());
  1888. }
  1889. /**
  1890. * Ping is similar to read except only enough of the image is read to
  1891. * determine the image columns, rows, and filesize. The columns, rows,
  1892. * and fileSize attributes are valid after invoking ping.
  1893. * The image data is not valid after calling ping.
  1894. */
  1895. void ping(string filename)
  1896. {
  1897. options.filename = filename;
  1898. MagickCoreImage* image = PingImages(options.imageInfo, DMagickExceptionInfo());
  1899. //Make sure a single image (frame) is read.
  1900. if ( image.next !is null )
  1901. {
  1902. MagickCoreImage* nextImage;
  1903. nextImage = image.next;
  1904. image.next = null;
  1905. nextImage.previous = null;
  1906. DestroyImageList(nextImage);
  1907. }
  1908. imageRef = ImageRef(image);
  1909. }
  1910. ///ditto
  1911. void ping(void[] blob)
  1912. {
  1913. MagickCoreImage* image =
  1914. PingBlob(options.imageInfo, blob.ptr, blob.length, DMagickExceptionInfo());
  1915. imageRef = ImageRef(image);
  1916. }
  1917. /**
  1918. * Produce an image that looks like a Polaroid® instant picture.
  1919. * If the image has a "Caption" property, the value is used as a caption.
  1920. *
  1921. * Params:
  1922. * angle = The resulting image is rotated by this amount,
  1923. * measured in degrees.
  1924. */
  1925. void polaroid(double angle)
  1926. {
  1927. MagickCoreImage* image =
  1928. PolaroidImage(imageRef, options.drawInfo, angle, DMagickExceptionInfo());
  1929. imageRef = ImageRef(image);
  1930. }
  1931. /**
  1932. * Reduces the image to a limited number of colors for a "poster" effect.
  1933. *
  1934. * Params:
  1935. * levels = Number of color levels allowed in each channel.
  1936. * Very low values (2, 3, or 4) have the most
  1937. * visible effect.
  1938. * dither = If true, dither the image.
  1939. */
  1940. void posterize(size_t levels = 4, bool dither = false)
  1941. {
  1942. PosterizeImage(imageRef, levels, dither);
  1943. DMagickException.throwException(&(imageRef.exception));
  1944. }
  1945. /**
  1946. * Creates an image that contains 9 small versions of the receiver
  1947. * image. The center image is the unchanged receiver. The other 8
  1948. * images are variations created by transforming the receiver according
  1949. * to the specified preview type with varying parameters.
  1950. *
  1951. * A preview image is an easy way to "try out" a transformation method.
  1952. */
  1953. Image preview(PreviewType preview)
  1954. {
  1955. MagickCoreImage* image =
  1956. PreviewImage(imageRef, preview, DMagickExceptionInfo());
  1957. return new Image(image, options.clone());
  1958. }
  1959. /**
  1960. * Execute the named process module, passing any arguments arguments.
  1961. * An exception is thrown if the requested process module does not exist,
  1962. * fails to load, or fails during execution.
  1963. *
  1964. * Params:
  1965. * name = The name of a module.
  1966. * arguments = The arguments to pass to the module.
  1967. */
  1968. void process(string name, string[] arguments)
  1969. {
  1970. MagickCoreImage* image = imageRef;
  1971. const(char)*[] args = new const(char)*[arguments.length];
  1972. foreach( i, arg; arguments )
  1973. args[i] = toStringz(arg);
  1974. InvokeDynamicImageFilter(toStringz(name), &image, cast(int)args.length, args.ptr, DMagickExceptionInfo());
  1975. imageRef = ImageRef(image);
  1976. }
  1977. /**
  1978. * Analyzes the colors within a reference image and chooses a fixed
  1979. * number of colors to represent the image. The goal of the algorithm
  1980. * is to minimize the difference between the input and output image
  1981. * while minimizing the processing time.
  1982. *
  1983. * Params:
  1984. * measureError = Set to true to calculate quantization errors
  1985. * when quantizing the image. These can be accessed
  1986. * with: normalizedMeanError, normalizedMaxError
  1987. * and meanErrorPerPixel.
  1988. */
  1989. void quantize(bool measureError = false)
  1990. {
  1991. options.quantizeInfo.measure_error = measureError;
  1992. QuantizeImage(options.quantizeInfo, imageRef);
  1993. DMagickException.throwException(&(imageRef.exception));
  1994. }
  1995. /**
  1996. * Creates a simulated three-dimensional button-like effect by
  1997. * lightening and darkening the edges of the image.
  1998. *
  1999. * Params:
  2000. * width = The width of the raised edge in pixels.
  2001. * height = The height of the raised edge in pixels.
  2002. * raised = If true, the image is raised, otherwise lowered.
  2003. */
  2004. void raise(size_t width, size_t height, bool raised = true)
  2005. {
  2006. RectangleInfo raiseInfo;
  2007. raiseInfo.width = width;
  2008. raiseInfo.height = height;
  2009. RaiseImage(imageRef, &raiseInfo, raised);
  2010. DMagickException.throwException(&(imageRef.exception));
  2011. }
  2012. /**
  2013. * Changes the value of individual pixels based on the intensity of
  2014. * each pixel compared to a random threshold. The result is a
  2015. * low-contrast, two color image.
  2016. *
  2017. * Params:
  2018. * thresholds = A geometry string containing LOWxHIGH thresholds.
  2019. * The string is in the form `XxY'. The Y value may be
  2020. * omitted, in which case it is assigned the value
  2021. * QuantumRange-X. If an % appears in the string then
  2022. * the values are assumed to be percentages of
  2023. * QuantumRange. If the string contains 2x2, 3x3, or
  2024. * 4x4, then an ordered dither of order 2, 3, or 4
  2025. * will be performed instead.
  2026. * channel = The affected channels.
  2027. */
  2028. void randomThreshold(Geometry thresholds, ChannelType channel = ChannelType.DefaultChannels)
  2029. {
  2030. RandomThresholdImageChannel(imageRef, channel, toStringz(thresholds.toString()), DMagickExceptionInfo());
  2031. }
  2032. /**
  2033. * Read an Image by reading from the file or
  2034. * URL specified by filename.
  2035. */
  2036. void read(string filename)
  2037. {
  2038. options.filename = filename;
  2039. MagickCoreImage* image = ReadImage(options.imageInfo, DMagickExceptionInfo());
  2040. //Make sure a single image (frame) is read.
  2041. if ( image.next !is null )
  2042. {
  2043. MagickCoreImage* nextImage;
  2044. nextImage = image.next;
  2045. image.next = null;
  2046. nextImage.previous = null;
  2047. DestroyImageList(nextImage);
  2048. }
  2049. imageRef = ImageRef(image);
  2050. }
  2051. /**
  2052. * Read an Image by reading from the file or
  2053. * URL specified by filename with the specified size.
  2054. * Usefull for images that don't specify their size.
  2055. */
  2056. void read(string filename, Geometry size)
  2057. {
  2058. options.size = size;
  2059. read(filename);
  2060. }
  2061. /**
  2062. * Reads an image from an in-memory blob.
  2063. * The Blob size, depth and magick format may also be specified.
  2064. *
  2065. * Some image formats require size to be specified,
  2066. * the default depth Imagemagick uses is the Quantum size
  2067. * it's compiled with. If it doesn't match the depth of the image
  2068. * it may need to be specified.
  2069. *
  2070. * Imagemagick can usualy detect the image format, when the
  2071. * format can't be detected a magick format must be specified.
  2072. */
  2073. void read(void[] blob)
  2074. {
  2075. MagickCoreImage* image =
  2076. BlobToImage(options.imageInfo, blob.ptr, blob.length, DMagickExceptionInfo());
  2077. //Make sure a single image (frame) is read.
  2078. if ( image.next !is null )
  2079. {
  2080. MagickCoreImage* nextImage;
  2081. nextImage = image.next;
  2082. image.next = null;
  2083. nextImage.previous = null;
  2084. DestroyImageList(nextImage);
  2085. }
  2086. imageRef = ImageRef(image);
  2087. }
  2088. ///ditto
  2089. void read(void[] blob, Geometry size)
  2090. {
  2091. options.size = size;
  2092. read(blob);
  2093. }
  2094. ///ditto
  2095. void read(void[] blob, Geometry size, size_t depth)
  2096. {
  2097. options.size = size;
  2098. options.depth = depth;
  2099. read(blob);
  2100. }
  2101. ///ditto
  2102. void read(void[] blob, Geometry size, size_t depth, string magick)
  2103. {
  2104. options.size = size;
  2105. options.depth = depth;
  2106. options.magick = magick;
  2107. //Also set the filename to the image format
  2108. options.filename = magick ~":";
  2109. read(blob);
  2110. }
  2111. ///ditto
  2112. void read(void[] blob, Geometry size, string magick)
  2113. {
  2114. options.size = size;
  2115. options.magick = magick;
  2116. //Also set the filename to the image format
  2117. options.filename = magick ~":";
  2118. read(blob);
  2119. }
  2120. /**
  2121. * Reads an image from an array of pixels.
  2122. *
  2123. * Params:
  2124. * width = The number of columns in the image.
  2125. * height = The number of rows in the image.
  2126. * map = A string describing the expected ordering
  2127. * of the pixel array. It can be any combination
  2128. * or order of R = red, G = green, B = blue, A = alpha
  2129. * , C = cyan, Y = yellow, M = magenta, K = black,
  2130. * or I = intensity (for grayscale).
  2131. * storage = The pixel Staroage type (CharPixel,
  2132. * ShortPixel, IntegerPixel, FloatPixel, or DoublePixel).
  2133. * pixels = The pixel data.
  2134. */
  2135. void read(T)(size_t width, size_t height, string map, T[] pixels)
  2136. {
  2137. StorageType storage = getStorageType!(T);
  2138. MagickCoreImage* image =
  2139. ConstituteImage(width, height, toStringz(map), storage, pixels.ptr, DMagickExceptionInfo());
  2140. imageRef = ImageRef(image);
  2141. }
  2142. /**
  2143. * Smooths the contours of an image while still preserving edge
  2144. * information. The algorithm works by replacing each pixel with its
  2145. * neighbor closest in value.
  2146. *
  2147. * Params:
  2148. * radius = A neighbor is defined by radius. Use a radius of 0
  2149. * and reduceNoise selects a suitable radius for you.
  2150. */
  2151. void reduceNoise(size_t radius = 0)
  2152. {
  2153. static if ( is(typeof(StatisticImage)) )
  2154. {
  2155. MagickCoreImage* image =
  2156. StatisticImage(imageRef, StatisticType.NonpeakStatistic, radius, radius, DMagickExceptionInfo());
  2157. }
  2158. else
  2159. {
  2160. MagickCoreImage* image =
  2161. ReduceNoiseImage(imageRef, radius, DMagickExceptionInfo());
  2162. }
  2163. imageRef = ImageRef(image);
  2164. }
  2165. /**
  2166. * Reduce the number of colors in img to the colors used by reference.
  2167. * If a dither method is set then the given colors are dithered over
  2168. * the image as necessary, otherwise the closest color
  2169. * (in RGB colorspace) is selected to replace that pixel in the image.
  2170. */
  2171. void remap(Image reference)
  2172. {
  2173. RemapImage(options.quantizeInfo, imageRef, reference.imageRef);
  2174. DMagickException.throwException(&(imageRef.exception));
  2175. }
  2176. /**
  2177. * Resize image in terms of its pixel size, so that when displayed at
  2178. * the given resolution it will be the same size in terms of real world
  2179. * units as the original image at the original resolution.
  2180. *
  2181. * Params:
  2182. * xResolution = the target horizontal resolution
  2183. * yResolution = the target vertical resolution
  2184. * filter = The filter to use when resizing.
  2185. * blur = Values > 1 increase the blurriness.
  2186. * Values < 1 increase the sharpness.
  2187. */
  2188. void resample(
  2189. double xResolution,
  2190. double yResolution,
  2191. FilterTypes filter = FilterTypes.LanczosFilter,
  2192. double blur = 1)
  2193. {
  2194. MagickCoreImage* image =
  2195. ResampleImage(imageRef, xResolution, yResolution, filter, blur, DMagickExceptionInfo());
  2196. imageRef = ImageRef(image);
  2197. }
  2198. /**
  2199. * scales an image to the desired dimensions, using the given filter.
  2200. *
  2201. * Params:
  2202. * size = The desired width and height.
  2203. * filter = The filter to use when resizing.
  2204. * blur = Values > 1 increase the blurriness.
  2205. * Values < 1 increase the sharpness.
  2206. */
  2207. void resize(Geometry size, FilterTypes filter = FilterTypes.LanczosFilter, double blur = 1)
  2208. {
  2209. size = size.toAbsolute(columns, rows);
  2210. MagickCoreImage* image =
  2211. ResizeImage(imageRef, size.width, size.height, filter, blur, DMagickExceptionInfo());
  2212. imageRef = ImageRef(image);
  2213. }
  2214. /**
  2215. * Offsets an image as defined by xOffset and yOffset.
  2216. */
  2217. void roll(ssize_t xOffset, ssize_t yOffset)
  2218. {
  2219. MagickCoreImage* image =
  2220. RollImage(imageRef, xOffset, yOffset, DMagickExceptionInfo());
  2221. imageRef = ImageRef(image);
  2222. }
  2223. /**
  2224. * Rotate the image by specified number of degrees. Rotated images are
  2225. * usually larger than the originals and have 'empty' triangular corners.
  2226. * Empty triangles left over from shearing the image are filled with the
  2227. * background color defined by the 'backgroundColor' property
  2228. * of the image.
  2229. *
  2230. * Params:
  2231. * degrees = The number of degrees to rotate the image. Positive
  2232. * angles rotate counter-clockwise (right-hand rule),
  2233. * while negative angles rotate clockwise.
  2234. */
  2235. void rotate(double degrees)
  2236. {
  2237. MagickCoreImage* image =
  2238. RotateImage(imageRef, degrees, DMagickExceptionInfo());
  2239. imageRef = ImageRef(image);
  2240. }
  2241. /**
  2242. * Applies a rotational blur to the image.
  2243. *
  2244. * Params:
  2245. * angle = The angle of the rotational blur, in degrees.
  2246. * channel = If no channels are specified, blurs all the channels.
  2247. */
  2248. void rotationalBlur(double angle, ChannelType channel = ChannelType.DefaultChannels)
  2249. {
  2250. static if ( is(typeof(RotationalBlurImage)) )
  2251. {
  2252. MagickCoreImage* image =
  2253. RotationalBlurImageChannel(imageRef, channel, angle, DMagickExceptionInfo());
  2254. }
  2255. else
  2256. {
  2257. MagickCoreImage* image =
  2258. RadialBlurImageChannel(imageRef, channel, angle, DMagickExceptionInfo());
  2259. }
  2260. imageRef = ImageRef(image);
  2261. }
  2262. /** ditto */
  2263. alias rotationalBlur radialBlur;
  2264. /**
  2265. * scales an image to the desired dimensions with pixel sampling.
  2266. * Unlike other scaling methods, this method does not introduce any
  2267. * additional color into the scaled image.
  2268. */
  2269. void sample(Geometry size)
  2270. {
  2271. size = size.toAbsolute(columns, rows);
  2272. MagickCoreImage* image =
  2273. SampleImage(imageRef, size.width, size.height, DMagickExceptionInfo());
  2274. imageRef = ImageRef(image);
  2275. }
  2276. /**
  2277. * Resize image by using simple ratio algorithm.
  2278. */
  2279. void scale(Geometry size)
  2280. {
  2281. size = size.toAbsolute(columns, rows);
  2282. MagickCoreImage* image =
  2283. ScaleImage(imageRef, size.width, size.height, DMagickExceptionInfo());
  2284. imageRef = ImageRef(image);
  2285. }
  2286. /**
  2287. * Segments an image by analyzing the histograms of the color
  2288. * components and identifying units that are homogeneous with the
  2289. * fuzzy c-means technique. Also uses quantizeColorSpace and
  2290. * verbose image properties.
  2291. *
  2292. * Params:
  2293. * clusterThreshold =
  2294. * The number of pixels in each cluster must exceed the
  2295. * the cluster threshold to be considered valid.
  2296. * smoothingThreshold =
  2297. * The smoothing threshold eliminates noise in the second
  2298. * derivative of the histogram. As the value is increased,
  2299. * you can expect a smoother second derivative.
  2300. */
  2301. void segment(double clusterThreshold = 1, double smoothingThreshold = 1.5)
  2302. {
  2303. SegmentImage(imageRef, options.quantizeColorSpace, options.verbose, clusterThreshold, smoothingThreshold);
  2304. DMagickException.throwException(&(imageRef.exception));
  2305. }
  2306. /**
  2307. * Selectively blur pixels within a contrast threshold.
  2308. *
  2309. * Params:
  2310. * radius = The radius of the Gaussian in pixels,
  2311. * not counting the center pixel.
  2312. * sigma = The standard deviation of the Laplacian, in pixels.
  2313. * threshold = Threshold level represented as a percentage
  2314. * of the quantum range.
  2315. * channel = The channels to blur.
  2316. */
  2317. void selectiveBlur(
  2318. double radius,
  2319. double sigma,
  2320. double threshold,
  2321. ChannelType channel = ChannelType.DefaultChannels)
  2322. {
  2323. MagickCoreImage* image =
  2324. SelectiveBlurImageChannel(imageRef, channel, radius, sigma, threshold, DMagickExceptionInfo());
  2325. imageRef = ImageRef(image);
  2326. }
  2327. /**
  2328. * applies a special effect to the image, similar to the effect achieved
  2329. * in a photo darkroom by sepia toning. A threshold of 80% is a good
  2330. * starting point for a reasonable tone.
  2331. *
  2332. * Params:
  2333. * threshold = Threshold ranges from 0 to QuantumRange and is
  2334. * a measure of the extent of the sepia toning.
  2335. * A value lower than 1 is treated as a percentage.
  2336. */
  2337. void sepiatone(double threshold = QuantumRange)
  2338. {
  2339. if ( threshold < 1 )
  2340. threshold *= QuantumRange;
  2341. MagickCoreImage* image =
  2342. SepiaToneImage(imageRef, threshold, DMagickExceptionInfo());
  2343. imageRef = ImageRef(image);
  2344. }
  2345. /**
  2346. * shines a distant light on an image to create a three-dimensional
  2347. * effect. You control the positioning of the light with azimuth and
  2348. * elevation.
  2349. *
  2350. * Params:
  2351. * azimuth = The amount of degrees off the X axis.
  2352. * elevation = The amount of pixels above the Z axis.
  2353. * shading = If true, shade shades the intensity of each pixel.
  2354. */
  2355. void shade(double azimuth = 30, double elevation = 30, bool shading = false)
  2356. {
  2357. MagickCoreImage* image =
  2358. ShadeImage(imageRef, shading, azimuth, elevation, DMagickExceptionInfo());
  2359. imageRef = ImageRef(image);
  2360. }
  2361. /**
  2362. * Simulates a shadow from the specified image and returns it.
  2363. * This method only works when the image has opaque parts and
  2364. * transparent parts. Note that the resulting image is just the shadow.
  2365. *
  2366. * Params:
  2367. * xOffset = The shadow x offset.
  2368. * yOffset = The shadow y offset.
  2369. * sigma = The standard deviation of the Gaussian operator used
  2370. * to produce the shadow. The higher the number, the
  2371. * "blurrier" the shadow, but the longer it takes to
  2372. * produce the shadow.
  2373. * opacity = The percent opacity of the shadow.
  2374. * A number between 0.1 and 1.0
  2375. * Returns: The shadows for this image.
  2376. */
  2377. Image shadowImage(ssize_t xOffset, ssize_t yOffset, double sigma = 4, double opacity = 1)
  2378. {
  2379. MagickCoreImage* image =
  2380. ShadowImage(imageRef, opacity, sigma, xOffset, yOffset, DMagickExceptionInfo());
  2381. return new Image(image);
  2382. }
  2383. /**
  2384. * Sharpens an image. We convolve the image with a Gaussian operator
  2385. * of the given radius and standard deviation (sigma). For reasonable
  2386. * results, radius should be larger than sigma. Use a radius of 0 and
  2387. * sharpen selects a suitable radius for you.
  2388. *
  2389. * Params:
  2390. * radius = The radius of the Gaussian in pixels,
  2391. * not counting the center pixel.
  2392. * sigma = The standard deviation of the Laplacian, in pixels.
  2393. * channel = If no channels are specified, sharpens all the channels.
  2394. */
  2395. void sharpen(double radius = 0, double sigma = 1, ChannelType channel = ChannelType.DefaultChannels)
  2396. {
  2397. MagickCoreImage* image =
  2398. SharpenImageChannel(imageRef, channel, radius, sigma, DMagickExceptionInfo());
  2399. imageRef = ImageRef(image);
  2400. }
  2401. /**
  2402. * Removes pixels from the edges of the image,
  2403. * leaving the center rectangle.
  2404. *
  2405. * Params:
  2406. * geometry = The region of the image to crop.
  2407. */
  2408. void shave(Geometry geometry)
  2409. {
  2410. RectangleInfo rectangle = geometry.rectangleInfo;
  2411. MagickCoreImage* image = ShaveImage(imageRef, &rectangle, DMagickExceptionInfo());
  2412. imageRef = ImageRef(image);
  2413. }
  2414. /**
  2415. * Shearing slides one edge of an image along the X or Y axis, creating
  2416. * a parallelogram. An X direction shear slides an edge along the X axis,
  2417. * while a Y direction shear slides an edge along the Y axis. The amount
  2418. * of the shear is controlled by a shear angle. For X direction shears,
  2419. * xShearAngle is measured relative to the Y axis, and similarly, for Y
  2420. * direction shears yShearAngle is measured relative to the X axis.
  2421. * Empty triangles left over from shearing the image are filled with
  2422. * the background color.
  2423. */
  2424. void shear(double xShearAngle, double yShearAngle)
  2425. {
  2426. MagickCoreImage* image =
  2427. ShearImage(imageRef, xShearAngle, yShearAngle, DMagickExceptionInfo());
  2428. imageRef = ImageRef(image);
  2429. }
  2430. /**
  2431. * Adjusts the contrast of an image channel with a non-linear sigmoidal
  2432. * contrast algorithm. Increases the contrast of the image using a
  2433. * sigmoidal transfer function without saturating highlights or shadows.
  2434. *
  2435. * Params:
  2436. * contrast = indicates how much to increase the contrast
  2437. * (0 is none; 3 is typical; 20 is pushing it)
  2438. * midpoint = indicates where midtones fall in the resultant
  2439. * image (0 is white; 50% is middle-gray; 100% is black).
  2440. * Specify an apsulute number of pixels or an
  2441. * percentage by passing a value between 1 and 0
  2442. * sharpen = Increase or decrease image contrast.
  2443. * channel = The channels to adjust.
  2444. */
  2445. void sigmoidalContrast(
  2446. double contrast = 3,
  2447. double midpoint = 50,
  2448. bool sharpen = false,
  2449. ChannelType channel = ChannelType.DefaultChannels)
  2450. {
  2451. if ( midpoint < 1 )
  2452. midpoint *= QuantumRange;
  2453. SigmoidalContrastImageChannel(imageRef, channel, sharpen, contrast, midpoint);
  2454. DMagickException.throwException(&(imageRef.exception));
  2455. }
  2456. /**
  2457. * Simulates a pencil sketch. For best results start with
  2458. * a grayscale image.
  2459. *
  2460. * Params:
  2461. * radius = The radius of the Gaussian, in pixels, not counting
  2462. * the center pixel.
  2463. * sigma = The standard deviation of the Gaussian, in pixels.
  2464. * angle = The angle toward which the image is sketched.
  2465. */
  2466. void sketch(double radius = 0, double sigma = 1, double angle = 0)
  2467. {
  2468. MagickCoreImage* image =
  2469. SketchImage(imageRef, radius, sigma, angle, DMagickExceptionInfo());
  2470. imageRef = ImageRef(image);
  2471. }
  2472. /**
  2473. * Applies a special effect to the image similar to the effect achieved
  2474. * in a photo darkroom by selectively exposing areas of photo sensitive
  2475. * paper to light.
  2476. *
  2477. * Params:
  2478. * threshold = The extent of the solarization.
  2479. * channel = The channels to adjust. Anything other than
  2480. * ChannelType.DefaultChannels requires ImageMagick 6.8.0
  2481. * ot higher.
  2482. */
  2483. void solarize(Quantum threshold, ChannelType channel = ChannelType.DefaultChannels)
  2484. {
  2485. static if ( is(typeof(SolarizeImageChannel)) )
  2486. {
  2487. SolarizeImageChannel(imageRef, channel, threshold, DMagickExceptionInfo());
  2488. }
  2489. else
  2490. {
  2491. SolarizeImage(imageRef, threshold);
  2492. DMagickException.throwException(&(imageRef.exception));
  2493. }
  2494. }
  2495. /**
  2496. * Fills the image with the specified color or colors, starting at
  2497. * the x,y coordinates associated with the color and using the specified
  2498. * interpolation method.
  2499. *
  2500. * Params:
  2501. * method = The method to fill in the gradient between the
  2502. * control points.
  2503. * args = A series of control points, and there Color.
  2504. *
  2505. * See_Also: $(LINK2 http://www.imagemagick.org/Usage/canvas/#sparse-color,
  2506. * Sparse Points of Color) at Examples of ImageMagick Usage.
  2507. */
  2508. void sparseColor(SparseColorMethod method, Tuple!(size_t, "x", size_t, "y", Color, "color")[] args ...)
  2509. {
  2510. double[] argv = new double[args.length * 6];
  2511. foreach( i, arg; args )
  2512. {
  2513. double[] values = argv[i*6 .. i*6+6];
  2514. values[0] = arg.x;
  2515. values[1] = arg.y;
  2516. values[2] = arg.color.redQuantum / QuantumRange;
  2517. values[3] = arg.color.greenQuantum / QuantumRange;
  2518. values[4] = arg.color.blueQuantum / QuantumRange;
  2519. values[5] = arg.color.opacityQuantum / QuantumRange;
  2520. }
  2521. MagickCoreImage* image =
  2522. SparseColorImage(imageRef,
  2523. ChannelType.DefaultChannels,
  2524. method, argv.length,
  2525. argv.ptr, DMagickExceptionInfo());
  2526. imageRef = ImageRef(image);
  2527. }
  2528. /**
  2529. * Splice the background color into the image as defined by the geometry.
  2530. * This method is the opposite of chop.
  2531. */
  2532. void splice(Geometry geometry)
  2533. {
  2534. RectangleInfo rectangle = geometry.rectangleInfo;
  2535. MagickCoreImage* image = SpliceImage(imageRef, &rectangle, DMagickExceptionInfo());
  2536. imageRef = ImageRef(image);
  2537. }
  2538. /**
  2539. * Randomly displaces each pixel in a block defined by the
  2540. * radius parameter.
  2541. */
  2542. void spread(double radius = 3)
  2543. {
  2544. MagickCoreImage* image =
  2545. SpreadImage(imageRef, radius, DMagickExceptionInfo());
  2546. imageRef = ImageRef(image);
  2547. }
  2548. /**
  2549. * Makes each pixel the min / max / median / mode / etc. of the
  2550. * neighborhood of the specified width and height.
  2551. *
  2552. * Params:
  2553. * type = The type pf statistic to apply.
  2554. * width = The width of the pixel neighborhood.
  2555. * height = The height of the pixel neighborhood.
  2556. */
  2557. void statistic()(StatisticType type, size_t width, size_t height)
  2558. {
  2559. static if ( is(typeof(StatisticImage)) )
  2560. {
  2561. MagickCoreImage* image =
  2562. StatisticImage(imageRef, type, width, height, DMagickExceptionInfo());
  2563. imageRef = ImageRef(image);
  2564. }
  2565. else
  2566. {
  2567. static assert(0, "dmagick.Image.Image.statistic requires MagickCore version >= 6.6.9");
  2568. }
  2569. }
  2570. /**
  2571. * Hides a digital watermark in the receiver. You can retrieve the
  2572. * watermark by reading the file with the stegano: prefix, thereby
  2573. * proving the authenticity of the file.
  2574. *
  2575. * The watermarked image must be saved in a lossless RGB format such
  2576. * as MIFF, or PNG. You cannot save a watermarked image in a lossy
  2577. * format such as JPEG or a pseudocolor format such as GIF. Once
  2578. * written, the file must not be modified or processed in any way.
  2579. *
  2580. * Params:
  2581. * watermark = An image or imagelist to be used as the watermark.
  2582. * The watermark must be grayscale and should be
  2583. * substantially smaller than the receiver. The recovery
  2584. * time is proportional to the size of the watermark.
  2585. * offset = The starting position within the receiver at which
  2586. * the watermark will be hidden. When you retrieve the
  2587. * watermark from the file, you must supply this value,
  2588. * along with the width and height of the watermark, in
  2589. * the size optional parameter to the read method.
  2590. */
  2591. void stegano(Image watermark, ssize_t offset)
  2592. {
  2593. imageRef.offset = offset;
  2594. MagickCoreImage* image =
  2595. SteganoImage(imageRef, watermark.imageRef, DMagickExceptionInfo());
  2596. imageRef = ImageRef(image);
  2597. }
  2598. /**
  2599. * Combines two images and produces a single image that is the composite
  2600. * of a left and right image of a stereo pair. Special red-green stereo
  2601. * glasses are required to view this effect.
  2602. */
  2603. void stereo(Image rightImage)
  2604. {
  2605. MagickCoreImage* image =
  2606. StereoImage(imageRef, rightImage.imageRef, DMagickExceptionInfo());
  2607. imageRef = ImageRef(image);
  2608. }
  2609. /**
  2610. * Strips an image of all profiles and comments.
  2611. */
  2612. void strip()
  2613. {
  2614. StripImage(imageRef);
  2615. DMagickException.throwException(&(imageRef.exception));
  2616. }
  2617. /**
  2618. * synchronizes image properties with the image profiles. Currently
  2619. * we only support updating the EXIF resolution and orientation.
  2620. */
  2621. void syncProfiles()
  2622. {
  2623. SyncImageProfiles(imageRef);
  2624. DMagickException.throwException(&(imageRef.exception));
  2625. }
  2626. /**
  2627. * Swirls the pixels about the center of the image, where degrees
  2628. * indicates the sweep of the arc through which each pixel is moved.
  2629. * You get a more dramatic effect as the degrees move from 1 to 360.
  2630. */
  2631. void swirl(double degrees)
  2632. {
  2633. MagickCoreImage* image =
  2634. SwirlImage(imageRef, degrees, DMagickExceptionInfo());
  2635. imageRef = ImageRef(image);
  2636. }
  2637. /**
  2638. * Changes the value of individual pixels based on the intensity of
  2639. * each pixel compared to threshold. The result is a high-contrast,
  2640. * two color image.
  2641. *
  2642. * See_Also: $(XREF Image, bilevel).
  2643. */
  2644. //TODO: deprecated ?
  2645. void threshold(Quantum value)
  2646. {
  2647. bilevel(value);
  2648. }
  2649. /**
  2650. * changes the size of an image to the given dimensions and removes
  2651. * any associated profiles. The goal is to produce small low cost
  2652. * thumbnail images suited for display on the Web.
  2653. */
  2654. void thumbnail(Geometry size)
  2655. {
  2656. size = size.toAbsolute(columns, rows);
  2657. MagickCoreImage* image =
  2658. ThumbnailImage(imageRef, size.width, size.height, DMagickExceptionInfo());
  2659. imageRef = ImageRef(image);
  2660. }
  2661. /**
  2662. * Creates a Binary Large OBject, a direct-to-memory
  2663. * version of the image.
  2664. *
  2665. * if an image format is selected which is capable of supporting
  2666. * fewer colors than the original image or quantization has been
  2667. * requested, the original image will be quantized to fewer colors.
  2668. * Use a copy of the original if this is a problem.
  2669. *
  2670. * Params:
  2671. * magick = specifies the image format to write.
  2672. * depth = specifies the image depth.
  2673. */
  2674. void[] toBlob(string magick = null, size_t depth = 0)
  2675. {
  2676. size_t length;
  2677. ExceptionInfo* exceptionInfo = AcquireExceptionInfo();
  2678. if ( magick !is null )
  2679. this.magick = magick;
  2680. if ( depth != 0 )
  2681. this.depth = depth;
  2682. string originalFilename = filename;
  2683. filename = this.magick ~ ":";
  2684. scope(exit) filename = originalFilename;
  2685. void* blob = ImageToBlob(options.imageInfo, imageRef, &length, exceptionInfo);
  2686. DMagickException.throwException(exceptionInfo);
  2687. void[] dBlob = blob[0 .. length].dup;
  2688. RelinquishMagickMemory(blob);
  2689. return dBlob;
  2690. }
  2691. unittest
  2692. {
  2693. Image example = new Image(Geometry(100, 100), new Color("green"));
  2694. example.toBlob("jpg");
  2695. }
  2696. /**
  2697. * Changes the opacity value of all the pixels that match color to
  2698. * the value specified by opacity. By default the pixel must match
  2699. * exactly, but you can specify a tolerance level by setting the fuzz
  2700. * attribute on the image.
  2701. *
  2702. * Params:
  2703. * target = The target color.
  2704. * opacity = The desired opacity.
  2705. * invert = If true, all pixels outside the range
  2706. * are set to opacity.
  2707. */
  2708. void transparent(Color color, Quantum opacity = TransparentOpacity, bool invert = false)
  2709. {
  2710. MagickPixelPacket target;
  2711. GetMagickPixelPacket(imageRef, &target);
  2712. setMagickPixelPacket(&target, color);
  2713. TransparentPaintImage(imageRef, &target, opacity, invert);
  2714. DMagickException.throwException(&(imageRef.exception));
  2715. }
  2716. /**
  2717. * Changes the opacity value associated with any pixel between low and
  2718. * high to the value defined by opacity.
  2719. *
  2720. * As there is one fuzz value for the all the channels, the transparent
  2721. * method is not suitable for the operations like chroma, where the
  2722. * tolerance for similarity of two color components (RGB) can be
  2723. * different, Thus we define this method take two target pixels (one
  2724. * low and one high) and all the pixels of an image which are lying
  2725. * between these two pixels are made transparent.
  2726. *
  2727. * Params:
  2728. * low = The low end of the pixel range.
  2729. * high = The high end of the pixel range.
  2730. * opacity = The desired opacity.
  2731. * invert = If true, all pixels outside the range
  2732. * are set to opacity.
  2733. */
  2734. void transparentChroma(Color low, Color high, Quantum opacity = TransparentOpacity, bool invert = false)
  2735. {
  2736. MagickPixelPacket lowTarget;
  2737. MagickPixelPacket highTarget;
  2738. GetMagickPixelPacket(imageRef, &lowTarget);
  2739. setMagickPixelPacket(&lowTarget, low);
  2740. GetMagickPixelPacket(imageRef, &highTarget);
  2741. setMagickPixelPacket(&highTarget, high);
  2742. TransparentPaintImageChroma(imageRef, &lowTarget, &highTarget, opacity, invert);
  2743. DMagickException.throwException(&(imageRef.exception));
  2744. }
  2745. /**
  2746. * Creates a horizontal mirror image by reflecting the pixels around
  2747. * the central y-axis while rotating them by 90 degrees.
  2748. */
  2749. void transpose()
  2750. {
  2751. MagickCoreImage* image = TransposeImage(imageRef, DMagickExceptionInfo());
  2752. imageRef = ImageRef(image);
  2753. }
  2754. /**
  2755. * Creates a vertical mirror image by reflecting the pixels around
  2756. * the central x-axis while rotating them by 270 degrees
  2757. */
  2758. void transverse()
  2759. {
  2760. MagickCoreImage* image = TransverseImage(imageRef, DMagickExceptionInfo());
  2761. imageRef = ImageRef(image);
  2762. }
  2763. /**
  2764. * Removes the edges that are exactly the same color as the corner
  2765. * pixels. Use the fuzz property to make trim remove edges that are
  2766. * nearly the same color as the corner pixels.
  2767. */
  2768. void trim()
  2769. {
  2770. MagickCoreImage* image = TrimImage(imageRef, DMagickExceptionInfo());
  2771. imageRef = ImageRef(image);
  2772. }
  2773. /**
  2774. * Constructs a new image with one pixel for each unique color in the
  2775. * image. The new image has 1 row. The row has 1 column for each unique
  2776. * pixel in the image.
  2777. */
  2778. Image uniqueColors()
  2779. {
  2780. MagickCoreImage* image = UniqueImageColors(imageRef, DMagickExceptionInfo());
  2781. return new Image(image);
  2782. }
  2783. /**
  2784. * Sharpens an image. We convolve the image with a Gaussian operator
  2785. * of the given radius and standard deviation (sigma). For reasonable
  2786. * results, radius should be larger than sigma. Use a radius of 0 and
  2787. * unsharpMask selects a suitable radius for you.
  2788. *
  2789. * Params:
  2790. * radius = The radius of the Gaussian operator.
  2791. * sigma = The standard deviation of the Gaussian operator.
  2792. * amount = The percentage of the blurred image to be added
  2793. * to the receiver, specified as a fraction between 0
  2794. * and 1.0. A good starting value is 1.0
  2795. * threshold = The threshold needed to apply the amount, specified
  2796. * as a fraction between 0 and 1.0.
  2797. * channel = The channels to sharpen.
  2798. */
  2799. void unsharpMask(
  2800. double radius = 0,
  2801. double sigma = 1,
  2802. double amount = 1,
  2803. double threshold = 0.05,
  2804. ChannelType channel = ChannelType.DefaultChannels)
  2805. {
  2806. MagickCoreImage* image =
  2807. UnsharpMaskImageChannel(imageRef, channel, radius, sigma, amount, threshold, DMagickExceptionInfo());
  2808. imageRef = ImageRef(image);
  2809. }
  2810. /**
  2811. * Get a view into the image. The ImageView can be used to modify
  2812. * individual pixels of the image.
  2813. *
  2814. * Params:
  2815. * area = The area accessible through the view.
  2816. */
  2817. dmagick.ImageView.ImageView view(Geometry area = Geometry.init )
  2818. {
  2819. if ( area == Geometry.init )
  2820. {
  2821. area.width = columns;
  2822. area.height = rows;
  2823. }
  2824. return new dmagick.ImageView.ImageView(this, area);
  2825. }
  2826. /**
  2827. * Gradually shades the edges of the image by transforming the pixels
  2828. * into the background color.
  2829. *
  2830. * Larger values of sigma increase the blurring at the expense of
  2831. * increased execution time. In general, radius should be larger than
  2832. * sigma, although if radius is 0 then ImageMagick will choose a suitable
  2833. * value. Sigma must be non-zero. Choose a very small value for sigma to
  2834. * produce a "hard" edge.
  2835. * Params:
  2836. * xOffset = Influences the amount of background color in the
  2837. * horizontal dimension.
  2838. * yOffset = Influences the amount of background color in the
  2839. * vertical dimension.
  2840. * radius = The radius of the pixel neighborhood.
  2841. * sigma = The standard deviation of the Gaussian, in pixels.
  2842. */
  2843. void vignette(ssize_t xOffset, ssize_t yOffset, double radius = 0, double sigma = 10)
  2844. {
  2845. MagickCoreImage* image =
  2846. VignetteImage(imageRef, radius, sigma, xOffset, yOffset, DMagickExceptionInfo());
  2847. imageRef = ImageRef(image);
  2848. }
  2849. /**
  2850. * Creates a "ripple" effect in the image by shifting the pixels
  2851. * vertically along a sine wave whose amplitude and wavelength is
  2852. * specified by the given parameters.Creates a "ripple" effect in the
  2853. * image by shifting the pixels vertically along a sine wave whose
  2854. * amplitude and wavelength is specified by the given parameters.
  2855. */
  2856. void wave(double amplitude = 25, double wavelength = 150)
  2857. {
  2858. MagickCoreImage* image =
  2859. WaveImage(imageRef, amplitude, wavelength, DMagickExceptionInfo());
  2860. imageRef = ImageRef(image);
  2861. }
  2862. /**
  2863. * Forces all pixels above the threshold into white while leaving
  2864. * all pixels below the threshold unchanged.
  2865. *
  2866. * Params:
  2867. * threshold = The threshold value for red green and blue.
  2868. * channel = One or more channels to adjust.
  2869. */
  2870. void whiteThreshold(Quantum threshold, ChannelType channel = ChannelType.DefaultChannels)
  2871. {
  2872. whiteThreshold(threshold, threshold, threshold, 0, channel);
  2873. }
  2874. ///ditto
  2875. void whiteThreshold(
  2876. Quantum red,
  2877. Quantum green,
  2878. Quantum blue,
  2879. Quantum opacity = 0,
  2880. ChannelType channel = ChannelType.DefaultChannels)
  2881. {
  2882. string thresholds = std.string.format("%s,%s,%s,%s", red, green, blue, opacity);
  2883. WhiteThresholdImageChannel(
  2884. imageRef, channel, toStringz(thresholds), DMagickExceptionInfo()
  2885. );
  2886. }
  2887. /**
  2888. * Writes the image to the specified file. ImageMagick
  2889. * determines image format from the prefix or extension.
  2890. *
  2891. * if an image format is selected which is capable of supporting
  2892. * fewer colors than the original image or quantization has been
  2893. * requested, the original image will be quantized to fewer colors.
  2894. * Use a copy of the original if this is a problem.
  2895. */
  2896. void write(string filename)
  2897. {
  2898. this.filename = filename;
  2899. WriteImage(options.imageInfo, imageRef);
  2900. DMagickException.throwException(&(imageRef.exception));
  2901. }
  2902. /**
  2903. * Set a flag to indicate whether or not to use alpha channel data.
  2904. */
  2905. void alpha(AlphaChannelType type)
  2906. {
  2907. SetImageAlphaChannel(imageRef, type);
  2908. }
  2909. ///ditto
  2910. bool alpha() const
  2911. {
  2912. return GetImageAlphaChannel(imageRef) != 0;
  2913. }
  2914. /**
  2915. * Number time which must expire before displaying the
  2916. * next image in an animated sequence.
  2917. */
  2918. void animationDelay(Duration delay)
  2919. {
  2920. imageRef.delay = cast(size_t)(delay.total!"msecs"() * imageRef.ticks_per_second) / 1000;
  2921. }
  2922. ///ditto
  2923. Duration animationDelay() const
  2924. {
  2925. return dur!"msecs"((imageRef.delay * 1000) / imageRef.ticks_per_second);
  2926. }
  2927. /**
  2928. * Number of iterations to loop an animation.
  2929. */
  2930. void animationIterations(size_t iterations)
  2931. {
  2932. imageRef.iterations = iterations;
  2933. }
  2934. ///ditto
  2935. size_t animationIterations() const
  2936. {
  2937. return imageRef.iterations;
  2938. }
  2939. /**
  2940. * Set the image background color. The default is "white".
  2941. */
  2942. void backgroundColor(string color)
  2943. {
  2944. backgroundColor = new Color(color);
  2945. }
  2946. ///ditto
  2947. void backgroundColor(Color color)
  2948. {
  2949. options.backgroundColor(color);
  2950. imageRef.background_color = color.pixelPacket;
  2951. }
  2952. ///ditto
  2953. Color backgroundColor() const
  2954. {
  2955. return options.backgroundColor;
  2956. }
  2957. /**
  2958. * Set the image border color. The default is "#dfdfdf".
  2959. */
  2960. void borderColor(string color)
  2961. {
  2962. borderColor = new Color(color);
  2963. }
  2964. ///ditto
  2965. void borderColor(Color color)
  2966. {
  2967. options.borderColor = color;
  2968. imageRef.border_color = color.pixelPacket;
  2969. }
  2970. ///ditto
  2971. Color borderColor() const
  2972. {
  2973. return options.borderColor;
  2974. }
  2975. /**
  2976. * Return smallest bounding box enclosing non-border pixels.
  2977. * The current fuzz value is used when discriminating between pixels.
  2978. */
  2979. Geometry boundingBox() const
  2980. {
  2981. RectangleInfo box = GetImageBoundingBox(imageRef, DMagickExceptionInfo());
  2982. return Geometry(box);
  2983. }
  2984. /**
  2985. * Pixel cache threshold in megabytes. Once this threshold is exceeded,
  2986. * all subsequent pixels cache operations are to/from disk.
  2987. * This is a static method and the attribute it sets is shared
  2988. * by all Image objects
  2989. */
  2990. static void cacheThreshold(size_t threshold)
  2991. {
  2992. SetMagickResourceLimit(ResourceType.MemoryResource, threshold);
  2993. }
  2994. /**
  2995. * returns true if any pixel in the image has been altered
  2996. * since it was first constituted.
  2997. */
  2998. bool changed() const
  2999. {
  3000. return IsTaintImage(imageRef) != 0;
  3001. }
  3002. /**
  3003. * Channel modulus depth. The channel modulus depth represents
  3004. * the minimum number of bits required to support the channel without loss.
  3005. * Setting the channel's modulus depth modifies the channel (i.e. discards
  3006. * resolution) if the requested modulus depth is less than the current
  3007. * modulus depth, otherwise the channel is not altered. There is no
  3008. * attribute associated with the modulus depth so the current modulus
  3009. * depth is obtained by inspecting the pixels. As a result, the depth
  3010. * returned may be less than the most recently set channel depth.
  3011. * Subsequent image processing may result in increasing the channel depth.
  3012. */
  3013. //TODO: Is this a property?
  3014. void channelDepth(ChannelType channel, size_t depth)
  3015. {
  3016. SetImageChannelDepth(imageRef, channel, depth);
  3017. }
  3018. ///ditto
  3019. size_t channelDepth(ChannelType channel) const
  3020. {
  3021. size_t depth = GetImageChannelDepth(imageRef, channel, DMagickExceptionInfo());
  3022. return depth;
  3023. }
  3024. /**
  3025. * The red, green, blue, and white-point chromaticity values.
  3026. */
  3027. void chromaticity(ChromaticityInfo chroma)
  3028. {
  3029. imageRef.chromaticity = chroma;
  3030. }
  3031. ///ditto
  3032. ChromaticityInfo chromaticity() const
  3033. {
  3034. return imageRef.chromaticity;
  3035. }
  3036. /**
  3037. * The image's storage class. If DirectClass then the pixels
  3038. * contain valid RGB or CMYK colors. If PseudoClass then the
  3039. * image has a colormap referenced by the pixel's index member.
  3040. */
  3041. void classType(ClassType type)
  3042. {
  3043. if ( imageRef.storage_class == ClassType.PseudoClass && type == ClassType.DirectClass )
  3044. {
  3045. SyncImage(imageRef);
  3046. colormap() = null;
  3047. }
  3048. else if ( imageRef.storage_class == ClassType.DirectClass && type == ClassType.PseudoClass )
  3049. {
  3050. options.quantizeColors = MaxColormapSize;
  3051. quantize();
  3052. }
  3053. imageRef.storage_class = type;
  3054. }
  3055. ///ditto
  3056. ClassType classType() const
  3057. {
  3058. return imageRef.storage_class;
  3059. }
  3060. /**
  3061. * Associate a clip mask image with the current image.
  3062. * The clip mask image must have the same dimensions as the current
  3063. * image or an exception is thrown. Clipping occurs wherever pixels are
  3064. * transparent in the clip mask image. Clipping Pass an invalid image
  3065. * to unset an existing clip mask.
  3066. */
  3067. void clipMask(const(Image) image)
  3068. {
  3069. if ( image is null )
  3070. {
  3071. SetImageClipMask(imageRef, null);
  3072. return;
  3073. }
  3074. //Throw a chatchable exception when the size differs.
  3075. if ( image.columns != columns || image.rows != rows )
  3076. throw new ImageException("image size differs");
  3077. SetImageClipMask(imageRef, image.imageRef);
  3078. }
  3079. ///ditto
  3080. Image clipMask() const
  3081. {
  3082. MagickCoreImage* image = CloneImage(imageRef.clip_mask, 0, 0, true, DMagickExceptionInfo());
  3083. return new Image(image);
  3084. }
  3085. /**
  3086. * Access the image color map.
  3087. * Only ClassType.PsseudoClass images have a colormap.
  3088. * ----------------------------------
  3089. * Color color = image.colormap[2];
  3090. * image.colormap()[2] = color;
  3091. * ----------------------------------
  3092. * To asign the complete colormap at once:
  3093. * ----------------------------------
  3094. * Color[] colors = new Colors[255];
  3095. * image.colormap() = colors;
  3096. * //Or
  3097. * image.colormap.size = 255;
  3098. * foreach(i, color; colors)
  3099. * image.colormap()[i] = color;
  3100. * ----------------------------------
  3101. * Bugs: because of dmd bug 2152 the parentheses are needed when assigning;
  3102. */
  3103. auto colormap()
  3104. {
  3105. struct Colormap
  3106. {
  3107. Image img;
  3108. this(Image img)
  3109. {
  3110. this.img = img;
  3111. }
  3112. Color opIndex(uint index)
  3113. {
  3114. if ( index >= img.colormapSize )
  3115. throw new Exception("Index out of bounds");
  3116. return new Color(img.imageRef.colormap[index]);
  3117. }
  3118. void opIndexAssign(Color value, size_t index)
  3119. {
  3120. if ( index >= img.colormapSize )
  3121. throw new Exception("Index out of bounds");
  3122. img.imageRef.colormap[index] = value.pixelPacket;
  3123. }
  3124. void opAssign(Color[] colors)
  3125. {
  3126. img.colormapSize = colors.length;
  3127. if ( colors.length == 0 )
  3128. return;
  3129. foreach(i, color; colors)
  3130. this[i] = color;
  3131. }
  3132. void opOpAssign(string op)(Color color) if ( op == "~" )
  3133. {
  3134. img.colormapSize = img.colormapSize + 1;
  3135. this[img.colormapSize] = color;
  3136. }
  3137. void opOpAssign(string op)(Color[] colors) if ( op == "~" )
  3138. {
  3139. uint oldSize = img.colormapSize;
  3140. img.colormapSize = oldSize + colors.length;
  3141. foreach ( i; oldSize..img.colormapSize)
  3142. this[i] = colors[i];
  3143. }
  3144. /**
  3145. * compresses the colormap by removing any
  3146. * duplicate or unused color entries.
  3147. */
  3148. void compress()
  3149. {
  3150. CompressImageColormap(img.imageRef);
  3151. DMagickException.throwException(&(img.imageRef.exception));
  3152. }
  3153. size_t size()
  3154. {
  3155. return img.colormapSize;
  3156. }
  3157. void size(size_t s)
  3158. {
  3159. img.colormapSize = s;
  3160. }
  3161. }
  3162. return Colormap(this);
  3163. }
  3164. /**
  3165. * The number of colors in the colormap. Only meaningful for PseudoClass images.
  3166. *
  3167. * Setting the colormap size may extend or truncate the colormap.
  3168. * The maximum number of supported entries is specified by the
  3169. * MaxColormapSize constant, and is dependent on the value of
  3170. * QuantumDepth when ImageMagick is compiled. An exception is thrown
  3171. * if more entries are requested than may be supported.
  3172. * Care should be taken when truncating the colormap to ensure that
  3173. * the image colormap indexes reference valid colormap entries.
  3174. */
  3175. void colormapSize(size_t size)
  3176. {
  3177. if ( size > MaxColormapSize )
  3178. throw new OptionException(
  3179. "the size of the colormap can't exceed MaxColormapSize");
  3180. if ( size == 0 && imageRef.colors > 0 )
  3181. {
  3182. imageRef.colormap = cast(PixelPacket*)RelinquishMagickMemory( imageRef.colormap );
  3183. imageRef.colors = 0;
  3184. return;
  3185. }
  3186. if ( imageRef.colormap is null )
  3187. {
  3188. AcquireImageColormap(imageRef, size);
  3189. imageRef.colors = 0;
  3190. }
  3191. else
  3192. {
  3193. imageRef.colormap = cast(PixelPacket*)
  3194. ResizeMagickMemory(imageRef.colormap, size * PixelPacket.sizeof);
  3195. }
  3196. //Initialize the colors as black.
  3197. foreach ( i; imageRef.colors .. size )
  3198. {
  3199. imageRef.colormap[i].blue = 0;
  3200. imageRef.colormap[i].green = 0;
  3201. imageRef.colormap[i].red = 0;
  3202. imageRef.colormap[i].opacity = 0;
  3203. }
  3204. imageRef.colors = size;
  3205. }
  3206. ///ditto
  3207. size_t colormapSize() const
  3208. {
  3209. return imageRef.colors;
  3210. }
  3211. /**
  3212. * The colorspace used to represent the image pixel colors.
  3213. * Image pixels are always stored as RGB(A) except for the case of CMY(K).
  3214. */
  3215. void colorspace(ColorspaceType type)
  3216. {
  3217. TransformImageColorspace(imageRef, type);
  3218. options.colorspace = type;
  3219. }
  3220. ///ditto
  3221. ColorspaceType colorspace() const
  3222. {
  3223. return imageRef.colorspace;
  3224. }
  3225. /**
  3226. * The width of the image in pixels.
  3227. */
  3228. size_t columns() const
  3229. {
  3230. return imageRef.columns;
  3231. }
  3232. /**
  3233. * Composition operator to be used when composition is
  3234. * implicitly used (such as for image flattening).
  3235. */
  3236. void compose(CompositeOperator op)
  3237. {
  3238. imageRef.compose = op;
  3239. }
  3240. ///ditto
  3241. CompositeOperator compose() const
  3242. {
  3243. return imageRef.compose;
  3244. }
  3245. /**
  3246. * The image compression type. The default is the
  3247. * compression type of the specified image file.
  3248. */
  3249. void compression(CompressionType type)
  3250. {
  3251. imageRef.compression = type;
  3252. options.compression = type;
  3253. }
  3254. ///ditto
  3255. CompressionType compression() const
  3256. {
  3257. return imageRef.compression;
  3258. }
  3259. /**
  3260. * The vertical and horizontal resolution in pixels of the image.
  3261. * This option specifies an image density when decoding
  3262. * a Postscript or Portable Document page.
  3263. *
  3264. * The default is "72x72".
  3265. */
  3266. void density(Geometry value)
  3267. {
  3268. options.density = value;
  3269. imageRef.x_resolution = value.width;
  3270. imageRef.y_resolution = ( value.width != 0 ) ? value.width : value.height;
  3271. }
  3272. ///ditto
  3273. Geometry density() const
  3274. {
  3275. ssize_t width = cast(ssize_t)rndtol(imageRef.x_resolution);
  3276. ssize_t height = cast(ssize_t)rndtol(imageRef.y_resolution);
  3277. return Geometry(width, height);
  3278. }
  3279. /**
  3280. * Image depth. Used to specify the bit depth when reading or writing
  3281. * raw images or when the output format supports multiple depths.
  3282. * Defaults to the quantum depth that ImageMagick is compiled with.
  3283. */
  3284. void depth(size_t value)
  3285. {
  3286. if ( value > MagickQuantumDepth)
  3287. value = MagickQuantumDepth;
  3288. imageRef.depth = value;
  3289. options.depth = value;
  3290. }
  3291. ///ditto
  3292. size_t depth() const
  3293. {
  3294. return imageRef.depth;
  3295. }
  3296. /**
  3297. * Tile names from within an image montage.
  3298. * Only valid after calling montage or reading a MIFF file
  3299. * which contains a directory.
  3300. */
  3301. string directory() const
  3302. {
  3303. return to!(string)(imageRef.directory);
  3304. }
  3305. /**
  3306. * Specify (or obtain) endian option for formats which support it.
  3307. */
  3308. void endian(EndianType type)
  3309. {
  3310. imageRef.endian = type;
  3311. options.endian = type;
  3312. }
  3313. ///ditto
  3314. EndianType endian() const
  3315. {
  3316. return imageRef.endian;
  3317. }
  3318. /**
  3319. * The EXIF profile.
  3320. */
  3321. void exifProfile(void[] blob)
  3322. {
  3323. StringInfo* profile = AcquireStringInfo(blob.length);
  3324. SetStringInfoDatum(profile, cast(ubyte*)blob.ptr);
  3325. SetImageProfile(imageRef, "exif", profile);
  3326. DestroyStringInfo(profile);
  3327. }
  3328. ///ditto
  3329. void[] exifProfile() const
  3330. {
  3331. const(StringInfo)* profile = GetImageProfile(imageRef, "exif");
  3332. if ( profile is null )
  3333. return null;
  3334. return GetStringInfoDatum(profile)[0 .. GetStringInfoLength(profile)].dup;
  3335. }
  3336. /**
  3337. * The image filename.
  3338. */
  3339. void filename(string str)
  3340. {
  3341. copyString(imageRef.filename, str);
  3342. options.filename = str;
  3343. }
  3344. ///ditto
  3345. string filename() const
  3346. {
  3347. return imageRef.filename[0 .. strlen(imageRef.filename.ptr)].idup;
  3348. }
  3349. /**
  3350. * The image filesize in bytes.
  3351. */
  3352. MagickSizeType fileSize() const
  3353. {
  3354. return GetBlobSize(imageRef);
  3355. }
  3356. /**
  3357. * Filter to use when resizing image. The reduction filter employed
  3358. * has a significant effect on the time required to resize an image
  3359. * and the resulting quality. The default filter is Lanczos which has
  3360. * been shown to produce high quality results when reducing most images.
  3361. */
  3362. void filter(FilterTypes type)
  3363. {
  3364. imageRef.filter = type;
  3365. }
  3366. ///ditto
  3367. FilterTypes filter() const
  3368. {
  3369. return imageRef.filter;
  3370. }
  3371. /**
  3372. * The image encoding format. For example, "GIF" or "PNG".
  3373. */
  3374. string format() const
  3375. {
  3376. const(MagickInfo)* info = GetMagickInfo(imageRef.magick.ptr, DMagickExceptionInfo());
  3377. return to!(string)( info.description );
  3378. }
  3379. /**
  3380. * Colors within this distance are considered equal.
  3381. * A number of algorithms search for a target color.
  3382. * By default the color must be exact. Use this option to match
  3383. * colors that are close to the target color in RGB space.
  3384. */
  3385. void fuzz(double f)
  3386. {
  3387. options.fuzz = f;
  3388. imageRef.fuzz = f;
  3389. }
  3390. ///ditto
  3391. double fuzz() const
  3392. {
  3393. return options.fuzz;
  3394. }
  3395. /**
  3396. * Gamma level of the image. The same color image displayed on
  3397. * two different workstations may look different due to differences
  3398. * in the display monitor. Use gamma correction to adjust for this
  3399. * color difference.
  3400. */
  3401. double gamma() const
  3402. {
  3403. return imageRef.gamma;
  3404. }
  3405. /**
  3406. * Preferred size of the image when encoding.
  3407. */
  3408. void geometry(string str)
  3409. {
  3410. copyString(imageRef.geometry, str);
  3411. }
  3412. ///ditto
  3413. void geometry(Geometry value)
  3414. {
  3415. geometry(value.toString());
  3416. }
  3417. ///ditto
  3418. Geometry geometry() const
  3419. {
  3420. return Geometry( to!(string)(imageRef.geometry) );
  3421. }
  3422. /**
  3423. * GIF disposal method. This attribute is used to control how
  3424. * successive images are rendered (how the preceding image
  3425. * is disposed of) when creating a GIF animation.
  3426. */
  3427. void gifDisposeMethod(DisposeType type)
  3428. {
  3429. imageRef.dispose = type;
  3430. }
  3431. ///ditto
  3432. DisposeType gifDisposeMethod() const
  3433. {
  3434. return imageRef.dispose;
  3435. }
  3436. /**
  3437. * Returns true if all the pixels in the image have the same red,
  3438. * green, and blue intensities.
  3439. */
  3440. bool gray()
  3441. {
  3442. return dmagick.c.attribute.IsGrayImage(imageRef, DMagickExceptionInfo()) == 1;
  3443. }
  3444. /**
  3445. * Computes the number of times each unique color appears in the image.
  3446. * You may want to quantize the image before using this property.
  3447. *
  3448. * Returns: A associative array. Each key reprecents a color in the Image.
  3449. * The value is the number of times the color apears in the image.
  3450. */
  3451. MagickSizeType[Color] histogram() const
  3452. {
  3453. size_t count;
  3454. MagickSizeType[Color] hashMap;
  3455. ColorPacket* colorPackets;
  3456. colorPackets = GetImageHistogram(imageRef, &count, DMagickExceptionInfo());
  3457. foreach ( packet; colorPackets[0 .. count] )
  3458. {
  3459. hashMap[new Color(packet.pixel)] = packet.count;
  3460. }
  3461. RelinquishMagickMemory(colorPackets);
  3462. return hashMap;
  3463. }
  3464. /**
  3465. * ICC color profile.
  3466. */
  3467. void iccColorProfile(void[] blob)
  3468. {
  3469. profile("icm", blob);
  3470. }
  3471. ///ditto
  3472. void[] iccColorProfile() const
  3473. {
  3474. return profile("icm");
  3475. }
  3476. /**
  3477. * Specify the _type of interlacing scheme for raw image formats
  3478. * such as RGB or YUV. NoInterlace means do not _interlace,
  3479. * LineInterlace uses scanline interlacing, and PlaneInterlace
  3480. * uses plane interlacing. PartitionInterlace is like PlaneInterlace
  3481. * except the different planes are saved to individual files
  3482. * (e.g. image.R, image.G, and image.B). Use LineInterlace or
  3483. * PlaneInterlace to create an interlaced GIF or
  3484. * progressive JPEG image. The default is NoInterlace.
  3485. */
  3486. void interlace(InterlaceType type)
  3487. {
  3488. imageRef.interlace = type;
  3489. options.interlace = type;
  3490. }
  3491. ///ditto
  3492. InterlaceType interlace() const
  3493. {
  3494. return imageRef.interlace;
  3495. }
  3496. /**
  3497. * The International Press Telecommunications Council profile.
  3498. */
  3499. void iptcProfile(void[] blob)
  3500. {
  3501. profile("iptc", blob);
  3502. }
  3503. ///ditto
  3504. void[] iptcProfile() const
  3505. {
  3506. return profile("iptc");
  3507. }
  3508. /**
  3509. * Image format (e.g. "GIF")
  3510. */
  3511. void magick(string str)
  3512. {
  3513. copyString(imageRef.magick, str);
  3514. options.magick = str;
  3515. }
  3516. ///ditto
  3517. string magick() const
  3518. {
  3519. if ( imageRef.magick[0] !is '\0' )
  3520. {
  3521. return imageRef.magick[0 .. strlen(imageRef.magick.ptr)].idup;
  3522. }
  3523. return options.magick;
  3524. }
  3525. /**
  3526. * Set the image transparent color. The default is "#bdbdbd".
  3527. */
  3528. void matteColor(string color)
  3529. {
  3530. matteColor = new Color(color);
  3531. }
  3532. ///ditto
  3533. void matteColor(Color color)
  3534. {
  3535. imageRef.matte_color = color.pixelPacket;
  3536. options.matteColor = color;
  3537. }
  3538. ///ditto
  3539. Color matteColor() const
  3540. {
  3541. return new Color(imageRef.matte_color);
  3542. }
  3543. /**
  3544. * The mean error per pixel computed when an image is color reduced.
  3545. * This parameter is only valid if verbose is set to true and the
  3546. * image has just been quantized.
  3547. */
  3548. double meanErrorPerPixel() const
  3549. {
  3550. return imageRef.error.mean_error_per_pixel;
  3551. }
  3552. /**
  3553. * Image modulus depth (minimum number of bits required to
  3554. * support red/green/blue components without loss of accuracy).
  3555. * The pixel modulus depth may be decreased by supplying a value
  3556. * which is less than the current value, updating the pixels
  3557. * (reducing accuracy) to the new depth. The pixel modulus depth
  3558. * can not be increased over the current value using this method.
  3559. */
  3560. void modulusDepth(size_t depth)
  3561. {
  3562. SetImageDepth(imageRef, depth);
  3563. options.depth = depth;
  3564. }
  3565. ///ditto
  3566. size_t modulusDepth() const
  3567. {
  3568. size_t depth = GetImageDepth(imageRef, DMagickExceptionInfo());
  3569. return depth;
  3570. }
  3571. /**
  3572. * Establish a progress monitor. Most Image and ImageList methods
  3573. * will periodically call the monitor with arguments indicating the
  3574. * progress of the method.
  3575. *
  3576. * The delegate receves the folowing $(B parameters): $(BR)
  3577. * $(TABLE
  3578. * $(ROW string $(I methodName), The name of the monitored method.)
  3579. * $(ROW long $(I offset ), A number between 0 and extent that
  3580. * identifies how much of the operation has been completed
  3581. * (or, in some cases, remains to be completed).)
  3582. * $(ROW ulong $(I extent ), The number of quanta needed to
  3583. * complete the operation.)
  3584. * )
  3585. */
  3586. void monitor(bool delegate(string methodName, long offset, ulong extent) progressMonitor)
  3587. {
  3588. if ( this.progressMonitor is null )
  3589. SetImageProgressMonitor(imageRef, cast(MagickProgressMonitor)&ImageProgressMonitor, cast(void*)this);
  3590. this.progressMonitor = progressMonitor;
  3591. if ( progressMonitor is null )
  3592. SetImageProgressMonitor(imageRef, null, null);
  3593. }
  3594. ///ditto
  3595. bool delegate(string, long, ulong) monitor()
  3596. {
  3597. return progressMonitor;
  3598. }
  3599. static extern(C) MagickBooleanType ImageProgressMonitor(
  3600. const(char)* methodName,
  3601. MagickOffsetType offset,
  3602. MagickSizeType extend,
  3603. Image image)
  3604. {
  3605. return image.progressMonitor(to!(string)(methodName), offset, extend);
  3606. }
  3607. /**
  3608. * Tile size and offset within an image montage.
  3609. * Only valid for images produced by montage.
  3610. */
  3611. Geometry montageGeometry() const
  3612. {
  3613. return Geometry( to!(string)(imageRef.geometry) );
  3614. }
  3615. /**
  3616. * The normalized max error per pixel computed when
  3617. * an image is color reduced. This parameter is only
  3618. * valid if verbose is set to true and the image
  3619. * has just been quantized.
  3620. */
  3621. double normalizedMaxError() const
  3622. {
  3623. return imageRef.error.normalized_maximum_error;
  3624. }
  3625. /**
  3626. * The normalized mean error per pixel computed when
  3627. * an image is color reduced. This parameter is only
  3628. * valid if verbose is set to true and the image
  3629. * has just been quantized.
  3630. */
  3631. double normalizedMeanError() const
  3632. {
  3633. return imageRef.error.normalized_mean_error;
  3634. }
  3635. /**
  3636. * Sets the value of the image property. An image may have any number
  3637. * of properties. ImageMagick predefines some properties, including
  3638. * attribute, label, caption, comment, signature, and in some cases EXIF.
  3639. */
  3640. void opDispatch(string property)(string value)
  3641. if ( property != "popFront" )
  3642. {
  3643. SetImageProperty(imageRef, toStringz(property), toStringz(value));
  3644. return;
  3645. }
  3646. /**
  3647. * Returns the value of the image property.
  3648. */
  3649. auto opDispatch(string property)()
  3650. if ( property != "popFront" )
  3651. {
  3652. return to!(string)(GetImageProperty(imageRef, toStringz(property)));
  3653. }
  3654. unittest
  3655. {
  3656. Image image = new Image();
  3657. image.comment = "unittest";
  3658. assert(image.comment == "unittest");
  3659. }
  3660. /**
  3661. * Image orientation. Supported by some file formats
  3662. * such as DPX and TIFF. Useful for turning the right way up.
  3663. */
  3664. void orientation(OrientationType orientation)
  3665. {
  3666. imageRef.orientation = orientation;
  3667. }
  3668. ///ditto
  3669. OrientationType orientation() const
  3670. {
  3671. return imageRef.orientation;
  3672. }
  3673. /**
  3674. * When compositing, this attribute describes the position
  3675. * of this image with respect to the underlying image.
  3676. *
  3677. * Use this option to specify the dimensions and position of
  3678. * the Postscript page in dots per inch or a TEXT page in pixels.
  3679. * This option is typically used in concert with density.
  3680. *
  3681. * Page may also be used to position a GIF image
  3682. * (such as for a scene in an animation).
  3683. */
  3684. void page(Geometry geometry)
  3685. {
  3686. options.page = geometry;
  3687. imageRef.page = geometry.rectangleInfo;
  3688. }
  3689. ///ditto
  3690. Geometry page() const
  3691. {
  3692. return Geometry(imageRef.page);
  3693. }
  3694. /**
  3695. * The pixel color interpolation method. Some methods (such
  3696. * as wave, swirl, implode, and composite) use the pixel color
  3697. * interpolation method to determine how to blend adjacent pixels.
  3698. */
  3699. void pixelInterpolationMethod(InterpolatePixelMethod method)
  3700. {
  3701. imageRef.interpolate = method;
  3702. }
  3703. ///ditto
  3704. InterpolatePixelMethod pixelInterpolationMethod() const
  3705. {
  3706. return imageRef.interpolate;
  3707. }
  3708. /**
  3709. * Get/set/remove a named profile. Valid names include "*",
  3710. * "8BIM", "ICM", "IPTC", or a user/format-defined profile name.
  3711. */
  3712. void profile(string name, void[] blob)
  3713. {
  3714. ProfileImage(imageRef, toStringz(name), blob.ptr, blob.length, false);
  3715. }
  3716. ///ditto
  3717. void[] profile(string name) const
  3718. {
  3719. const(StringInfo)* profile = GetImageProfile(imageRef, toStringz(name));
  3720. if ( profile is null )
  3721. return null;
  3722. return GetStringInfoDatum(profile)[0 .. GetStringInfoLength(profile)].dup;
  3723. }
  3724. /**
  3725. * JPEG/MIFF/PNG compression level (default 75).
  3726. */
  3727. void quality(size_t quality)
  3728. {
  3729. imageRef.quality = quality;
  3730. options.quality = quality;
  3731. }
  3732. ///ditto
  3733. size_t quality() const
  3734. {
  3735. return imageRef.quality;
  3736. }
  3737. /**
  3738. * The type of rendering intent.
  3739. * See_Also:
  3740. * $(LINK http://www.cambridgeincolour.com/tutorials/color-space-conversion.htm)
  3741. */
  3742. void renderingIntent(RenderingIntent intent)
  3743. {
  3744. imageRef.rendering_intent = intent;
  3745. }
  3746. ///ditto
  3747. RenderingIntent renderingIntent() const
  3748. {
  3749. return imageRef.rendering_intent;
  3750. }
  3751. /**
  3752. * Units of image resolution
  3753. */
  3754. void resolutionUnits(ResolutionType type)
  3755. {
  3756. imageRef.units = type;
  3757. options.resolutionUnits = type;
  3758. }
  3759. ///ditto
  3760. ResolutionType resolutionUnits() const
  3761. {
  3762. return options.resolutionUnits;
  3763. }
  3764. /**
  3765. * The scene number assigned to the image the last
  3766. * time the image was written to a multi-image image file.
  3767. */
  3768. void scene(size_t value)
  3769. {
  3770. imageRef.scene = value;
  3771. }
  3772. ///ditto
  3773. size_t scene() const
  3774. {
  3775. return imageRef.scene;
  3776. }
  3777. /**
  3778. * The height of the image in pixels.
  3779. */
  3780. size_t rows() const
  3781. {
  3782. return imageRef.rows;
  3783. }
  3784. /**
  3785. * Width and height of a image.
  3786. */
  3787. Geometry size() const
  3788. {
  3789. return Geometry(imageRef.columns, imageRef.rows);
  3790. }
  3791. /**
  3792. * Number of colors in the image.
  3793. */
  3794. size_t totalColors() const
  3795. {
  3796. size_t colors = GetNumberColors(imageRef, null, DMagickExceptionInfo());
  3797. return colors;
  3798. }
  3799. /**
  3800. * Image type.
  3801. */
  3802. void type(ImageType imageType)
  3803. {
  3804. options.type = imageType;
  3805. SetImageType(imageRef, imageType);
  3806. }
  3807. ///ditto
  3808. ImageType type() const
  3809. {
  3810. if (options.type != ImageType.UndefinedType )
  3811. return options.type;
  3812. ImageType imageType = GetImageType(imageRef, DMagickExceptionInfo());
  3813. return imageType;
  3814. }
  3815. /**
  3816. * Specify how "virtual pixels" behave. Virtual pixels are
  3817. * pixels that are outside the boundaries of the image.
  3818. * Methods such as blurImage, sharpen, and wave use virtual pixels.
  3819. */
  3820. void virtualPixelMethod(VirtualPixelMethod method)
  3821. {
  3822. options.virtualPixelMethod = method;
  3823. SetImageVirtualPixelMethod(imageRef, method);
  3824. }
  3825. ///ditto
  3826. VirtualPixelMethod virtualPixelMethod() const
  3827. {
  3828. return GetImageVirtualPixelMethod(imageRef);
  3829. }
  3830. /**
  3831. * Horizontal resolution of the image.
  3832. */
  3833. double xResolution() const
  3834. {
  3835. return imageRef.x_resolution;
  3836. }
  3837. /**
  3838. * Vertical resolution of the image.
  3839. */
  3840. double yResolution() const
  3841. {
  3842. return imageRef.y_resolution;
  3843. }
  3844. private void setMagickPixelPacket(MagickPixelPacket* magick, Color color)
  3845. {
  3846. magick.red = color.redQuantum;
  3847. magick.green = color.greenQuantum;
  3848. magick.blue = color.blueQuantum;
  3849. magick.opacity = color.opacityQuantum;
  3850. }
  3851. private string wordWrap(string text, Geometry boundingBox)
  3852. {
  3853. size_t pos;
  3854. string[] lines;
  3855. if ( text.empty )
  3856. return text;
  3857. double lineHeight = getTypeMetrics([text[0]]).height;
  3858. size_t maxLines = cast(size_t)(boundingBox.height / lineHeight);
  3859. while ( !text.empty )
  3860. {
  3861. for ( size_t i; i < text.length; i++ )
  3862. {
  3863. if ( isWhite(text[i]) || i == text.length-1 )
  3864. {
  3865. TypeMetric metric = getTypeMetrics(text[0..i]);
  3866. if ( metric.width > boundingBox.width )
  3867. {
  3868. if ( pos == 0 )
  3869. pos = i;
  3870. break;
  3871. }
  3872. pos = i;
  3873. if ( text[i] == '\n' )
  3874. break;
  3875. if ( i == text.length-1 )
  3876. pos++;
  3877. }
  3878. }
  3879. lines ~= text[0 .. pos].strip();
  3880. text = text[min(pos+1, text.length) .. $];
  3881. pos = 0;
  3882. if ( lines.length == maxLines )
  3883. break;
  3884. }
  3885. return join(lines, "\n");
  3886. }
  3887. unittest
  3888. {
  3889. Image img = new Image(Geometry(200, 200), new Color());
  3890. img.options.font = "/usr/share/fonts/TTF/DejaVuSans.ttf";
  3891. string wraped = img.wordWrap("Lorem ipsum dolor sit amet.", Geometry(100, 200));
  3892. assert(wraped == "Lorem ipsum\ndolor sit amet.");
  3893. }
  3894. private template getStorageType(T)
  3895. {
  3896. static if ( is( T == byte) )
  3897. {
  3898. enum getStorageType = StorageType.CharPixel;
  3899. }
  3900. else static if ( is( T == short) )
  3901. {
  3902. enum getStorageType = StorageType.ShortPixel;
  3903. }
  3904. else static if ( is( T == int) )
  3905. {
  3906. enum getStorageType = StorageType.IntegerPixel;
  3907. }
  3908. else static if ( is( T == long) )
  3909. {
  3910. enum getStorageType = StorageType.LongPixel;
  3911. }
  3912. else static if ( is( T == float) )
  3913. {
  3914. enum getStorageType = StorageType.FloatPixel;
  3915. }
  3916. else static if ( is( T == double) )
  3917. {
  3918. enum getStorageType = StorageType.DoublePixel;
  3919. }
  3920. else
  3921. {
  3922. static assert(false, "Unsupported type");
  3923. }
  3924. }
  3925. unittest
  3926. {
  3927. StorageType storage = getStorageType!(int);
  3928. assert( storage == StorageType.IntegerPixel );
  3929. }
  3930. }
  3931. /*
  3932. * Initialize ImageMagick, causes an access violation on Windows.
  3933. */
  3934. version (Posix)
  3935. {
  3936. shared static this()
  3937. {
  3938. MagickCoreGenesis(null, false);
  3939. }
  3940. shared static ~this()
  3941. {
  3942. MagickCoreTerminus();
  3943. }
  3944. }