winapicanvasfirefox-addoniconsjsctypes

CreateIconFromResourceEx on canvas.mozFetchAsStream, how to feed the stream?


My goal is to make a .ico or a HICON out of canvas drawing. Below is a snippet for windows. What it does is lets you file pick. You pick an image and then it adds canvas to the document in current tab (at way bottom) and then draws the 32px logo on cavas and then on that it overlays the browsed image. Now im trying to us jsctypes of winapi CreateIconFromResourceEx with canvas.mozFetchAsStream but I can't figure it out.

I'm stuck on this line: alert(streamData).

Also for PBYTE in my CreateIconFromResourceEx is it correct to set it to ctypes.unsigned_char.ptr? (Because byte I know is just ctypes.unsigned_char right?)

Components.utils.import('resource://gre/modules/ctypes.jsm');

var user32 = ctypes.open('user32.dll');


/* http://msdn.microsoft.com/en-us/library/windows/desktop/ms648061%28v=vs.85%29.aspx
* HICON WINAPI CreateIconFromResourceEx(
* __in_  PBYTE pbIconBits,
* __in_  DWORD cbIconBits,
* __in_  BOOL fIcon,
* __in_  DWORD dwVersion,
* __in_  int cxDesired,
* __in_  int cyDesired,
* __in_  UINT uFlags
* );
*/
var CreateIconFromResourceEx = user32.declare('CreateIconFromResourceEx', ctypes.winapi_abi, ctypes.voidptr_t,
    ctypes.unsigned_char.ptr, /* PBYTE pbIconBits */
    ctypes.unsigned_long, /* DWORD cbIconBits */
    ctypes.bool, /* BOOL fIcon */
    ctypes.unsigned_long, /* DWORD dwVersion */
    ctypes.int, /* int cxDesired */
    ctypes.int, /* int cyDesired */
    ctypes.unsigned_int /* UINT uFlags */
);

/////////////// running stuff below. above was just defining stuff
var me = Services.wm.getMostRecentWindow(null);

var canvas = document.createElementNS('http://www.w3.org/1999/xhtml', 'canvas');
var ctx = canvas.getContext('2d');
gBrowser.contentDocument.documentElement.appendChild(canvas);

var fp = Cc['@mozilla.org/filepicker;1'].createInstance(Ci.nsIFilePicker);
fp.init(window, 'Select Badge Image', Ci.nsIFilePicker.modeOpen);
fp.appendFilters(Ci.nsIFilePicker.filterAll | Ci.nsIFilePicker.filterText);

var rv = fp.show();
if (rv == Ci.nsIFilePicker.returnOK || rv == Ci.nsIFilePicker.returnReplace) {
  var file = fp.file;
  // Get the path as string. Note that you usually won't 
  // need to work with the string paths.
  var path = fp.file.path;


    var oImage = new Image();
    oImage.src = 'chrome://branding/content/icon32.png'; //Services.io.newFileURI(file).spec;
    oImage.onload = function() {
        alert('loaded')
        canvas.width = this.width;
        canvas.height = this.height;
        ctx.clearRect(0, 0, this.width, this.height);
        ctx.drawImage(this, 0, 0);

        var oImage = new Image();
        oImage.src = Services.io.newFileURI(file).spec;
        oImage.onload = function() {
            alert('loaded')
            ctx.drawImage(this, canvas.width-this.width, canvas.height-this.width);
            //mozFetchAsStream stuff: https://github.com/mozilla/build-partner-repacks/blob/885947b726c5d6e131af4e4aae621d51109bded4/partners/yandex-drp/distribution/extensions/vb%40yandex.ru/cbapp/parts/screenshotsGrabber.js#L295
            var asyncStreamCallback = {
                    onInputStreamReady: function (streamData) {
                        alert(streamData)
                    },
                    QueryInterface: XPCOMUtils.generateQI([
                        Ci.nsISupports,
                        Ci.nsIInputStreamCallback
                    ])
                };
            canvas.mozFetchAsStream(asyncStreamCallback, 'image/vnd.microsoft.icon')
            //now do canvas.mozGetAFile(blob) then reconstruct icon
        }
    }
}

////////////////

user32.close();

Solution

  • I don't know what's wrong with your code. But since your objective is to turn the canvas contents to an ICO, here is another way (somewaht more straightfoward I dare to say).

    // assuming ctx holds your drawings
    let imgdata = ctx.getImageData(0,0,32,32); // this is a 32x32 icon, right?
    let icoencoder = Cc["@mozilla.org/image/encoder;2?type=image/vnd.microsoft.icon"].createInstance(Ci.imgIEncoder);
    icoencoder.initFromData(imgdata.data, imgdata.data.length, 32, 32, 32*4, Ci.imgIEncoder.INPUT_FORMAT_RGBA, "");
    icoencoder.QueryInterface(Ci.nsIInputStream);
    
    var icofile = new FileUtils.File("/path/to/canvas.ico");
    var output = FileUtils.openSafeFileOutputStream(icofile);
    NetUtil.asyncCopy(icoencoder, output, youroptionalcallback);
    

    The result is a proper canvas.ico file which you can pass to the windows api functions.