diff --git a/src/ImageProcessor/Imaging/OctreeQuantizer.cs b/src/ImageProcessor/Imaging/OctreeQuantizer.cs
index d9c9928a2..3cdd17bf0 100644
--- a/src/ImageProcessor/Imaging/OctreeQuantizer.cs
+++ b/src/ImageProcessor/Imaging/OctreeQuantizer.cs
@@ -121,20 +121,78 @@ namespace ImageProcessor.Imaging
///
private class Octree
{
+ #region Fields
+ ///
+ /// Mask used when getting the appropriate pixels for a given node
+ ///
+ private static int[] mask = new int[8] { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
+
+ ///
+ /// The root of the octree
+ ///
+ private OctreeNode root;
+
+ ///
+ /// Number of leaves in the tree
+ ///
+ private int leafCount;
+
+ ///
+ /// Array of reducible nodes
+ ///
+ private OctreeNode[] reducibleNodes;
+
+ ///
+ /// Maximum number of significant bits in the image
+ ///
+ private int maxColorBits;
+
+ ///
+ /// Store the last node quantized
+ ///
+ private OctreeNode previousNode;
+
+ ///
+ /// Cache the previous color quantized
+ ///
+ private int previousColor;
+ #endregion
+
+ #region Constructors
///
/// Initializes a new instance of the Octree class.
///
- /// The maximum number of significant bits in the image
- public Octree(int maxColorBits)
+ /// The maximum number of significant bits in the image
+ public Octree(int maxBits)
{
- this._maxColorBits = maxColorBits;
- this._leafCount = 0;
- this._reducibleNodes = new OctreeNode[9];
- this._root = new OctreeNode(0, this._maxColorBits, this);
- this._previousColor = 0;
- this._previousNode = null;
+ this.maxColorBits = maxBits;
+ this.leafCount = 0;
+ this.reducibleNodes = new OctreeNode[9];
+ this.root = new OctreeNode(0, this.maxColorBits, this);
+ this.previousColor = 0;
+ this.previousNode = null;
+ }
+ #endregion
+
+ #region Properties
+ ///
+ /// Gets or sets the number of leaves in the tree
+ ///
+ public int Leaves
+ {
+ get { return this.leafCount; }
+ set { this.leafCount = value; }
}
+ ///
+ /// Gets the array of reducible nodes
+ ///
+ protected OctreeNode[] ReducibleNodes
+ {
+ get { return this.reducibleNodes; }
+ }
+ #endregion
+
///
/// Add a given colour value to the octree
///
@@ -144,25 +202,25 @@ namespace ImageProcessor.Imaging
public void AddColor(Color32 pixel)
{
// Check if this request is for the same colour as the last
- if (this._previousColor == pixel.ARGB)
+ if (this.previousColor == pixel.ARGB)
{
// If so, check if I have a previous node setup. This will only occur if the first colour in the image
// happens to be black, with an alpha component of zero.
- if (null == this._previousNode)
+ if (null == this.previousNode)
{
- this._previousColor = pixel.ARGB;
- this._root.AddColor(pixel, this._maxColorBits, 0, this);
+ this.previousColor = pixel.ARGB;
+ this.root.AddColor(pixel, this.maxColorBits, 0, this);
}
else
{
// Just update the previous node
- this._previousNode.Increment(pixel);
+ this.previousNode.Increment(pixel);
}
}
else
{
- this._previousColor = pixel.ARGB;
- this._root.AddColor(pixel, this._maxColorBits, 0, this);
+ this.previousColor = pixel.ARGB;
+ this.root.AddColor(pixel, this.maxColorBits, 0, this);
}
}
@@ -172,48 +230,22 @@ namespace ImageProcessor.Imaging
public void Reduce()
{
// Find the deepest level containing at least one reducible node
- int index = this._maxColorBits - 1;
- while ((index > 0) && (this._reducibleNodes[index] == null))
+ int index = this.maxColorBits - 1;
+ while ((index > 0) && (this.reducibleNodes[index] == null))
{
index--;
}
// Reduce the node most recently added to the list at level 'index'
- OctreeNode node = this._reducibleNodes[index];
- this._reducibleNodes[index] = node.NextReducible;
+ OctreeNode node = this.reducibleNodes[index];
+ this.reducibleNodes[index] = node.NextReducible;
// Decrement the leaf count after reducing the node
- this._leafCount -= node.Reduce();
+ this.leafCount -= node.Reduce();
// And just in case I've reduced the last color to be added, and the next color to
// be added is the same, invalidate the previousNode...
- this._previousNode = null;
- }
-
- ///
- /// Get or sets the number of leaves in the tree
- ///
- public int Leaves
- {
- get { return this._leafCount; }
- set { this._leafCount = value; }
- }
-
- ///
- /// Return the array of reducible nodes
- ///
- protected OctreeNode[] ReducibleNodes
- {
- get { return this._reducibleNodes; }
- }
-
- ///
- /// Keep track of the previous node that was quantized
- ///
- /// The node last quantized
- protected void TrackPrevious(OctreeNode node)
- {
- this._previousNode = node;
+ this.previousNode = null;
}
///
@@ -231,92 +263,132 @@ namespace ImageProcessor.Imaging
// Now palletize the nodes
ArrayList palette = new ArrayList(this.Leaves);
int paletteIndex = 0;
- this._root.ConstructPalette(palette, ref paletteIndex);
+ this.root.ConstructPalette(palette, ref paletteIndex);
// And return the palette
return palette;
}
///
- /// Get the palette index for the passed colour
+ /// Get the palette index for the passed colour.
///
- ///
- ///
+ ///
+ /// The color to return the palette index for.
+ ///
+ ///
+ /// The palette index for the passed colour.
+ ///
public int GetPaletteIndex(Color32 pixel)
{
- return this._root.GetPaletteIndex(pixel, 0);
+ return this.root.GetPaletteIndex(pixel, 0);
}
///
- /// Mask used when getting the appropriate pixels for a given node
+ /// Keep track of the previous node that was quantized
///
- private static int[] mask = new int[8] { 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01 };
+ /// The node last quantized
+ protected void TrackPrevious(OctreeNode node)
+ {
+ this.previousNode = node;
+ }
///
- /// The root of the octree
+ /// Class which encapsulates each node in the tree
///
- private OctreeNode _root;
+ protected class OctreeNode
+ {
+ #region Fields
+ ///
+ /// Flag indicating that this is a leaf node
+ ///
+ private bool leaf;
- ///
- /// Number of leaves in the tree
- ///
- private int _leafCount;
+ ///
+ /// Number of pixels in this node
+ ///
+ private int pixelCount;
- ///
- /// Array of reducible nodes
- ///
- private OctreeNode[] _reducibleNodes;
+ ///
+ /// Red component
+ ///
+ private int red;
- ///
- /// Maximum number of significant bits in the image
- ///
- private int _maxColorBits;
+ ///
+ /// Green Component
+ ///
+ private int green;
- ///
- /// Store the last node quantized
- ///
- private OctreeNode _previousNode;
+ ///
+ /// Blue component
+ ///
+ private int blue;
- ///
- /// Cache the previous color quantized
- ///
- private int _previousColor;
+ ///
+ /// Pointers to any child nodes
+ ///
+ private OctreeNode[] children;
- ///
- /// Class which encapsulates each node in the tree
- ///
- protected class OctreeNode
- {
///
- /// Construct the node
+ /// The index of this node in the palette
///
- /// The level in the tree = 0 - 7
- /// The number of significant color bits in the image
- /// The tree to which this node belongs
+ private int paletteIndex;
+ #endregion
+
+ #region Constructors
+ ///
+ /// Initializes a new instance of the class.
+ ///
+ ///
+ /// The level in the tree = 0 - 7
+ ///
+ ///
+ /// The number of significant color bits in the image
+ ///
+ ///
+ /// The tree to which this node belongs
+ ///
public OctreeNode(int level, int colorBits, Octree octree)
{
// Construct the new node
- this._leaf = (level == colorBits);
+ this.leaf = level == colorBits;
- this._red = _green = _blue = 0;
- this._pixelCount = 0;
+ this.red = this.green = this.blue = 0;
+ this.pixelCount = 0;
// If a leaf, increment the leaf count
- if (this._leaf)
+ if (this.leaf)
{
octree.Leaves++;
- this._nextReducible = null;
- this._children = null;
+ this.NextReducible = null;
+ this.children = null;
}
else
{
// Otherwise add this to the reducible nodes
- this._nextReducible = octree.ReducibleNodes[level];
+ this.NextReducible = octree.ReducibleNodes[level];
octree.ReducibleNodes[level] = this;
- this._children = new OctreeNode[8];
+ this.children = new OctreeNode[8];
}
- }
+ }
+ #endregion
+
+ #region Properties
+ ///
+ /// Gets or the next reducible node
+ ///
+ public OctreeNode NextReducible { get; private set; }
+
+ ///
+ /// Gets the child nodes
+ ///
+ private OctreeNode[] Children
+ {
+ get { return this.children; }
+ }
+ #endregion
+
+ #region Methods
///
/// Add a color into the tree
///
@@ -327,9 +399,10 @@ namespace ImageProcessor.Imaging
public void AddColor(Color32 pixel, int colorBits, int level, Octree octree)
{
// Update the color information if this is a leaf
- if (this._leaf)
+ if (this.leaf)
{
- Increment(pixel);
+ this.Increment(pixel);
+
// Setup the previous node
octree.TrackPrevious(this);
}
@@ -339,38 +412,20 @@ namespace ImageProcessor.Imaging
int shift = 7 - level;
int index = ((pixel.Red & mask[level]) >> (shift - 2)) |
((pixel.Green & mask[level]) >> (shift - 1)) |
- ((pixel.Blue & mask[level]) >> (shift));
+ ((pixel.Blue & mask[level]) >> shift);
- OctreeNode child = this._children[index];
+ OctreeNode child = this.Children[index];
if (null == child)
{
// Create a new child node & store in the array
child = new OctreeNode(level + 1, colorBits, octree);
- this._children[index] = child;
+ this.Children[index] = child;
}
// Add the color to the child node
child.AddColor(pixel, colorBits, level + 1, octree);
}
-
- }
-
- ///
- /// Get or Sets the next reducible node
- ///
- public OctreeNode NextReducible
- {
- get { return _nextReducible; }
- set { _nextReducible = value; }
- }
-
- ///
- /// Return the child nodes
- ///
- public OctreeNode[] Children
- {
- get { return _children; }
}
///
@@ -379,73 +434,84 @@ namespace ImageProcessor.Imaging
/// The number of leaves removed
public int Reduce()
{
- this._red = this._green = this._blue = 0;
- int children = 0;
+ this.red = this.green = this.blue = 0;
+ int childPosition = 0;
// Loop through all children and add their information to this node
for (int index = 0; index < 8; index++)
{
- if (null != this._children[index])
+ if (null != this.Children[index])
{
- this._red += this._children[index]._red;
- this._green += this._children[index]._green;
- this._blue += this._children[index]._blue;
- this._pixelCount += this._children[index]._pixelCount;
- ++children;
- this._children[index] = null;
+ this.red += this.Children[index].red;
+ this.green += this.Children[index].green;
+ this.blue += this.Children[index].blue;
+ this.pixelCount += this.Children[index].pixelCount;
+ ++childPosition;
+ this.Children[index] = null;
}
}
// Now change this to a leaf node
- this._leaf = true;
+ this.leaf = true;
// Return the number of nodes to decrement the leaf count by
- return children - 1;
+ return childPosition - 1;
}
///
/// Traverse the tree, building up the color palette
///
/// The palette
- /// The current palette index
- public void ConstructPalette(ArrayList palette, ref int paletteIndex)
+ /// The current palette index
+ public void ConstructPalette(ArrayList palette, ref int currentPaletteIndex)
{
- if (_leaf)
+ if (this.leaf)
{
// Consume the next palette index
- _paletteIndex = paletteIndex++;
+ this.paletteIndex = currentPaletteIndex++;
// And set the color of the palette entry
- palette.Add(Color.FromArgb(_red / _pixelCount, _green / _pixelCount, _blue / _pixelCount));
+ palette.Add(Color.FromArgb(this.red / this.pixelCount, this.green / this.pixelCount, this.blue / this.pixelCount));
}
else
{
// Loop through children looking for leaves
for (int index = 0; index < 8; index++)
{
- if (null != _children[index])
- _children[index].ConstructPalette(palette, ref paletteIndex);
+ if (null != this.children[index])
+ {
+ this.children[index].ConstructPalette(palette, ref currentPaletteIndex);
+ }
}
}
}
///
- /// Return the palette index for the passed color
+ /// Return the palette index for the passed color.
///
+ ///
+ /// The pixel.
+ ///
+ ///
+ /// The level.
+ ///
+ ///
+ /// The palette index for the passed color.
+ ///
public int GetPaletteIndex(Color32 pixel, int level)
{
- int paletteIndex = _paletteIndex;
+ int currentPaletteIndex = this.paletteIndex;
- if (!_leaf)
+ if (!this.leaf)
{
int shift = 7 - level;
int index = ((pixel.Red & mask[level]) >> (shift - 2)) |
((pixel.Green & mask[level]) >> (shift - 1)) |
- ((pixel.Blue & mask[level]) >> (shift));
+ ((pixel.Blue & mask[level]) >> shift);
- if (null != _children[index])
+ if (null != this.children[index])
{
- paletteIndex = _children[index].GetPaletteIndex(pixel, level + 1);
+ currentPaletteIndex = this.children[index].GetPaletteIndex(pixel, level + 1);
}
else
{
@@ -453,59 +519,23 @@ namespace ImageProcessor.Imaging
}
}
- return paletteIndex;
+ return currentPaletteIndex;
}
///
/// Increment the pixel count and add to the color information
///
+ ///
+ /// The pixel.
+ ///
public void Increment(Color32 pixel)
{
- this._pixelCount++;
- this._red += pixel.Red;
- this._green += pixel.Green;
- this._blue += pixel.Blue;
- }
-
- ///
- /// Flag indicating that this is a leaf node
- ///
- private bool _leaf;
-
- ///
- /// Number of pixels in this node
- ///
- private int _pixelCount;
-
- ///
- /// Red component
- ///
- private int _red;
-
- ///
- /// Green Component
- ///
- private int _green;
-
- ///
- /// Blue component
- ///
- private int _blue;
-
- ///
- /// Pointers to any child nodes
- ///
- private OctreeNode[] _children;
-
- ///
- /// Pointer to next reducible node
- ///
- private OctreeNode _nextReducible;
-
- ///
- /// The index of this node in the palette
- ///
- private int _paletteIndex;
+ this.pixelCount++;
+ this.red += pixel.Red;
+ this.green += pixel.Green;
+ this.blue += pixel.Blue;
+ }
+ #endregion
}
}
}
diff --git a/src/ImageProcessor/Imaging/TextLayer.cs b/src/ImageProcessor/Imaging/TextLayer.cs
index 785daac0f..9d2bc9f61 100644
--- a/src/ImageProcessor/Imaging/TextLayer.cs
+++ b/src/ImageProcessor/Imaging/TextLayer.cs
@@ -51,7 +51,7 @@ namespace ImageProcessor.Imaging
public string Text { get; set; }
///
- /// Gets or sets TextColor.
+ /// Gets or sets the Color to render the font.
///
public Color TextColor
{
@@ -74,7 +74,7 @@ namespace ImageProcessor.Imaging
}
///
- /// Gets or sets the FontStyle.
+ /// Gets or sets the FontStyle of the textlayer.
///
public FontStyle Style
{
@@ -83,7 +83,7 @@ namespace ImageProcessor.Imaging
}
///
- /// Gets or sets the Opacity.
+ /// Gets or sets the Opacity of the textlayer.
///
public int Opacity
{
@@ -101,13 +101,18 @@ namespace ImageProcessor.Imaging
}
///
- /// Gets or sets Position.
+ /// Gets or sets the Position of the textlayer.
///
public Point Position
{
get { return this.position; }
set { this.position = value; }
}
+
+ ///
+ /// Gets or sets a value indicating whether a DropShadow should be drawn.
+ ///
+ public bool DropShadow { get; set; }
#endregion
}
}
diff --git a/src/ImageProcessor/Processors/Filter.cs b/src/ImageProcessor/Processors/Filter.cs
index 0dec514bf..df31ffce8 100644
--- a/src/ImageProcessor/Processors/Filter.cs
+++ b/src/ImageProcessor/Processors/Filter.cs
@@ -165,6 +165,7 @@ namespace ImageProcessor.Processors
{
Bitmap newImage = null;
Image image = factory.Image;
+
// Bitmaps for comic pattern
Bitmap hisatchBitmap = null;
Bitmap patternBitmap = null;
@@ -329,6 +330,15 @@ namespace ImageProcessor.Processors
}
}
+ // Add a vignette to finish the effect.
+ // TODO: This feels a bit mucky so I might chop it out.
+ if (this.DynamicParameter == "polaroid" || this.DynamicParameter == "lomograph")
+ {
+ factory.Image = newImage;
+ Vignette vignette = new Vignette();
+ newImage = (Bitmap)vignette.ProcessImage(factory);
+ }
+
// Reassign the image.
image.Dispose();
image = newImage;
diff --git a/src/ImageProcessor/Processors/Watermark.cs b/src/ImageProcessor/Processors/Watermark.cs
index 48a56cc05..147ee9fe9 100644
--- a/src/ImageProcessor/Processors/Watermark.cs
+++ b/src/ImageProcessor/Processors/Watermark.cs
@@ -7,9 +7,12 @@
namespace ImageProcessor.Processors
{
#region Using
+
+ using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Text;
+ using System.Linq;
using System.Text.RegularExpressions;
using ImageProcessor.Helpers.Extensions;
using ImageProcessor.Imaging;
@@ -60,6 +63,11 @@ namespace ImageProcessor.Processors
///
private static readonly Regex OpacityRegex = new Regex(@"opacity-\d{1,2}(?!\d)|opacity-100", RegexOptions.Compiled);
+ ///
+ /// The regular expression to search strings for the shadow attribute.
+ ///
+ private static readonly Regex ShadowRegex = new Regex(@"shadow-true", RegexOptions.Compiled);
+
#region IGraphicsProcessor Members
///
/// Gets the name.
@@ -157,6 +165,7 @@ namespace ImageProcessor.Processors
textLayer.Font = this.ParseFontFamily(toParse);
textLayer.Style = this.ParseFontStyle(toParse);
textLayer.Opacity = this.ParseOpacity(toParse);
+ textLayer.DropShadow = this.ParseDropShadow(toParse);
this.DynamicParameter = textLayer;
}
@@ -218,6 +227,23 @@ namespace ImageProcessor.Processors
// Set the hinting and draw the text.
graphics.TextRenderingHint = TextRenderingHint.AntiAliasGridFit;
+
+ if (textLayer.DropShadow)
+ {
+ // Shadow opacity should change with the base opacity.
+ int shadowOpacity = opacity - (int)Math.Ceiling((30 / 100f) * 255);
+ int finalShadowOpacity = shadowOpacity > 0 ? shadowOpacity : 0;
+
+ using (Brush shadowBrush = new SolidBrush(Color.FromArgb(finalShadowOpacity, Color.Black)))
+ {
+ // Scale the shadow position to match the font size.
+ // Magic number but it's based on artistic preference.
+ int shadowDiff = (int)Math.Ceiling(fontSize / 24f);
+ Point shadowPoint = new Point(origin.X + shadowDiff, origin.Y + shadowDiff);
+ graphics.DrawString(text, font, shadowBrush, shadowPoint, drawFormat);
+ }
+ }
+
graphics.DrawString(text, font, brush, origin, drawFormat);
}
}
@@ -439,6 +465,21 @@ namespace ImageProcessor.Processors
// full opacity - matches the Textlayer default.
return 100;
}
+
+ ///
+ /// Returns a value indicating whether the watermark is to have a shadow.
+ ///
+ ///
+ /// The input string containing the value to parse.
+ ///
+ ///
+ /// The true if the watermark is to have a shadow; otherwise false.
+ ///
+ private bool ParseDropShadow(string input)
+ {
+ return ShadowRegex.Matches(input).Cast().Any();
+ }
+
#endregion
}
}
diff --git a/src/Test/Test/Images/color-vision-test.gif.REMOVED.git-id b/src/Test/Test/Images/color-vision-test.gif.REMOVED.git-id
new file mode 100644
index 000000000..5c4f4195d
--- /dev/null
+++ b/src/Test/Test/Images/color-vision-test.gif.REMOVED.git-id
@@ -0,0 +1 @@
+35a926115b13b61dc37308f8d903b42d14f92924
\ No newline at end of file