Simple Functions for HTML Interop

Apr 3, 2010

I used the below functions on several occasions to transfer data between a server and Silverilght application.

The blog integration between .NET Blog Engine and my Silverlight navigation on top of this site is done using this method.

You can see the blog title “You Had Me At Hello World” in the Silverlighg navigation window on top.

The interop is easy and I tried it in IE, Firefox, Chrome, Opera. Define an HTML like this:


<span style="visibility: hidden; display: none; height: 0px; width: 0px; border: 0px">
    <span id="titleText">You Had Me At 'Hello World!'</span>
    <span id="titleLink">http://nokola.com/blog/</span>
    <span id="titleDescription">Coding and Special Effects for Silverlight</span>
</span>

And then use the functions from within your Silverlight app like this:

HtmlDocument doc = HtmlPage.Document;
string str = doc.GetTextFromHtmlPage("titleText", "nokola.com");
string uriString = doc.GetTextFromHtmlPage("titleLink", "">http://nokola.com");
string str3 = doc.GetTextFromHtmlPage("titleDescription", null);

Nothing fancy, but works :)

Here’s the source:

using System.Windows.Browser;

namespace EasyPainter
{
    /// <summary>
    /// Common functions for HTML interop
    /// </summary>

    public static class HtmlDocumentExtensions
    {
        /// <summary>
        /// Retrieves the text contents of a given HTML element by ID
        /// Sample html:
        ///    <span style="visibility: hidden; display: none; height: 0px; width: 0px; border: 0px">
        ///        <span id="titleText">You Had Me At 'Hello World!'</span>
        ///    </span>
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="elementId"></param>
        /// <returns></returns>

        public static string GetTextFromHtmlPage(this HtmlDocument doc, string elementId)
        {
            HtmlElement elementById = doc.GetElementById(elementId);
            if (elementById != null)
            {
                object property = elementById.GetProperty("innerText");
                if (property != null)
                {
                    return property.ToString();
                }
                property = elementById.GetProperty("textContent");
                if (property != null)
                {
                    return property.ToString();
                }
            }
            return null;
        }

        /// <summary>
        /// Retrieves the text contenst of HTML element by ID with a default value
        /// in case the element is missing or empty
        /// </summary>
        /// <param name="doc"></param>
        /// <param name="elementId"></param>
        /// <param name="defaultValue"></param>
        /// <returns></returns>

        public static string GetTextFromHtmlPage(this HtmlDocument doc, string elementId, string defaultValue)
        {
            string textFromHtmlPage = doc.GetTextFromHtmlPage(elementId);
            if (string.IsNullOrEmpty(textFromHtmlPage))
            {
                return defaultValue;
            }
            return textFromHtmlPage;
        }
    }

}

EasyPainter: Cloud Turbulence and Particle Buzz

Apr 2, 2010

Two new effects for EasyPainter with source code!

Cloud Turbulence: works great on clouds, but not much anything else I tried (OK on metal – makes it look like liquid).

Before:

CloudsBefore

After:

CloudsAfter

Particle Buzz

This effect has a lot of potential, since it’s the first displacement effect in EasyPainter based on image. Currently, it just uses random noise (Random.Next(256) for the R,G,B and 255 for the A channels).

buzz_before

buzz_after

Using other images (such as pencil strokes, grass, etc) for displacement yields very nice results! I’m still experimenting :)

Source Code

Particle Buzz. The noise is a 256x256 texture initialized to random values and remember to set this.DdxUvDdyUvRegisterIndex = 1; when initializing the shader in code behind.

sampler2D Noise : register(S1);

/// <summary>Width of the input.</summary>
/// <minValue>2</minValue>
/// <maxValue>100</maxValue>
/// <defaultValue>30</defaultValue>
float Shake : register(C0);

/// <summary>Input size</summary>
/// <type>Size</type>
/// <minValue>10,10</minValue>
/// <maxValue>100,100</maxValue>
/// <defaultValue>#01000000</defaultValue>
float4 dxdyShift : register(c1);

/// <summary>Input size</summary>
/// <type>Size</type>
/// <defaultValue>1024,768</defaultValue>
float2 ImageSize : register(C2);

//--------------------------------------------------------------------------------------
// Sampler Inputs (Brushes, including ImplicitInput)
//--------------------------------------------------------------------------------------

sampler2D Input : register(S0);

float4 main(float2 uv : TEXCOORD) : COLOR
{
    float2 dx = dxdyShift.xy;
    float2 dy = dxdyShift.zw;
    float4 noiseValue = tex2D(Noise, frac(2*uv*(ImageSize/256.0)));
    noiseValue -= 0.5;
    noiseValue *= Shake;
    return tex2D(Input, saturate(uv+(dx+dy)*noiseValue.rb));
}

Cloud Turbulence

Needs this.DdxUvDdyUvRegisterIndex = 1; too.

/// <summary>Width of the input.</summary>
/// <minValue>2</minValue>
/// <maxValue>100</maxValue>
/// <defaultValue>30</defaultValue>
float Shake : register(C0);

/// <summary>Input size</summary>
/// <type>Size</type>
/// <minValue>10,10</minValue>
/// <maxValue>100,100</maxValue>
/// <defaultValue>#01000000</defaultValue>
float4 dxdyShift : register(c1);

sampler2D Input : register(S0);

float4 main(float2 uv : TEXCOORD) : COLOR
{
    //pixel step vectors
    float2 dx = dxdyShift.xy;
    float2 dy = dxdyShift.zw;
    float4 noiseValue = tex2D(Input, uv);
    noiseValue.rg = float2((noiseValue.r + noiseValue.b/4), noiseValue.g + noiseValue.r/2);
    noiseValue -= 0.5;
    noiseValue *= Shake;
    return tex2D(Input, saturate(uv+(dx+dy)*noiseValue.rg));
}

Hope you like it! :)

EasyPainter Source Pack 3: Adorners, Mouse Cursors and Frames

Mar 17, 2010

Adorners went a long way since their first release few months ago.

They integrate with zoom now, have more options for controlling functionality and can use a shared "cursor plane" for displaying mouse cursors consistently across your application. Extensibility is improved allowing definition of custom adorner frames. There are 7 custom cursors and 4 custom frames to get you started :)

Download source code

I use the same adorner implementation in EasyPainter.

Here's a live demo with different adorner frame for each image:

Features

  • Custom, Extensible Mouse Cursors allow you to use the system or your own mouse cursors even if you don't use the adorner functionality. See CursorPlane.cs for more info

The custom cursors support:

    • Size (for example, bigger cursor for bigger brush size)
    • Rotation (used to make the resize cursor perpendicular to the adorner frame)

The current list supports all system cursors as well as these:

Size, // Size cursor that supports angle
Rotate, // Rotate cursor supporting angle
Move, // Move cursor
Brush, // Brush cursor, supports size
SelectionBrush, // Selection brush cursor
Crosshair, // Crosshair cursor - like the one used on snipers or in image editing programs to pinpoint that pixel
MarkerSelect, // Cursor used to select a marker on the surface (e.g. used to select point for applying origin for zoom blur effect)

To use the cursors you have to set up a cursor plane in XAML:

<adorners:CursorPlane Name='cursorPlane' />

And then initialize it in code-behind:

    InitializeComponent();
    ctlCursorPlane.SetSystemCursorElement(frameRoot);

The frameRoot specifies which control will display the cursors. For example, in EasyPainter the Crop adorner spans only the image (the adorner is parented to a different parent than the cursor plane), but the rotate/resize cursor go outside of the image. The frameRoot is used to calculate the offsets so that the adorners (or someone else using the cursor plane) can account for the different parents and draw the cursors at the correct locations on screen.

  • Customizable Adorners with extensible adorner frames

You can now define your custom adorner frames by creating a UserControl and implementing the functionality you need from the IAdornerFrame interface.

There are 4 existing frames currently: AnimatedRectangleFrame, CropFrame, GlassFrame and PointFrame. The last one is just a dot being used to specify locations of effects in EasyPainter – e.g. Zoom motion effect.

The existing frames can be zoom-aware. For example the CropFrame displays the correct image width and height in the top-left corner regardless of zoom.

/// Represents an adorner frame, e.g. glass border
public interface IAdornerFrame {
    /// Called when the frame is attached to adorner
    void Attached();

    /// Called when the frame is detached from adorner
    void Detached();

    /// Sets the zoom level for this adorner frame. The zoom level is used by the frame to display correct coordinates (e.g. Width and Height)
    void SetZoom(double newZoomLevel);

    void SizeChanged(double newWidth, double newHeight);
}

  • More adorner options: bool CanRotate, CanResize, CanMove, void SetBounds(x, y, width, height), SetZoom(double zoomLevel) allow you to change adorner capabilities, restrict it to specific area, and make it zoom-aware

I didn't have time to add the behaviours back since I don't use those yet and I'm mainly using Visual Studio for development. If anyone is interested in porting these over to the current version Andrej Tozon created the Behaviours for the original version. Here's a link to his version. Thanks Andrej!

The source code is licensed under MS-PL.

Pictures are licensed under the following Creative Commons License: http://creativecommons.org/licenses/by/2.0/
Picture locations:
http://www.flickr.com/photos/proimos/3954987905/
http://www.flickr.com/photos/proimos/4300827122/
http://www.flickr.com/photos/tenioman/2771903123/

Hope you like it! Please comment!

    

EasyPainter Source Pack 2: Flickr, ComboCheckBox and more!

Mar 11, 2010

Yes! EasyPainter Source Pack 2 is here!

This source pack contains Flickr integration sample amongst other also important things (read below).

Download Source Code

The live demo:

Usage

  1. Go to http://www.flickr.com/services/api/, sign up and get an API key for your app
  2. Put the key in Flickr\FlickrHelper.cs
  3. Use the below code behind and XAML:

public MainPage() {
    InitializeComponent();
    Globals.ToolbarRadioButtonStyle = (Style)Application.Current.Resources["ToolbarRadioButton"];
    IGenericUserInterface ui = (IGenericUserInterface)openDialog1;
    ui.Result += new GenericResultHandler(ui_Result);
}

void ui_Result(IGenericUserInterface sender, EasyPainter.Imaging.ResultType result, bool uiDismissed) {
    MessageBox.Show(String.Format("Result type: {0}\nPhoto Url: {1}", result, openDialog1.PhotoUrl));
}

XAML:

<Grid x:Name="LayoutRoot" Background="White">
    <my:OpenDialog x:Name='openDialog1' />
</Grid>

License

The source code of the sample is licensed under MS-PL

Flickr usage is governed by their own license: check the terms of use on http://www.flickr.com/services/api/ for more details.

Source Code Information

Apart from Flickr, there are some other things interesting to developers in this sample:

  • Queuing for WebClient. The WebClient class does not allow to call DownloadStringAsync() while another download using WebClient is in progress. The WebClientQueue class in the sample allows you to queue work items, cancel them, gracefully display and recover from errors and get updates on progress.
  • Caching for WebClient REST API and other requests. The current cache is set in WebClientQeueue to 5 minutes. Huge speed improvement when browsing through the images!
  • ComboCheckBox control (far from perfect, but still usable for some cases)
  • Minimalistic WrapPanel. The 10KB version of the WrapPanel extracted from the Silverlight toolkit that is typically around 280 KB.
  • Lightweight XML parsing. Using XMLReader instead of Linq like other samples do helps shave off about 100KB of size from the sample
  • Small size! Combining the 2 above items, and the XAP file is only 27K (I wish it was less but life if life :))
  • ImageList control for showing and paging through images
  • ImageSwitchHelper – a "fit-and-finish" control that helps avoid "jumping" when the image on the right changes. The control preserves the frame size until the next image is loaded.
  • An easy way to keep UI alive and preserve the dialog view (DialogStore.cs). If you open EasyPainter you'll notice that every time you click "Open from Flickr" you will go to your previous view instead of resetting the UI.
  • IUserInterface – a kind of generic interface for reusable UIs and for connecting UIs with other parts of your program. This interface is used in all dialogs in EasyPainter, for example to update the view when filter values change. It also allows to click through effects without clicking Cancel on each dialog (for example). All EasyPainter filter UI dialogs are built on-the-fly from metadata info assigned to the filter (this would come in another sample sometime).

I almost missed something important: once you have the image Url, you can display it in an Image tag. If you want to use it from within WriteableBitmap though, you'll have to round-trip it through the server that serves the .xap file (in EasyPainter's case that's www.nokola.com).

Also note that Source Pack 1 has a refresh today too containing new features and bug fixes!

I hope this sample will help you make more responsive/pleasing UIs in addition to Flickr!

Hope you like it! Please comment :)

  

EasyPainter Source Pack 1 Refresh

Mar 11, 2010

There were several major bugfixes in EasyPainter controls and few updates.

Here are the main two:

  • Changing the color the first time when using the API in the color picker now works (heh :)).
  • Editable slider has a new DisplayFormat property allowing you to override the format (e.g. DisplayFormat="X: 0.0" – see below)

The sample is also moved to Visual Studio 2010 RC.

Here is a refresh of source pack 1 with the changes:

Download Source Code

The live demo:

EasyPainter: Lion Hair styling

Mar 5, 2010

I am playing with EasyPainter recently :) There are no new additions to the software (yet).

Here's how to apply a hair style to a lion.

Original:

lions

Hair styled:

Lions_after

The original image is from: http://www.flickr.com/photos/wwarby/2404544591/ (search for "lion" in EasyPainter)

I'd say the second one looks way cooler, especially with the blue Raiden-like eyes. Raiden is the god of thunder from Mortal Kombat!

Effects used: Color shift for the eyes, followed by Brigthness. Waves for the "hair" and a finishing touch of Life-like.

  

10 New Effects in EasyPainter

Feb 26, 2010

Yes! :) After spending about 1 hour last night, here are 10 more shaders in EasyPainter.

Since I don't have time to write about each one now, here is a sample image leveraging a few:

Original:

_original

Manipulated:

_severalFilters

  • Zoom Motion in the lower left corner making the trees appear moving fast towards the viewer
  • Ripples in the sea
  • Magnify to enlarge the cloud on the right of the image
  • Pinch to change the shape of the lower coconuts
  • Tint and Color Shift to change the color of the coconuts
  • Sharpen on the houses in the backgroundto make the contrast with Zoom Motion stand out more
  • Magnifier Lens to make the trunk of the palm thinner
  • Life-Like Colors to reveal the missing colors

Shaders now have color pickers (such as Tint), and positions (such as Zoom Motion). The position adorner can be dragged on screen with the mouse.

I also made few bugfixes to the ColorPicker control and the RangeSlider, and will publish these shortly.

Most of the shaders are small modifications or a combination of http://wpffx.codeplex.com shaders and http://shazzam.codeplex.com/.

Moreover, EasyPainter is 12 KB less than its last version, almost 300 KB now. I may still be able to get it in the 250KB range..we'll see.

Hope you like it!

EasyPainter: Watercolor, Blood and Gloom Pixel Shaders

Feb 20, 2010

Click here to try the new effects in EasyPainter.

Download source code.

Original vs Zombie – can you figure out which one is which? :)

nikolami zombie

The new Blood shader works only on skin colors. To use it, you have to use the selection brush to select skin tones from the image.

Here is the Watercolor shader. It's very subtle, but I think it looks nice nevertheless. If anyone has suggestions for improvements, including a proper name for this shader, since it is really subtle - please let me know!

image watercolor

I also added a Gloom effect, the one from http://wpffx.codeplex.com

Little Tech Stuff

The "blood" is in fact the pastel texture from the Pastel effect, blended with Burn.

The watercolor is based on real tutorials on doing water color effects: it is a combination of [cheap horizontal] blur and sobel edge detection. Unfortunately, the blur has to be 2-pass to look good, and span larger area. I might improve it in the future. Anyway, the effect looks pleasing to me for now :)

  

Old Photo, Pastel, and Pencil Sketch Pixel Shaders For Silverlight

Feb 18, 2010

EasyPainter has new filters! Source code below! Try them out here

Edit: I'd actually like to improve the pastel shader further - real-life pastel is usually on white background, and the shaders are on black...

Samples

Original (http://www.flickr.com/photos/-arpi/4280428638/):

Original

Pastel Drawing:

Pastel

Old Photo:

OldPhoto1

Old Photo ("younger"): More...

  

Simplex Noise (like Perlin Noise) in Pixel Shader 2.0 for Silverlight and WPF

Feb 11, 2010

Yay! 2D and 3D noise in pixel shaders!

After tackling the Hue shader I decided to move to something even more interesting, Simplex noise.

You've probably heard of Perlin Noise, if not see Rene's post with realtime 3D noise here and also check out my Living Noise sample and Living Noise blog post .

The "Noise" can be used to make effects such as fire, water, marbles, smooth motion, hand-drawn UI, space travel effects, nebulas and a lot more!

Simplex noise has similar characteristics to Perlin noise - it's continuous, smooth and its first derivative is smooth too. The nice thing about the Simplex, is that its much easier to compute and thus can be implemented in Pixel shaders.

Another advantage of the Simplex noise over Perlin noise is that the permutation bitmaps compress very well (8KB total), making the whole XAP about 30KB total, including the sample JPEG.  In comparison Perlin noise's bitmaps compress to about 350KB on my box.

Note you can also generate the maps almost instantly on the fly, eliminating the "size argument" when comparing Perlin and Simplex.

The source code download contains both 2D and 3D Pixel shader Simplex noise implementation. The 3D implementation still lacks some features - read on below.

Here is the live sample:

Download Source Code

The sample is based on this public powerpoint presentation by Natalya Tatarchu (AMD), who is the hero of today and I believe she made a lot of people happy with her great implementation of the Simplex noise! :)

I also re-visited Ken Perlin's homepage to remind myself of the noise a couple of times.

2D noise

This was (relatively) easy, since the reference implementation readily fits into PS 2.0 shaders, with lots of instructions available to experiement.

To overlay couple of noises on top of each other, I had to use the shader several times.

The biggest issue was generating the maps - I had to make sure to set the alpha to 255, otherwise weird things may happen (due to alpha premultiplication).

3D Noise

The 3d noise was more challenging - the implementation I have does not fit in PS 2.0 (84 instructions total, and PS 2.0 is 64 max).

I had to split the function in the middle, and make 2 shaders that build on top of each other. This means that the second shader works with the output from the first. There are several challenges:

1. There is color and shader information to be passed around, which means that either I have to pass the original image every time to the shader, or I have to compress the color space

I chose to compress the color space, and make it (R, S, G+B) where S is the shader value from the previous pass and RGB are the original image components. In the source code, I skipped the G+B part altogether ending with (R, S, 0) (monochrome source image), but you may change it to fix the issue.

If you do, please let me know, since I'm not planning to do it for now!

Note: in .NET 4.0 the 3D Noise shader fits into the PS 3.0 instruction limits. I left the original function in the source for people who want to use it with WPF.

Lot of writing! Hope you enjoy it!

 

        

nokola.com | Terms | Log in

Recent

About the author

Happy & enjoying life. Software enthusiast.
The opinions I express here and on nokola.com are mine and not my employeer's (Microsoft).
This is the official blog of nokola.com. You can find Silverlight samples, coding stuff, and hopefully other interesting things here.