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