Javascript 从画布保存/转换后文件质量降低的问题

Javascript 从画布保存/转换后文件质量降低的问题,javascript,canvas,firefox-addon,ico,Javascript,Canvas,Firefox Addon,Ico,这是我正在使用的代码。(代码在这篇文章的最下面,但这里是链接)它是将PastaTable复制到scratchpad(我试过小提琴,但它需要特权范围)。运行时,它将要求您选择一个16x16映像。然后它会把firefox图标放在画布上,然后把你浏览到的图标覆盖在右下角。然后它会将其转换为.ico,并保存到您的桌面,作为profilist16.ico和profilist32.ico。然后,它将更改所有firefox窗口的图标 完成上述操作后,请打开一个新的firefox窗口,然后在alt+选项卡中,您

这是我正在使用的代码。(代码在这篇文章的最下面,但这里是链接)它是将PastaTable复制到scratchpad(我试过小提琴,但它需要特权范围)。运行时,它将要求您选择一个16x16映像。然后它会把firefox图标放在画布上,然后把你浏览到的图标覆盖在右下角。然后它会将其转换为
.ico
,并保存到您的桌面,作为
profilist16.ico
profilist32.ico
。然后,它将更改所有firefox窗口的图标

完成上述操作后,请打开一个新的firefox窗口,然后在alt+选项卡中,您将看到带徽章图标的firefox徽标更脏

在底部你可以看到原始的画布(看起来很模糊,但我认为这是我在firefox上的缩放级别)。图标很清晰,但如果你注意到边缘(尤其是顶部)上的徽章图标(在右侧),你会看到污垢,像黑色锯齿状的东西,这在通常的图标(在左侧)中是看不到的

var-win=Services.wm.getMostRecentWindow(null);
var me=赢;
//这些应该是全局变量
变量大小=[]//依赖操作系统
var img={}//保存每个大小的图像的图像
var-osIconFileType='ico'//依赖操作系统
var cOS='Windows';
函数badgeIt(){
var fp=Cc[“@mozilla.org/filepicker;1”].createInstance(Ci.nsIFilePicker);
fp.init(win,“选择徽章图像”,Ci.nsIFilePicker.modeOpen);
var fpCallback=函数(rv){
if(rv==Ci.nsIFilePicker.returnOK | | rv==Ci.nsIFilePicker.returnReplace){
如果(size.length==0){
//找出这是什么操作系统,并用该操作系统所需的大小填充大小
sizes=[32,16];//注意:询问如何确定操作系统图标的大小?
}
loadBadgeImage();
}否则{
//用户未选择要标记的文件
}
}
var ranOnce0=假;
var checkAllDefaultImagesLoaded=函数(){

对于(var i=0;i在Win7上测试的代码,应用后的图标非常糟糕。在下图中,我们看到画布中的图标非常完美。在alt+tab菜单中,第一个图标非常糟糕,第二个图标非常完美,第三个和第五个图标正常糟糕

图片如下:

编辑: 通过更改win7的这一行修复了超级垃圾,它是32,32,我做了256,256,不知道为什么它修复了超级垃圾:

var hIconBig=LoadImage(targetWindow\u句柄,OS.Path.join(OS.Constants.Path.desktopDir,'profilist32.'+osconfiletype),IMAGE\u图标,256,256,LR\u LOADFROMFILE);

之前是:
var hIconBig=LoadImage(targetWindow\u句柄,OS.Path.join(OS.Constants.Path.desktopDir,'profilist32.+osIconFileType),IMAGE\u图标,32,32,LR\u LOADFROMFILE);

然而,通常的垃圾,黑色粗糙的边缘仍然存在。

好的。这实际上是相当可复制的,但仅当使用
BMP
图标时,而不是
PNG
图标时

看起来Firefox提供的图标编码器确实很糟糕/有缺陷(对于RGBA的东西)。实际上,ICO编码器使用的是BMP编码器

所以,由于比利时/阿尔及利亚(比赛,足球,而不是美国)现在大多很无聊,我写了自己的图标编码器,其实并不难

下面是我完整的示例代码,包括图标编码器(仅设置32x32图标),但它没有取消图标。但作为一个额外的功能,它展示了如何通过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);
    }
});
Cu.import('resource://gre/modules/ctypes.jsm');
Cu.进口resource://gre/modules/osfile.jsm');
让图像_位图=0;
让图像_图标=1;
设WM_SETICON=128;
设GCLP_HICON=-14;
让user32=ctypes.open('user32.dll');
让SendMessage=user32.declare(
“SendMessageW”,
ctypes.winapi_abi,
ctypes.intptr\t,
ctypes.voidptr\u t,//HWND
ctypes.uint32\u t,//MSG
ctypes.uintptr\u t,//WPARAM
ctypes.intptr\u t//LPARAM
);
让CreateIconFromResourceEx=user32.declare(
“CreateIconFromResourceEx”,
ctypes.winapi_abi,
ctypes.voidptr\t,
ctypes.uint8\u t.ptr,//图标
ctypes.uint32\u t,//大小
ctypes.int32\u t,//图标
ctypes.uint32\u t,//dwVersion
ctypes.int,//dx
ctypes.int,//dy
ctypes.uint32\u t//标志
);
让SetClassLongPtr=user32.declare(
ctypes.intptr_t.size==8?'SetClassLongPtrW':'SetClassLongW',
ctypes.winapi_abi,
ctypes.uintptr\t,
ctypes.voidptr\u t,//HWND
ctypes.int,//索引
ctypes.uintpttr\u//value
);
让gdi32=ctypes.open('gdi32.dll');
让DeleteObject=gdi32.declare(
“删除对象”,
ctypes.winapi_abi,
ctypes.int,
ctypes.voidptr\u t//对象
);
设setPerWindow=false;
让徽章=[
'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',    
];
函数getRandomInt(最小值、最大值){
返回Math.floor(Math.random()*(max-min+1))+min;
}
Task.spawn(函数*setIcon(){
“严格使用”;
试一试{
设p=Promise.defer();
设img=新图像();
img.onload=()=>p.resolve();
img.src=chrome://branding/content/icon32.png';
收益率p.promise;
p=承诺。延迟();
让badge=新图像();
badge.onload=()=>p.resolve();
badge.src=badges[getRandomInt(0,badges.length-1)];
控制台日志(badge.src);
收益率p.promise;
让canvas=document.createElements(
'http://www.w3.org/1999/xhtml',
“画布”);
canvas.width=img.naturalWidth;
canvas.height=img.naturalHeight;
设ctx=canvas.getContext('2d');
ctx.drawImage(img,0,0);
设onethird=canvas.width
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);
    }
});