Posts Tagged 'Generating Icons'

C# How to: Swapping Bitmap ARGB Colour Channels

Article Purpose

The intention of is to explain and illustrate the various possible combinations that can be implemented when swapping the underlying colour channels related to a  image. The concepts explained can easily be replicated by making use of the included sample application.

Sample source code

is accompanied by a sample source code Visual Studio project which is available for download here.

Using the sample Application

The sample application associated with allows the user to select a source image, apply a colour shifting option. The user is provided  with the option to save to disk the resulting new . The below is a screenshot of the Bitmap ARGB Swapping application in action:

SampleAppScreenshot

The scenario illustrated above shows an of flowers being transformed by swapping the underlying colour channels. In this case the ShiftLeft algorithm had been applied. The original is licenced under the , the original image can be downloaded from Wikipedia.

Types of Colour Swapping

The sample source code defines the type ColorSwapType, which represents the possible combinations of colour channel swapping that can be applied to a . The source code extract below provides the definition of the ColorSwapType :

public enum ColorSwapType
{
    ShiftRight,
    ShiftLeft,
    SwapBlueAndRed,
    SwapBlueAndGreen,
    SwapRedAndGreen,
}

When directly manipulating a object’s pixel values an important detail should be noted: Bitmap colour channels in memory are represented in the order Blue, Green, Red and Alpha despite being commonly referred to by abbreviation ARGB!

The following list describes each colour swapping type’s outcome:

  • ShiftRight: Starting at Blue, each colour’s value is set to the colour channel to the right. The value of Blue is applied to Red, Red’s original value applied to Green, Green’s original value applied to Blue.
  • ShiftLeft: Starting at Blue, each colour’s value is set to the colour channel to the left. The value of Blue is applied to Green, Green’s original value applied to Red, Red’s original value applied to Blue.
  • SwapBlueAndRed: The value of the Blue channel is applied to the Red channel and the original value of the Red channel is then applied to the Blue channel. The value of the Green channel remains unchanged.
  • SwapBlueAndGreen: The value of the Blue channel is applied to the Green channel and the original value of the Green channel is then applied to the Blue channel. The value of the Red  channel remains unchanged.
  • SwapRedAndGreen: The value of the Red channel is applied to the Green channel and the original value of the Green channel is then applied to the Red channel. The value of the Blue channel remains unchanged.

The Colour Swap Filter

The sample source code defines the ColorSwapFilter class. This class provides several member properties, which in combination represent the options involved in applying a colour swap filter. The source code snippet below provides the definition of the ColorSwapFilter type:

public class ColorSwapFilter
{
   private ColorSwapType swapType = ColorSwapType.ShiftRight;
   public ColorSwapType SwapType
   {
        get{ return swapType;}
        set{ swapType = value;}
   }

private bool swapHalfColorValues = false; public bool SwapHalfColorValues { get{ return swapHalfColorValues;} set{ swapHalfColorValues = value;} }
private bool invertColorsWhenSwapping = false; public bool InvertColorsWhenSwapping { get{ return invertColorsWhenSwapping;} set{ invertColorsWhenSwapping = value;} }
public enum ColorSwapType { ShiftRight, ShiftLeft, SwapBlueAndRed, SwapBlueAndGreen, SwapRedAndGreen, } }

The member properties defined by the ColorSwapFilter class:

  • Implementing the ColorSwapType discussed earlier, the SwapType member property defines the type of colour channel swapping to apply.
  • Before swapping colour channel values, colour values can be inverted depending on whether InvertColorsWhenSwapping equates to true.
  • In order to reduce the intensity of the resulting image, the SwapHalfColorValues property should be set to true. The end result being destination colour channels are set to 50% of relevant source colour channel values.

Applying the Colour Swap Filter

The sample source code accompanying defines the SwapColorsCopy method, an targeting class. When invoking the SwapColorsCopy extension method, the calling code is required to specify an input and an instance of the ColorSwapFilter class. By virtue of being an the input/source will be specified by the object instance invoking the SwapColorsCopy method.

The source code listing below provides the definition of the SwapColorsCopy .

public static Bitmap SwapColorsCopy(this Bitmap originalImage, ColorSwapFilter swapFilterData)
{
    BitmapData sourceData = originalImage.LockBits
                            (new Rectangle(0, 0, originalImage.Width, originalImage.Height),
                            ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height]; Marshal.Copy(sourceData.Scan0, resultBuffer, 0, resultBuffer.Length); originalImage.UnlockBits(sourceData);
byte sourceBlue = 0, resultBlue = 0, sourceGreen = 0, resultGreen = 0, sourceRed = 0, resultRed = 0; byte byte2 = 2, maxValue = 255;
for (int k = 0; k < resultBuffer.Length; k += 4) { sourceBlue = resultBuffer[k]; sourceGreen = resultBuffer[k + 1]; sourceRed = resultBuffer[k + 2];
if (swapFilterData.InvertColorsWhenSwapping == true) { sourceBlue = (byte)(maxValue - sourceBlue); sourceGreen = (byte)(maxValue - sourceGreen); sourceRed = (byte)(maxValue - sourceRed); }
if (swapFilterData.SwapHalfColorValues == true) { sourceBlue = (byte)(sourceBlue / byte2); sourceGreen = (byte)(sourceGreen / byte2); sourceRed = (byte)(sourceRed / byte2); }
switch (swapFilterData.SwapType) { case ColorSwapFilter.ColorSwapType.ShiftRight: { resultBlue = sourceGreen; resultRed = sourceBlue; resultGreen = sourceRed; break; } case ColorSwapFilter.ColorSwapType.ShiftLeft: { resultBlue = sourceRed; resultRed = sourceGreen; resultGreen = sourceBlue; break; } case ColorSwapFilter.ColorSwapType.SwapBlueAndRed: { resultBlue = sourceRed; resultRed = sourceBlue; break; } case ColorSwapFilter.ColorSwapType.SwapBlueAndGreen: { resultBlue = sourceGreen; resultGreen = sourceBlue; break; } case ColorSwapFilter.ColorSwapType.SwapRedAndGreen: { resultRed = sourceGreen; resultGreen = sourceGreen; break; } }
resultBuffer[k] = resultBlue; resultBuffer[k + 1] = resultGreen; resultBuffer[k + 2] = resultRed; }
Bitmap resultBitmap = new Bitmap(originalImage.Width, originalImage.Height, PixelFormat.Format32bppArgb); BitmapData resultData = resultBitmap.LockBits (new Rectangle(0, 0, resultBitmap.Width, resultBitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length); resultBitmap.UnlockBits(resultData);
return resultBitmap; }

Due to the architecture and implementation of the .net when manipulating a object’s underlying colour values we need to ensure locking the relevant data buffer in memory. When invoking the class’ method the calling code prevents the from shifting and updating memory references. Once a ’s underlying pixel buffer has been locked in memory the source code creates a data buffer of type byte array and then copies the ’s underlying pixel buffer data.

BitmapData sourceData = originalImage.LockBits
                        (new Rectangle(0, 0, originalImage.Width, originalImage.Height),
                        ImageLockMode.ReadOnly, PixelFormat.Format32bppArgb);

byte[] resultBuffer = new byte[sourceData.Stride * sourceData.Height]; Marshal.Copy(sourceData.Scan0, resultBuffer, 0, resultBuffer.Length); originalImage.UnlockBits(sourceData);

The sample source code next iterates the pixel buffer array. Notice how the for loop increments by 4 with each loop. Every four elements of the data buffer in combination represents one pixel, each colour channel expressed as a value ranging from 0 to 255 inclusive.

for (int k = 0; k < resultBuffer.Length; k += 4)

If required each colour channel will first be assigned to a value equating to its inverse value by subtracting from 255.

if (swapFilterData.InvertColorsWhenSwapping == true)
{
     sourceBlue = (byte)(maxValue - sourceBlue);
     sourceGreen = (byte)(maxValue - sourceGreen);
     sourceRed = (byte)(maxValue - sourceRed);
}

When the supplied ColorSwapFilter object method parameter defines SwapHalfColorValues as true the source colour value will be divided by 2.

if (swapFilterData.SwapHalfColorValues == true)
{
     sourceBlue = (byte)(sourceBlue / byte2);
     sourceGreen = (byte)(sourceGreen / byte2);
     sourceRed = (byte)(sourceRed / byte2);
}
 

The next section implements a case statement, each option implementing the required colour channel swap algorithm. The last step expressed as part of the for loop results in assigning newly manipulated values to the data buffer.

The SwapColorsCopy extension method can be described as being immutable in the sense that the input value remains unchanged, instead manipulating and returning a copy of the input data. Following the data buffer iteration the sample source creates a new instance of the class and locks it into memory by invoking the method. By implementing the method the source code copies the data buffer to the underlying buffer associated with the newly created object.

 Bitmap resultBitmap = new Bitmap(originalImage.Width, originalImage.Height, 
                                     PixelFormat.Format32bppArgb);
 
BitmapData resultData = resultBitmap.LockBits (new Rectangle(0, 0, resultBitmap.Width, resultBitmap.Height), ImageLockMode.WriteOnly, PixelFormat.Format32bppArgb);
Marshal.Copy(resultBuffer, 0, resultData.Scan0, resultBuffer.Length); resultBitmap.UnlockBits(resultData);
return resultBitmap;

The implementation: a

The sample source code accompanying defines a , the intention of which being to illustrate a test implementation. The following series of images were created using the sample application:

The source/input image is licenced under the , the original image can be downloaded from Wikipedia.

The Original Image

800px-HK_Sheung_Wan_Hollywood_Road_Park_Flowers_in_Purple

The ShiftLeft Colour Swapping algorithm:

ShiftLeft

Inverted:

ShiftLeft_inverted

The ShiftRight Colour Swapping algorithm:

ShiftRight

Inverted:

ShiftRight_inverted

The SwapBlueAndGreen Colour Swapping algorithm:

SwapBlueAndGreen

Inverted:

SwapBlueAndGreen_inverted

The SwapBlueAndRed Colour Swapping algorithm:

SwapBlueAndRed

Inverted:

SwapBlueAndRed_inverted

The SwapRedAndGreen Colour Swapping algorithm:

SwapRedAndGreen

Inverted:

SwapRedAndGreen_inverted

Related Articles and Feedback

Feedback and questions are always encouraged. If you know of an alternative implementation or have ideas on a more efficient implementation please share in the comments section.

I’ve published a number of articles related to imaging and images of which you can find URL links here:

C# How to: Generating Icons from Images

Article Purpose

This illustrates the process of generating files (*.ico) from user specified input . The accompanying Sample Source Code implements a , allowing for easily testing the generation process.

Sample source code

This is accompanied by a sample source code Visual Studio project which is available for download .

Using the sample Application

The Sample Application can be used to test/implement the concepts described in this . The user interface enables the user to browse and select an file from the file system, which loads as a scaled preview. In addition, the user can select an size from a list of standard dimensions: 16×16, 24×24, 32×32, 48×48, 64×64, 96×96 and 128×128 pixels. When a user clicks on the “Save Icon” button the sample application generates an in memory based on the specified size converting and scaling the provided input . If an was successfully generated, the in-memory representation will be saved to the file system, based on the filename and file path specified by the user.

The image below is screenshot of the Image to Icon Generator application in action:

Image To Icon Generator

The source features Bellis perennis also known as the common European Daisy (see Wikipedia). The file is licensed under the Creative Commons Attribution-Share Alike 2.5 Generic license. The original can be downloaded from .

The resulting file generated by the sample application:

Generating Icons from Images

Scaling and Aspect Ratio

conform to a set of standard dimensions, all of which equate to a square due to the width and height values being equal. A potential issue exists in that the specified source might not have exact square dimensions. In other words, the width and height values of specified source might differ. The solution lies in creating a square based on the specified source . Consider the concept of a square canvas onto which is drawn the source whilst maintaining its aspect ratio, implementing center alignment from the horizontal and vertical aspect. Listed below is the implementation of an defined as CopyToSquareCanvas, targeting the class.

public static Bitmap CopyToSquareCanvas(this Bitmap sourceBitmap, Color canvasBackground)
{
    int maxSide = sourceBitmap.Width > sourceBitmap.Height ? sourceBitmap.Width : sourceBitmap.Height;

Bitmap bitmapResult = new Bitmap(maxSide, maxSide, PixelFormat.Format32bppArgb);
using (Graphics graphicsResult = Graphics.FromImage(bitmapResult)) { graphicsResult.Clear(canvasBackground);
int xOffset = (sourceBitmap.Width - maxSide) / 2; int yOffset = (sourceBitmap.Height - maxSide) / 2;
graphicsResult.DrawImage(sourceBitmap, new Point(xOffset, xOffset)); }
return bitmapResult; }

The size of the resulting is determined by the source ’s longest side, either width or height. To ensure middle alignment both vertically and horizontally the source is drawn at an offset, determined by the additional buffer area added by the canvas.

Generating the Icon

Once we have created an conforming to exact square dimensions the next step would be to scale said to the desired size. A convenient method of quickly scaling source to icon dimensions comes in the form of creating .

The sample source code defines the enumeration IconSizeDimensions, which serves to provide a developer friendly reference coupled with actual dimension values by means of specifying explicit enumeration values. Consider the following code snippet:

public enum IconSizeDimensions
{
    IconSize16x16Pixels = 16,
    IconSize24x24Pixels = 24,
    IconSize32x32Pixels = 32,
    IconSize48x48Pixels = 48,
    IconSize64x64Pixels = 64,
    IconSize96x96Pixels = 96,
    IconSize128x128Pixels = 128
}

The crux of this and sample source code can considered to be the definition of the CreateIcon , which targets the class. The definition is as follows:

public static Icon CreateIcon(this Bitmap sourceBitmap, IconSizeDimensions iconSize)
{
    Bitmap squareCanvas = sourceBitmap.CopyToSquareCanvas(Color.Transparent);
    squareCanvas = (Bitmap)squareCanvas.GetThumbnailImage((int)iconSize, (int)iconSize, null, new IntPtr());

Icon iconResult = Icon.FromHandle(squareCanvas.GetHicon());
return iconResult; }

As discussed the first step is to ensure that the source conforms to square dimensions, implemented here by invoking the CopyToSquareCanvas . Next the source code implements scaling by creating a of which the size is based on the specified IconSizeDimensions value. The method returns a handle to an in the form of an , which serves as a parameter to the static method, which returns an instance of the class.

The Implementation

When the user clicks the “Save” button the Sample Application will present the user with a file save dialog. After the user specifies a file name and file path the Sample Application creates a reference of the source by casting the picturebox’s property to type .

private void btnSaveIcon_Click(object sender, EventArgs e)
{
    if (picSource.Image != null)
    {
        SaveFileDialog sfd = new SaveFileDialog();
        sfd.Title = "Specify a file name and file path";
        sfd.Filter = "Icon Files(*.ico)|*.ico"; 
if (sfd.ShowDialog() == System.Windows.Forms.DialogResult.OK) { System.Drawing.Icon tempIcon = ((Bitmap)picSource.Image).CreateIcon( (IconSizeDimensions)cmbIconSize.SelectedItem);
using (StreamWriter streamWriter = new StreamWriter(sfd.FileName, false)) { tempIcon.Save(streamWriter.BaseStream);
streamWriter.Flush(); streamWriter.Close(); } } } }

When the CreateIcon is invoked, the dimensions selected through the user interface will be passed as a parameter. The last step performed involves persisting the in-memory data to the file system.


Dewald Esterhuizen

Blog Stats

  • 674,466 hits

Enter your email address to follow and receive notifications of new posts by email.

Join 221 other followers

Archives

Twitter feed


%d bloggers like this: