/Visual Studio 2008/CSSL3WriteableBitmap/ReadMe.html
# · HTML · 353 lines · 348 code · 5 blank · 0 comment · 0 complexity · b57b8dc8a41e56145cf435c2851dda8c MD5 · raw file
- <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
- <html xmlns="http://www.w3.org/1999/xhtml">
-
- <head>
- <meta content="zh-cn" http-equiv="Content-Language" />
- <meta content="text/html; charset=utf-8" http-equiv="Content-Type" />
- <title>Working with Silverlight Writeab</title>
- <style type="text/css">
- h1 {
- color: #FF0000;
- }
- h2 {
- color: #3399FF;
- }
- </style>
- </head>
-
- <body>
-
- <h1>Working with Silverlight WriteableBitmap</h1>
- <h2>Introduction</h2>
- <p>WriteableBitmap is a new feature in Silverlight 3. It allows you to work with
- raw pixel data of a bitmap image. This enables a lot of useful scenarios.
- However, due to the limitation in the security sandbox (for example, unable to
- write unsafe code that uses pointers), you may need to write more code compared
- to a similar desktop application.</p>
- <p>This sample contains 3 sub samples which demonstrate some common tasks when
- working with WriteableBitmap:</p>
- <ul>
- <li>The gun shoot sample. It allows you to select two images of different
- size, scales them to the same resolution, and puts one on top of the other.
- When you click the top image to fire some bullets, a gun mark will be
- created, and you'll be able to see through the bottom image. This sample
- demonstrates how to scale a source image to a specific resolution (800 * 600
- in this case), as well as how to modify individual pixels.</li>
- <li>The fill color sample. It implements a common feature in painting
- programmers: Fill an area with a solid color. It demonstrates how to apply
- common algorithms to Silverlight WriteableBitmap, as well as how to avoid
- stack overflow in very deep recursive methods by simulating the way CLR
- invokes methods.</li>
- <li>The screenshot sample. It demonstrates another feature of
- WriteableBitmap: Create bitmap screenshots from either bitmap or non-bitmap
- elements. It allows you to save the result to a bmp file. It also
- demonstrates the limitation of the screenshot feature.</li>
- </ul>
- <p>To work with WriteableBitmap in any advanced scenarios, solid foundation of
- computer graphics is required. You should at least understand how bitmap is
- stored in memory, the essential concepts related to pixels and colors, and how
- to work with bitwise shift operations to improve performance.</p>
- <h2>Important: When to use WriteableBitmap, and when not</h2>
- <p>Traditionally, it may be attempted to use some CPU based solutions similar to
- WriteableBitmap whenever you need to modify a pixel on the screen. However,
- these days we have much better GPU based solutions. For example: The mighty
- shader effect. Keep in mind that when using WriteableBitmap, all jobs are
- performed on the CPU, and if you need to do the job on each pixel, you will have
- to do them pixel by pixel. Even if you create multiple threads, you can process
- at least 4 pixels at one time on a quad-core CPU. GPU, on the other hand, can
- process all pixels in parallel, no matter how many pixels the scenario contains.
- Also, in most scenarios, it is much easier to write HLSL compared to a CPU based
- algorithm, because you can think with a single pixel rather than find where the
- pixel is, get its color in an integer value, extract the ARGB information using
- bitwise shift operations, and so on. So it is recommended to use shader effect
- whenever possible.</p>
- <p>However, shader effects do have limitations:</p>
- <ul>
- <li>Silverlight 3 supports pixel shader only. No vertex shader and geometry
- shader. Without geometry shader, you cannot create GPU primitives
- dynamically in your HLSL program.</li>
- <li>Silverlight 3 supports shader 2.0 only (while Direct 3D 11 supports
- shader 5.0). There're a lot of limitations in shader 2.0 due to the
- limitation of GPU at that time. For example, up to 64 arithmetic instructions may be given in a HLSL program, limited numbers of GPU
- registries can be used, and so on. Keep in mind GPU does not support loop.
- So when compiling HLSL, all loops will be expanded. That will introduce more
- arithmetic instructions than you may expect. Silverlight needs to support
- low end GPUs (in contrast to Direct 3D). That's why shader 2.0 is chosen.</li>
- <li>Some features are just difficult to be written in shader effect. For
- example, one pixel's processing relies on another pixel's result. Shader
- effects are usually used to perform the same operation to all pixels. Of
- course, you can pass different parameters to achieve different results.
- Think of a swirl effect. All pixels are performed using the same algorithm,
- just the angle parameter is different. So shader effect can be used.
- However, if you want to fill an area with a solid color, or maybe even
- gradient, it can be very difficult to do with shader effect.</li>
- </ul>
- <p>So use WriteableBitmap when:</p>
- <ul>
- <li>Your algorithm is too complex for a shader 2.0 program.</li>
- <li>You have to support extremely low end computers where even shader 2.0 is
- not supported (in this case, CPU's SSE instructions set will be used if you
- choose shader effect, whose performance is not ideal).</li>
- <li>In your algorithm, one pixel's processing relies on another pixel's
- result.</li>
- </ul>
- <h2>Difference between Silverlight and WPF's WriteableBitmap</h2>
- <p>While this sample targets Silverlight, you may want to port it to WPF. You
- must be aware of some key differences between Silverlight and WPF's
- WriteableBitmap:</p>
- <ul>
- <li>Silverlight doesn't allow you to write unsafe code that uses pointers,
- while you will have to write unsafe code when working with WPF
- WriteableBitmap.</li>
- <li>Silverlight exposes a Pixels property whose type is int[], while WPF
- exposes a BackBuffer property which is a pointer (convert it to a byte*) to
- the pixels' data.</li>
- <li>In Silverlight, since the pixel's color is of type int, you will have to
- use bitwise shift operations to extract ARGB information (you can use other
- solutions, but they're slow). In WPF, you can control the pointer to point
- to whatever position you like. For example, point to A, and then to R with
- pointer++.</li>
- <li>Silverlight's WriteableBitmap supports Pbgra32 format only, so the back
- buffer stride is always pixel width * 4. WPF supports a lot of formats. So
- you need to get the back buffer stride from the BackBufferStride property.</li>
- <li>In WPF, you need to wrap the write operation in a Lock and Unlock pair,
- while in Silverlight this is not required.</li>
- <li>WPF allows you to use PLINQ to work with multi-core CPUs without
- worrying about threads, while in Silverlight you have to implement your own
- if you want to take advantage of multi-core CPUs.</li>
- </ul>
- <p>So, if you want to convert the following Silverlight code (extracted from the
- fill color sample) to WPF,</p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:green">//Start from the clicked point.<o:p></o:p></span></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:#2B91AF">Point</span> clickedPoint =
- e.GetPosition(img);<o:p></o:p></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:green">//The position of the clicked point in the
- pixel array.<o:p></o:p></span></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:blue">int</span> clickedColorPosition = bmp.PixelWidth
- * (<span style="color:blue">int</span>)clickedPoint.Y + (<span style="color:blue">int</span>)clickedPoint.X;<o:p></o:p></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:green">//The color of the clicked point.<o:p></o:p></span></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:blue">int</span> clickedColor =
- bmp.Pixels[clickedColorPosition];<o:p></o:p></span></p>
- <p>you will need to write:</p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span>bmp.Lock();<o:p></o:p></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:green">//</span></span><span style="color:green"><span style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">The start position in the back buffer.</span></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:blue">byte</span>* start = (<span style="color:blue">byte</span>*)bmp.BackBuffer.ToPointer();<o:p></o:p></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:green">//<span lang="EN-US" style="font-size: 9.0pt; font-family: 新宋体; mso-hansi-font-family: "Times New Roman"; mso-bidi-font-family: "Times New Roman"; mso-font-kerning: 0pt; mso-no-proof: yes"><span style="color: green">Start
- from the clicked point.<o:p></o:p></span></span></span></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:#2B91AF">Point</span> c</span><span style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">licked</span><span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">Point = e.GetPosition(img);<o:p></o:p></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:green">//</span></span><span style="color:green"><span style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">Get how many bytes a pixel occupies.</span></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:blue">int</span> bytesPerPixel = bmp.BackBufferStride
- / bmp.PixelWidth;<o:p></o:p></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:green">//</span></span><span lang="EN-US" style="font-size: 9.0pt; font-family: 新宋体; mso-hansi-font-family: "Times New Roman"; mso-bidi-font-family: "Times New Roman"; mso-font-kerning: 0pt; mso-no-proof: yes"><span style="color: green">The
- position of the clicked point in the pixel array.<o:p></o:p></span></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:blue">byte</span>* c</span><span style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">licked</span><span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">Color</span><span style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">Position</span><span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"> = start + (<span style="color:blue">int</span>)c</span><span style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">licked</span><span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">Point.X
- * bytesPerPixel + bmp.BackBufferStride * (<span style="color:blue">int</span>)c</span><span style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">licked</span><span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">Point.Y;<o:p></o:p></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:green">//<span lang="EN-US" style="font-size: 9.0pt; font-family: 新宋体; mso-hansi-font-family: "Times New Roman"; mso-bidi-font-family: "Times New Roman"; mso-font-kerning: 0pt; mso-no-proof: yes"><span style="color: green">The
- color of the clicked point.<o:p></o:p></span></span></span></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:blue">byte</span>[] c</span><span style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">licked</span><span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">Color =
- <span style="color:blue">new</span> <span style="color:blue">byte</span>[bytesPerPixel];<o:p></o:p></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span><span style="color:blue">for</span> (<span style="color:blue">int</span>
- i = 0; i < bytesPerPixel; i++)<o:p></o:p></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span>{<o:p></o:p></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:4">
- </span>c</span><span style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">licked</span><span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">Color[i] = c</span><span style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">licked</span><span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">Color</span><span style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">Position</span><span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">[i];<o:p></o:p></span></p>
- <p align="left" class="MsoNormal">
- <span lang="EN-US" style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes"><span style="mso-tab-count:3">
- </span>}</span><o:p></o:p><br/><o:p>//Other operations.</o:p>
- <span style="font-size:9.0pt;font-family:
- 新宋体;mso-hansi-font-family:"Times New Roman";mso-bidi-font-family:"Times New Roman";
- mso-font-kerning:0pt;mso-no-proof:yes">
- <br/><o:p>bmp.Unlock();</o:p></span>
- <p> </p>
- <p>In the future, we may also write WPF WriteableBitmap samples.</p>
- <h2>Walkthrough of each sample</h2>
- <h3>Gun shoot</h3>
- <p>When you select 2 bitmap images, 2 WriteableBitmaps will be created and
- stored in memory. Since the images' size may be different, we need to convert
- them to the same resolution. This is done by calculate a ratio and then
- multiplies each pixel's position with the ratio. For example, if we need to
- convert a 1600*1200 image to 800*600, we simply multiplies each pixel's position
- with 0.5</p>
- <p>After the WriteableBitmaps are created, we set the source of the Image
- Control to the top one, and the source images are no longer needed.</p>
- <p>When you click on the image, the program calculates a circle whose radius is
- 50. It also calculates the rectangle bound of the circle so we don't need to
- walkthrough all pixels. Then for each pixel in the bound, if it is in the
- radius, we paint the pixel with the color in the bottom image at the same
- position. So you will be able to see through the bottom image at this pixel. We
- also draw a gun mark at the edge of the circle. This demonstrates how to extract
- the ARGB values, and how to modify them.</p>
- <p>Finally, do not forget you must once again set the Image Control's Source
- property to the WriteableBitmap, or it won't be updated.</p>
- <h3>Fill color</h3>
- <p>This samples implements a very simple version of edge detection algorithm:
- The growth method. It starts from the clicked point. If the clicked point's
- color is the same as the filling color, then terminates the algorithm. Otherwise
- extract the left, right, top, and bottom pixels (grow to the 4 pixels). If the
- color is different from the clicked pixel, then we need to fill this pixel with
- the filling color. And then we grow the graph to the left, right, top, and
- bottom pixels of the new pixel.</p>
- <p>If you have a very large area to be filled, you may soon get into stack
- overflow due to a very deep recursive. To avoid this, we can try to put the
- recursive body inside a normal loop, and manually simulate the way CLR invokes a
- method. To do so, first we need a stack. The number of elements inside the stack
- represents the number of the method needs to be invoked. The data inside the
- stack is the method's parameter. Now whenever we need to invoke the recursive
- method, we instead populates a parameter data, and push it to the stack. When
- the recursive body is executed, we pop the stack to get the parameter of this
- recursion. Once there's no data in the stack, it means the recursive method has
- finally completed, so we can exit the loop.</p>
- <p>If you want to provide your own image for this sample, please choose images
- with large areas that can be filled. Also be aware of anti-aliased images. The
- sample's implementation of the algorithm is simple, and do not take
- anti-aliasing into account. For example, it may be difficult to see the
- difference between 0xfffffffe and 0xffffffff, but the algorithm thinks the two
- pixels have different color.</p>
- <h3>Screenshot</h3>
- <p>In this sample, you will see two rectangles. The above one is a vector
- graphic that is wrapped in a Canvas, and the below one is a bitmap that is
- captured from the above rectangle from time to time. It is very easy to use the
- screenshot feature of WriteableBitmap. Just provide a UIElement and its
- transform to the constructor. However, as demonstrated in this sample, there're
- some limitations.</p>
- <p>Supported features:</p>
- <ul>
- <li>The current value in the animation.</li>
- <li>RenderTransform.</li>
- <li>Shader Effects.</li>
- </ul>
- <p>Unsupported features:</p>
- <ul>
- <li>Projection.</li>
- <li>Non-UIElement, such as html elements.</li>
- </ul>
- <p>So be aware if you want to use WriteableBitmap to capture screenshots because
- projection will be lost.</p>
- <p>This sample also implements a bmp encoder to allow you to save the result to
- a bmp file. Silverlight doesn't ship the various bitmap encoders out of box. So
- you will have to implement your own if you need.</p>
-
- </body>
-
- </html>