tags:

views:

256

answers:

3

I'm loading an image to a background, and I want the silverlight canvas containing it all to resize to the image size (or maximum or minimum that I determine) of whatever the size of the image is. So I've done the following:

private int ImageMargin = 4;

    public Page()
    {
        InitializeComponent();

        BitmapImage bi = new BitmapImage(new Uri("1.jpg", UriKind.Relative));
        backgroundImage.Source = bi;
        bi.DownloadProgress += new EventHandler<DownloadProgressEventArgs>(bi_DownloadProgress);

        backgroundImage.SizeChanged += new SizeChangedEventHandler(backgroundImage_SizeChanged);

    }

    void bi_DownloadProgress(object sender, DownloadProgressEventArgs e)
    {
        if (e.Progress == 100)
        {
          LayoutRoot.Height = backgroundImage.Height + ImageMargin * 2;
          LayoutRoot.Width = backgroundImage.Width + ImageMargin * 2;
        }
    }

The image loads correctly, but no matter where I seem to put the backgroundImage.Height/Width (off .Loaded event, .SizeChanged event [1], Using a border [2], etc), it always returns 0.0 or NaN. What am I doing wrong?

[1] http://silverlight.net/forums/t/14855.aspx [2] http://silverlight.net/forums/t/90235.aspx

+1  A: 

You have to probably wait one UI loop cycle to make sure Silverlight has a chance to update layouts. Also you have to be sure that image is actually visible. I actually wrote a small routine which get the image size from image binary. Downside is you'll have to download image explicitly via WebClient but the good thing is you'll know dimensions without actually displaying anything:

namespace StatMap.Silverlight.Services.UIHelpers
{
    public static class ImageSizeExtractor
    {
        public static bool TryGetImageSize(byte[] buf, out int width, out int height)
        {
            return TryGetPngSize(buf, out width, out height) || TryGetJpegSize(buf, out width, out height);
        }

        private static bool IsPng(byte[] buf)
        {
            return (buf.Length > 8)&&(buf[0] == 137) && (buf[1] == 80) && (buf[2] == 78) && (buf[3] == 71)
                   && (buf[4]==13)&&(buf[5]== 10)&&(buf[6]== 26)&&(buf[7]== 10);
        }

        private static bool TryGetPngSize(byte[] buf, out int width, out int height)
        {
            width = 0;
            height = 0;
            if (IsPng(buf))
            {
                int index = -1;
                for (int i = 8; i < buf.Length - 12;i++)
                {
                    if ((buf[i]==0x49)&&(buf[i+1]==0x48)&&(buf[i+2]==0x44)&&(buf[i+3]==0x52))
                    {
                        index = i + 4;
                        break;
                    }
                }

                if (index<0)
                {
                    return false;
                }
                width = buf[index + 3] + buf[index + 2]*256;
                height = buf[index + 7] + buf[index + 6]*256;
                return true;
            }
            return false;
        }

        private static bool TryGetJpegSize(byte[] buf, out int width, out int height)
        {
            //static char get_jpeg_size(unsigned char* data, unsigned int data_size, unsigned short *width, unsigned short *height) {
            //Check for valid JPEG image
            int i = 0; // Keeps track of the position within the file
            width = 0;
            height = 0;
            if (buf[i] == 0xFF && buf[i + 1] == 0xD8 && buf[i + 2] == 0xFF && buf[i + 3] == 0xE0)
            {
                i += 4;
                // Check for valid JPEG header (null terminated JFIF)
                if (buf[i + 2] == 'J' && buf[i + 3] == 'F' && buf[i + 4] == 'I' && buf[i + 5] == 'F' &&
                    buf[i + 6] == 0x00)
                {
                    //Retrieve the block length of the first block since the first block will not contain the size of file
                    int blockLength = buf[i]*256 + buf[i + 1];
                    while (i < buf.Length)
                    {
                        i += blockLength; //Increase the file index to get to the next block
                        if (i >= buf.Length) return false; //Check to protect against segmentation faults
                        if (buf[i] != 0xFF) return false; //Check that we are truly at the start of another block
                        if (buf[i + 1] == 0xC0)
                        {
                            //0xFFC0 is the "Start of frame" marker which contains the file size
                            //The structure of the 0xFFC0 block is quite simple [0xFFC0][ushort length][uchar precision][ushort x][ushort y]
                            width = buf[i + 5]*256 + buf[i + 6];
                            height = buf[i + 7]*256 + buf[i + 8];
                            return true;
                        }
                        i += 2; //Skip the block marker
                        blockLength = buf[i]*256 + buf[i + 1]; //Go to the next block
                    }
                    return false; //If this point is reached then no size was found
                }
                return false;
            }
            return false;
        }
    }
}

Hope this helps.

Karol Kolenda
this is very impressive! but isn't it insane overkill for what seems like a pretty straightforward problem? I don't even really need to know the size, in truth, all i really need is to size the canvas correctly around it.
aronchick
Have you tries to use backgroundImage.ActualWidth/Height instead of Width/Height?
Karol Kolenda
yes, those didn't help I'm afraid.
aronchick
+1  A: 

Ah, solution! Silverlight Tip of the Day #13 - How to Get an Images Dimensions in Silverlight.

private Image image;

    public Page()
    {
        InitializeComponent();
        LoadImage("image.png");
    }

    private void LoadImage(string path)
    {

        Uri uri = new Uri(path, UriKind.Relative);
        BitmapImage bitmapImage = new BitmapImage();
        bitmapImage.UriSource = uri;
        bitmapImage.DownloadProgress += 
            new EventHandler<DownloadProgressEventArgs>(bitmapImage_DownloadProgress);
    }

    void bitmapImage_DownloadProgress(object sender, DownloadProgressEventArgs e)
    {
        if (e.Progress == 100)
        {
            Dispatcher.BeginInvoke(delegate()
            {
               double height = image.ActualHeight;
               double width = image.ActualWidth;
            });
        }
    }

The KEY was this line:

The call to Dispatcher.BeginInvoke(delegate() { … } ); is required before you obtain the Width/Height or they might be intermittently zero.

aronchick
A: 

The download progress event doesn't seem to fire at all .... :(