Browse Source

Finalising animated gif and improving filters

Former-commit-id: 0feb975e5a07d50e812cd113e3335fe6b7a4271d
af/merge-core
James South 12 years ago
parent
commit
7c2ff654c0
  1. 9
      src/ImageProcessor.Web/NET45/ImageFactoryExtensions.cs
  2. 92
      src/ImageProcessor/ImageFactory.cs
  3. 2
      src/ImageProcessor/ImageProcessor.csproj
  4. 4
      src/ImageProcessor/Imaging/Filters/LomographMatrixFilter.cs
  5. 37
      src/ImageProcessor/Imaging/Filters/MatrixFilterRegexAttribute.cs
  6. 140
      src/ImageProcessor/Imaging/Filters/MatrixFilters.cs
  7. 3
      src/ImageProcessor/Imaging/GifEncoder.cs
  8. 102
      src/ImageProcessor/Processors/Filter.cs
  9. 2
      src/ImageProcessorConsole/Program.cs
  10. 2
      src/ImageProcessorConsole/images/output/120430.gif.REMOVED.git-id
  11. 1
      src/ImageProcessorConsole/images/output/nLpfllv .gif.REMOVED.git-id
  12. 1
      src/ImageProcessorConsole/images/output/nLpfllv.gif.REMOVED.git-id

9
src/ImageProcessor.Web/NET45/ImageFactoryExtensions.cs

@ -60,7 +60,7 @@ namespace ImageProcessor.Web
// Loop through and process the image.
foreach (IGraphicsProcessor graphicsProcessor in graphicsProcessors)
{
ProcessImage(graphicsProcessor.ProcessImage, factory);
ApplyProcessor(graphicsProcessor.ProcessImage, factory);
}
}
}
@ -77,13 +77,16 @@ namespace ImageProcessor.Web
/// <param name="factory">
/// The factory.
/// </param>
private static void ProcessImage(Func<ImageFactory, Image> processor, ImageFactory factory)
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))
{
@ -96,7 +99,7 @@ namespace ImageProcessor.Web
}
stream.Position = 0;
factory.Update(new Bitmap(stream));
factory.Update(Image.FromStream(stream));
}
else
{

92
src/ImageProcessor/ImageFactory.cs

@ -22,6 +22,7 @@ namespace ImageProcessor
using ImageProcessor.Extensions;
using ImageProcessor.Imaging;
using ImageProcessor.Imaging.Filters;
using ImageProcessor.Processors;
#endregion
@ -298,8 +299,7 @@ namespace ImageProcessor
}
Alpha alpha = new Alpha { DynamicParameter = percentage };
this.Image = alpha.ProcessImage(this);
this.ApplyProcessor(alpha.ProcessImage);
}
return this;
@ -326,8 +326,7 @@ namespace ImageProcessor
}
Brightness brightness = new Brightness { DynamicParameter = percentage };
this.Image = brightness.ProcessImage(this);
this.ApplyProcessor(brightness.ProcessImage);
}
return this;
@ -375,8 +374,7 @@ namespace ImageProcessor
}
Contrast contrast = new Contrast { DynamicParameter = percentage };
this.Image = contrast.ProcessImage(this);
this.ApplyProcessor(contrast.ProcessImage);
}
return this;
@ -396,10 +394,7 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
CropLayer cropLayer = new CropLayer(rectangle.Left, rectangle.Top, rectangle.Right, rectangle.Bottom, CropMode.Pixels);
Crop crop = new Crop { DynamicParameter = cropLayer };
this.Image = crop.ProcessImage(this);
return this.Crop(cropLayer);
}
return this;
@ -419,13 +414,12 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
Crop crop = new Crop { DynamicParameter = cropLayer };
this.Image = crop.ProcessImage(this);
this.ApplyProcessor(crop.ProcessImage);
}
return this;
}
/// <summary>
/// Applies a filter to the current image.
/// </summary>
@ -435,13 +429,34 @@ namespace ImageProcessor
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
[Obsolete("Will be removed in next major version. Filter(IMatrixFilter matrixFilter) instead.")]
public ImageFactory Filter(string filterName)
{
if (this.ShouldProcess)
{
Filter filter = new Filter { DynamicParameter = filterName };
this.ApplyProcessor(filter.ProcessImage);
}
return this;
}
this.Image = filter.ProcessImage(this);
/// <summary>
/// Applies a filter to the current image. Use the <see cref="MatrixFilters"/> class to
/// assign the correct filter.
/// </summary>
/// <param name="matrixFilter">
/// The <see cref="IMatrixFilter"/> of the filter to add to the image.
/// </param>
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public ImageFactory Filter(IMatrixFilter matrixFilter)
{
if (this.ShouldProcess)
{
Filter filter = new Filter { DynamicParameter = matrixFilter };
this.ApplyProcessor(filter.ProcessImage);
}
return this;
@ -465,8 +480,7 @@ namespace ImageProcessor
: RotateFlipType.RotateNoneFlipY;
Flip flip = new Flip { DynamicParameter = rotateFlipType };
this.Image = flip.ProcessImage(this);
this.ApplyProcessor(flip.ProcessImage);
}
return this;
@ -512,8 +526,7 @@ namespace ImageProcessor
if (this.ShouldProcess && size > 0)
{
GaussianLayer layer = new GaussianLayer(size);
GaussianBlur gaussianBlur = new GaussianBlur { DynamicParameter = layer };
this.Image = gaussianBlur.ProcessImage(this);
return this.GaussianBlur(layer);
}
return this;
@ -534,7 +547,7 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
GaussianBlur gaussianBlur = new GaussianBlur { DynamicParameter = gaussianLayer };
this.Image = gaussianBlur.ProcessImage(this);
this.ApplyProcessor(gaussianBlur.ProcessImage);
}
return this;
@ -560,8 +573,7 @@ namespace ImageProcessor
if (this.ShouldProcess && size > 0)
{
GaussianLayer layer = new GaussianLayer(size);
GaussianSharpen gaussianSharpen = new GaussianSharpen { DynamicParameter = layer };
this.Image = gaussianSharpen.ProcessImage(this);
return this.GaussianSharpen(layer);
}
return this;
@ -582,7 +594,7 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
GaussianSharpen gaussianSharpen = new GaussianSharpen { DynamicParameter = gaussianLayer };
this.Image = gaussianSharpen.ProcessImage(this);
this.ApplyProcessor(gaussianSharpen.ProcessImage);
}
return this;
@ -625,7 +637,6 @@ namespace ImageProcessor
int height = size.Height;
ResizeLayer resizeLayer = new ResizeLayer(new Size(width, height));
return this.Resize(resizeLayer);
}
@ -648,8 +659,7 @@ namespace ImageProcessor
var resizeSettings = new Dictionary<string, string> { { "MaxWidth", resizeLayer.Size.Width.ToString("G") }, { "MaxHeight", resizeLayer.Size.Height.ToString("G") } };
Resize resize = new Resize { DynamicParameter = resizeLayer, Settings = resizeSettings };
this.ProcessImage(resize.ProcessImage, resizeLayer.Size.Width, resizeLayer.Size.Height);
this.ApplyProcessor(resize.ProcessImage);
}
return this;
@ -675,8 +685,7 @@ namespace ImageProcessor
}
Rotate rotate = new Rotate { DynamicParameter = rotateLayer };
this.Image = rotate.ProcessImage(this);
this.ApplyProcessor(rotate.ProcessImage);
}
return this;
@ -701,8 +710,7 @@ namespace ImageProcessor
}
RoundedCorners roundedCorners = new RoundedCorners { DynamicParameter = roundedCornerLayer };
this.Image = roundedCorners.ProcessImage(this);
this.ApplyProcessor(roundedCorners.ProcessImage);
}
return this;
@ -729,8 +737,7 @@ namespace ImageProcessor
}
Saturation saturate = new Saturation { DynamicParameter = percentage };
this.Image = saturate.ProcessImage(this);
this.ApplyProcessor(saturate.ProcessImage);
}
return this;
@ -747,8 +754,7 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
Vignette vignette = new Vignette();
this.Image = vignette.ProcessImage(this);
this.ApplyProcessor(vignette.ProcessImage);
}
return this;
@ -769,8 +775,7 @@ namespace ImageProcessor
if (this.ShouldProcess)
{
Watermark watermark = new Watermark { DynamicParameter = textLayer };
this.Image = watermark.ProcessImage(this);
this.ApplyProcessor(watermark.ProcessImage);
}
return this;
@ -974,26 +979,23 @@ namespace ImageProcessor
}
/// <summary>
/// The process image.
/// Applies the given processor the current image.
/// </summary>
/// <param name="processor">
/// The processor.
/// </param>
/// <param name="width">
/// The width.
/// The processor delegate.
/// </param>
/// <param name="height">
/// The height.
/// </param>
private void ProcessImage(Func<ImageFactory, Image> processor, int width, int height)
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, width, height, imageInfo.LoopCount))
using (GifEncoder encoder = new GifEncoder(stream, null, null, imageInfo.LoopCount))
{
foreach (GifFrame frame in imageInfo.GifFrames)
{

2
src/ImageProcessor/ImageProcessor.csproj

@ -70,6 +70,8 @@
<Compile Include="Imaging\Convolution.cs" />
<Compile Include="Imaging\CropLayer.cs" />
<Compile Include="Imaging\CropMode.cs" />
<Compile Include="Imaging\Filters\MatrixFilterRegexAttribute.cs" />
<Compile Include="Imaging\Filters\MatrixFilters.cs" />
<Compile Include="Imaging\GaussianLayer.cs" />
<Compile Include="Imaging\GifEncoder.cs" />
<Compile Include="Imaging\GifFrame.cs" />

4
src/ImageProcessor/Imaging/Filters/LomographMatrixFilter.cs

@ -11,10 +11,6 @@
namespace ImageProcessor.Imaging.Filters
{
#region Using
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Drawing;
using System.Drawing.Imaging;
using ImageProcessor.Processors;

37
src/ImageProcessor/Imaging/Filters/MatrixFilterRegexAttribute.cs

@ -0,0 +1,37 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MatrixFilterRegexAttribute.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// The filter attribute for identifying matrix filter properties.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging.Filters
{
using System;
/// <summary>
/// The filter attribute for identifying matrix filter properties.
/// </summary>
[AttributeUsage(AttributeTargets.Property, AllowMultiple = false)]
public class MatrixFilterRegexAttribute : Attribute
{
/// <summary>
/// Initializes a new instance of the <see cref="MatrixFilterRegexAttribute"/> class.
/// </summary>
/// <param name="regexIdentifier">
/// The regex identifier.
/// </param>
public MatrixFilterRegexAttribute(string regexIdentifier)
{
this.RegexIdentifier = regexIdentifier;
}
/// <summary>
/// Gets the regex identifier.
/// </summary>
public string RegexIdentifier { get; private set; }
}
}

140
src/ImageProcessor/Imaging/Filters/MatrixFilters.cs

@ -0,0 +1,140 @@
// --------------------------------------------------------------------------------------------------------------------
// <copyright file="MatrixFilters.cs" company="James South">
// Copyright (c) James South.
// Licensed under the Apache License, Version 2.0.
// </copyright>
// <summary>
// The filters available to the Filter <see cref="IGraphicsProcessor" />.
// </summary>
// --------------------------------------------------------------------------------------------------------------------
namespace ImageProcessor.Imaging.Filters
{
using ImageProcessor.Processors;
/// <summary>
/// The filters available to the Filter <see cref="IGraphicsProcessor"/>.
/// </summary>
public static class MatrixFilters
{
/// <summary>
/// Gets the black white filter.
/// </summary>
[MatrixFilterRegex("blackwhite")]
public static IMatrixFilter BlackWhite
{
get
{
return new BlackWhiteMatrixFilter();
}
}
/// <summary>
/// Gets the comic filter.
/// </summary>
[MatrixFilterRegex("comic")]
public static IMatrixFilter Comic
{
get
{
return new ComicMatrixFilter();
}
}
/// <summary>
/// Gets the gotham filter.
/// </summary>
[MatrixFilterRegex("gotham")]
public static IMatrixFilter Gotham
{
get
{
return new GothamMatrixFilter();
}
}
/// <summary>
/// Gets the greyscale filter.
/// </summary>
[MatrixFilterRegex("greyscale")]
public static IMatrixFilter GreyScale
{
get
{
return new GreyScaleMatrixFilter();
}
}
/// <summary>
/// Gets the high saturation filter.
/// </summary>
[MatrixFilterRegex("hisatch")]
public static IMatrixFilter HiSatch
{
get
{
return new HiSatchMatrixFilter();
}
}
/// <summary>
/// Gets the invert filter.
/// </summary>
[MatrixFilterRegex("invert")]
public static IMatrixFilter Invert
{
get
{
return new InvertMatrixFilter();
}
}
/// <summary>
/// Gets the lomograph filter.
/// </summary>
[MatrixFilterRegex("lomograph")]
public static IMatrixFilter Lomograph
{
get
{
return new LomographMatrixFilter();
}
}
/// <summary>
/// Gets the low saturation filter.
/// </summary>
[MatrixFilterRegex("losatch")]
public static IMatrixFilter LoSatch
{
get
{
return new LomographMatrixFilter();
}
}
/// <summary>
/// Gets the polaroid filter.
/// </summary>
[MatrixFilterRegex("polaroid")]
public static IMatrixFilter Polaroid
{
get
{
return new PolaroidMatrixFilter();
}
}
/// <summary>
/// Gets the sepia filter.
/// </summary>
[MatrixFilterRegex("sepia")]
public static IMatrixFilter Sepia
{
get
{
return new SepiaMatrixFilter();
}
}
}
}

3
src/ImageProcessor/Imaging/GifEncoder.cs

@ -9,6 +9,7 @@
// Always wire this up in a using block.
// Disposing the encoder will complete the file.
// Uses default .NET GIF encoding and adds animation headers.
// Adapted from <see cref="http://github.com/DataDink/Bumpkit/blob/master/BumpKit/BumpKit/GifEncoder.cs"/>
// </remarks>
// </summary>
// --------------------------------------------------------------------------------------------------------------------
@ -17,7 +18,6 @@ namespace ImageProcessor.Imaging
{
#region Using
using System;
using System.Drawing;
using System.Drawing.Imaging;
using System.IO;
using System.Linq;
@ -29,6 +29,7 @@ namespace ImageProcessor.Imaging
/// Always wire this up in a using block.
/// Disposing the encoder will complete the file.
/// Uses default .NET GIF encoding and adds animation headers.
/// Adapted from <see cref="http://github.com/DataDink/Bumpkit/blob/master/BumpKit/BumpKit/GifEncoder.cs"/>
/// </remarks>
/// </summary>
public class GifEncoder : IDisposable

102
src/ImageProcessor/Processors/Filter.cs

@ -11,9 +11,13 @@
namespace ImageProcessor.Processors
{
#region Using
using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Imaging;
using System.Linq;
using System.Reflection;
using System.Text;
using System.Text.RegularExpressions;
using ImageProcessor.Imaging.Filters;
#endregion
@ -26,7 +30,7 @@ namespace ImageProcessor.Processors
/// <summary>
/// The regular expression to search strings for.
/// </summary>
private static readonly Regex QueryRegex = new Regex(@"filter=(lomograph|polaroid|blackwhite|sepia|greyscale|gotham|invert|hisatch|losatch|comic)", RegexOptions.Compiled);
private static readonly Regex QueryRegex = BuildRegex();
#region IGraphicsProcessor Members
/// <summary>
@ -115,45 +119,12 @@ namespace ImageProcessor.Processors
{
Bitmap newImage = null;
Image image = factory.Image;
IMatrixFilter matrix = null;
try
{
newImage = new Bitmap(image.Width, image.Height, PixelFormat.Format32bppPArgb);
switch ((string)this.DynamicParameter)
{
case "polaroid":
matrix = new PolaroidMatrixFilter();
break;
case "lomograph":
matrix = new LomographMatrixFilter();
break;
case "sepia":
matrix = new SepiaMatrixFilter();
break;
case "blackwhite":
matrix = new BlackWhiteMatrixFilter();
break;
case "greyscale":
matrix = new GreyScaleMatrixFilter();
break;
case "gotham":
matrix = new GothamMatrixFilter();
break;
case "invert":
matrix = new InvertMatrixFilter();
break;
case "hisatch":
matrix = new HiSatchMatrixFilter();
break;
case "losatch":
matrix = new LoSatchMatrixFilter();
break;
case "comic":
matrix = new ComicMatrixFilter();
break;
}
IMatrixFilter matrix = this.DynamicParameter as IMatrixFilter ?? this.ParseFilter((string)this.DynamicParameter);
if (matrix != null)
{
@ -171,5 +142,66 @@ namespace ImageProcessor.Processors
return image;
}
#endregion
/// <summary>
/// Builds a regular expression from the <see cref="MatrixFilters"/> type, this allows extensibility.
/// </summary>
/// <returns>
/// The <see cref="Regex"/> to match matrix filters.
/// </returns>
private static Regex BuildRegex()
{
const BindingFlags Flags = BindingFlags.Public | BindingFlags.Static;
Type type = typeof(MatrixFilters);
IEnumerable<PropertyInfo> filters = type.GetProperties(Flags)
.Where(p => p.IsDefined(typeof(MatrixFilterRegexAttribute), false))
.ToList();
StringBuilder stringBuilder = new StringBuilder();
stringBuilder.Append("filter=(");
int counter = 0;
foreach (PropertyInfo filter in filters)
{
MatrixFilterRegexAttribute attribute = (MatrixFilterRegexAttribute)filter.GetCustomAttributes(typeof(MatrixFilterRegexAttribute), false).First();
if (counter == 0)
{
stringBuilder.Append(attribute.RegexIdentifier);
}
else
{
stringBuilder.AppendFormat("|{0}", attribute.RegexIdentifier);
}
counter++;
}
stringBuilder.Append(")");
return new Regex(stringBuilder.ToString(), RegexOptions.IgnoreCase | RegexOptions.Compiled);
}
/// <summary>
/// Parses the filter.
/// </summary>
/// <param name="identifier">
/// The identifier.
/// </param>
/// <returns>
/// The <see cref="IMatrixFilter"/>.
/// </returns>
private IMatrixFilter ParseFilter(string identifier)
{
const BindingFlags Flags = BindingFlags.Public | BindingFlags.Static;
Type type = typeof(MatrixFilters);
PropertyInfo filter =
type.GetProperties(Flags)
.Where(p => p.IsDefined(typeof(MatrixFilterRegexAttribute), false))
.First(p => ((MatrixFilterRegexAttribute)p.GetCustomAttributes(typeof(MatrixFilterRegexAttribute), false).First()).RegexIdentifier == identifier);
return filter.GetValue(null, null) as IMatrixFilter;
}
}
}

2
src/ImageProcessorConsole/Program.cs

@ -11,6 +11,7 @@ namespace ImageProcessorConsole
using System.IO;
using ImageProcessor;
using ImageProcessor.Imaging.Filters;
class Program
{
@ -42,6 +43,7 @@ namespace ImageProcessorConsole
// Load, resize, set the format and quality and save an image.
imageFactory.Load(inStream)
.Constrain(size)
.Filter(MatrixFilters.Comic)
.Format(format)
.Save(Path.GetFullPath(Path.Combine(Path.GetDirectoryName(path), @"..\..\images\output", fileInfo.Name)));
}

2
src/ImageProcessorConsole/images/output/120430.gif.REMOVED.git-id

@ -1 +1 @@
30ec5c05548fd350f9b7c699715848b9fbfb8ca9
7880376ac9108d4e74412efb31f544484079ce16

1
src/ImageProcessorConsole/images/output/nLpfllv .gif.REMOVED.git-id

@ -0,0 +1 @@
673e6c6d9223e0906fe342a17496168b8d9017fb

1
src/ImageProcessorConsole/images/output/nLpfllv.gif.REMOVED.git-id

@ -1 +0,0 @@
23a1c81a2d1422076373796e0c47f5d968c56d0b
Loading…
Cancel
Save