 # ClockworkPDF Beta Release

Finally it is here. I apologize for the delay as I’ve been working on a couple other projects concurrently. This is the first public and testable version, don’t except it to be stable and production ready. I’ve tested it much as I can but if you encounter problems with certain PDF versions, please let me know. Any feedback, ideas for new features or suggestions are gladly welcome. # Oil Painting Filter in C#

Converting an image to make it look like an oil painting is not only a nice effect, but is also an easy to implement algorithm. This article demonstrates how to create an oil painting effect from an image. The oil painting filter consists of two main components: color gradients and pixel color intensities. Result images express a lesser degree of detail and tend to appear to have smaller color ranges.

### Implementation

The following steps are required for the algorithm:

1. Iterate each pixel – Every pixel forming part of the source image should be iterated. When iterating a pixel determine the neighboring pixel values based on the specified filter size (or radius).
2. Calculate color intensityDetermine the Color Intensity of each pixel being iterated and that of the neighboring pixels. The neighboring pixels included should extend to a range determined by the Filter Size specified. The calculated value should be reduced in order to match a value ranging from zero to the number of Intensity Levels specified.
3. Determine maximum neighborhood color intensity – When calculating the color intensities of a pixel neighborhood determine the maximum intensity value. In addition, record the occurrence of each intensity level and sum each of the Red, Green and Blue pixel color component values equating to the same intensity level.
4. Assign the result pixel – The value assigned to the corresponding pixel in the resulting image equates to the pixel color sum total, where those pixels expressed the same intensity level. The sum total should be averaged by dividing the color sum total by the intensity level occurrence

When calculating color intensity reduced to fit the number of levels specified the formula can be expressed as follows: The variables in the above formula can be described as follows:

• i – Intensity: the calculated intensity value.
• R – Red: The value of the pixel’s red color component.
• G – Green:
• B – Blue:
• l – Number of intensity levels: the maximum number of intensity levels specified.

Result Image: Filter 15, Levels 30 The sample source code defines `OilPaintFilter` method, an extension method targeting the `Bitmap` class. This method determines the maximum color intensity from a pixel’s neighbors.

```public static Bitmap OilPaintFilter(this Bitmap sourceBitmap, int levels, int filterSize)
{
Bitmap image;
ArgbColor[] originalData;
ArgbColor[] resultData;
Size size;

image = sourceBitmap;
size = image.Size;

originalData = image.GetPixelsFrom32BitArgbImage();
resultData = new ArgbColor[size.Width * size.Height];

int[] intensityBin = new int[levels];
int[] blueBin = new int[levels];
int[] greenBin = new int[levels];
int[] redBin = new int[levels];

levels = levels - 1;

int filterOffset = (filterSize - 1) / 2;
int byteOffset = 0;
int calcOffset = 0;
int currentIntensity = 0;
int maxIntensity = 0;
int maxIndex = 0;

double blue = 0;
double green = 0;
double red = 0;

for (int offsetY = filterOffset; offsetY &lt; size.Height - filterOffset; offsetY++)
{
for (int offsetX = filterOffset; offsetX &lt; size.Width - filterOffset; offsetX++)
{
ArgbColor result;

blue = green = red = 0;

currentIntensity = maxIntensity = maxIndex = 0;

intensityBin = new int[levels + 1];
blueBin = new int[levels + 1];
greenBin = new int[levels + 1];
redBin = new int[levels + 1];

byteOffset = offsetY * size.Width + offsetX;

for (int filterY = -filterOffset; filterY &lt;= filterOffset; filterY++)
{
for (int filterX = -filterOffset; filterX &lt;= filterOffset; filterX++)
{
calcOffset = byteOffset + (filterX) + (filterY * size.Width);

currentIntensity = (int)Math.Round(((double)(originalData[calcOffset].R +
originalData[calcOffset].G + originalData[calcOffset].B) / 3.0 * (levels)) / 255.0);

intensityBin[currentIntensity] += 1;
blueBin[currentIntensity] += originalData[calcOffset].B;
greenBin[currentIntensity] += originalData[calcOffset].G;
redBin[currentIntensity] += originalData[calcOffset].R;

if (intensityBin[currentIntensity] &gt; maxIntensity)
{
maxIntensity = intensityBin[currentIntensity];
maxIndex = currentIntensity;
}
}
}

blue = blueBin[maxIndex] / maxIntensity;
green = greenBin[maxIndex] / maxIntensity;
red = redBin[maxIndex] / maxIntensity;

result.A = 255;
result.R = ClipByte(red);
result.G = ClipByte(green);
result.B = ClipByte(blue);

resultData[byteOffset] = result;
}
}

return resultData.ToBitmap(size); ;
}
```