how to create CCSprite from image url?

Topics: Sprites and SpriteBatch, Windows Phone 8
Mar 4, 2014 at 7:07 AM
Edited Mar 4, 2014 at 7:08 AM
Hi,
I use HttpWebRequest to download image as stream. Then create cctexture2D from result stream.
Deployment.Current.Dispatcher.BeginInvoke(() =>
                                                          {
                                                              var request = HttpWebRequest.Create(myUri);
                                                              request.BeginGetResponse(asyncResult =>
                                                                                       {
                                                                                           Deployment.Current.Dispatcher.BeginInvoke(() =>
                                                                                                            {
                                                                                                                var response = (HttpWebResponse)((HttpWebRequest)asyncResult.AsyncState).EndGetResponse(asyncResult);
                                                                                                                using (response)
                                                                                                                {
                                                                                                                    var texture2D = Texture2D.FromStream(CCApplication.SharedApplication.GraphicsDevice, response.GetResponseStream());
                                                                                                                    var ccTexture = new CCTexture2D();
                                                                                                                    if (ccTexture.InitWithTexture(texture2D))
                                                                                                                    {
                                                                                                                        var sprite = new CCSprite(ccTexture, new CCRect(0, 0, 100, 100));
                                                                                                                        AddChild(sprite);
                                                                                                                    }
                                                                                                                }
                                                                                                            });
                                                                                       }, request);
                                                          });
But i always get an exception:
An exception of type 'System.Runtime.InteropServices.COMException' occurred in Microsoft.Phone.ni.dll but was not handled in user code

Additional information: The image header is unrecognized. (Exception from HRESULT: 0x88982F61)
In Texture2D.cs, at lines:
Threading.BlockOnUIThread(() =>
            {
                // Note that contrary to the method name this works for both JPEG and PNGs.
                writableBitmap = Microsoft.Phone.PictureDecoder.DecodeJpeg(stream);
            });
Please help me fix this error? or are there any others way?
Thanks.
Coordinator
Mar 4, 2014 at 3:32 PM
ha! Clever method of creating the texture. This error:

The image header is unrecognized

This means the stream data is truncated. My guess is this line:

var response = (HttpWebResponse)((HttpWebRequest)asyncResult.AsyncState).EndGetResponse(asyncResult);

This actually terminates the response? Note sure, I am no expert on what you are doing with the http request. If that EndGetResponse really does terminate the response, then this line:

var texture2D = Texture2D.FromStream(CCApplication.SharedApplication.GraphicsDevice, response.GetResponseStream());

This will always fail because the response stream is at EOF.
Mar 5, 2014 at 2:24 AM
oh. maybe stream is at EOF.
so, how can i get full stream with http request?
PS:
Other case. in XAML, when i binding this stream to Image, it's displayed.
Coordinator
Mar 5, 2014 at 4:00 AM
See this:

http://msdn.microsoft.com/en-us/library/system.net.httpwebrequest.endgetresponse(v=vs.110).aspx

maybe you need a special callback to handle the response....
Mar 5, 2014 at 6:11 AM
i'll try that way.
thanks u!
Coordinator
Mar 5, 2014 at 3:23 PM
report back if it works an we'll add the technique to the framework.
Mar 10, 2014 at 3:23 AM
Edited Mar 10, 2014 at 3:23 AM
i tried use IAsyncResult callback. But it doesn't work for me.
I got some runtime exceptions like this: "The thread 0xf74 has exited with code 259 (0x103)"
I googled and found some topics with same trouble:
http://stackoverflow.com/questions/15660741/httpwebrequest-begingetresponse-callback-not-firing-on-wp8-working-on-wp7
http://social.msdn.microsoft.com/Forums/wpapps/en-us/e7a2698b-16a8-4f32-8cb5-00addfc2fb51/wp8-breaking-change-automatic-referers-for-bitmapimage-and-webclient-have-changed?forum=wpdevelop
But i'm not solved yet.
Here's my code:
public class XHttpDownloader
    {
        public delegate void DownloadCompletedHandler(byte[] resultData);
        public event DownloadCompletedHandler DownloadCompleted;

        public static ManualResetEvent allDone = new ManualResetEvent(false);
        const int BUFFER_SIZE = 1024;
        /// <summary>
        /// 2 minutes timeout
        /// </summary>
        const int DefaultTimeout = 2 * 60 * 1000;

        private readonly string downloadUrl;
        public XHttpDownloader(string url)
        {
            downloadUrl = url;
        }

        public static XHttpDownloader Create(string url)
        {
            return new XHttpDownloader(url);
        }

        public void StartDownload()
        {
            if (!string.IsNullOrEmpty(downloadUrl))
            {
                GetImageAsync(downloadUrl);   
            }
        }

        private void GetImageAsync(string url)
        {
            try
            {
                // Create a HttpWebrequest object to the desired URL.
                var myHttpWebRequest = (HttpWebRequest)WebRequest.Create(url);
                // Create an instance of the RequestState and assign the previous myHttpWebRequest1 
                // object to it's request field.  
                var myRequestState = new RequestState();
                myRequestState.Request = myHttpWebRequest;
                // Start the asynchronous request.
                var result = (IAsyncResult)myHttpWebRequest.BeginGetResponse(new AsyncCallback(RespCallback), myRequestState);
                // this line implements the timeout, if there is a timeout, the callback fires and the request becomes aborted
                //ThreadPool.RegisterWaitForSingleObject(result.AsyncWaitHandle, new WaitOrTimerCallback(TimeoutCallback), myHttpWebRequest, DefaultTimeout, true);
                allDone.WaitOne();
                // Release the HttpWebResponse resource.
                myRequestState.Response.Close();
            }
            catch (Exception e)
            {
                Debug.WriteLine("\nException raised!");
                Debug.WriteLine("Source :{0} ", e.Source);
                Debug.WriteLine("Message :{0} ", e.Message);
            }
        }

        // Abort the request if the timer fires.
        private void TimeoutCallback(object state, bool timedOut)
        {
            if (timedOut)
            {
                var request = state as HttpWebRequest;
                if (request != null)
                {
                    request.Abort();
                }
            }
        }

        private void RespCallback(IAsyncResult asynchronousResult)
        {
            try
            {
                // State of request is asynchronous.
                var myRequestState = (RequestState)asynchronousResult.AsyncState;
                HttpWebRequest myHttpWebRequest2 = myRequestState.Request;
                myRequestState.Response = (HttpWebResponse)myHttpWebRequest2.EndGetResponse(asynchronousResult);
                // Read the response into a Stream object.
                Stream responseStream = myRequestState.Response.GetResponseStream();
                myRequestState.StreamResponse = responseStream;
                // Begin the Reading of the contents of the HTML page and print it to the Debug.
                IAsyncResult asynchronousInputRead = responseStream.BeginRead(myRequestState.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), myRequestState);
            }
            catch (WebException e)
            {
                Debug.WriteLine("\nException raised!");
                Debug.WriteLine("\nMessage:{0}", e.Message);
                Debug.WriteLine("\nStatus:{0}", e.Status);
            }
        }

        private void ReadCallBack(IAsyncResult asyncResult)
        {
            try
            {
                var myRequestState = (RequestState)asyncResult.AsyncState;
                Stream responseStream = myRequestState.StreamResponse;
                int read = responseStream.EndRead(asyncResult);
                // Read the HTML page and then print it to the Debug. 
                if (read > 0)
                {
                    int currentLength = myRequestState.RequestData.Length;
                    Array.Resize(ref myRequestState.RequestData, currentLength + read);
                    Array.Copy(myRequestState.BufferRead, 0, myRequestState.RequestData, currentLength, read);
                    IAsyncResult asynchronousResult = responseStream.BeginRead(myRequestState.BufferRead, 0, BUFFER_SIZE, new AsyncCallback(ReadCallBack), myRequestState);
                }
                else
                {
                    if (myRequestState.RequestData.Length > 1 && DownloadCompleted != null)
                    {
                        DownloadCompleted(myRequestState.RequestData);
                    }
                    responseStream.Close();
                    allDone.Set();
                }
            }
            catch (WebException e)
            {
                Debug.WriteLine("\nException raised!");
                Debug.WriteLine("\nMessage:{0}", e.Message);
                Debug.WriteLine("\nStatus:{0}", e.Status);
            }
        }
    }

    // The RequestState class passes data across async calls.
    public class RequestState
    {
        public const int BufferSize = 1024;
        public byte[] RequestData;
        public byte[] BufferRead;
        public HttpWebRequest Request;
        public HttpWebResponse Response;
        public Stream StreamResponse;
        // Create Decoder for appropriate enconding type.
        public Decoder StreamDecode = Encoding.UTF8.GetDecoder();
        public RequestState()
        {
            BufferRead = new byte[BufferSize];
            RequestData = new byte[0];
            Request = null;
            StreamResponse = null;
        }
    }
Coordinator
Mar 10, 2014 at 9:21 PM
Edited Mar 10, 2014 at 9:23 PM
I believe Error 259 may be an empty stream or else the thread exits because there are no more task items to process.

ERROR_NO_MORE_ITEMS259 (0x103)
No more data is available.

Also, why are you nesting your async read? Why not just read all of the content into a buffer in your async read method instead of calling back into itself.

Instead of catching WebException try just catching all exceptions to see if any other error is being raised.

Here is where I got the error code message:

http://msdn.microsoft.com/en-us/library/windows/desktop/ms681382(v=vs.85).aspx

This is for windows desktop, but it may be relevant to windows phone 8 ...

Also this:

http://stackoverflow.com/questions/16272319/after-port-app-wp7-to-wp8-throws-exception-the-thread-has-exited