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