locked
Setting source of img element from IRandomAccessStream

    Question

  • Hi all,

    I have a C++/CX component that can return an image as a stream. Currently, the component's return type is IRandomAccessStream and the actual stream is a InMemoryRandomAccessStream.

    I know the stream contains the bytes of the image I want, since I works for C# and I can create an array from it with the correct elements.

    The issue I have is that when I try to use it as a source for an img object in a HTML/JS app, I run into a problem. I've tried to use URL.createObjectURL but when I do I get an error saying "No such interface supported".

    Looking up URL.createObjectURL it seems that the IRandomAccessStream isn't enough, I need an IRandomAccessStreamWithContentType, which when I look doesn't even have a section for JavaScript...

    So, I'm sitting here with an IRandomAccessStream and an HTML/JS app that has an <img> element just waiting to be used.

    Potentially, I guess I could change the return type of my C++/CX component so that whatever I return has a   ContentType property. However, I'm not entirely clear on what the best way to do this is, since I don't really know what type of stream I should be using to get that, or if I have to create my own class?

    Where do I go from here?

    PS: I am fairly poor at JavaScript and HTML, so something a little more in-depth would be nice.

    Thanks,

    Tomas

    Friday, January 4, 2013 1:30 AM

Answers

  • Hi,

    Try to get the image file as a blob. For example:

    (function () {
    
        // IndexedDB
    
        var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB,
    
            IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.OIDBTransaction || window.msIDBTransaction,
    
            dbVersion = 1.0;
    
     
    
        // Create/open database
    
        var request = indexedDB.open("elephantFiles", dbVersion),
    
            db,
    
            createObjectStore = function (dataBase) {
    
                // Create an objectStore
    
                console.log("Creating objectStore")
    
                dataBase.createObjectStore("elephants");
    
            },
    
     
    
            getImageFile = function () {
    
                // Create XHR
    
                var xhr = new XMLHttpRequest(),
    
                    blob;
    
     
    
                xhr.open("GET", "elephant.png", true);
    
                // Set the responseType to blob
    
                xhr.responseType = "blob";
    
     
    
                xhr.addEventListener("load", function () {
    
                    if (xhr.status === 200) {
    
                        console.log("Image retrieved");
    
                        
    
                        // Blob as response
    
                        blob = xhr.response;
    
                        console.log("Blob:" + blob);
    
     
    
                        // Put the received blob into IndexedDB
    
                        putElephantInDb(blob);
    
                    }
    
                }, false);
    
                // Send XHR
    
                xhr.send();
    
            },
    
     
    
            putElephantInDb = function (blob) {
    
                console.log("Putting elephants in IndexedDB");
    
     
    
                // Open a transaction to the database
    
                var transaction = db.transaction(["elephants"], IDBTransaction.READ_WRITE);
    
     
    
                // Put the blob into the dabase
    
                var put = transaction.objectStore("elephants").put(blob, "image");
    
     
    
                // Retrieve the file that was just stored
    
                transaction.objectStore("elephants").get("image").onsuccess = function (event) {
    
                    var imgFile = event.target.result;
    
                    console.log("Got elephant!" + imgFile);
    
     
    
                    // Get window.URL object
    
                    var URL = window.URL || window.webkitURL;
    
     
    
                    // Create and revoke ObjectURL
    
                    var imgURL = URL.createObjectURL(imgFile);
    
     
    
                    // Set img src to ObjectURL
    
                    var imgElephant = document.getElementById("elephant");
    
                    imgElephant.setAttribute("src", imgURL);
    
     
    
                    // Revoking ObjectURL
    
                    URL.revokeObjectURL(imgURL);
    
                };
    
            };
    
     
    
        request.onerror = function (event) {
    
            console.log("Error creating/accessing IndexedDB database");
    
        };
    
     
    
        request.onsuccess = function (event) {
    
            console.log("Success creating/accessing IndexedDB database");
    
            db = request.result;
    
     
    
            db.onerror = function (event) {
    
                console.log("Error creating/accessing IndexedDB database");
    
            };
    
            
    
            // Interim solution for Google Chrome to create an objectStore. Will be deprecated
    
            if (db.setVersion) {
    
                if (db.version != dbVersion) {
    
                    var setVersion = db.setVersion(dbVersion);
    
                    setVersion.onsuccess = function () {
    
                        createObjectStore(db);
    
                        getImageFile();
    
                    };
    
                }
    
                else {
    
                    getImageFile();
    
                }
    
            }
    
            else {
    
                getImageFile();
    
            }
    
        }
    
        
    
        // For future use. Currently only in latest Firefox versions
    
        request.onupgradeneeded = function (event) {
    
            createObjectStore(event.target.result);
    
        };
    
    })();
    
    

     

    Roy
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by Song Tian Tuesday, January 8, 2013 2:54 AM
    Friday, January 4, 2013 7:22 AM
  • Alright, I think I solved it.

    You can use a RandomAccessStreamReference from the inputstream and then create a IRandomAccessStreamWithContent from that, using openReadAsync().


    var iras = getBMPAsIRandomAccessStream() // my function
    var rasr = Windows.Storage.Streams.RandomAccessStreamReference.createFromStream(iras);
      rasr.openReadAsync().then(function (iraswct) {
      // iraswct is an IRandomAccessStreamWithReference
    
      var url = URL.createObjectURL(iraswct, { oneTimeOnly: true });

    Tomas

    • Marked as answer by T Hofmann Monday, January 7, 2013 4:37 PM
    Monday, January 7, 2013 4:37 PM

All replies

  • Hi,

    Try to get the image file as a blob. For example:

    (function () {
    
        // IndexedDB
    
        var indexedDB = window.indexedDB || window.webkitIndexedDB || window.mozIndexedDB || window.OIndexedDB || window.msIndexedDB,
    
            IDBTransaction = window.IDBTransaction || window.webkitIDBTransaction || window.OIDBTransaction || window.msIDBTransaction,
    
            dbVersion = 1.0;
    
     
    
        // Create/open database
    
        var request = indexedDB.open("elephantFiles", dbVersion),
    
            db,
    
            createObjectStore = function (dataBase) {
    
                // Create an objectStore
    
                console.log("Creating objectStore")
    
                dataBase.createObjectStore("elephants");
    
            },
    
     
    
            getImageFile = function () {
    
                // Create XHR
    
                var xhr = new XMLHttpRequest(),
    
                    blob;
    
     
    
                xhr.open("GET", "elephant.png", true);
    
                // Set the responseType to blob
    
                xhr.responseType = "blob";
    
     
    
                xhr.addEventListener("load", function () {
    
                    if (xhr.status === 200) {
    
                        console.log("Image retrieved");
    
                        
    
                        // Blob as response
    
                        blob = xhr.response;
    
                        console.log("Blob:" + blob);
    
     
    
                        // Put the received blob into IndexedDB
    
                        putElephantInDb(blob);
    
                    }
    
                }, false);
    
                // Send XHR
    
                xhr.send();
    
            },
    
     
    
            putElephantInDb = function (blob) {
    
                console.log("Putting elephants in IndexedDB");
    
     
    
                // Open a transaction to the database
    
                var transaction = db.transaction(["elephants"], IDBTransaction.READ_WRITE);
    
     
    
                // Put the blob into the dabase
    
                var put = transaction.objectStore("elephants").put(blob, "image");
    
     
    
                // Retrieve the file that was just stored
    
                transaction.objectStore("elephants").get("image").onsuccess = function (event) {
    
                    var imgFile = event.target.result;
    
                    console.log("Got elephant!" + imgFile);
    
     
    
                    // Get window.URL object
    
                    var URL = window.URL || window.webkitURL;
    
     
    
                    // Create and revoke ObjectURL
    
                    var imgURL = URL.createObjectURL(imgFile);
    
     
    
                    // Set img src to ObjectURL
    
                    var imgElephant = document.getElementById("elephant");
    
                    imgElephant.setAttribute("src", imgURL);
    
     
    
                    // Revoking ObjectURL
    
                    URL.revokeObjectURL(imgURL);
    
                };
    
            };
    
     
    
        request.onerror = function (event) {
    
            console.log("Error creating/accessing IndexedDB database");
    
        };
    
     
    
        request.onsuccess = function (event) {
    
            console.log("Success creating/accessing IndexedDB database");
    
            db = request.result;
    
     
    
            db.onerror = function (event) {
    
                console.log("Error creating/accessing IndexedDB database");
    
            };
    
            
    
            // Interim solution for Google Chrome to create an objectStore. Will be deprecated
    
            if (db.setVersion) {
    
                if (db.version != dbVersion) {
    
                    var setVersion = db.setVersion(dbVersion);
    
                    setVersion.onsuccess = function () {
    
                        createObjectStore(db);
    
                        getImageFile();
    
                    };
    
                }
    
                else {
    
                    getImageFile();
    
                }
    
            }
    
            else {
    
                getImageFile();
    
            }
    
        }
    
        
    
        // For future use. Currently only in latest Firefox versions
    
        request.onupgradeneeded = function (event) {
    
            createObjectStore(event.target.result);
    
        };
    
    })();
    
    

     

    Roy
    MSDN Community Support | Feedback to us
    Develop and promote your apps in Windows Store
    Please remember to mark the replies as answers if they help and unmark them if they provide no help.

    • Marked as answer by Song Tian Tuesday, January 8, 2013 2:54 AM
    Friday, January 4, 2013 7:22 AM
  • Thanks for the reply, but to be honest I don't really understand what is going on with your code. As I said, my experience with JS is quite limited and right now I'm just trying to create my first sample app. I'm assuming that somehow you're creating a tiny database, then you shove the image in and retrieve it again as a blob? 

    Also, if that's the kind of hoopla I need to go through to get the stream to work with URL.createObjectURL, then I should probably look at my C++/CX code and make it return something in a format that is ready to be used out of the box. I guess that if I could make it return an IRandomAccessStreamWithContentType it would solve my problem (provided I can figure out what the content type should be).

    The image I'm talking about is rendered by the C++/CX component. The result of the rendering operation is a byte-array, which I then read into a InMemoryRandomAccessStream. The reason I wanted to return an IRandomAccessStream is because it can be consumed by both JavaScript and C#/VB and is a bit "cleaner" than just dumping an array (though I guess I could use an IBuffer). At the end of the day, what I've got is some form of array like structure (I've managed to extract the array from the stream in JS anyway) or stream with pixel values in the form of ARGB that I would like to use as the source for my image.

    It seems to me that reading bytes from a stream or array and shoving them into an UI image element shouldn't be too complicated.

    Thanks,

    Tomas

    Friday, January 4, 2013 5:14 PM
  • I tried changing my C++/CX program so that now it instead returns an IRandomAccessStreamWithContentType. To do this, I created my own class which implements that interface. It wraps an InMemoryRandomAccessStream and basically forwards everything there, except that it adds a dummy ContentType property.

    The issue now is that my JavaScript app crashes as soon as I try to call the function that returns this value. I can't get a stack trace because it asks me to use VS 2003 for debugging... On another note, the new function works well with C#, where I can use it to set the source of an image. I'm quite confounded as to why something can work well in C# but crash when it's being called from JavaScript. Unless there's something that is "illegal" in JS with the wrapping I've done, I don't see why this would happen. I also can't provide much more detail, since I don't get any error report. Perhaps someone knows how to get more useful info from a crash like this?

    Anyone knows what it means when URL.createObjectURL breaks with the error message:

    0x80004002 - JavaScript runtime error: No such interface supported.

    I'm assuming it means that the input is missing some function or similar that it is looking for. Shouldn't it be possible to simply attach the correct property if necessary. I think the only thing I'm missing is the contentType, so I tried mystream.contentType = "bmp" before I call URL.createObjectURL, but the error remains.

    Tomas

    Friday, January 4, 2013 11:55 PM
  • Alright, I think I solved it.

    You can use a RandomAccessStreamReference from the inputstream and then create a IRandomAccessStreamWithContent from that, using openReadAsync().


    var iras = getBMPAsIRandomAccessStream() // my function
    var rasr = Windows.Storage.Streams.RandomAccessStreamReference.createFromStream(iras);
      rasr.openReadAsync().then(function (iraswct) {
      // iraswct is an IRandomAccessStreamWithReference
    
      var url = URL.createObjectURL(iraswct, { oneTimeOnly: true });

    Tomas

    • Marked as answer by T Hofmann Monday, January 7, 2013 4:37 PM
    Monday, January 7, 2013 4:37 PM