function draw(canvas)
{
var ctx = canvas.getContext('2d');
ctx.beginPath();
var p = Math.min(canvas.width, canvas.height);
ctx.fillStyle = "yellow";
ctx.lineWidth = p / 16 / 1.5;
ctx.lineCap = "round";
ctx.arc(50*p/100, 50*p/100, 39*p/100, 0, Math.PI * 2, true); // Outer circle
ctx.save();
var radgrad = ctx.createRadialGradient(50*p/100,50*p/100,0,50*p/100,50*p/100,50*p/100);
radgrad.addColorStop(0, 'rgba(128,128,128,1)');
radgrad.addColorStop(0.3, 'rgba(128,128,128,.8)');
radgrad.addColorStop(0.5, 'rgba(128,128,128,.5)');
radgrad.addColorStop(0.8, 'rgba(128,128,128,.2)');
radgrad.addColorStop(1, 'rgba(128,128,128,0)');
ctx.fillStyle = radgrad;
ctx.fillRect(0, 0, p, p);
ctx.restore();
ctx.fill();
ctx.moveTo(77*p/100, 50*p/100);
ctx.arc(50*p/100, 50*p/100, 27*p/100, 0, Math.PI, false); // Mouth (clockwise)
ctx.moveTo(41*p/100, 42*p/100);
ctx.arc(38*p/100, 42*p/100, 3*p/100, 0, Math.PI * 2, true); // Left eye
ctx.moveTo(65*p/100, 42*p/100);
ctx.arc(62*p/100, 42*p/100, 3*p/100, 0, Math.PI * 2, true); // Right eye
ctx.stroke();
}
let images = [];//array of image data. each image as an array of bytes
for(let i= 0, a = [16,24,32,48,64,128,256,512,1024]; i < a.length; i++)
{
let canvas = document.createElement("canvas");
canvas.width = a[i];
canvas.height = a[i];
draw(canvas);
// Convert canvas to Blob, then Blob to ArrayBuffer.
canvas.toBlob(function (blob) {
const reader = new FileReader();
reader.addEventListener('loadend', () => {
images[i] = new Uint8Array(reader.result);
if (images.length == a.length)//all canvases converted to png
{
let icoData = pngToIco(images), //array of bytes
type = "image/x-ico",
blob = new Blob([new Uint8Array(icoData)], {type: type}),
a = document.getElementById("download");
a.download = "smile.ico";
a.href = window.URL.createObjectURL(blob);
a.dataset.downloadurl = [type, a.download, a.href].join(':');
document.getElementById("img").src = a.href;
}
});
reader.readAsArrayBuffer(blob);
}, "image/png");
document.body.appendChild(canvas);
}
function pngToIco( images )
{
let icoHead = [ //.ico header
0, 0, // Reserved. Must always be 0 (2 bytes)
1, 0, // Specifies image type: 1 for icon (.ICO) image, 2 for cursor (.CUR) image. Other values are invalid. (2 bytes)
images.length & 255, (images.length >> 8) & 255 // Specifies number of images in the file. (2 bytes)
],
icoBody = [],
pngBody = [];
for(let i = 0, num, pngHead, pngData, offset = 0; i < images.length; i++)
{
pngData = Array.from( images[i] );
pngHead = [ //image directory (16 bytes)
0, // Width 0-255, should be 0 if 256 pixels (1 byte)
0, // Height 0-255, should be 0 if 256 pixels (1 byte)
0, // Color count, should be 0 if more than 256 colors (1 byte)
0, // Reserved, should be 0 (1 byte)
1, 0, // Color planes when in .ICO format, should be 0 or 1, or the X hotspot when in .CUR format (2 bytes)
32, 0 // Bits per pixel when in .ICO format, or the Y hotspot when in .CUR format (2 bytes)
];
num = pngData.length;
for (let i = 0; i < 4; i++)
pngHead[pngHead.length] = ( num >> ( 8 * i )) & 255; // Size of the bitmap data in bytes (4 bytes)
num = icoHead.length + (( pngHead.length + 4 ) * images.length ) + offset;
for (let i = 0; i < 4; i++)
pngHead[pngHead.length] = ( num >> ( 8 * i )) & 255; // Offset in the file (4 bytes)
offset += pngData.length;
icoBody = icoBody.concat(pngHead); // combine image directory
pngBody = pngBody.concat(pngData); // combine actual image data
}
return icoHead.concat(icoBody, pngBody);
}
<a id="download">download image <img id="img" width="128"></a>
<br>