Browse Source

Added new cache mechanism and new type tests.

Former-commit-id: 2e50a327ccaa6c4c91aa051bacd690138aed14a2
af/merge-core
JimBobSquarePants 13 years ago
parent
commit
e93a60ab56
  1. 66
      src/ImageProcessor.Web/Caching/CachedImage.cs
  2. 5
      src/ImageProcessor.Web/Caching/DiskCache.cs
  3. 321
      src/ImageProcessor.Web/Caching/LockedDictionary.cs
  4. 193
      src/ImageProcessor.Web/Caching/PersistantDictionary.cs
  5. 5
      src/ImageProcessor.Web/Config/ImageProcessorConfig.cs
  6. 2
      src/ImageProcessor.Web/Helpers/FileCompareLastwritetime.cs
  7. 50
      src/ImageProcessor.Web/ImageFactoryExtensions.cs
  8. 3
      src/ImageProcessor.Web/ImageProcessor.Web.csproj
  9. 15
      src/Test/Test/Content/style.css
  10. 1
      src/Test/Test/Images/Penguins.bmp.REMOVED.git-id
  11. 1
      src/Test/Test/Images/Penguins.gif.REMOVED.git-id
  12. 1
      src/Test/Test/Images/Penguins.png.REMOVED.git-id
  13. 4
      src/Test/Test/Test.csproj
  14. 455
      src/Test/Test/Views/Home/Index.cshtml

66
src/ImageProcessor.Web/Caching/CachedImage.cs

@ -0,0 +1,66 @@
// -----------------------------------------------------------------------
// <copyright file="CachedImage.cs" company="James South">
// Copyright (c) James South.
// Dual licensed under the MIT or GPL Version 2 licenses.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Web.Caching
{
#region Using
using System;
#endregion
/// <summary>
/// Describes a cached image
/// </summary>
public class CachedImage
{
/// <summary>
/// Initializes a new instance of the <see cref="CachedImage"/> class.
/// </summary>
/// <param name="value">
/// The value of the cached item.
/// </param>
/// <param name="lastWriteTimeUtc">
/// The last write time of the cached item.
/// </param>
public CachedImage(string value, DateTime lastWriteTimeUtc)
{
this.Value = value;
this.LastWriteTimeUtc = lastWriteTimeUtc;
}
/// <summary>
/// Gets the value date time delimiter.
/// </summary>
public static string ValueLastWriteTimeDelimiter
{
get
{
return "|*|";
}
}
/// <summary>
/// Gets or sets the value of the cached item
/// </summary>
public string Value { get; set; }
/// <summary>
/// Gets or sets the last write time of the cached item
/// </summary>
public DateTime LastWriteTimeUtc { get; set; }
/// <summary>
/// The value and last write time as a string.
/// </summary>
/// <returns>
/// The <see cref="string"/>.
/// </returns>
public string ValueAndLastWriteTimeUtcToString()
{
return string.Format("{0}{1}{2}", this.Value, ValueLastWriteTimeDelimiter, this.LastWriteTimeUtc);
}
}
}

5
src/ImageProcessor.Web/Caching/DiskCache.cs

@ -131,7 +131,7 @@ namespace ImageProcessor.Web.Caching
}
/// <summary>
/// Purges any files from the filesystem cache in a background thread.
/// Purges any files from the file-system cache in a background thread.
/// </summary>
internal static void PurgeCachedFolders()
{
@ -184,7 +184,7 @@ namespace ImageProcessor.Web.Caching
}
/// <summary>
/// Purges any files from the filesystem cache in the given folders.
/// Purges any files from the file-system cache in the given folders.
/// </summary>
private static void PurgeFolders()
{
@ -231,7 +231,6 @@ namespace ImageProcessor.Web.Caching
}
}
}
});
});
}

321
src/ImageProcessor.Web/Caching/LockedDictionary.cs

@ -0,0 +1,321 @@
// -----------------------------------------------------------------------
// <copyright file="LockedDictionary.cs" company="James South">
// Copyright (c) James South.
// Dual licensed under the MIT or GPL Version 2 licenses.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Web.Caching
{
#region Using
using System.Collections.Generic;
using System.Linq;
#endregion
/// <summary>
/// Represents a collection of keys and values that are thread safe.
/// </summary>
/// <typeparam name="TKey">
/// The type of the keys in the dictionary.
/// </typeparam>
/// <typeparam name="TVal">
/// The type of the values in the dictionary.
/// </typeparam>
public class LockedDictionary<TKey, TVal> : IDictionary<TKey, TVal>
{
/// <summary>
/// The _inner.
/// </summary>
private readonly Dictionary<TKey, TVal> innerDictionary = new Dictionary<TKey, TVal>();
#region Constructors
/// <summary>
/// Initializes a new instance of the <see cref="LockedDictionary{TKey,TVal}"/> class.
/// </summary>
/// <param name="val">
/// The value to initialize the LockedDictionary with.
/// </param>
public LockedDictionary(IDictionary<TKey, TVal> val = null)
{
if (val != null)
{
this.innerDictionary = val.ToDictionary(x => x.Key, x => x.Value);
}
}
#endregion
#region Properties
/// <summary>
/// Gets a collection containing the keys in the <see cref="LockedDictionary{TKey,TVal}"/>.
/// </summary>
public ICollection<TKey> Keys
{
get
{
lock (this.innerDictionary)
{
return this.innerDictionary.Keys.ToArray();
}
}
}
/// <summary>
/// Gets a collection containing the values in the <see cref="LockedDictionary{TKey,TVal}"/>.
/// </summary>
public ICollection<TVal> Values
{
get
{
lock (this.innerDictionary)
{
return this.innerDictionary.Values.ToArray();
}
}
}
/// <summary>
/// Gets the number of key/value pairs contained in the <see cref="LockedDictionary{TKey,TVal}"/>.
/// </summary>
public int Count
{
get
{
lock (this.innerDictionary)
{
return this.innerDictionary.Count;
}
}
}
/// <summary>
/// Gets a value indicating whether the <see cref="LockedDictionary{TKey,TVal}"/> is read only.
/// </summary>
public bool IsReadOnly
{
get { return false; }
}
/// <summary>
/// Gets or sets the value associated with the specified key.
/// </summary>
/// <param name="key">
/// The key of the value to get or set.
/// </param>
/// <returns>
/// TThe value associated with the specified key. If the specified key is not found,
/// a get operation throws a <see cref="T:System.Collections.Generic.KeyNotFoundException"/> ,
/// and a set operation creates a new element with the specified key.
/// </returns>
public TVal this[TKey key]
{
get
{
lock (this.innerDictionary)
{
return this.innerDictionary[key];
}
}
set
{
lock (this.innerDictionary)
{
this.innerDictionary[key] = value;
}
}
}
#endregion
#region Methods
/// <summary>
/// Adds the specified key and value to the dictionary.
/// </summary>
/// <param name="key">
/// The key of the element to add.
/// </param>
/// <param name="value">
/// The value of the element to add. The value can be null for reference types.
/// </param>
public void Add(TKey key, TVal value)
{
lock (this.innerDictionary)
{
this.innerDictionary.Add(key, value);
}
}
/// <summary>
/// Determines whether the LockedDictionary contains the specified key.
/// </summary>
/// <param name="key">
/// The key to locate in the LockedDictionary.
/// </param>
/// <returns>
/// true if the LockedDictionary contains the key; otherwise, false.
/// </returns>
public bool ContainsKey(TKey key)
{
lock (this.innerDictionary)
{
return this.innerDictionary.ContainsKey(key);
}
}
/// <summary>
/// Removes the value with the specified key from the <see cref="LockedDictionary{TKey,TVal}"/>.
/// </summary>
/// <param name="key">
/// The key of the element to remove.
/// </param>
/// <returns>
/// true if the element is successfully found and removed; otherwise, false.
/// This method returns false if key is not found in the <see cref="LockedDictionary{TKey,TVal}"/>.
/// </returns>
public bool Remove(TKey key)
{
lock (this.innerDictionary)
{
return this.innerDictionary.Remove(key);
}
}
/// <summary>
/// Gets the value associated with the specified key.
/// </summary>
/// <param name="key">
/// The key of the value to get.
/// </param>
/// <param name="value">
/// When this method returns, contains the value associated with the specified key, if the key is found;
/// otherwise, the default value for the type of the value parameter. This parameter is passed uninitialized.
/// </param>
/// <returns>
/// true if the <see cref="LockedDictionary{TKey,TVal}"/> contains an element with
/// the specified key; otherwise, false.
/// </returns>
public bool TryGetValue(TKey key, out TVal value)
{
lock (this.innerDictionary)
{
return this.innerDictionary.TryGetValue(key, out value);
}
}
/// <summary>
/// Adds the specified key and value to the dictionary.
/// </summary>
/// <param name="item">
/// The <see cref="System.Collections.Generic.KeyValuePair{TKey, TVal}"/> representing
/// the item to add.
/// </param>
public void Add(KeyValuePair<TKey, TVal> item)
{
lock (this.innerDictionary)
{
this.innerDictionary.Add(item.Key, item.Value);
}
}
/// <summary>
/// Removes all keys and values from the <see cref="LockedDictionary{TKey,TVal}"/>.
/// </summary>
public void Clear()
{
lock (this.innerDictionary)
{
this.innerDictionary.Clear();
}
}
/// <summary>
/// Determines whether the <see cref="LockedDictionary{TKey,TVal}"/> contains the specified key.
/// </summary>
/// <param name="item">
/// The <see cref="System.Collections.Generic.KeyValuePair{TKey, TVal}"/> representing
/// the item to locate.
/// </param>
/// <returns>
/// true if the <see cref="LockedDictionary{TKey,TVal}"/> contains an element
/// with the specified key; otherwise, false.
/// </returns>
public bool Contains(KeyValuePair<TKey, TVal> item)
{
lock (this.innerDictionary)
{
var inner = this.innerDictionary as IDictionary<TKey, TVal>;
return inner.Contains(item);
}
}
/// <summary>
/// Copies the elements of an <see cref="LockedDictionary{TKey,TVal}"/> to a one-dimensional
/// <see cref="T:System.Array"/> starting at a particular <see cref="T:System.Array"/> index.
/// </summary>
/// <param name="array">
/// The one-dimensional <see cref="T:System.Array"/> that is the destination of the elements copied
/// from <see cref="LockedDictionary{TKey,TVal}"/>.KeyCollection.
/// The <see cref="T:System.Array"/> must have zero-based indexing.
/// </param>
/// <param name="arrayIndex">
/// The zero-based index in array at which copying begins.
/// </param>
public void CopyTo(KeyValuePair<TKey, TVal>[] array, int arrayIndex)
{
lock (this.innerDictionary)
{
var inner = this.innerDictionary as IDictionary<TKey, TVal>;
inner.CopyTo(array, arrayIndex);
}
}
/// <summary>
/// Removes the item with the specified <see cref="System.Collections.Generic.KeyValuePair{TKey, TVal}"/>
/// from the <see cref="LockedDictionary{TKey,TVal}"/>
/// </summary>
/// <param name="item">
/// The <see cref="System.Collections.Generic.KeyValuePair{TKey, TVal}"/>representing the item to remove.
/// </param>
/// <returns>
/// This method returns false if item is not found in the <see cref="LockedDictionary{TKey,TVal}"/>.
/// </returns>
public bool Remove(KeyValuePair<TKey, TVal> item)
{
lock (this.innerDictionary)
{
var inner = this.innerDictionary as IDictionary<TKey, TVal>;
return inner.Remove(item);
}
}
/// <summary>
/// Returns an enumerator that iterates through the <see cref="LockedDictionary{TKey,TVal}"/>.KeyCollection.
/// </summary>
/// <returns>
/// A <see cref="System.Collections.Generic.Dictionary{TKey,TValue}.KeyCollection.Enumerator"/>
/// for the <see cref="System.Collections.Generic.Dictionary{TKey,TValue}.KeyCollection"/>.
/// </returns>
public IEnumerator<KeyValuePair<TKey, TVal>> GetEnumerator()
{
lock (this.innerDictionary)
{
return this.innerDictionary.ToList().GetEnumerator();
}
}
/// <summary>
/// Returns an enumerator that iterates through the <see cref="LockedDictionary{TKey,TVal}"/>.KeyCollection.
/// </summary>
/// <returns>
/// A <see cref="System.Collections.Generic.Dictionary{TKey,TValue}.Enumerator"/>
/// for the <see cref="System.Collections.Generic.Dictionary{TKey,TValue}"/>.
/// </returns>
System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator()
{
lock (this.innerDictionary)
{
return this.innerDictionary.ToArray().GetEnumerator();
}
}
#endregion
}
}

193
src/ImageProcessor.Web/Caching/PersistantDictionary.cs

@ -0,0 +1,193 @@
// -----------------------------------------------------------------------
// <copyright file="PersistantDictionary.cs" company="James South">
// Copyright (c) James South.
// Dual licensed under the MIT or GPL Version 2 licenses.
// </copyright>
// -----------------------------------------------------------------------
namespace ImageProcessor.Web.Caching
{
#region Using
using System;
using System.IO;
using System.Web.Hosting;
using ImageProcessor.Web.Config;
#endregion
/// <summary>
/// Represents a collection of keys and values whose operations are concurrent.
/// </summary>
public class PersistantDictionary : LockedDictionary<string, CachedImage>
{
/// <summary>
/// A new instance Initializes a new instance of the <see cref="T:ImageProcessor.Web.Caching.PersistantDictionary"/> class.
/// initialized lazily.
/// </summary>
private static readonly Lazy<PersistantDictionary> Lazy =
new Lazy<PersistantDictionary>(() => new PersistantDictionary());
/// <summary>
/// The default path for cached folders on the server.
/// </summary>
private static readonly string CachePath = ImageProcessorConfig.Instance.VirtualCachePath;
/// <summary>
/// The object to lock against.
/// </summary>
private static readonly object SyncRoot = new object();
/// <summary>
/// The cached index location.
/// </summary>
private readonly string cachedIndexFile = Path.Combine(HostingEnvironment.MapPath(CachePath), "imagecache.bin");
#region Constructors
/// <summary>
/// Prevents a default instance of the <see cref="T:ImageProcessor.Web.Caching.PersistantDictionary"/> class
/// from being created.
/// </summary>
private PersistantDictionary()
{
this.LoadCache();
}
#endregion
/// <summary>
/// Gets the current instance of the <see cref="T:ImageProcessor.Web.Caching.PersistantDictionary"/> class.
/// </summary>
public static PersistantDictionary Instance
{
get
{
return Lazy.Value;
}
}
#region Public
/// <summary>
/// Tries to remove the value associated with the specified key.
/// </summary>
/// <param name="key">
/// The key of the item to remove.
/// </param>
/// <param name="value">
/// The value to assign the returned value to.
/// </param>
/// <returns>
/// true if the <see cref="PersistantDictionary"/> removes an element with
/// the specified key; otherwise, false.
/// </returns>
public bool TryRemove(string key, out CachedImage value)
{
// No CachedImage to remove.
if (!this.ContainsKey(key))
{
value = default(CachedImage);
return false;
}
// Remove the CachedImage.
lock (SyncRoot)
{
value = this[key];
this.Remove(key);
this.SaveCache();
return true;
}
}
/// <summary>
/// Adds the specified key and value to the dictionary or returns the value if it exists.
/// </summary>
/// <param name="key">
/// The key.
/// </param>
/// <param name="factory">
/// The delegate method that returns the value.
/// </param>
/// <returns>
/// The value of the item to add or get.
/// </returns>
public CachedImage GetOrAdd(string key, Func<string, CachedImage> factory)
{
// Get the CachedImage.
if (this.ContainsKey(key))
{
return this[key];
}
lock (SyncRoot)
{
// Add the CachedImage.
CachedImage ret = factory(key);
this[key] = ret;
this.SaveCache();
return ret;
}
}
#endregion
/// <summary>
/// Saves the in memory cache to the file-system.
/// </summary>
private void SaveCache()
{
using (FileStream fileStream = File.Create(this.cachedIndexFile))
{
using (BinaryWriter binaryWriter = new BinaryWriter(fileStream))
{
// Put the count.
binaryWriter.Write(this.Count);
// Put the values.
foreach (var pair in this)
{
binaryWriter.Write(pair.Key);
binaryWriter.Write(pair.Value.ValueAndLastWriteTimeUtcToString());
}
}
}
}
/// <summary>
/// Loads the cache file to populate the in memory cache.
/// </summary>
private void LoadCache()
{
lock (SyncRoot)
{
if (File.Exists(this.cachedIndexFile))
{
using (FileStream fileStream = File.OpenRead(this.cachedIndexFile))
{
using (BinaryReader binaryReader = new BinaryReader(fileStream))
{
// Get the count.
int count = binaryReader.ReadInt32();
// Read in all pairs.
for (int i = 0; i < count; i++)
{
// Read the key/value strings
string key = binaryReader.ReadString();
string value = binaryReader.ReadString();
// Create a CachedImage
string[] valueAndLastWriteTime = value.Split(new[] { CachedImage.ValueLastWriteTimeDelimiter }, StringSplitOptions.None);
DateTime lastWriteTime = DateTime.Parse(valueAndLastWriteTime[1]);
CachedImage cachedImage = new CachedImage(valueAndLastWriteTime[0], lastWriteTime);
// Assign the value
this[key] = cachedImage;
}
}
}
}
}
}
}
}

5
src/ImageProcessor.Web/Config/ImageProcessorConfig.cs

@ -10,14 +10,16 @@ namespace ImageProcessor.Web.Config
#region Using
using System;
using System.Collections.Generic;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
using ImageProcessor.Processors;
#endregion
/// <summary>
/// Encapsulates methods to allow the retrieval of imageprocessor settings.
/// Encapsulates methods to allow the retrieval of ImageProcessor settings.
/// http://csharpindepth.com/Articles/General/Singleton.aspx
/// </summary>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
public class ImageProcessorConfig
{
#region Fields
@ -107,6 +109,7 @@ namespace ImageProcessor.Web.Config
/// <summary>
/// Gets a list of whitelisted urls that images can be downloaded from.
/// </summary>
[SuppressMessage("StyleCop.CSharp.DocumentationRules", "SA1650:ElementDocumentationMustBeSpelledCorrectly", Justification = "Reviewed. Suppression is OK here.")]
public Uri[] RemoteFileWhiteList
{
get

2
src/ImageProcessor.Web/Helpers/FileCompareLastwritetime.cs

@ -49,7 +49,7 @@ namespace ImageProcessor.Web.Helpers
/// <summary>
/// Returns a hash code for the specified <see cref="T:System.IO.FileInfo"/>.
/// </summary>
/// <param name="fi">The FileInfo to return the hashcode for.</param>
/// <param name="fi">The FileInfo to return the hash code for.</param>
/// <returns>A hash code for the specified <see cref="T:System.IO.FileInfo"/>.</returns>
public int GetHashCode(System.IO.FileInfo fi)
{

50
src/ImageProcessor.Web/ImageFactoryExtensions.cs

@ -15,14 +15,17 @@ namespace ImageProcessor.Web
#endregion
/// <summary>
/// Extends the ImageFactory class to provide a fluent api.
/// Extends the ImageFactory class to provide a fluent API.
/// </summary>
public static class ImageFactoryExtensions
{
/// <summary>
/// The object to lock against.
/// </summary>
private static readonly object SyncRoot = new object();
/// <summary>
/// Auto processes image files based on any querystring parameters added to the image path.
/// Auto processes image files based on any query string parameters added to the image path.
/// </summary>
/// <param name="factory">
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class
@ -31,32 +34,29 @@ namespace ImageProcessor.Web
/// <returns>
/// The current instance of the <see cref="T:ImageProcessor.ImageFactory"/> class.
/// </returns>
public static ImageFactory AutoProcess(this ImageFactory factory)
{
if (factory.ShouldProcess)
{
// TODO: This is going to be a bottleneck for speed. Find a faster way.
lock (SyncRoot)
public static ImageFactory AutoProcess(this ImageFactory factory)
{
// Get a list of all graphics processors that have parsed and matched the querystring.
List<IGraphicsProcessor> list =
ImageProcessorConfig.Instance.GraphicsProcessors
.Where(x => x.MatchRegexIndex(factory.QueryString) != int.MaxValue)
.OrderBy(y => y.SortOrder)
.ToList();
// Loop through and process the image.
foreach (IGraphicsProcessor graphicsProcessor in list)
if (factory.ShouldProcess)
{
factory.Image = graphicsProcessor.ProcessImage(factory);
// TODO: This is going to be a bottleneck for speed. Find a faster way.
lock (SyncRoot)
{
// Get a list of all graphics processors that have parsed and matched the querystring.
List<IGraphicsProcessor> list =
ImageProcessorConfig.Instance.GraphicsProcessors
.Where(x => x.MatchRegexIndex(factory.QueryString) != int.MaxValue)
.OrderBy(y => y.SortOrder)
.ToList();
// Loop through and process the image.
foreach (IGraphicsProcessor graphicsProcessor in list)
{
factory.Image = graphicsProcessor.ProcessImage(factory);
}
}
}
}
}
return factory;
}
return factory;
}
}
}

3
src/ImageProcessor.Web/ImageProcessor.Web.csproj

@ -51,7 +51,10 @@
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Caching\CachedImage.cs" />
<Compile Include="Caching\DiskCache.cs" />
<Compile Include="Caching\LockedDictionary.cs" />
<Compile Include="Caching\PersistantDictionary.cs" />
<Compile Include="Config\ImageCacheSection.cs" />
<Compile Include="Config\ImageProcessingSection.cs" />
<Compile Include="Config\ImageProcessorConfig.cs" />

15
src/Test/Test/Content/style.css

@ -19,25 +19,14 @@ section section {
}
section section:nth-child(2n) {
color: #fff;
}
section section:nth-child(2) {
background-color: #00a9ec;
color: #fff;
}
section section:nth-child(4) {
section section:nth-child(4n) {
background-color: #ED1C24;
}
img {
/*background: #fff;
padding: 3px;
-webkit-box-shadow: 0 0 5px rgb(0, 0, 0, 0.4);
box-shadow: 0 0 5px rgb(0, 0, 0, 0.4);*/
}
.no-bullets {
padding-left: 0;
}

1
src/Test/Test/Images/Penguins.bmp.REMOVED.git-id

@ -0,0 +1 @@
8150b46ab27c62ba51aaba551eef3f1a30f08de9

1
src/Test/Test/Images/Penguins.gif.REMOVED.git-id

@ -0,0 +1 @@
6ad3b846d4697584ff601ac481b14a4d3bbb5736

1
src/Test/Test/Images/Penguins.png.REMOVED.git-id

@ -0,0 +1 @@
a2c796fbb7de948230a22982ab74892891dd5198

4
src/Test/Test/Test.csproj

@ -89,8 +89,12 @@
<Content Include="Global.asax" />
<Content Include="Images\1182076_e8c402e938_z.jpg" />
<Content Include="Images\Desert.jpg" />
<Content Include="Images\Lighthouse.jpg" />
<Content Include="Images\MSwanson - Wide Large - Rock 02.jpg" />
<Content Include="Images\Penguins.bmp" />
<Content Include="Images\Penguins.gif" />
<Content Include="Images\Penguins.jpg" />
<Content Include="Images\Penguins.png" />
<Content Include="Images\Tulips.jpg" />
<Content Include="Images\war_horse_quad.jpg" />
<Content Include="Scripts\img.srcsect.pollyfill.js" />

455
src/Test/Test/Views/Home/Index.cshtml

@ -1,94 +1,367 @@
@{
ViewBag.Title = "Home Page";
}
<section>
<div class="container">
<div class="clmn2">
<h2>Resized</h2>
<img src="/images/Penguins.jpg?width=300" />
</div>
<div class="clmn2">
<h2>Cropped </h2>
<img src="/images/Penguins.jpg?crop=0-0-300-225" />
</div>
</div>
</section>
<section>
<div class="container">
<h2>Filter</h2>
<ul class="no-bullets clearfix">
<li>
<h3>blackwhite</h3>
<img src="/images/Penguins.jpg?width=300&filter=blackwhite" />
</li>
<li>
<h3>comic</h3>
<img src="/images/Penguins.jpg?width=300&filter=comic" />
</li>
<li>
<h3>lomograph</h3>
<img src="/images/Penguins.jpg?width=300&filter=lomograph" />
</li>
<li>
<h3>greyscale</h3>
<img src="/images/Penguins.jpg?width=300&filter=greyscale" />
</li>
<li>
<h3>polaroid</h3>
<img src="/images/Penguins.jpg?width=300&filter=polaroid" />
</li>
<li>
<h3>sepia</h3>
<img src="/images/Penguins.jpg?width=300&filter=sepia" />
</li>
<li>
<h3>gotham</h3>
<img src="/images/Penguins.jpg?width=300&filter=gotham" />
</li>
<li>
<h3>hisatch</h3>
<img src="/images/Penguins.jpg?width=300&filter=hisatch" />
</li>
<li>
<h3>losatch</h3>
<img src="/images/Penguins.jpg?width=300&filter=losatch" />
</li>
</ul>
</div>
</section>
<section>
<div class="container">
<div class="clmn2">
<h2>Watermark</h2>
<img src="/images/Penguins.jpg?width=300&watermark=text-test|color-fff|size-48|style-italic|opacity-100|position-100-100|shadow-true|font-arial" />
</div>
<div class="clmn2">
<h2>Format</h2>
<img src="/images/Penguins.jpg?width=300&format=gif" />
</div>
</div>
</section>
<section>
<div class="container">
<div class="clmn2">
<h2>Rotate</h2>
<img src="/images/Penguins.jpg?width=300&rotate=angle-54|bgcolor-fff" />
</div>
<div class="clmn2">
<h2>Quality</h2>
<img src="/images/Penguins.jpg?width=300&quality=5" />
</div>
</div>
</section>
<section>
<div class="container">
<div class="clmn2">
<h2>Alpha</h2>
<img src="/images/Penguins.jpg?width=300&format=png&alpha=50" />
</div>
<div class="clmn2">
<h2>Remote</h2>
<img src="/remote.axd?http://images.mymovies.net/images/film/cin/500x377/fid11707.jpg?width=300" />
</div>
</div>
</section>
<article>
<h1 class="container">Jpg</h1>
<section>
<div class="container">
<div class="clmn2">
<h2>Resized</h2>
<img src="/images/Penguins.jpg?width=300" />
</div>
<div class="clmn2">
<h2>Cropped </h2>
<img src="/images/Penguins.jpg?crop=0-0-300-225" />
</div>
</div>
</section>
<section>
<div class="container">
<h2>Filter</h2>
<ul class="no-bullets clearfix">
<li>
<h3>blackwhite</h3>
<img src="/images/Penguins.jpg?width=300&filter=blackwhite" />
</li>
<li>
<h3>comic</h3>
<img src="/images/Penguins.jpg?width=300&filter=comic" />
</li>
<li>
<h3>lomograph</h3>
<img src="/images/Penguins.jpg?width=300&filter=lomograph" />
</li>
<li>
<h3>greyscale</h3>
<img src="/images/Penguins.jpg?width=300&filter=greyscale" />
</li>
<li>
<h3>polaroid</h3>
<img src="/images/Penguins.jpg?width=300&filter=polaroid" />
</li>
<li>
<h3>sepia</h3>
<img src="/images/Penguins.jpg?width=300&filter=sepia" />
</li>
<li>
<h3>gotham</h3>
<img src="/images/Penguins.jpg?width=300&filter=gotham" />
</li>
<li>
<h3>hisatch</h3>
<img src="/images/Penguins.jpg?width=300&filter=hisatch" />
</li>
<li>
<h3>losatch</h3>
<img src="/images/Penguins.jpg?width=300&filter=losatch" />
</li>
</ul>
</div>
</section>
<section>
<div class="container">
<div class="clmn2">
<h2>Watermark</h2>
<img src="/images/Penguins.jpg?width=300&watermark=text-test|color-fff|size-48|style-italic|opacity-100|position-100-100|shadow-true|font-arial" />
</div>
<div class="clmn2">
<h2>Format</h2>
<img src="/images/Penguins.jpg?width=300&format=gif" />
</div>
</div>
</section>
<section>
<div class="container">
<div class="clmn2">
<h2>Rotate</h2>
<img src="/images/Penguins.jpg?width=300&rotate=angle-54|bgcolor-fff" />
</div>
<div class="clmn2">
<h2>Quality</h2>
<img src="/images/Penguins.jpg?width=300&quality=5" />
</div>
</div>
</section>
<section>
<div class="container">
<div class="clmn2">
<h2>Alpha</h2>
<img src="/images/Penguins.jpg?width=300&format=png&alpha=50" />
</div>
<div class="clmn2">
<h2>Remote</h2>
<img src="/remote.axd?http://images.mymovies.net/images/film/cin/500x377/fid11707.jpg?width=300" />
</div>
</div>
</section>
</article>
<article>
<h1 class="container">Gif</h1>
<section>
<div class="container">
<div class="clmn2">
<h2>Resized</h2>
<img src="/images/Penguins.gif?width=300" />
</div>
<div class="clmn2">
<h2>Cropped </h2>
<img src="/images/Penguins.gif?crop=0-0-300-225" />
</div>
</div>
</section>
<section>
<div class="container">
<h2>Filter</h2>
<ul class="no-bullets clearfix">
<li>
<h3>blackwhite</h3>
<img src="/images/Penguins.gif?width=300&filter=blackwhite" />
</li>
<li>
<h3>comic</h3>
<img src="/images/Penguins.gif?width=300&filter=comic" />
</li>
<li>
<h3>lomograph</h3>
<img src="/images/Penguins.gif?width=300&filter=lomograph" />
</li>
<li>
<h3>greyscale</h3>
<img src="/images/Penguins.gif?width=300&filter=greyscale" />
</li>
<li>
<h3>polaroid</h3>
<img src="/images/Penguins.gif?width=300&filter=polaroid" />
</li>
<li>
<h3>sepia</h3>
<img src="/images/Penguins.gif?width=300&filter=sepia" />
</li>
<li>
<h3>gotham</h3>
<img src="/images/Penguins.gif?width=300&filter=gotham" />
</li>
<li>
<h3>hisatch</h3>
<img src="/images/Penguins.gif?width=300&filter=hisatch" />
</li>
<li>
<h3>losatch</h3>
<img src="/images/Penguins.gif?width=300&filter=losatch" />
</li>
</ul>
</div>
</section>
<section>
<div class="container">
<div class="clmn2">
<h2>Watermark</h2>
<img src="/images/Penguins.gif?width=300&watermark=text-test|color-fff|size-48|style-italic|opacity-100|position-100-100|shadow-true|font-arial" />
</div>
<div class="clmn2">
<h2>Format</h2>
<img src="/images/Penguins.gif?width=300&format=png" />
</div>
</div>
</section>
<section>
<div class="container">
<div class="clmn2">
<h2>Rotate</h2>
<img src="/images/Penguins.gif?width=300&rotate=angle-54|bgcolor-fff" />
</div>
<div class="clmn2">
<h2>Quality</h2>
<img src="/images/Penguins.gif?width=300&quality=5" />
</div>
</div>
</section>
<section>
<div class="container">
<div class="clmn2">
<h2>Alpha</h2>
<img src="/images/Penguins.gif?width=300&alpha=50" />
</div>
</div>
</section>
</article>
<article>
<h1 class="container">Png</h1>
<section>
<div class="container">
<div class="clmn2">
<h2>Resized</h2>
<img src="/images/Penguins.png?width=300" />
</div>
<div class="clmn2">
<h2>Cropped </h2>
<img src="/images/Penguins.png?crop=0-0-300-225" />
</div>
</div>
</section>
<section>
<div class="container">
<h2>Filter</h2>
<ul class="no-bullets clearfix">
<li>
<h3>blackwhite</h3>
<img src="/images/Penguins.png?width=300&filter=blackwhite" />
</li>
<li>
<h3>comic</h3>
<img src="/images/Penguins.png?width=300&filter=comic" />
</li>
<li>
<h3>lomograph</h3>
<img src="/images/Penguins.png?width=300&filter=lomograph" />
</li>
<li>
<h3>greyscale</h3>
<img src="/images/Penguins.png?width=300&filter=greyscale" />
</li>
<li>
<h3>polaroid</h3>
<img src="/images/Penguins.png?width=300&filter=polaroid" />
</li>
<li>
<h3>sepia</h3>
<img src="/images/Penguins.png?width=300&filter=sepia" />
</li>
<li>
<h3>gotham</h3>
<img src="/images/Penguins.png?width=300&filter=gotham" />
</li>
<li>
<h3>hisatch</h3>
<img src="/images/Penguins.png?width=300&filter=hisatch" />
</li>
<li>
<h3>losatch</h3>
<img src="/images/Penguins.png?width=300&filter=losatch" />
</li>
</ul>
</div>
</section>
<section>
<div class="container">
<div class="clmn2">
<h2>Watermark</h2>
<img src="/images/Penguins.png?width=300&watermark=text-test|color-fff|size-48|style-italic|opacity-100|position-100-100|shadow-true|font-arial" />
</div>
<div class="clmn2">
<h2>Format</h2>
<img src="/images/Penguins.png?width=300&format=bmp" />
</div>
</div>
</section>
<section>
<div class="container">
<div class="clmn2">
<h2>Rotate</h2>
<img src="/images/Penguins.png?width=300&rotate=angle-54|bgcolor-fff" />
</div>
<div class="clmn2">
<h2>Quality</h2>
<img src="/images/Penguins.png?width=300&quality=5" />
</div>
</div>
</section>
<section>
<div class="container">
<div class="clmn2">
<h2>Alpha</h2>
<img src="/images/Penguins.png?width=300&alpha=50" />
</div>
</div>
</section>
</article>
<article>
<h1 class="container">Bmp</h1>
<section>
<div class="container">
<div class="clmn2">
<h2>Resized</h2>
<img src="/images/Penguins.bmp?width=300" />
</div>
<div class="clmn2">
<h2>Cropped </h2>
<img src="/images/Penguins.bmp?crop=0-0-300-225" />
</div>
</div>
</section>
<section>
<div class="container">
<h2>Filter</h2>
<ul class="no-bullets clearfix">
<li>
<h3>blackwhite</h3>
<img src="/images/Penguins.bmp?width=300&filter=blackwhite" />
</li>
<li>
<h3>comic</h3>
<img src="/images/Penguins.bmp?width=300&filter=comic" />
</li>
<li>
<h3>lomograph</h3>
<img src="/images/Penguins.bmp?width=300&filter=lomograph" />
</li>
<li>
<h3>greyscale</h3>
<img src="/images/Penguins.bmp?width=300&filter=greyscale" />
</li>
<li>
<h3>polaroid</h3>
<img src="/images/Penguins.bmp?width=300&filter=polaroid" />
</li>
<li>
<h3>sepia</h3>
<img src="/images/Penguins.bmp?width=300&filter=sepia" />
</li>
<li>
<h3>gotham</h3>
<img src="/images/Penguins.bmp?width=300&filter=gotham" />
</li>
<li>
<h3>hisatch</h3>
<img src="/images/Penguins.bmp?width=300&filter=hisatch" />
</li>
<li>
<h3>losatch</h3>
<img src="/images/Penguins.bmp?width=300&filter=losatch" />
</li>
</ul>
</div>
</section>
<section>
<div class="container">
<div class="clmn2">
<h2>Watermark</h2>
<img src="/images/Penguins.bmp?width=300&watermark=text-test|color-fff|size-48|style-italic|opacity-100|position-100-100|shadow-true|font-arial" />
</div>
<div class="clmn2">
<h2>Format</h2>
<img src="/images/Penguins.bmp?width=300&format=jpg" />
</div>
</div>
</section>
<section>
<div class="container">
<div class="clmn2">
<h2>Rotate</h2>
<img src="/images/Penguins.bmp?width=300&rotate=angle-54|bgcolor-fff" />
</div>
<div class="clmn2">
<h2>Quality</h2>
<img src="/images/Penguins.bmp?width=300&quality=5" />
</div>
</div>
</section>
<section>
<div class="container">
<div class="clmn2">
<h2>Alpha</h2>
<img src="/images/Penguins.bmp?width=300&alpha=50" />
</div>
</div>
</section>
</article>

Loading…
Cancel
Save