Issue with reduced quality on file after saving/converting from canvas

前端 未结 2 2150
余生分开走
余生分开走 2021-01-26 09:53

This is the code I am using. (code is at way bottom of this post but here is link to GitHubGist :: Noitidart / _ff-addon-snippet-browseForBadgeThenCreateSaveAnApply.js) It is c

相关标签:
2条回答
  • 2021-01-26 09:59

    Tested the code on Win7, the icon once applied is real real crappy. In image below, we see the icon in canvas is perfect. In the alt+tab menu the first icon is SUPER crappy, second icon is unbadged so perfect, and third and fifth icons are normal crappy.

    Image is here: http://i.stack.imgur.com/47dIr.png

    Edit: Fixed the SUPER crappiness by changing this line for win7, it was 32, 32, i made it 256, 256, no clue why it fixed the SUPER crap:

    var hIconBig = LoadImage(targetWindow_handle, OS.Path.join(OS.Constants.Path.desktopDir, 'profilist32.' + osIconFileType), IMAGE_ICON, 256, 256, LR_LOADFROMFILE);

    before it was: var hIconBig = LoadImage(targetWindow_handle, OS.Path.join(OS.Constants.Path.desktopDir, 'profilist32.' + osIconFileType), IMAGE_ICON, 32, 32, LR_LOADFROMFILE); However the usual crap, black rough edge remains.

    0 讨论(0)
  • 2021-01-26 10:11

    Alright. This is actually quite reproducible, but only when using BMP icons, but not PNG icons.

    Seems the icon encoder that Firefox ships is pretty bad/buggy indeed (for RGBA stuff). Well, actually it is the BMP encoder that the ICO encoder uses...

    So since Belgium/Algeria (the game, football, not American) was mostly boring just now, I wrote my own icon encoder, which isn't too hard actually.

    So here is my complete example code incl. icon encoder (just setting the 32x32 icon), but which lacks deposing of icons. But as a bonus, it shows how to set the icon via the WNDCLASS.

    Cu.import('resource://gre/modules/ctypes.jsm');
    Cu.import('resource://gre/modules/osfile.jsm');
    
    let IMAGE_BITMAP = 0;
    let IMAGE_ICON = 1;
    let WM_SETICON = 128;
    let GCLP_HICON = -14;
    
    let user32 = ctypes.open('user32.dll');
    let SendMessage = user32.declare(
        'SendMessageW',
        ctypes.winapi_abi,
        ctypes.intptr_t,
        ctypes.voidptr_t, // HWND
        ctypes.uint32_t, // MSG
        ctypes.uintptr_t, // WPARAM
        ctypes.intptr_t // LPARAM
    );
    let CreateIconFromResourceEx = user32.declare(
        'CreateIconFromResourceEx',
        ctypes.winapi_abi,
        ctypes.voidptr_t,
        ctypes.uint8_t.ptr, // icon
        ctypes.uint32_t, // size
        ctypes.int32_t, // icon
        ctypes.uint32_t, // dwVersion
        ctypes.int, // dx
        ctypes.int, // dy
        ctypes.uint32_t // flags
    );
    let SetClassLongPtr = user32.declare(
        ctypes.intptr_t.size == 8 ? 'SetClassLongPtrW' : 'SetClassLongW',
        ctypes.winapi_abi,
        ctypes.uintptr_t,
        ctypes.voidptr_t, // HWND
        ctypes.int, // index
        ctypes.uintptr_t // value
    );
    
    let gdi32 = ctypes.open('gdi32.dll');
    let DeleteObject = gdi32.declare(
        'DeleteObject',
        ctypes.winapi_abi,
        ctypes.int,
        ctypes.voidptr_t // Object
    );
    
    let setPerWindow = false;
    
    let badges = [
        'chrome://browser/skin/places/starred48.png',
        'chrome://browser/skin/places/downloads.png',
        'chrome://browser/skin/places/tag.png',
        'chrome://browser/skin/places/livemark-item.png',
        'chrome://browser/skin/places/query.png',
        'chrome://browser/skin/pluginInstall-64.png',
        'chrome://browser/skin/pluginInstall-16.png',    
    ];
    
    function getRandomInt(min, max) {
      return Math.floor(Math.random() * (max - min + 1)) + min;
    }
    
    Task.spawn(function* setIcon() {
        "use strict";
        try {
           let p = Promise.defer();
           let img = new Image();
           img.onload = () => p.resolve();
           img.src = 'chrome://branding/content/icon32.png';
           yield p.promise;
    
           p = Promise.defer();
           let badge = new Image();
           badge.onload = () => p.resolve();
           badge.src = badges[getRandomInt(0, badges.length - 1)];
           console.log(badge.src);
           yield p.promise;
    
           let canvas = document.createElementNS(
              'http://www.w3.org/1999/xhtml',
              'canvas');
           canvas.width = img.naturalWidth;
           canvas.height = img.naturalHeight;
           let ctx = canvas.getContext('2d');
           ctx.drawImage(img, 0, 0);
           let onethird = canvas.width / 3;
           ctx.drawImage(
              badge,
              onethird,
              onethird,
              canvas.width - onethird,
              canvas.height - onethird);
    
           // Our own little ico encoder
           // http://msdn.microsoft.com/en-us/library/ms997538.aspx
           // Note: We would have been able to skip ICONDIR/ICONDIRENTRY,
           // if we were to use CreateIconFromResourceEx only instead of also
           // writing the icon to a file.
           let data = ctx.getImageData(0, 0, canvas.width, canvas.height).data;
           let XOR = data.length;
           let AND = canvas.width * canvas.height / 8;
           let size = 22 /* ICONDIR + ICONDIRENTRY */ + 40 /* BITMAPHEADER */ + XOR + AND;
           let buffer = new ArrayBuffer(size);
    
           // ICONDIR
           let view = new DataView(buffer);
           view.setUint16(2, 1, true); // type 1
           view.setUint16(4, 1, true); // count;
    
           // ICONDIRENTRY
           view = new DataView(buffer, 6);
           view.setUint8(0, canvas.width % 256);
           view.setUint8(1, canvas.height % 256);
           view.setUint16(4, 1, true); // Planes
           view.setUint16(6, 32, true); // BPP
           view.setUint32(8, 40 + XOR + AND, true); // data size
           view.setUint32(12, 22, true); // data start
    
           // BITMAPHEADER
           view = new DataView(buffer, 22);
           view.setUint32(0, 40, true); // BITMAPHEADER size
           view.setInt32(4, canvas.width, true);
           view.setInt32(8, canvas.height * 2, true);
           view.setUint16(12, 1, true); // Planes
           view.setUint16(14, 32, true); // BPP
           view.setUint32(20, XOR + AND, true); // size of data
    
           // Reorder RGBA -> BGRA
           for (let i = 0; i < XOR; i += 4) {
              let temp = data[i];
              data[i] = data[i + 2];
              data[i + 2] = temp;
           }
           let ico = new Uint8Array(buffer, 22 + 40);
           let stride = canvas.width * 4;
           // Write bottom to top
           for (let i = 0; i < canvas.height; ++i) {
              let su = data.subarray(XOR - i * stride, XOR - i * stride + stride);
              ico.set(su, i * stride);
           }
    
           // Write the icon to inspect later. (We don't really need to write it at all)
           let writePath = OS.Path.join(OS.Constants.Path.desktopDir, 'icon32.ico');
           yield OS.File.writeAtomic(writePath, new Uint8Array(buffer), {
              tmpPath: writePath + '.tmp'
           });
    
           // Cut off ICONDIR/ICONDIRENTRY for CreateIconFromResourceEx
           buffer = buffer.slice(22);
           let hicon = CreateIconFromResourceEx(
              ctypes.uint8_t.ptr(buffer),
              buffer.byteLength,
              IMAGE_ICON,
              0x30000,
              0,
              0,
              0);
           if (hicon.isNull()) {
              throw new Error("Failed to load icon");
           }
           if (setPerWindow) {
               let DOMWindows = Services.wm.getEnumerator(null);
               while (DOMWindows.hasMoreElements()) {
                  let win = DOMWindows.getNext().QueryInterface(Ci.nsIInterfaceRequestor).
                     getInterface(Ci.nsIWebNavigation).
                     QueryInterface(Ci.nsIDocShellTreeItem).
                     treeOwner.QueryInterface(Ci.nsIInterfaceRequestor).
                     getInterface(Ci.nsIBaseWindow);
                  let handle = ctypes.voidptr_t(ctypes.UInt64(win.nativeHandle));
                  if (handle.isNull()) {
                     console.error("Failed to get window handle");
                     continue;
                  }
                  var lparam = ctypes.cast(hicon, ctypes.intptr_t);
                  var oldIcon = SendMessage(handle, WM_SETICON, 1, lparam);
                  if (ctypes.voidptr_t(oldIcon).isNull()) {
                     console.log("There was no old icon", oldIcon.toString());
                  }
                  else {
                     console.log("There was an old icon already", oldIcon.toString());
                     // In a perfect world, we should actually kill our old icons
                     // using DeleteObject...
                  }
               }
           }
           else {    
               let win = Services.wm.getMostRecentWindow(null).
                  QueryInterface(Ci.nsIInterfaceRequestor).
                  getInterface(Ci.nsIWebNavigation).
                  QueryInterface(Ci.nsIDocShellTreeItem).
                  treeOwner.QueryInterface(Ci.nsIInterfaceRequestor).
                  getInterface(Ci.nsIBaseWindow);
               let handle = ctypes.voidptr_t(ctypes.UInt64(win.nativeHandle));
               if (handle.isNull()) {
                   throw new Error("Failed to get window handle");
               }
               let oldIcon = SetClassLongPtr(handle, GCLP_HICON, ctypes.cast(hicon, ctypes.uintptr_t));
               if (ctypes.voidptr_t(oldIcon).isNull()) {
                   console.log("There was no old icon", oldIcon.toString());
               }
               else {
                   console.log("There was an old icon already", oldIcon.toString());
                   // In a perfect world, we should actually kill our old icons
                   // using DeleteObject...
               }
           }
           console.log("done", badge.src);
        } 
        catch (ex) {
           console.error(ex);
        }
    });
    

    PS: Here is a screenshot from the Task Switcher on XP:

    Composed icons as displayed in XP Task Switcher

    Composed icons as displayed in Win7 Task Switcher

    0 讨论(0)
提交回复
热议问题