/// <summary> /// Creates a deep copy of an image allowing you to set the pixel format. /// Disposing of the original is the responsibility of the user. /// <remarks> /// Unlike the native <see cref="Image.Clone"/> method this also copies animation frames. /// </remarks> /// </summary> /// <param name="source">The source image to copy.</param> /// <param name="animationProcessMode">The process mode for frames in animated images.</param> /// <param name="format">The <see cref="PixelFormat"/> to set the copied image to.</param> /// <param name="preserveExifData">Whether to preserve exif metadata. Defaults to false.</param> /// <returns> /// The <see cref="Image"/>. /// </returns> public static Image Copy(this Image source, AnimationProcessMode animationProcessMode, PixelFormat format = PixelFormat.Format32bppPArgb, bool preserveExifData = false) { if (source.RawFormat.Equals(ImageFormat.Gif)) { // Read from the correct first frame when performing additional processing source.SelectActiveFrame(FrameDimension.Time, 0); GifDecoder decoder = new GifDecoder(source, animationProcessMode); GifEncoder encoder = new GifEncoder(null, null, decoder.LoopCount); // Have to use Octree here, there's no way to inject it. OctreeQuantizer quantizer = new OctreeQuantizer(); for (int i = 0; i < decoder.FrameCount; i++) { GifFrame frame = decoder.GetFrame(source, i); frame.Image = quantizer.Quantize(((Bitmap)frame.Image).Clone(new Rectangle(0, 0, frame.Image.Width, frame.Image.Height), format)); ((Bitmap)frame.Image).SetResolution(source.HorizontalResolution, source.VerticalResolution); encoder.AddFrame(frame); } return encoder.Save(); } // Create a new image and copy it's pixels. Bitmap copy = new Bitmap(source.Width, source.Height, format); copy.SetResolution(source.HorizontalResolution, source.VerticalResolution); using (Graphics graphics = Graphics.FromImage(copy)) { graphics.DrawImageUnscaled(source, 0, 0); } if (preserveExifData) { foreach (PropertyItem item in source.PropertyItems) { copy.SetPropertyItem(item); } } return copy; }
/// <summary> /// Processes the image. /// </summary> /// <param name="processor"> /// The processor. /// </param> /// <param name="factory"> /// The factory. /// </param> private static void ApplyProcessor(Func<ImageFactory, Image> processor, ImageFactory factory) { ImageInfo imageInfo = factory.Image.GetImageInfo(factory.ImageFormat); if (imageInfo.IsAnimated) { OctreeQuantizer quantizer = new OctreeQuantizer(255, 8); // We don't dispose of the memory stream as that is disposed when a new image is created and doing so // beforehand will cause an exception. MemoryStream stream = new MemoryStream(); using (GifEncoder encoder = new GifEncoder(stream, null, null, imageInfo.LoopCount)) { foreach (GifFrame frame in imageInfo.GifFrames) { factory.Update(frame.Image); frame.Image = quantizer.Quantize(processor.Invoke(factory)); encoder.AddFrame(frame); } } stream.Position = 0; factory.Update(Image.FromStream(stream)); } else { factory.Update(processor.Invoke(factory)); } // Set the property item information from any Exif metadata. // We do this here so that they can be changed between processor methods. if (factory.PreserveExifData) { foreach (KeyValuePair<int, PropertyItem> propertItem in factory.ExifPropertyItems) { try { factory.Image.SetPropertyItem(propertItem.Value); } // ReSharper disable once EmptyGeneralCatchClause catch { // Do nothing. The image format does not handle EXIF data. // TODO: empty catch is fierce code smell. } } } }
/// <summary> /// Uses the <see cref="T:ImageProcessor.Imaging.OctreeQuantizer"/> /// to fix the color palette of gif images. /// </summary> private void FixGifs() { // Fix the colour palette of gif images. // TODO: Why does the palette not get fixed when resized to the same dimensions. if (object.Equals(this.ImageFormat, ImageFormat.Gif)) { OctreeQuantizer quantizer = new OctreeQuantizer(255, 8); this.Image = quantizer.Quantize(this.Image); } }
/// <summary> /// Applies the given processor the current image. /// </summary> /// <param name="processor"> /// The processor delegate. /// </param> private void ApplyProcessor(Func<ImageFactory, Image> processor) { ImageInfo imageInfo = this.Image.GetImageInfo(this.ImageFormat); if (imageInfo.IsAnimated) { OctreeQuantizer quantizer = new OctreeQuantizer(255, 8); // We don't dispose of the memory stream as that is disposed when a new image is created and doing so // beforehand will cause an exception. MemoryStream stream = new MemoryStream(); using (GifEncoder encoder = new GifEncoder(stream, null, null, imageInfo.LoopCount)) { foreach (GifFrame frame in imageInfo.GifFrames) { this.Image = frame.Image; frame.Image = quantizer.Quantize(processor.Invoke(this)); encoder.AddFrame(frame); } } stream.Position = 0; this.Image = Image.FromStream(stream); } else { this.Image = processor.Invoke(this); } }