Geeks With Blogs

News


Timmy Kokke's Blog

↑ Grab this Headline Animator

Timmy Kokke at Blogged
Timmy Kokke …just sorting my bubbles…

Introduction

In an earlier article I explained how to use .NET assemblies in php. This concept is the basis of the image generation as used in my entry for the WinPHP challenge.

Because the user is in control of selection colors, all images used throughout the weblog need to be generated. Php provides a number of graphical functions, but none is capable of creating rounded corners an gradients with ease. Everything has to be done by hand. WPF on the other hand uses xaml which supports everything you can think of in modern vector graphics design. Using a .NET assembly that takes a string of xaml and returns a PNG image would be the best in this case.

 

Rendering Xaml in .NET

The method exposed to the outside world is the GenImageFromXaml method.

public string GenImageFromXaml(string xaml)

As you can see, this method takes a string of xaml. Although the method returns an images, a conversion to a base64 string is necessary for proper handling in php.

Wpf provides the XamlReader class. This class makes it possible to convert a string of xaml to .NET objects. These objects can be used in the same way as “normal” objects. The first thing this method needs to do is calling the static Parse method on the XamlReader class. Because this method returns an object and the process later needs a FrameworkElement a cast is inevitable.

var element = (FrameworkElement)XamlReader.Parse(xaml);

Next, this element needs to be rendered to an image. Luckily  the framework provides some functionality for that. I’ll explain that a bit later. The render method, GetPngImage, returns an array of bytes which represent the png image. Convert.ToBase64String method takes that string and converts it to a base64 string. This is returned by the GenImageFromXaml method to be handled by the caller of this method, php in this case.

return Convert.ToBase64String(GetPngImage(element));

The GetPngImage method is a bit more complex.

private static byte[] GetPngImage(FrameworkElement element)

It uses the element parameter to gather information about it’s size and calculates at which size it is supposed to be rendered.

var size = new Size(element.Width, element.Height);
element.Measure(size);
element.Arrange(new Rect(size));

The image that is rendered needs a place to stay, thus a RenderTargetBitmap is needed. The constructor of this class needs a few parameters: The height and width of the image in pixels; The DotsPerInch or DPI for the X and Y axis of the image; and a PixelFormat that defines where all color information should go.

var renderTarget =
  new RenderTargetBitmap((int)element.RenderSize.Width,
                         (int)element.RenderSize.Height,
                         96, 96,
                         PixelFormats.Pbgra32);

The renderer of the image uses an VisualBrush, which is used to paint areas with visuals.

var sourceBrush = new VisualBrush(element);
var drawingVisual = new DrawingVisual();

After all declarations and pre-calculations are done, it’s finally time for the actual drawing and rendering of the image. By using a using construction the DrawingContext is disposed immediately after the closing bracket.  The DrawingContext is used to draw the sourceBrush defined earlier into the drawingVisual, giving it the maximum size of the element. The drawingVisual is than rendered into the renderTaget bitmap.

using (DrawingContext drawingContext = drawingVisual.RenderOpen())
{
    drawingContext.DrawRectangle(
        sourceBrush, null, new Rect(
                               new Point(0, 0),
                               new Point(element.RenderSize.Width,
                               element.RenderSize.Height)));
}
renderTarget.Render(drawingVisual);

To be able to use the final bitmap in php and make it appropriate for the web, I chose to use PNG as an image format. Mainly because PNG supports transparency and it doesn’t show many artifacts of compression and anti-aliasing as many other formats do. Encoding PNG isn’t hard in .NET. Simply instantiate a new PngBitmapEncoder and add the previously rendered image, renderTarget, to it.

var pngEncoder = new PngBitmapEncoder();
pngEncoder.Frames.Add(BitmapFrame.Create(renderTarget));

The last part of the render method is returning the bytes of the PNG image. The PNG is saved into a MemoryStream. This stream is than returned in the form of a Byte Array.

using (var outputStream = new MemoryStream())
{
    pngEncoder.Save(outputStream);
    return outputStream.ToArray();
}

Rendering Xaml in PHP

The rendering from php using the .NET assembly created above isn’t very complex. It starts by creating a new COM class by using the assembly. Next, it uses an inline string passed into $xaml. Everything between <<<EOT and EOT is xaml and will be rendered to an png image. This string is passed to the GenImageFromXaml method on the class using the $xaml string. Because it is base64 encoded, php needs to decode the result. After that an image is created using this string and returned to the caller.

<?php
$ImgGen = new COM("BlogSnorLib.ImgGen");
$xaml = <<<EOT
<UserControl
    xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
    xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
    x:Name="UserControl"
    Width="50" Height="50">
    <Border x:Name="LayoutRoot" BorderThickness="1,1,1,1" 
            CornerRadius="10,5,20,5" BorderBrush="#FF096900">
        <Border.Background>
            <LinearGradientBrush EndPoint="0.5,1" StartPoint="0.5,0">
                <GradientStop Color="#FF16FF00" Offset="0"/>
                <GradientStop Color="#FFFFFFFF" Offset="1"/>
            </LinearGradientBrush>
        </Border.Background>
        <Ellipse Stroke="#FF332D00" Height="25.059" Width="25.059">
            <Ellipse.Fill>
                <RadialGradientBrush>
                    <RadialGradientBrush.RelativeTransform>
                        <TransformGroup>
                            <ScaleTransform CenterX="0.5" 
                                            CenterY="0.5"
                                            ScaleX="1.341" 
                                            ScaleY="1.341"/>
                            <SkewTransform AngleX="0" 
                                           AngleY="0" 
                                           CenterX="0.5" 
                                           CenterY="0.5"/>
                            <RotateTransform Angle="0" CenterX="0.5" CenterY="0.5"/>
                            <TranslateTransform X="-0.18" Y="-0.161"/>
                        </TransformGroup>
                    </RadialGradientBrush.RelativeTransform>
                    <GradientStop Color="#FFFFDF00" Offset="0.014"/>
                    <GradientStop Color="#FF756700" Offset="1"/>
                </RadialGradientBrush>
            </Ellipse.Fill>
        </Ellipse>
    </Border>
</UserControl>
EOT;
$Img = $ImgGen->GenImageFromXaml($xaml);
$Img = base64_decode($Img);
$finalImage = imagecreatefromstring($Img);
imagesavealpha($finalImage, true);
$ImgGen = null;
header('Content-type: image/png');
imagepng($finalImage);
imagedestroy($finalImage);
?>

You can use this php code as an image in html like <img src=”ImgGen.php” />, where ImgGen.php is the file containing the code above.

The rendered image should look something like the image below. Note the rounded corners and radial gradient which aren’t provided by php natively, but can be used now using the .NET assembly mechanism.

example

Posted on Monday, June 1, 2009 5:29 PM WPF , WinPHP , PHP , C# | Back to top


Comments on this post: Image generation in PHP using WPF

No comments posted yet.
Your comment:
 (will show your gravatar)


Copyright © Timmy Kokke | Powered by: GeeksWithBlogs.net