Wednesday, September 30, 2015

Tuesday, September 29, 2015

Easier way to convert BitmapImage to Grayscale in WPF

By adding the xmlns namespace xmlns:extra="" I was able to use the FormatConvertedBitmap class.

One way to use it is to define the Bitmap image as a StaticResource:
<Grid x:Name="LayoutRoot" Width="304" Height="360">

   <BitmapImage x:Key="MyPhoto" UriSource="Images/myPhoto.png"/>    <extra:FormatConvertedBitmap x:Key="convertedImage" Source="{StaticResource MyPhoto}" DestinationFormat="Gray32Float" />


  <Border x:Name="ImageBorder" ClipToBounds="True">

   <Image HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Source="{StaticResource convertedImage}" Margin="-25" ClipToBounds="True" />


Another way I was able to use it - was to put the Binding (from the control's view model) into the actual definition - and still keep the source of the Image as the StaticResource.

<extra:FormatConvertedBitmap x:Key="convertedImage" Source="{Binding ImageSource}" DestinationFormat="Gray32Float" />

More information here.

Saturday, September 26, 2015

Silverlight and Color

A while back I was looking for a decent color picker for Silverlight - one that didn't require me to actually use an image for the colors since that isn't editable by the user.

I had looked into what it took to create custom colors and brushes dynamically. It was mainly because a client had asked about being able to choose custom colors for a theme. At that time I couldn't figure anything out, however the current project I am on gave me a bit more insight on how I could possibly make the brushes a bit more interactive and dynamic.

Note: the goal was to be able to dynamically change the color without going through a theme, without choosing from a bitmap image and to show the actual color value that reflects the updated color more accurately. So, instead of creating several brushes in a resource dictionary it uses the mathematical color formula (for the HSL color space in this project). 

The linear gradient brush is not scanned in, but created as the app loops through the formulas, using the index value as the hue value, and creates the colors based it and the formulas. 

Most of the formulas came from:
Math behind colorspaces
Math stack exchange

I actually found using a Tuple with three items very helpful in calculating the colors

Tuple<double,double,double> rgbColorValues(double hue, double lum, double sat)
            double lumValue = lum / 100;
            double temp_One = 0;
            temp_One = luminousValue(lum, sat);
            double temp_Two;
            temp_Two = 2 * lumValue - temp_One;
            double temp_Red = 0;
            double temp_Green = 0;
            double temp_Blue = 0;
            temp_Red = colorTest(hue, "red");
            temp_Green = colorTest(hue, "green");
            temp_Blue = colorTest(hue, "blue");
            double colorRed = 0;
            double colorGreen = 0;
            double colorBlue = 0;
            colorRed = colorConversion(temp_One, temp_Two, temp_Red);
            colorGreen = colorConversion(temp_One, temp_Two, temp_Green);
            colorBlue = colorConversion(temp_One, temp_Two, temp_Blue);
            return Tuple.Create(colorRed, colorGreen, colorBlue);

I admit, it should be lightness and I have luminous as the variable, but I am a bit more concerned with getting it to work at the moment :) - and this helps a lot. It brings in the three values used to calculate the color's value: hue (angle), saturation and lightness. Hue is 0-360 while saturation and lightness are both 0-100.

Currently the issue I have is when the Lightness is 50 or higher. I did find a formula that should fix it, however - getting it into the code is challenging without accidentally breaking something - but here is what I currently have that works well for below 50 (and used in the code above)

public double luminousValue(double lumNumber, double satNumber)
            double lumValue = lumNumber / 100;
            double satValue = satNumber / 100;
            double tempNumber;
            if(lumValue <= 0.5)
                tempNumber = lumValue * (1 + satValue);
                tempNumber = (lumValue + satValue) - (lumValue * satValue);
            return tempNumber;

While the hue is the same for all the colors, the difference is the formula used to calculate what the value is for Red, Green and Blue. Luckily a nice little switch statement helps with this:

public double colorTest(double hue, string color)
            double hueValue = hue / 360;
            string colorName = color;
            double tempColor;
            double colorValue;
                    tempColor = hueValue + 0.333;
                    tempColor = hueValue;
                    tempColor = hueValue - 0.333;
                    tempColor = hueValue;
            if (tempColor < 0)
                colorValue = tempColor + 1;
            else if(tempColor > 1)
                colorValue = tempColor - 1;
                colorValue = tempColor;
            return colorValue;

And the actual conversion is dependent on the value of a certain calculations, and I found putting it in a separate method helped me too:

public double colorConversion(double tempOne, double tempTwo, double colorValue)
            double convertedColor;
            if(6*colorValue <1.0)
                convertedColor = Math.Round(255*(tempTwo + (tempOne - tempTwo) * 6 * colorValue),2);  
            else if(2*colorValue<1)
                convertedColor = Math.Round(255*tempOne,2);
            else if(3*colorValue<2)
                convertedColor = Math.Round(255*(tempTwo + (tempOne - tempTwo) * (0.667 - colorValue) * 6),2);
                convertedColor = Math.Round(255*tempTwo,2);
            return convertedColor;

Here is where the brush is built - and a list for the values that gives me access to both the hex value and location in the gradient brush. By creating the list the same time I create the brush - and they are both tied to the value of the hue, I can call up the color value of a specific area in the gradient brush by calling up the corresponding index of the color list. If the value of the hue is 30, it pulls the value from the item with an index of 30 in the list.

public void buildColorDefs()
            LinearGradientBrush colorRangeBrush = new LinearGradientBrush();
            colorRangeBrush.StartPoint = new Point(0, 0);
            colorRangeBrush.EndPoint = new Point(1, 1);
            colorRangeBrush.MappingMode = BrushMappingMode.RelativeToBoundingBox;
            IList colorStops = colorRangeBrush.GradientStops;
            for (int i = 0; i < 360; i++)
                TextBlock textBlock = new TextBlock();
                textBlock.Text = convertFromHue(i).ToString();
                textBlock.VerticalAlignment = VerticalAlignment.Center;
                textBlock.HorizontalAlignment = HorizontalAlignment.Center;
                string hexColor = convertFromHue(i).ToString();
                hexColor = hexColor.Replace("#"string.Empty);
                SolidColorBrush fillBrush = new SolidColorBrush(
                    Convert.ToByte(hexColor.Substring(0, 2), 16),
                    Convert.ToByte(hexColor.Substring(2, 2), 16),
                    Convert.ToByte(hexColor.Substring(4, 2), 16),
                    Convert.ToByte(hexColor.Substring(6, 2), 16)));
                Color newColor = fillBrush.Color;
                double stopValue = i / 3.5;
                stopValue = stopValue / 100;
                var indexText = i.ToString();
                GradientStop newBrush = new GradientStop();
                newBrush.Color = fillBrush.Color;
                newBrush.Offset = stopValue;
                Grid container = new Grid();
                container.HorizontalAlignment = HorizontalAlignment.Stretch;
                container.VerticalAlignment = VerticalAlignment.Stretch;
                container.Margin = new Thickness(0);
                container.MinWidth = 175;
                Border background = new Border();
                background.HorizontalAlignment = HorizontalAlignment.Stretch;
                background.VerticalAlignment = VerticalAlignment.Stretch;
                background.Background = fillBrush;
                Ellipse listRectangle = new Ellipse();
                listRectangle.Height = 25;
                listRectangle.Width = 25;
                listRectangle.Fill = fillBrush;
                StackPanel colorPanel = new StackPanel();
                colorPanel.Orientation = Orientation.Horizontal;
            swatchRectangle.Fill = colorRangeBrush;
            HueSlider.Background = colorRangeBrush;

To make sure that when it loads up the user can see the colors - I went ahead and have the values load up with some defaults.

public void setInitialPreviewColor(object sender, RoutedEventArgs e)
            double lum = 50;
            double hue = 0;
            double sat = 100;
             var rgbColorTuple = rgbColorValues(hue, lum, sat);
             //var calculatedRGBValues = calcHSLRGBValues(hue, lum, sat);
             double colorRed = rgbColorTuple.Item1;
             double colorGreen = rgbColorTuple.Item2;
             double colorBlue = rgbColorTuple.Item3;
            RedValueDisplay.Text = colorRed.ToString();
            GreenValueDisplay.Text = colorGreen.ToString();
            BlueValueDisplay.Text = colorBlue.ToString();
            SolidColorBrush previewBrush = new SolidColorBrush(Color.FromArgb(255, (byte)rgbColorTuple.Item1, (byte)rgbColorTuple.Item2, (byte)rgbColorTuple.Item3));
            ColorPreview.Fill = previewBrush;

You can download the Silverlight project here. 

Thursday, September 24, 2015

Displaying, and animating, ListBoxItem content as pie chart

On a recent project, it was requested to have the different values presented to a user not only displayed as a pie chart - but to have the pie chart animate to the appropriate value. Sounds easy enough, but Storyboards don't play nice with bindings. At least, not in XAML - so I had to dive into C# to accomplish the desired result.

The first step was to go ahead and create a UserControl with DependencyProperties and location for the Storyboard associated with this particular element. I created three properties : DisplayEndAngle, DisplayChartColor and PieChartOrder.

DisplayEndAngle is for the actual value of the chart, that has gone through a converter to determine the correct EndAngle value to be used by the Arc control.

DisplayChartColor allows me to customize the color of the Arc stroke, and change it in the ListBox's Selected state.

PieChartOrder - I gave each ListBoxItem a name, and used that to determine the start time of the Arc's animation sequence.

I used this forum post as a springboard for the codebehind.

This ended up to be one of the few controls where I ended up writing more codebehind than I did XAML for it to work correctly. Using a switch statement I was able to use the Name and assign the appropriate BeginTime and KeyTimes required. In the actual XAML, I didn't assign the EndAngle a value, every time I did it would show with that value, disappear and then animate. By just using the codebehind for the value binding I was still able to display the correct information.

For this demo, I didn't use a ResourceDictionary for the templates and styles - but simply defined them in the UserControl.Resources. This includes the reference to the converter used to make sure the Arc displays the correct value: PercentToAngleConverter.

Using RelativeSource bindings, I found it easier to set the color of the Arc and even change it in the Selected state of the ListBoxItem.

And, when I run it :) each of the elements animates in sequence to the correct value.

Wednesday, September 23, 2015

Using the Arc control to create a pie chart

I was just recently working on a project that required a simple pie chart but the third party controls that were being used causing the display to load slowly. The Arc control worked well to create the chart, especially since there was only one value being displayed - however I wanted to try to make a pie chart that showed different percentages using the Arc control.

Note: the percentages have to equal 100% and not go over, this may seem like a 'no-brainer' but if you're not careful and watch the values they could go over. So - to try it out I made a very simple list with names, percentages and color values

Nothing fancy - just to test out if it could work. Next - I created an empty ListBox, Grid and a style for the ListBoxItems to display the values. The ListBox acts as a very simple legend. 
To create the pie chart, I just used a foreach statement and added a control as a child to the Grid. One of the key properties - to make sure the Arcs line up correctly - is to make sure the Stretch property is set to "None". I am sure most of the styling could be moved out of the codebehind and into a style - but this was me seeing if I could do it and use this idea later on. Maybe even style it a bit differently.. definitely much easier to set the colors than in some of the charting controls I've used.

If you notice, I have it end with making the StartAngle equal what the previous EndAngle was, and the next EndAngle is the product of the calculated angle and the value of the Arc's StartAngle. This is what rotates the values around the 'Pie Chart" - and because the Stretch is set to None they line up. (was soooo frustrating to get it almost there only to look like a beach ball Salvador Dali would have painted).

Below is the final output.

Definitely nothing fancy - but a start and at least I don't have to wonder if I can do it anymore :).