Article Index Rating:
- Developer (5) of 5 (Intermediate)
- Architect (2.86) of 5 (Beginner)
- Graphic Designer (2.38) of 5 (Beginner)
Table of Contents
In the previous article in this series some goals were outlined. The main goal of the series is to bridge the gap between all the stakeholders an application under development. The stakeholders in this scenario are the employees of a low budget startup company trying to raise capital thru various resources. These resources are not in the scope of this article and the company has no management to report to. Due to the limited funds our starving Artist, Developer, and Architect are utilizing rapid application development methodologies for their vapor wear product. The products purpose is to create a paradigm shift for artists by creating interactive digital works of art for the internet community to consume.
This series is divided up into sections for: Beginner (no programming experience needed), Intermediate (some programming knowledge helpful), and Advanced (advanced knowledge of programming concepts very helpful) audiences.
Beginner Series:
1. Silverlight 1.0 Desktop Art Animation
2. Silverlight 2.0 Desktop Art Animation
3. XAML 2d Graphics Concepts
4. XAML 3d Graphics Concepts
Intermediate Series:
5. Dynamic XAML
6. WPF Application Design and Architecture
7. Silverlight Application Design and Architecture
8. Introduction to Advanced Concepts: DirectX, OpenGL, and Filtering
Advanced Series:
9. Anatomy of a GPU and how XAML is Optimized for GPU's
10. GPU Optimization Techniques
11. Mixing it all Up: DirectX, OpenGL, and More
12. Bringing Everything Together, an Approach on Designing Custom Graphics Engines
Now the the stage is set for our actors the architecture of the prototype application can be discovered. In a previous meeting of our actors they identified the graphic designer as the weakest link in their workflow. Instead of out casting the weakest link 'as seen on TV' they decide to build the application around the designer as they have also identified their role as the principle for the application under development. The designer tested the tools the company purchased for the development of this application and found that it was not easy to use, as some other tools are. The designer's chief complaint was the inability to easily take the design and integrate it with the application. The architect and developer sympathized with the designer and proposed a solution which would maximize the designers effectiveness in being a productive team member. In this scenario the designer has a background in design and web development as a web master of a popular web site. The team agrees upon investing resources to develop an in house shell based script utility the designer can use to rapidly integrate their designs into the prototype application base.
Architectural Rules:
- Code Behind Isolation - No code behind for base XAML (page, window) (host Layer)
- Code Behind Isolation - Code behind preference on resource dictionaries (logic layer)
- Code Behind Isolation - Code behind for user controls event routing preference (event routing)
- Vectorized Image Isolation - Vectorized raster images will be isolated in separate resource dictionaries
- Layer abstraction - Ability to define rules for all abstractions. (code policy enforcement)
- Resource Identity - Resources will be strictly typed in XAML using the x:Type attribute
- RAD - Tools for RAD will automate code and XAML stubs. Generators parse designer level XAML.
Designer Rules:
- Layer abstraction - Ability to graphically isolate custom code thru layer definition
- Shell scripting - Interface for applying code generators via shell script
Developer Rules:
- Layer abstraction - Ability to convert XAML to resources.
- Layer abstraction - Ability to define custom code transformations
Using the new rapid application tools tools developed for the company the designer begins to revise the original prototype. The designer starts by defining layers overlaid on the image. Each layer is then divvied into regions which define properties which will be associated to the graphic elements found in the region. For example the designer wants to create an effect of the sun shining through a tree (not seen in the image) casting shadows and light on the vines growing on the side of the building. The architect designed a simple xml parser which takes the name of the layer, and then locates all objects contained the the in each region of the layer. The name of the layer tells the parser what effect to apply on the objects found by the code generator. The designer is much relieved by the architect's innovation. The designer also would like create shimmering effect(s) in various regions so a different layer is created and the developer works on designing the new effect. Additionally, a prospective investor looked at the first prototype and thought it would be great if the next prototype demonstrated an interactive effect of ripples following the courser when the mouse scrolled across the art work so the designer creates two more layers for this effect: one to map the regions effected by this event, and another to define what effect will be applied for the event.
In this scenario the developer has the most work to do. Not only has the team burdened the developer with creating a new tool, but the architect has also defined rules for the automatic code generation. The developer agrees this is going to worth the effort and feels up to the challenge. Unfortunately the developer quickly finds that this idea is so unique that he must write 80% of the code by hand and there are no other resources which the developer can utilize for this portion of the application other than the team. The developer in this scenario was bought onto this project for his expertise in rapid application development techniques. First the developer reviews the code guidelines and technologies which will be used in the prototype. A pattern then starts to emerge. After reading technical articles on the graphics package he realizes that identifying the objects in the framework can easily be found by trying to cast the reference object to the base type of the framework. At this point the developer must make a decision about the usability of this method. A simple test application is written using C#'s reflections class. The UI assembly is opened using reflections, a for each loop is constructed for each class in the assembly. The loop simply increments two counters: one for count all classes discovered by reflections, and another to record the hit count for classes which can be cast to the framework's base class. Then the hit count is divided by class count. This give the developer an approximation of the code coverage he can expect to be covered using this method. Here is the listing for the test code and results:
using System;
using System.Threading;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Windows;
namespace XAMLBox
{
class Localizer
{
static void Main(string[] args)
{
Thread t = new Thread(CastingHitCounter);
t.SetApartmentState(ApartmentState.STA);
t.Start();
}
private static void CastingHitCounter()
{
FrameworkElement fe = new FrameworkElement();
Type t = Type.GetType(fe.GetType().AssemblyQualifiedName);
Assembly FrameWork = Assembly.GetAssembly(t);
Object RefClass = new Object();
Type[] TypeArr = FrameWork.GetTypes();
int qualifiedTypes = TypeArr.Length;
int classCount = 0;
int badInstance = 0;
int feCount = 0;
int fceCount = 0;
int castCount = 0;
int missCount = 0;
foreach (Type thisType in TypeArr)
{
try
{
RefClass = FrameWork.CreateInstance _
(thisType.AssemblyQualifiedName.ToString());
try
{
RefClass = (FrameworkElement)RefClass;
feCount++;
castCount++;
RefClass = FrameWork.CreateInstance _
(
RefClass = (FrameworkContentElement)RefClass;
fceCount++;
castCount++;
}
catch (Exception e)
{
try
{
RefClass = (FrameworkContentElement)RefClass;
fceCount++;
castCount++;
}
catch (Exception ex)
{
missCount++;
}
}
finally
{
RefClass = null;
classCount++;
}
}
catch (Exception e)
{
classCount++;
badInstance++;
}
}
Console.Out.WriteLine("Total Qualified Types: " + _
qualifiedTypes);
Console.Out.WriteLine("Total Qualified Classes: " + _
classCount);
Console.Out.WriteLine("Total Bad Instances: " + _
badInstance);
Console.Out.WriteLine("Total Workable FrameWork Types: " _
+ castCount);
Console.Out.WriteLine("Total FrameworkElements: " _
+ feCount);
Console.Out.WriteLine("Total FrameworkContentElements: " _
+ fceCount);
Console.Out.WriteLine("Total Missed Types: " + missCount);
Console.Out.WriteLine("Qualified Type to Missed Type Ratio: " _
+
Console.Out.WriteLine("FrameworkElement to Qualified Class Ratio: " _
+
Console.Out.WriteLine("FrameworkContentElement to Qualified Class Ratio: " _
+ ((fceCount / classCount) * 100));
Console.Out.WriteLine("Bad Instance to Qualified Class Ratio: " + _
((badInstance / classCount) * 100));
Console.Out.WriteLine("Error Ratio: " + _
(((badInstance + missCount / classCount) * 100));
Console.Out.WriteLine("End of Report");
}
}
}
Output of localizer test tool:
Total Qualified Types: 2336
Total Qualified Classes: 2336
Total Bad Instances: 0
Total Workable FrameWork Types: 4672
Total FrameworkElements: 2336
Total FrameworkContentElements: 2336
Total Missed Types: 0
Qualified Type to Missed Type Ratio: 0
FrameworkElement to Qualified Class Ratio: 100
FrameworkContentElement to Qualified Class Ratio: 100
Bad Instance to Qualified Class Ratio: 0
Error Ratio: 0
After reviewing the results of the localizer tool, the developer decides this correct course of action and the architect agrees. After a code review by the architect, errors are found and corrected. Although the results seem to fit the frameworks design pattern it is a little suspicious that there is a 100% correlation to the base reflected class to all classes in the framework. This can be a common mistake of over ambitious development cycles. Here are the results after the defects are corrected:
Total Qualified Types: 2336
Total Qualified Classes: 2336
Total Cast able FrameWork Types: 131
Total Cast able FrameworkElements: 105
Total Cast able FrameworkContentElements: 26
Total Unique Base Classes: 306
Total Missed Types: 883
Total Bad Instances: 1427
Total Direct Descendants of FrameworkElement: 0
Total Direct Descendants of FrameworkContentElement: 0
Total Ancestors of FrameworkElement: 145
Total Ancestors of FrameworkContentElement: 30
Total Sealed Classes: 1112
Total Abstract Classes: 288
Qualified Type to Missed Type Ratio: 37.7996575342466
FrameworkElement to Qualified Class Ratio: 4.49486301369863
FrameworkContentElement to Qualified Class Ratio: 1.11301369863014
Bad Instance to Qualified Class Ratio: 61.0873287671233
Unique Base Type to Qualified Class Ratio: 13.0993150684931
Direct Descendants of FrameworkElement to Class Count: 0
Direct Descendants of FrameworkContentElement to Class Count: 0
Direct Descendants of FrameworkElement to Unique Base Types: 0
Direct Descendants of FrameworkContentElement to Unique Base Types: 0
Ancestors of FrameworkElement to Class Count: 6.20719178082192
Ancestors of FrameworkContentElement to Class Count: 1.28424657534247
Ancestors of FrameworkElement to Unique Base Types: 47.3856209150327
Ancestors of FrameworkContentElement to Unique Base Types: 9.80392156862745
Error Ratio: 98.8869863013699
Interpretation of the results can be a little tricky. At first glance an error ratio of 98% seems to indicate a bad correlation. However a combined hit ratio of 57% on base types indicates that this is a good entry point into this frame work. Documentation on the framework confirms this indicator. However the absence of direct descendants is still suspicious as at least one class in the framework must directly derive from the reflected classes unless they share a common interface. The documentation indicates that the classes used in this test are not good candidates for use as base classes and that higher level entry points should be used for deriving new classes. This point is mute for the intended use of these classes. The process of generating code automatically using code generation or Domain Specific Languages simplified by using low-level API's which have a common ancestor or interface. The ability to polymorph a high-level API class to a common type simplifies the amount of custom code branches with out sacrificing any customized high-level implementations. Understanding the difference in implementation of inheritance based on an common inter face and common ancestor is necessary as casting to a base ancestor class would reduce the implementation to it's lowest level implementation and would looses any customizations applied by higher level classes. There is much ambiguity between either implementation style as each can be implemented in such a way as to add value to the framework. It is more a choice of design and architecture as to how the low-level API are designed. Both are useful alone or working in conjunction if the design is implemented correctly. A balance on simplicity and usability is the common preference of good OO designs.
Upon investigation of the ratios calculated for "Direct Descendants" reveals further error in the code. The following modifications were made for calculating direct descendants:
if (BaseType.FullName.ToString() == "System.Windows.FrameworkElement")
fwDdCount++;
if (BaseType.FullName.ToString() == "System.Windows.FrameworkContentElement")
fwcDdCount++;
The ratio results after modification:
Direct Descendants of FrameworkElement to Class Count: 1.41267123287671
Direct Descendants of FrameworkContentElement to Class Count: 0.256849315068493
Direct Descendants of FrameworkElement to Unique Base Types: 10.7843137254902
Direct Descendants of FrameworkContentElement to Unique Base Types: 1.96078431372549
From these results can branching ratios can calculated:
(fwDdCount/fwAnsestorCount) * 100 = 23%
(fwcDdCount/fwcAnsestorCount) * 100 = 20%
Almost equal.
After calculating the branching of the framework some proto types need to be created to get a performance result of the effects which are to be used. The first two effects are the sparkle and sun light effects. The proto type uses an editable image buffer to render the effects and then streams them to the browser via a PNG encoder. The effects are displayed using an ImageBrush
an alternate method would be to recode and encode the effect and then display using a VideoBrush
. The alternate will be explored in the next article in this series. A test application is created with a XAML test page and a SparkleFilter
class. The sparkle filter uses the EditableImage
and PngEncode
classes to render the filter animation. Some modifications were made to the editable image class to help performance and extend some functionality like alpha blending.
The test runs at 100% CPU on an Intel Pentium 4 Willamette running at 1.76Ghz at 20 frames per second. 50% at 18 fps, and 7% at 10 fps. Since the target image to be using in this prototype has over 4000 image elements to be animated an alternate method or more performance modifications must be made to allow the animation to run smoothly at 20 fps.
The architect first reviews the technology of many designer applications, file formats, code restrictions, security considerations and integration artifacts. A matrix of is created for all artifacts discovered. The list is then indexed by: usability, code ability, and isolation level. Each index is weighed on a scale of one to five. One signifying not relevant five signifying core reliance. Once the matrix is complete the index value for each artifact is calculated as a ratio. The ratio index for each artifact above 3 is then defined as a rule the architecture must follow. Artifacts are discovered based on business decisions, team feedback, and the architect's knowledge and experience of designing reusable code.
After looking over the code the architect notices that the developer made some clumsey mistakes. First mistake is that the code does not correctly dynamically generate the reference class. The result is that the reflections code generated the same class over and over agian. The second mistake is using int(s) for precision math. Ratios can not be correctly caculated unless floating point precision is used. The architect also added more code to further understand the dependancies and relationships of the classes in the assembly. Here is the corrected listing:
using System;using System.Threading;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Reflection;
using System.Windows;
namespace XAMLBox
{
class Localizer
{
static Thread STAThread = new Thread(CastingHitCounter);
static void Main(string[] args)
{
STAThread.SetApartmentState(ApartmentState.STA);
STAThread.Start();
}
private static void CastingHitCounter()
{
FrameworkElement fe = new FrameworkElement();
FrameworkContentElement fce = new FrameworkContentElement();
Type t = Type.GetType(fe.GetType().AssemblyQualifiedName);
Assembly FrameWork = Assembly.GetAssembly(t);
Object RefClass = new Object();
Type BaseType;
Type[] TypeArr = FrameWork.GetTypes();
ArrayList UBaseTypes = new ArrayList(250);
double qualifiedTypes = TypeArr.Length;
double classCount = 0;
double baseTypeCount = 0;
double fwDdCount = 0;
double fwcDdCount = 0;
double fwAnsestorCount = 0;
double fwcAnsestorCount = 0;
double sealedClasses = 0;
double abstractClasses = 0;
double badInstance = 0;
double feCount = 0;
double fceCount = 0;
double castCount = 0;
double missCount = 0;
foreach (Type thisType in TypeArr)
{
try
{
BaseType = thisType.BaseType;
if (UBaseTypes.IndexOf(BaseType) == -1)
{
UBaseTypes.Add(BaseType);
baseTypeCount++;
}
if (thisType.FullName.ToString() == "System.Windows.FrameworkElement")
fwDdCount++;
if (thisType.FullName.ToString() == "System.Windows.FrameworkContentElement")
fwcDdCount++;
if (thisType.IsSubclassOf(fe.GetType()))
fwAnsestorCount++;
if (thisType.IsSubclassOf(fce.GetType()))
fwcAnsestorCount++;
if (thisType.IsSealed)
sealedClasses++;
if (thisType.IsAbstract)
abstractClasses++;
Thread.BeginCriticalRegion();
RefClass = FrameWork.CreateInstance(thisType.FullName.ToString());
try
{
RefClass = (FrameworkElement)RefClass;
feCount++;
castCount++;
RefClass = FrameWork.CreateInstance(thisType.FullName.ToString());
RefClass = (FrameworkContentElement)RefClass;
fceCount++;
castCount++;
}
catch (Exception e)
{
try
{
RefClass = (FrameworkContentElement)RefClass;
fceCount++;
castCount++;
}
catch (Exception ex)
{
missCount++;
}
}
finally
{
RefClass = null;
classCount++;
}
Thread.EndCriticalRegion();
}
catch (Exception e)
{
classCount++;
badInstance++;
}
}
Console.Out.WriteLine("Total Qualified Types: " + qualifiedTypes);
Console.Out.WriteLine("Total Qualified Classes: " + classCount);
Console.Out.WriteLine("Total Cast able FrameWork Types: " + castCount);
Console.Out.WriteLine("Total Cast able FrameworkElements: " + feCount);
Console.Out.WriteLine("Total Cast able FrameworkContentElements: " + fceCount);
Console.Out.WriteLine("Total Unique Base Classes: " + UBaseTypes.Count);
Console.Out.WriteLine("\nTotal Missed Types: " + missCount);
Console.Out.WriteLine("Total Bad Instances: " + badInstance);
Console.Out.WriteLine("\nTotal Direct Decendants of FrameworkElement: " + fwDdCount);
Console.Out.WriteLine("Total Direct Decendants of FrameworkContentElement: " + fwcDdCount);
Console.Out.WriteLine("Total Ansestors of FrameworkElement: " + fwAnsestorCount);
Console.Out.WriteLine("Total Ansestors of FrameworkContentElement: " + fwcAnsestorCount);
Console.Out.WriteLine("Total Sealed Classes: " + sealedClasses);
Console.Out.WriteLine("Total Abstract Classes: " + abstractClasses);
Console.Out.WriteLine("\nQualified Type to Missed Type Ratio: " + ((missCount / qualifiedTypes) * 100));
Console.Out.WriteLine("FrameworkElement to Qualified Class Ratio: " + ((feCount / classCount) * 100));
Console.Out.WriteLine("FrameworkContentElement to Qualified Class Ratio: " + ((fceCount / classCount) * 100));
Console.Out.WriteLine("Bad Instance to Qualified Class Ratio: " + ((badInstance / classCount) * 100));
Console.Out.WriteLine("\nUnique Base Type to Qualified Class Ratio: " + ((UBaseTypes.Count / classCount) * 100));
Console.Out.WriteLine("\nDirect Decendants of FrameworkElement to ClassCount: " + ((fwDdCount / classCount) * 100));
Console.Out.WriteLine("Direct Decendants of FrameworkContentElement to ClassCount: " + ((fwcDdCount / classCount) * 100));
Console.Out.WriteLine("Direct Decendants of FrameworkElement to Unique Base Types: " + ((fwDdCount / UBaseTypes.Count) * 100));
Console.Out.WriteLine("Direct Decendants of FrameworkContentElement to Unique Base Types: " + ((fwcDdCount / UBaseTypes.Count) * 100));
Console.Out.WriteLine("\nAnsestors of FrameworkElement to ClassCount: " + ((fwAnsestorCount / classCount) * 100));
Console.Out.WriteLine("Ansestors of FrameworkContentElement to ClassCount: " + ((fwcAnsestorCount / classCount) * 100));
Console.Out.WriteLine("Ansestors of FrameworkElement to Unique Base Types: " + ((fwAnsestorCount / UBaseTypes.Count) * 100));
Console.Out.WriteLine("Ansestors of FrameworkContentElement to Unique Base Types: " + ((fwcAnsestorCount / UBaseTypes.Count) * 100));
Console.Out.WriteLine("\nError Ratio: " + (((badInstance + missCount) / classCount) * 100));
Console.Out.WriteLine("End of Report");
}
}
}
The use of the sparkle effect caused many problems for performance. However using Silverlight it is not possible to sample any of the colors in the generated image the browser displays. So it was necessary to create a special filter using Joe Stegman's EditableImage
and PngEncode
classes. Link to Joe's WebBlog. The SparkleFilter
class uses 4 image buffers for image processing: AlphaBuffer
, SparkleBuffer
, Back Buffer
, and Cache Buffer
. The alpha buffer contains the instructions for applying an alpha filter, the sparkle buffer contains instructions for applying luminance factors, the back buffer is where the animation is rendered and the cache buffer contains a copy of the original image which is copied to the back buffer at the beginning of each frame to refresh the source image. A Dictionary<Color, EditableImage>
is used to cache composite sparkle buffers for the same colors. Here is the code for test page:
<UserControl Name="UserControl1" x:Class="SilverlightTestApp.Page"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
Width="500" Height="500" Loaded="UserControl_Loaded">
<UserControl.Resources>
<SolidColorBrush x:Key="GreenBrush" Color="#FF00FF00"/>
<ImageBrush x:Key="IBrushRandom" ImageFailed="ImageBrush_ImageFailed" />
<SolidColorBrush x:Key="IBrushSparkle1" Color="Red" />
<SolidColorBrush x:Key="IBrushSparkle2" Color="Blue" />
<SolidColorBrush x:Key="IBrushSparkle3" Color="Green" />
<SolidColorBrush x:Key="IBrushSparkle4" Color="#70ff0000" />
<SolidColorBrush x:Key="IBrushSparkle5" Color="#7000ff00" />
<SolidColorBrush x:Key="IBrushSparkle6" Color="#700000ff" />
</UserControl.Resources>
<Canvas Height="500" Width="500">
<Rectangle Height="500" Width="500" Fill="#FF000000" Stroke="#FF000000"/>
<Grid Height="500" Width="500">
<StackPanel Margin="3,3,0,0">
<TextBlock x:Name="GreenFade"
Canvas.Left="5" Canvas.Top="200"
FontFamily="Verdana" FontSize="70" FontWeight="Bold"
Text="Green Fade" Foreground="{StaticResource GreenBrush}"/>
<TextBlock x:Name="RandomPixels"
Canvas.Left="5" Canvas.Top="200"
FontFamily="Verdana" FontSize="50" FontWeight="Bold"
Text="Random Pixels" Foreground="{StaticResource IBrushRandom}"/>
<TextBlock x:Name="SparkleText1"
Canvas.Left="5" Canvas.Top="200"
FontFamily="Verdana" FontSize="20" FontWeight="Bold"
Text="Sparkle" Foreground="{StaticResource IBrushSparkle1}"/>
<TextBlock x:Name="SparkleText2"
Canvas.Left="5" Canvas.Top="200"
FontFamily="Verdana" FontSize="40" FontWeight="Bold"
Text="Sparkle" Foreground="{StaticResource IBrushSparkle2}"/>
<TextBlock x:Name="SparkleText3"
Canvas.Left="5" Canvas.Top="200"
FontFamily="Verdana" FontSize="80" FontWeight="Bold"
Text="Sparkle" Foreground="{StaticResource IBrushSparkle3}"/>
</StackPanel>
<Canvas Height="174" VerticalAlignment="Bottom">
<Rectangle Height="174" Width="500" Stroke="#FF000000">
<Rectangle.Fill>
<LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
<GradientStop Color="#FF000000"/>
<GradientStop Color="#FFFFFFFF" Offset="1"/>
</LinearGradientBrush>
</Rectangle.Fill>
</Rectangle>
<Canvas Canvas.Top="35" Canvas.Left="145" Height="154" Width="190">
<Ellipse Name="BlueCircle" Canvas.Top="45" Canvas.Left="80" Height="90" Width="90" Fill="{StaticResource IBrushSparkle6}"/>
<Ellipse Name="GreenCircle" Canvas.Top="45" Canvas.Left="20" Height="90" Width="90" Fill="{StaticResource IBrushSparkle5}"/>
<Ellipse Name="RedCircle" Canvas.Left="50" Height="90" Width="90" Fill="{StaticResource IBrushSparkle4}"/>
</Canvas>
</Canvas>
</Grid>
</Canvas>
</UserControl>
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Windows.Browser;
using Silverlight.Samples;
namespace SilverlightTestApp
{
public partial class Page : UserControl
{
private EditableImage image;
public Page()
{
InitializeComponent();
}
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
DispatcherTimer dt = new DispatcherTimer();
SparkleFilter sf1 = new SparkleFilter(SparkleText1, "Foreground", null);
SparkleFilter sf2 = new SparkleFilter(SparkleText2, "Foreground", null);
SparkleFilter sf3 = new SparkleFilter(SparkleText3, "Foreground", null);
SparkleFilter sf4 = new SparkleFilter(RedCircle, "Fill", null);
SparkleFilter sf5 = new SparkleFilter(GreenCircle, "Fill", null);
SparkleFilter sf6 = new SparkleFilter(BlueCircle, "Fill", null);
dt.Interval = new TimeSpan(0, 0, 0, 0, 100);
dt.Tick += new EventHandler(dt_Tick1);
dt.Tick += new EventHandler(dt_Tick2);
dt.Tick += new EventHandler(sf1.TimerTick);
dt.Tick += new EventHandler(sf2.TimerTick);
dt.Tick += new EventHandler(sf3.TimerTick);
dt.Tick += new EventHandler(sf4.TimerTick);
dt.Tick += new EventHandler(sf5.TimerTick);
dt.Tick += new EventHandler(sf6.TimerTick);
sf1.Timer = dt;
sf2.Timer = dt;
sf3.Timer = dt;
sf4.Timer = dt;
sf5.Timer = dt;
sf6.Timer = dt;
sf1.StartDispatcher();
}
bool peak = true;
byte b;
void dt_Tick1(object sender, EventArgs e)
{
if (((SolidColorBrush)this.GreenFade.Foreground).Color.G == 255) peak = true;
else if (((SolidColorBrush)this.GreenFade.Foreground).Color.G == 0) peak = false;
b = ((SolidColorBrush)this.GreenFade.Foreground).Color.G;
if (peak)
((SolidColorBrush)this.GreenFade.Foreground).Color = Color.FromArgb(255, 0, --b, 0);
else ((SolidColorBrush)this.GreenFade.Foreground).Color = Color.FromArgb(255, 0, ++b, 0); ;
}
void dt_Tick2(object sender, EventArgs e)
{
BitmapImage bm = new BitmapImage();
Random rand = new Random();
int height = 128;
int width = 128;
bool first = true;
if (null == image)
{
image = new EditableImage(height, width);
}
for (int idx = 0; idx < height; idx++)
{
for (int jdx = 0; jdx < width; jdx++)
{
image.SetPixel(jdx, idx, (byte)rand.Next(255), (byte)rand.Next(255), (byte)rand.Next(255), 255);
}
}
bm.SetSource(image.GetStream);
RandomPixels.Foreground = new ImageBrush();
((ImageBrush)RandomPixels.Foreground).Stretch = System.Windows.Media.Stretch.UniformToFill;
((ImageBrush)RandomPixels.Foreground).ImageSource = bm;
if (first)
{
first = false;
}
}
private void ImageBrush_ImageFailed(object sender, ExceptionRoutedEventArgs e)
{
HtmlPage.Window.Alert(e.ErrorException.Message);
}
}
}
using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Windows.Threading;
using System.Windows.Browser;
using Silverlight.Samples;
namespace SilverlightTestApp
{
public class SparkleFilter
{
private EditableImage _ei;
private EditableImage _eiCache;
private EditableImage _backBuffer;
private BitmapImage _bm;
private Dictionary ColorMap = new Dictionary();
public SparkleFilter(FrameworkElement ReferenceElement, String ReferenceProperty, EditableImage ImageIn)
{
try
{
_backBuffer = new EditableImage(5, 5);
this.Stretch = Stretch.None;
this.Brush = new ImageBrush();
ReferenceColor = ((SolidColorBrush)ReferenceElement.GetType().GetProperty(ReferenceProperty).GetValue(ReferenceElement,null)).Color;
ReferenceElement.GetType().GetProperty(ReferenceProperty).SetValue(ReferenceElement,this.Brush, null);
}
catch (Exception Ex)
{
throw new ArgumentException("Reference object's refrence property not supported.", Ex);
}
try
{
this.ReferenceImageSize = new Rect(0, 0,((double)ReferenceElement.GetType().GetProperty("ActualWidth").GetValue(ReferenceElement, null)), ((double)ReferenceElement.GetType().GetProperty("ActualHeight").GetValue(ReferenceElement, null)));
}
catch (Exception Ex)
{
throw new ArgumentException("Reference object has no Geometry!", Ex);
}
if (ImageIn != null)
_ei = ImageIn;
this.SampleReference = true;
this.ReferenceElement = ReferenceElement;
this.ImageIn = ImageIn;
Sparkle();
}
public DispatcherTimer Timer {get; set;}
public FrameworkElement ReferenceElement { get; set; }
public EditableImage ImageIn
{
get { return _ei; }
set { _ei = value; Sparkle(); }
}
public EditableImage ImageOut
{
get { return _ei; }
}
public BitmapImage ImageSource
{
get { return _bm; }
}
public Color ReferenceColor { get; set; }
public Rect ReferenceImageSize { get; set; }
public bool SparkleDeltaBasedOnImageSize { get; set; }
public int SparkleWeight { get; set; }
private int[][] Sparklebuffer = new int[5][];
public int[][] SparkleBuffer { get { return Sparklebuffer; } set { Sparklebuffer = value; } }
private byte[][] alphabuffer = new byte[5][];
public byte[][] AlphaBuffer { get { return alphabuffer; } set { alphabuffer = value; } }
public bool SampleReference { get; set; }
public bool IsRendering { get; set; }
public void StartDispatcher() { Timer.Start(); }
public void StopDispatcher() { Timer.Stop(); }
public TimeSpan TimeIntrival { get; set; }
public ImageBrush Brush { get; set; }
public Stretch Stretch { get; set; }
public void Sparkle()
{
if (this.Timer == null)
{
this.Timer = new DispatcherTimer();
this.Timer.Interval = new TimeSpan(0, 0, 0, 0, 100);
}
if (this.TimeIntrival == null)
this.TimeIntrival = Timer.Interval;
if (SparkleWeight == 0)
SparkleWeight = 10;
if (Sparklebuffer[0] == null)
{
Sparklebuffer = new int[5][];
Sparklebuffer[0] = new int[] { 25, 50, 85, 50, 25 };
Sparklebuffer[1] = new int[] { 50, 85, 125, 85, 50 };
Sparklebuffer[2] = new int[] { 85, 125, 150, 125, 85 };
Sparklebuffer[3] = new int[] { 50, 85, 125, 85, 50 };
Sparklebuffer[4] = new int[] { 25, 50, 85, 50, 25 };
}
if (alphabuffer[0] == null)
{
alphabuffer = new byte[5][];
alphabuffer[0] = new byte[] { 20, 100, 130, 100, 20 };
alphabuffer[1] = new byte[] { 100, 130, 200, 130, 100 };
alphabuffer[2] = new byte[] { 130, 200, 250, 200, 130 };
alphabuffer[3] = new byte[] { 100, 130, 200, 130, 100 };
alphabuffer[4] = new byte[] { 20, 100, 130, 100, 20 };
}
Timer.Tick += new EventHandler(TimerTick);
}
private static Color LuminanceTransform(Color SampleColor, int Weight, byte Alpha)
{
byte Y, U, V;
Y = (byte)(((66 * SampleColor.R + 129 * SampleColor.G + 25 * SampleColor.B + 128) >> 8) + 16);
U = (byte)(((-38 * SampleColor.R - 74 * SampleColor.G + 112 * SampleColor.B + 128) >> 8) + 128);
V = (byte)(((112 * SampleColor.R - 94 * SampleColor.G - 18 * SampleColor.B + 128) >> 8) + 128);
Y = (byte)Math.Min((Y+Weight), 255);
int C = Y - 16;
int D = U - 128;
int E = V - 128;
SampleColor.R = (byte)Math.Max(Math.Min(((298 * C + 409 * E + 128) >> 8), 255), 0);
SampleColor.G = (byte)Math.Max(Math.Min(((298 * C - 100 * D - 208 * E + 128) >> 8), 255), 0);
SampleColor.B = (byte)Math.Max(Math.Min(((298 * C + 516 * D + 128) >> 8), 255), 0);
SampleColor.A = Alpha;
return SampleColor;
}
internal void TimerTick(object sender, EventArgs e)
{
this.IsRendering = true;
this.Timer.Stop();
_bm = new BitmapImage();
Random rand = new Random();
int height = 128;
int width = 128;
if (_ei == null)
{
width = Math.Min(128,Convert.ToInt32(ReferenceImageSize.Width));
height = Math.Min(128,Convert.ToInt32(ReferenceImageSize.Height));
if (width == 128 || height == 128)
Stretch = Stretch.UniformToFill;
_ei = new EditableImage(width, height);
for (int idx = 0; idx < height; idx++)
{
for (int jdx = 0; jdx < width; jdx++)
{
_ei.SetPixel(jdx, idx, ReferenceColor.R, ReferenceColor.G, ReferenceColor.B, ReferenceColor.A);
}
}
_eiCache = new EditableImage(_ei.Width,_ei.Height);
_eiCache.SetPixels(_ei);
}
int density = (Convert.ToInt32(ReferenceImageSize.Width) + Convert.ToInt32(ReferenceImageSize.Height))/2;
Color SampleColor;
int sampleY;
int sampleX;
_ei.SetPixels(_eiCache);
for (int sp = 0; sp < density; sp++)
{
sampleY = rand.Next(_ei.Height - 5);
sampleX = rand.Next(_ei.Width - 5);
if (SampleReference)
SampleColor = ReferenceColor;
else
SampleColor = _ei.GetPixel(sampleY,sampleX);
if (!ColorMap.ContainsKey(SampleColor))
{
for (int y = 0; y < 5; y++)
{
for (int x = 0; x < 5; x++)
{
Color c = LuminanceTransform(SampleColor, Sparklebuffer[y][x], alphabuffer[y][x]);
_backBuffer.SetPixel(x, y, c);
}
}
ColorMap.Add(SampleColor, _backBuffer);
}
else
{
_backBuffer = ColorMap[SampleColor];
}
for (int idx = sampleY, y1 = 0; idx < (_backBuffer.Height + sampleY); idx++, y1++)
{
for (int jdx = sampleX, x1 = 0; jdx < (_backBuffer.Width + sampleX); jdx++, x1++)
{
_ei.BlendAlphaPixel(jdx, idx, _backBuffer.GetPixel(y1, x1));
}
}
}
_bm.SetSource(_ei.GetStream);
((ImageBrush)this.Brush).Stretch = this.Stretch;
((ImageBrush)this.Brush).ImageSource = _bm;
this.Timer.Start();
this.IsRendering = false;
}
}
}
That's it!
It is important to use caution when using Reflection's in C#. Many errors can easily be made and security precautions must be taken to assure that malicious code can not inject method calls. This is best accomplished by limiting the accessibility of the methods containing reflection code with internal, private, or protected access.
Silverlight does not allow sampling of rendered content. Additionally all elements are re-blended when new content is added, this is avoided in this example using the Joe Stegman's EditableImage class.
- Graphic Designer (2.38)
- Developer (5)
- Architect (2.86)