Create Bitmap image from Outlook Category colour

D

Dorell

Hi All

I’m trying to create a 48x48 Bitmap image from the 3 colour properties
(CategoryGradientBottomColor, CategoryGradietTopColor and
CategoryGradientBorderColor) of a Category object so that it can be displayed
on a CommandBarButton. I have black displayed in the area to be painted with
a white background in a 48x48 image that is stored as an Image Resource. I
use this image as the mask for the button and as the template for drawing
Category colours.

I know that I need to apply the border property to the pixels around the
edge of the black image but how do I apply the 2 gradient properties to the
area of the image so that it appears as it would when displayed by Outlook?

Regards,
Jason

Here is the code I have so far...

Category category = Application.Session.Categories[index];
imageList.Images.Add(Properties.Resources.CategoryTemplate);

Color categoryGradientTopColor =
PictureConverter.OleColorToDrawingColor(category.CategoryGradientBottomColor);
Color categoryGradientTopColor =
PictureConverter.OleColorToDrawingColor(category.CategoryGradientTopColor);
Color categoryBorderColor =
PictureConverter.OleColorToDrawingColor(category.CategoryBorderColor);

Bitmap newCategoryImage = (Bitmap)ImageList.Images[0];

for (int x = 0; x < 48; x++)
{
for (int y = 0; y < 48; y++)
{
// Code to apply the 3 category color properties to newCategoryImage
}
}

IPictureDisp categoryImage =
PictureConverter.ImageToPictureDisp(newCategoryImage);
button.Picture = categoryImage;
button.Mask = PictureConverter.ImageToPictureDisp(imageList.Images[0]);


internal class PictureConverter : AxHost
{
private PictureConverter() : base(String.Empty) { }

public static stdole.IPictureDisp ImageToPictureDisp(Image image)
{
return (stdole.IPictureDisp)GetIPictureDispFromPicture(image);
}

public static Color OleColorToDrawingColor(uint OleColor)
{
return GetColorFromOleColor(OleColor);
}
}
 
K

Ken Slovak - [MVP - Outlook]

I haven't done any experimentation with that, but can you get the RECT for
that color swatch and go row by row checking the RGB color values and alpha
bits, and use that as a reference to apply a gradient row by row to the
CommandBarButton image?

One thing to bear in mind is that the spec for a CommandBarButton image is
16x16x256 colors with no alpha layer. Anything else may not display
properly.
 
D

Dorell

Hi Ken

Thanks for the response, I’ve just managed to get this working today. Yes I
had issues with using the 48x48 image because I think it was being converted
to a 16x16 image so some of the detail was lost. I managed to display the
gradient fill colour for the Category by;-converting the category picture
mask to a Graphics object, creating a LinearGradientBrush with the top and
bottom gradient colours and filling the graphic with the Linear Brush. I’ve
put the solution below, it works really well and is identical to the icon
created by Outlook but I do have a slight issue with performance. When I
change Categories the icon is updated immediately which is perfect but I have
a situation when no category is selected and I want to display a blank
category with only a border and no fill colour. I do this by setting the
picture to a category with a white fill colour and a black border and I then
use that same image as the mask which displays the command bar button colour
as the category fill colour. The problem is that sometimes it takes a while
to update (anything up to a second) but it doesn’t when you change between
category colours. It’s not all the time either sometimes it’s instant, I
think it may be to do with changing the mask as when you change between
colours the same mask is used.

Any idea why this could be happening?

In the solution below I iterated through all the pixels to apply the border
as it was faster than using a pen to draw the border. Would the method you
described above be faster than using a LinearGradientBrush?

Thanks
Jason

Category category = Application.Session.Categories[Index];

Bitmap categoryMaskBitmap = MaskGenerator.createCategoryMask();
Bitmap categoryPictureBitmap = PictureConverter.CategoryToBitmap(category,
categoryMaskBitmap);

button.Picture = PictureConverter.ImageToPictureDisp(categoryPictureBitmap);
button.Mask = PictureConverter.ImageToPictureDisp(categoryMaskBitmap);

internal class PictureConverter : AxHost
{
private PictureConverter() : base(String.Empty) { }

public static IPictureDisp ImageToPictureDisp(Image image)
{
return (IPictureDisp)GetIPictureDispFromPicture(image);
}

public static Bitmap CategoryToBitmap(Category category, Bitmap
bitmap)
{
Color categoryBorderColor =
GetColorFromOleColor(category.CategoryBorderColor);
Graphics categoryGraphics = Graphics.FromImage(bitmap);
LinearGradientBrush categoryFillColor = new
LinearGradientBrush(new Point(2, 2), new Point(2, 13),
GetColorFromOleColor(category.CategoryGradientTopColor),
GetColorFromOleColor(category.CategoryGradientBottomColor));
categoryGraphics.FillRectangle(categoryFillColor, 2, 2, 12, 12);
return applyCategoryBorder(bitmap, categoryBorderColor);
}

private static Bitmap applyCategoryBorder(Bitmap bitmap, Color
borderColor)
{
for (int x = 0; x < 16; x++)
{
for (int y = 0; y < 16; y++)
{
if (bitmap.GetPixel(x, y).ToArgb() != -1 &&
isBorderPixel(x, y))
{
bitmap.SetPixel(x, y, borderColor);
}
}
}
return bitmap;
}

private static bool isBorderPixel(int x, int y)
{
switch (x)
{
case 1:
return true;
case 2:
if (y == 2 || y == 13) { return true; }
break;
case 13:
if (y == 2 || y == 13) { return true; }
break;
case 14:
return true;
default:
switch (y)
{
case 1:
return true;
case 14:
return true;
}
break;
}
return false;
}
}


public class MaskGenerator
{
public static Bitmap createCategoryMask()
{
Bitmap bitmap = new Bitmap(16, 16);

for (int x = 0; x < 16; x++)
{
for (int y = 0; y < 16; y++)
{
if (isMaskArea(x, y))
{
bitmap.SetPixel(x, y, Color.Black);
}
else
{
bitmap.SetPixel(x, y, Color.White);
}
}
}
return bitmap;
}

private static bool isMaskArea(int x, int y)
{
switch (x)
{
case 0:
return false;
case 15:
return false;
default:
switch (y)
{
case 0:
return false;
case 15:
return false;
}
break;
}
if (isCornerPixel(x, y))
{
return false;
}
return true;
}

private static bool isCornerPixel(int x, int y)
{
switch (x)
{
case 1:
return isOuterEdge(y);
case 2:
return isInnerEdge(y);
case 13:
return isInnerEdge(y);
case 14:
return isOuterEdge(y);
}
return false;
}

private static bool isInnerEdge(int y)
{
switch (y)
{
case 1:
return true;
case 14:
return true;
}
return false;
}

private static bool isOuterEdge(int y)
{
switch (y)
{
case 1:
return true;
case 2:
return true;
case 13:
return true;
case 14:
return true;
}
return false;
}
}
}



Ken Slovak - said:
I haven't done any experimentation with that, but can you get the RECT for
that color swatch and go row by row checking the RGB color values and alpha
bits, and use that as a reference to apply a gradient row by row to the
CommandBarButton image?

One thing to bear in mind is that the spec for a CommandBarButton image is
16x16x256 colors with no alpha layer. Anything else may not display
properly.




Dorell said:
Hi All

I’m trying to create a 48x48 Bitmap image from the 3 colour properties
(CategoryGradientBottomColor, CategoryGradietTopColor and
CategoryGradientBorderColor) of a Category object so that it can be
displayed
on a CommandBarButton. I have black displayed in the area to be painted
with
a white background in a 48x48 image that is stored as an Image Resource. I
use this image as the mask for the button and as the template for drawing
Category colours.

I know that I need to apply the border property to the pixels around the
edge of the black image but how do I apply the 2 gradient properties to
the
area of the image so that it appears as it would when displayed by
Outlook?

Regards,
Jason

Here is the code I have so far...

Category category = Application.Session.Categories[index];
imageList.Images.Add(Properties.Resources.CategoryTemplate);

Color categoryGradientTopColor =
PictureConverter.OleColorToDrawingColor(category.CategoryGradientBottomColor);
Color categoryGradientTopColor =
PictureConverter.OleColorToDrawingColor(category.CategoryGradientTopColor);
Color categoryBorderColor =
PictureConverter.OleColorToDrawingColor(category.CategoryBorderColor);

Bitmap newCategoryImage = (Bitmap)ImageList.Images[0];

for (int x = 0; x < 48; x++)
{
for (int y = 0; y < 48; y++)
{
// Code to apply the 3 category color properties to newCategoryImage
}
}

IPictureDisp categoryImage =
PictureConverter.ImageToPictureDisp(newCategoryImage);
button.Picture = categoryImage;
button.Mask = PictureConverter.ImageToPictureDisp(imageList.Images[0]);


internal class PictureConverter : AxHost
{
private PictureConverter() : base(String.Empty) { }

public static stdole.IPictureDisp ImageToPictureDisp(Image image)
{
return (stdole.IPictureDisp)GetIPictureDispFromPicture(image);
}

public static Color OleColorToDrawingColor(uint OleColor)
{
return GetColorFromOleColor(OleColor);
}
}
 
K

Ken Slovak - [MVP - Outlook]

The speed difference could be partly due to changing the mask. Any
interaction with COM will have speed penalties for managed code.

If the mask is being reused for every switch I'd get it once and cache it.
Or even cache a few masks. That would shave a little time. So would getting
the category properties and passing the values instead of passing the COM
object. Whether that would be noticeable to anything but a timer is another
story :)

Thanks for posting your solution, BTW. It's always nice to have sample code
available to users of these groups.




Dorell said:
Hi Ken

Thanks for the response, I’ve just managed to get this working today. Yes
I
had issues with using the 48x48 image because I think it was being
converted
to a 16x16 image so some of the detail was lost. I managed to display the
gradient fill colour for the Category by;-converting the category picture
mask to a Graphics object, creating a LinearGradientBrush with the top and
bottom gradient colours and filling the graphic with the Linear Brush. I’ve
put the solution below, it works really well and is identical to the icon
created by Outlook but I do have a slight issue with performance. When I
change Categories the icon is updated immediately which is perfect but I
have
a situation when no category is selected and I want to display a blank
category with only a border and no fill colour. I do this by setting the
picture to a category with a white fill colour and a black border and I
then
use that same image as the mask which displays the command bar button
colour
as the category fill colour. The problem is that sometimes it takes a
while
to update (anything up to a second) but it doesn’t when you change between
category colours. It’s not all the time either sometimes it’s instant, I
think it may be to do with changing the mask as when you change between
colours the same mask is used.

Any idea why this could be happening?

In the solution below I iterated through all the pixels to apply the
border
as it was faster than using a pen to draw the border. Would the method you
described above be faster than using a LinearGradientBrush?

Thanks
Jason

Category category = Application.Session.Categories[Index];

Bitmap categoryMaskBitmap = MaskGenerator.createCategoryMask();
Bitmap categoryPictureBitmap = PictureConverter.CategoryToBitmap(category,
categoryMaskBitmap);

button.Picture =
PictureConverter.ImageToPictureDisp(categoryPictureBitmap);
button.Mask = PictureConverter.ImageToPictureDisp(categoryMaskBitmap);

internal class PictureConverter : AxHost
{
private PictureConverter() : base(String.Empty) { }

public static IPictureDisp ImageToPictureDisp(Image image)
{
return (IPictureDisp)GetIPictureDispFromPicture(image);
}

public static Bitmap CategoryToBitmap(Category category, Bitmap
bitmap)
{
Color categoryBorderColor =
GetColorFromOleColor(category.CategoryBorderColor);
Graphics categoryGraphics = Graphics.FromImage(bitmap);
LinearGradientBrush categoryFillColor = new
LinearGradientBrush(new Point(2, 2), new Point(2, 13),
GetColorFromOleColor(category.CategoryGradientTopColor),
GetColorFromOleColor(category.CategoryGradientBottomColor));
categoryGraphics.FillRectangle(categoryFillColor, 2, 2, 12,
12);
return applyCategoryBorder(bitmap, categoryBorderColor);
}

private static Bitmap applyCategoryBorder(Bitmap bitmap, Color
borderColor)
{
for (int x = 0; x < 16; x++)
{
for (int y = 0; y < 16; y++)
{
if (bitmap.GetPixel(x, y).ToArgb() != -1 &&
isBorderPixel(x, y))
{
bitmap.SetPixel(x, y, borderColor);
}
}
}
return bitmap;
}

private static bool isBorderPixel(int x, int y)
{
switch (x)
{
case 1:
return true;
case 2:
if (y == 2 || y == 13) { return true; }
break;
case 13:
if (y == 2 || y == 13) { return true; }
break;
case 14:
return true;
default:
switch (y)
{
case 1:
return true;
case 14:
return true;
}
break;
}
return false;
}
}


public class MaskGenerator
{
public static Bitmap createCategoryMask()
{
Bitmap bitmap = new Bitmap(16, 16);

for (int x = 0; x < 16; x++)
{
for (int y = 0; y < 16; y++)
{
if (isMaskArea(x, y))
{
bitmap.SetPixel(x, y, Color.Black);
}
else
{
bitmap.SetPixel(x, y, Color.White);
}
}
}
return bitmap;
}

private static bool isMaskArea(int x, int y)
{
switch (x)
{
case 0:
return false;
case 15:
return false;
default:
switch (y)
{
case 0:
return false;
case 15:
return false;
}
break;
}
if (isCornerPixel(x, y))
{
return false;
}
return true;
}

private static bool isCornerPixel(int x, int y)
{
switch (x)
{
case 1:
return isOuterEdge(y);
case 2:
return isInnerEdge(y);
case 13:
return isInnerEdge(y);
case 14:
return isOuterEdge(y);
}
return false;
}

private static bool isInnerEdge(int y)
{
switch (y)
{
case 1:
return true;
case 14:
return true;
}
return false;
}

private static bool isOuterEdge(int y)
{
switch (y)
{
case 1:
return true;
case 2:
return true;
case 13:
return true;
case 14:
return true;
}
return false;
}
}
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Top