4

Just wanted to check one thing with you. I'm using WriteableBitmap to create an image that I'm as a live tile. Works fine but I have noticed that text is getting a shadow around the edges. Makes the text look a bit dirty or messy.

Take a look at the image below. The left part is from a live tile created using the WriteableBitmap and the right part is a Windows Phone standard tile (The Internet Explorer tile). See the difference?

http://img268.imageshack.us/img268/8749/unled2imo.png http://img268.imageshack.us/img268/8749/unled2imo.png

Is there anything I can do about this? Have you noticed this before?

EDIT: Hmm, I think I'm looking at the wrong function. I think it could be the wbmp.SaveJpeg that is causing this? I'm putting text and a background image to the grid and then saving it with wbmp.SaveJpeg. Is this the reason? Any workarounds?

string sIsoStorePath = @"\Shared\ShellContent\tile.png";
using (IsolatedStorageFile appStorage =     IsolatedStorageFile.GetUserStoreForApplication())
{
    //ensure directory exists
    String sDirectory = System.IO.Path.GetDirectoryName(sIsoStorePath);
    if (!appStorage.DirectoryExists(sDirectory))
    {
        appStorage.CreateDirectory(sDirectory);
    }

    using (IsolatedStorageFileStream stream = new IsolatedStorageFileStream(sIsoStorePath, System.IO.FileMode.Create, appStorage))
    {
        wbmp.SaveJpeg(stream, 173, 173, 0, 100);
    }
}
John
  • 681
  • 1
  • 8
  • 20
  • 1
    "SaveJpeg"? Does that save it as a jpeg? What about compression artifacts? –  Nov 11 '11 at 17:20
  • 1
    No, the strange thing is that I'm using tile.png later in my code so the file looks like it becomes a png. But you're right, this does look like compression artifacts. Can I save it as a png? – John Nov 11 '11 at 17:22
  • Dunno. Haven't started WP7 development yet... –  Nov 11 '11 at 17:23
  • I don't see jpeg artifacts. The background color is wrong though, the anti-aliasing pixels only work on a very dark background, black probably. You probably forgot to fill the bitmap before drawing the text. – Hans Passant Nov 12 '11 at 15:06
  • What I have is a grid with an icon and that icon is 173x173 with transparent background. On that grid I also write the text you see above. So the text is written on a transparent image snce I want the accent color to act as a background. Then I write everything on the grid to a jpeg. I just cant see how I could do it in a different way? – John Nov 12 '11 at 15:15

2 Answers2

1

The documentation for WritableBitmap.Pixels states that the "format used by the Silverlight WriteableBitmap is ARGB32 (premultiplied RGB)". Perhaps then the live tiles expect a non-premultiplied pixel format.

I could not find any API in Silverlight to change the format, but I think the method in this article might be what you need:

http://nokola.com/blog/post/2010/01/27/The-Most-Important-Silverlight-WriteableBitmap-Gotcha-Does-It-LoseChange-Colors.aspx

Edit:

From my testing it seems like the problem is with JPEG compression artifacts after all, since SaveJpeg saves files in JPEG format even if you name them with a .png extension.

My example code below has a commented out call to MakeNonPremultiplied(bitmap.Pixels) that shows how you would call the filter to modify pixel format to non-premultiplied if you used some library to save it to a file format that works with transparencies and expects non-premultiplied format.

using System;
using System.IO;
using System.IO.IsolatedStorage;
using System.Linq;
using System.Windows;
using System.Windows.Media.Imaging;
using Microsoft.Phone.Shell;

namespace LiveTilePlayground
{
    public partial class LiveTileGenerator
    {
        /// <summary>
        /// Renders a FrameworkElement (control) to a bitmap
        /// the size of a live tile or a custom sized square.
        /// </summary>
        /// <param name="element">The element.</param>
        /// <param name="size">
        /// The size of the bitmap (in each dimension).
        /// </param>
        /// <returns></returns>
        public static WriteableBitmap RenderBitmap(
            FrameworkElement element,
            double size = 173.0)
        {
            element.Measure(new Size(size, size));
            element.Arrange(new Rect(0, 0, size, size));
            return new WriteableBitmap(element, null);
        }

        /// <summary>
        /// Updates the primary tile with specific title and background image.
        /// </summary>
        /// <param name="title">The title.</param>
        /// <param name="backgroundImage">The background image.</param>
        public static void UpdatePrimaryTile(string title, Uri backgroundImage)
        {
            ShellTile primaryTile = ShellTile.ActiveTiles.First();
            StandardTileData newTileData = new StandardTileData
            { Title = title, BackgroundImage = backgroundImage };
            primaryTile.Update(newTileData);
        }

        /// <summary>
        /// Saves the tile bitmap with a given file name and returns the URI.
        /// </summary>
        /// <param name="bitmap">The bitmap.</param>
        /// <param name="fileName">Name of the file.</param>
        /// <returns></returns>
        public static Uri SaveTileBitmap(
            WriteableBitmap bitmap, string fileName)
        {
            //MakeNonPremultiplied(bitmap.Pixels);

            using (var store = IsolatedStorageFile.GetUserStoreForApplication())
            {
                if (!store.DirectoryExists(@"Shared\ShellContent"))
                {
                    store.CreateDirectory(@"Shared\ShellContent");
                }

                using (
                    var stream = store.OpenFile(
                        @"Shared\ShellContent\" + fileName,
                        FileMode.OpenOrCreate))
                {
                    bitmap.SaveJpeg(stream, 173, 173, 0, 100);
                }
            }

            return new Uri(
                "isostore:/Shared/ShellContent/" + fileName, UriKind.Absolute);
        }

        /// <summary>
        /// Transforms bitmap pixels to a non-alpha premultiplied format.
        /// </summary>
        /// <param name="bitmapPixels">The bitmap pixels.</param>
        public static void MakeNonPremultiplied(int[] bitmapPixels)
        {
            int count = bitmapPixels.Length;

            // Iterate through all pixels and
            // make each semi-transparent pixel non-premultiplied
            for (int i = 0; i < count; i++)
            {
                uint pixel = unchecked((uint)bitmapPixels[i]);

                // Decompose ARGB structure from the uint into separate channels

                // Shift by 3 bytes to get Alpha
                double a = pixel >> 24;

                // If alpha is 255 (solid color) or 0 (completely transparent) -
                // skip this pixel.
                if ((a == 255) || (a == 0))
                {
                    continue;
                }

                // Shift 2 bytes and filter out the Alpha byte to get Red
                double r = (pixel >> 16) & 255;

                // Shift 1 bytes and filter out Alpha and Red bytes to get Green
                double g = (pixel >> 8) & 255;

                // Filter out Alpha, Red and Green bytes to get Blue
                double b = (pixel) & 255;

                // Divide by normalized Alpha to get non-premultiplied values
                double factor = 256 / a;
                uint newR = (uint)Math.Round(r * factor);
                uint newG = (uint)Math.Round(g * factor);
                uint newB = (uint)Math.Round(b * factor);

                // Compose back to ARGB uint
                bitmapPixels[i] =
                    unchecked((int)(
                        (pixel & 0xFF000000) |
                        (newR << 16) |
                        (newG << 8) |
                        newB));
            }
        }
    }
}
Filip Skakun
  • 31,624
  • 6
  • 74
  • 100
  • Yes! He has the same problem as I have so this is a known issue/feature. But as a beginner in all this his article doesn't help me a lot. I don't see how I should change my code to solve my problem? – John Nov 12 '11 at 15:19
  • I am playing with it now, but I don't see any improvement. I'll let you know when I find it out, but it might not be today. – Filip Skakun Nov 12 '11 at 15:52
  • Could be that this is JPG after all. The fact that you specified a different extension does not help - it still is saved in JPG format, which does not support transparencies. If you want to save it with transparencies and without artifacts - you have a few open source options on codeplex. – Filip Skakun Nov 12 '11 at 22:10
  • Ok, thanks for looking into it. Would you mind posting how you call the function and what you use as parameter in the function call? – John Nov 13 '11 at 08:43
  • Sure. I added the code with the context. Note that the call is actually commented out, since it makes it look wrong. – Filip Skakun Nov 13 '11 at 20:45
  • Thank you! I tried it but got the same result as you did. Actually, woth this code it got a bit worse since some of the dark pixels around the edges got black instead och grey. I don't think we can find a solution to this but I appreciate your help. – John Nov 14 '11 at 06:35
  • 1
    You can get it to work if you grab something like this: http://writeablebitmapex.codeplex.com/ and save the image as a PNG instead. – Filip Skakun Nov 14 '11 at 08:02
  • Thanks, I'll look into it and give it a try. – John Nov 14 '11 at 09:48
  • It might very well be the Premultiplied problem that is causing this but the solution is not helping. Even when saving to PNG you get the result. – haqwin Mar 09 '12 at 07:54
  • Then who knows - maybe the WriteableBitmap has a bug and does not render using the same blend modes as the display rendering does. – Filip Skakun Mar 11 '12 at 20:44
0

I had the exact same problem, but the simple answer for me was to check the transparent PNG used. It was set to use 16 bit color depth. Changing the PNGs to to 8 bit color depth solved the problem for me.

haqwin
  • 377
  • 3
  • 12
  • Wow, cool! Thanks for sharing. So you simply saved it "Save for web" in Photoshop? Did you use the settings PNG-8? What other settings did you use? – John Mar 09 '12 at 10:04
  • I was jumping the guns. I had two problems here. I was combining a transparent png file with some text and ha big problems with the png file. However I still have the problem with the produced result getting a slight shadow effect as you show in the first images. Sigh. Continuing the quest... – haqwin Mar 19 '12 at 13:58
  • Okej, sad to hear. Please keep us update if you find anything that works. – John Mar 19 '12 at 17:36