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