I made extensive 2 days research on the topic, but there is no really well explained piece that would work.
So the flow is following:
load mp3 (store bought) cbr 320 into wavesurfer
apply all the changes you need
download processed result back to mp3 file (without usage of server)
Ive seen online apps that can do that, nothing is transmitted to server, all happens in the browser.
when we inspect wavesurfer, we have access to these:
The goal would be to use already available references from wavesurfer to produce the download mp3.
from my understanding this can be done with MediaRecorder, WebCodecs API or some libraries like lamejs.
Ive tried to find working example of how to do it with two first methods but without luck. I also tried to do it with lamejs using their example provided on the git but i am getting errors from the lib that are hard to debug, most likely related to providing wrong input.
So far i only managed to download wav file using following script:
handleCopyRegion = (region, instance) => {
var segmentDuration = region.end - region.start;
var originalBuffer = instance.backend.buffer;
var emptySegment = instance.backend.ac.createBuffer(
originalBuffer.numberOfChannels,
Math.ceil(segmentDuration * originalBuffer.sampleRate),
originalBuffer.sampleRate
);
for (var i = 0; i < originalBuffer.numberOfChannels; i++) {
var chanData = originalBuffer.getChannelData(i);
var emptySegmentData = emptySegment.getChannelData(i);
var mid_data = chanData.subarray(
Math.ceil(region.start * originalBuffer.sampleRate),
Math.ceil(region.end * originalBuffer.sampleRate)
);
emptySegmentData.set(mid_data);
}
return emptySegment;
};
bufferToWave = (abuffer, offset, len) => {
var numOfChan = abuffer.numberOfChannels,
length = len * numOfChan * 2 + 44,
buffer = new ArrayBuffer(length),
view = new DataView(buffer),
channels = [],
i,
sample,
pos = 0;
// write WAVE header
setUint32(0x46464952); // "RIFF"
setUint32(length - 8); // file length - 8
setUint32(0x45564157); // "WAVE"
setUint32(0x20746d66); // "fmt " chunk
setUint32(16); // length = 16
setUint16(1); // PCM (uncompressed)
setUint16(numOfChan);
setUint32(abuffer.sampleRate);
setUint32(abuffer.sampleRate * 2 * numOfChan); // avg. bytes/sec
setUint16(numOfChan * 2); // block-align
setUint16(16); // 16-bit (hardcoded in this demo)
setUint32(0x61746164); // "data" - chunk
setUint32(length - pos - 4); // chunk length
// write interleaved data
for (i = 0; i < abuffer.numberOfChannels; i++)
channels.push(abuffer.getChannelData(i));
while (pos < length) {
for (i = 0; i < numOfChan; i++) {
// interleave channels
sample = Math.max(-1, Math.min(1, channels[i][offset])); // clamp
sample = (0.5 + sample < 0 ? sample * 32768 : sample * 32767) | 0; // scale to 16-bit signed int
view.setInt16(pos, sample, true); // update data chunk
pos += 2;
}
offset++; // next source sample
}
// create Blob
return new Blob([buffer], { type: "audio/wav" });
function setUint16(data) {
view.setUint16(pos, data, true);
pos += 2;
}
function setUint32(data) {
view.setUint32(pos, data, true);
pos += 4;
}
};
const cutSelection = this.handleCopyRegion(
this.wavesurfer.regions.list.cut,
this.wavesurfer
);
const blob = this.bufferToWave(cutSelection, 0, cutSelection.length);
// you can now download wav from the blob
Is there a way to avoid making wav and right away make mp3 and download it, or if not make mp3 from that wav, if so how it can be done?
I mainly tried to use wavesurfer.backend.buffer as input, because this reference is AudioBuffer and accessing .getChannelData(0|1) gives you left and right channels. But didnt accomplish anything, maybe i am thinking wrong.
Alright, here is the steps we need to do:
Get buffer data from the wavesurfer player
Analyze the buffer to get the number of Channels(STEREO or MONO), channels data and Sample rate.
Use lamejs library to convert buffer to the MP3 blob file
Then we can get that download link from blob
Here is a quick DEMO
and also the JS code:
function downloadMp3() {
var MP3Blob = analyzeAudioBuffer(wavesurfer.backend.buffer);
console.log('here is your mp3 url:');
console.log(URL.createObjectURL(MP3Blob));
}
function analyzeAudioBuffer(aBuffer) {
let numOfChan = aBuffer.numberOfChannels,
btwLength = aBuffer.length * numOfChan * 2 + 44,
btwArrBuff = new ArrayBuffer(btwLength),
btwView = new DataView(btwArrBuff),
btwChnls = [],
btwIndex,
btwSample,
btwOffset = 0,
btwPos = 0;
setUint32(0x46464952); // "RIFF"
setUint32(btwLength - 8); // file length - 8
setUint32(0x45564157); // "WAVE"
setUint32(0x20746d66); // "fmt " chunk
setUint32(16); // length = 16
setUint16(1); // PCM (uncompressed)
setUint16(numOfChan);
setUint32(aBuffer.sampleRate);
setUint32(aBuffer.sampleRate * 2 * numOfChan); // avg. bytes/sec
setUint16(numOfChan * 2); // block-align
setUint16(16); // 16-bit
setUint32(0x61746164); // "data" - chunk
setUint32(btwLength - btwPos - 4); // chunk length
for (btwIndex = 0; btwIndex < aBuffer.numberOfChannels; btwIndex++)
btwChnls.push(aBuffer.getChannelData(btwIndex));
while (btwPos < btwLength) {
for (btwIndex = 0; btwIndex < numOfChan; btwIndex++) {
// interleave btwChnls
btwSample = Math.max(-1, Math.min(1, btwChnls[btwIndex][btwOffset])); // clamp
btwSample = (0.5 + btwSample < 0 ? btwSample * 32768 : btwSample * 32767) | 0; // scale to 16-bit signed int
btwView.setInt16(btwPos, btwSample, true); // write 16-bit sample
btwPos += 2;
}
btwOffset++; // next source sample
}
let wavHdr = lamejs.WavHeader.readHeader(new DataView(btwArrBuff));
//Stereo
let data = new Int16Array(btwArrBuff, wavHdr.dataOffset, wavHdr.dataLen / 2);
let leftData = [];
let rightData = [];
for (let i = 0; i < data.length; i += 2) {
leftData.push(data[i]);
rightData.push(data[i + 1]);
}
var left = new Int16Array(leftData);
var right = new Int16Array(rightData);
//STEREO
if (wavHdr.channels===2)
return bufferToMp3(wavHdr.channels, wavHdr.sampleRate, left,right);
//MONO
else if (wavHdr.channels===1)
return bufferToMp3(wavHdr.channels, wavHdr.sampleRate, data);
function setUint16(data) {
btwView.setUint16(btwPos, data, true);
btwPos += 2;
}
function setUint32(data) {
btwView.setUint32(btwPos, data, true);
btwPos += 4;
}
}
function bufferToMp3(channels, sampleRate, left, right = null) {
var buffer = [];
var mp3enc = new lamejs.Mp3Encoder(channels, sampleRate, 128);
var remaining = left.length;
var samplesPerFrame = 1152;
for (var i = 0; remaining >= samplesPerFrame; i += samplesPerFrame) {
if (!right)
{
var mono = left.subarray(i, i + samplesPerFrame);
var mp3buf = mp3enc.encodeBuffer(mono);
}
else {
var leftChunk = left.subarray(i, i + samplesPerFrame);
var rightChunk = right.subarray(i, i + samplesPerFrame);
var mp3buf = mp3enc.encodeBuffer(leftChunk,rightChunk);
}
if (mp3buf.length > 0) {
buffer.push(mp3buf);//new Int8Array(mp3buf));
}
remaining -= samplesPerFrame;
}
var d = mp3enc.flush();
if(d.length > 0){
buffer.push(new Int8Array(d));
}
var mp3Blob = new Blob(buffer, {type: 'audio/mpeg'});
//var bUrl = window.URL.createObjectURL(mp3Blob);
// send the download link to the console
//console.log('mp3 download:', bUrl);
return mp3Blob;
}
Let me know if you have any question about the code
I am using piexifjs to manipulate exif data of jpg images. everything works fine. but when I try to modify the gps longitude and latitudes, I am having some issues.
as on https://www.exiv2.org/tags.html, it states the datatype for gps coordinates it a Rational and I am having trouble getting how it works.
gps[piexif.GPSIFD.GPSLatitude] = 23.234;
gps[piexif.GPSIFD.GPSLatitudeRef] = "S";
OR
gps[piexif.GPSIFD.GPSLatitude] = [23,23,23];
gps[piexif.GPSIFD.GPSLatitudeRef] = "S";
I can add all the others like Author, XPTitle... and yet it It doesn't work at all for gps coordinates.
This worked for me.
It accepts in an array of dimention [3,2] eg. [[59, 1], [26, 1], [794, 100]]
var filename1 = "image.jpg";
var filename2 = "out.jpg";
var jpeg = fs.readFileSync(filename1);
var data = jpeg.toString("binary");
var exifObj = piexif.load(data);
exifObj.GPS[piexif.GPSIFD.GPSLatitude] = degToDmsRational(23.2342);
exifObj.GPS[piexif.GPSIFD.GPSLatitudeRef] = "N";
exifObj.GPS[piexif.GPSIFD.GPSLongitude] = degToDmsRational(2.343);
exifObj.GPS[piexif.GPSIFD.GPSLongitudeRef] = "W";
var exifbytes = piexif.dump(exifObj);
var newData = piexif.insert(exifbytes, data);
var newJpeg = Buffer.from(newData, "binary");
fs.writeFileSync(filename2, newJpeg);
function degToDmsRational(degFloat) {
var minFloat = degFloat % 1 * 60
var secFloat = minFloat % 1 * 60
var deg = Math.floor(degFloat)
var min = Math.floor(minFloat)
var sec = Math.round(secFloat * 100)
deg = Math.abs(deg) * 1
min = Math.abs(min) * 1
sec = Math.abs(sec) * 1
return [[deg, 1], [min, 1], [sec, 100]]
}
thanks to https://github.com/hMatoba/piexifjs/issues/1#issuecomment-260176317
I'm trying to take a jpg photo, and make it grayscale and resize it to 48x48 for my model,
I tried this but it doesn't work:
let image = require("../assets/angry.jpg");
const imageAssetPath = Image.resolveAssetSource(image);
const response = await fetch(imageAssetPath.uri, {}, { isBinary: true });
const imageData = await response.arrayBuffer();
let imageTensor = imageToTensor(imageData);
const imageResize = tf.image.resizeBilinear(imageTensor, [48, 48], true);
const imageToTensor = (rawData: ArrayBuffer) => {
const { width, height, data } = jpeg.decode(rawData, true);
const buffer = new Uint8Array(width * height * 3);
let offset = 0;
for (let i = 0; i < buffer.length; i += 3) {
buffer[i] = data[offset]; //red
buffer[i + 1] = data[offset + 1]; //green
buffer[i + 2] = data[offset + 2]; //blue
offset += 4; //skips Alpha value
}
return tf.tensor4d(buffer, [1, height, width, 3]);
};
the image is resizing to 48x48 but how do I make it grayscale? I tried in imageToTensor function to change the array to [height,width,1] but it only messed up the picture, any suggestions?
I didn't know there are so many methods missing!
You can look at the source of rgb_to_grayscale in python
and you'll see, how they convert rgb images to grayscale.
I tried to implement it the same way in javascript, but there is no function called tf.tensordot.
Here's how you can do it.
image = tf.ones([224, 224, 3])
rgb_weights = [0.2989, 0.5870, 0.1140]
image = tf.mul(image, rgb_weights)
image = tf.sum(image, axis=-1)
image = tf.expandDims(image, axis=-1)
I want to convert image (png/jpeg) to ICO using javascript in frontend.
On searching the web, I came across this code on github: https://gist.github.com/twolfson/7656254 but unfortunately it uses fs module of nodejs (+ the code is very difficult to compehend).
Can someone tell guide me on what should I search/or a way through which I can convert png/jpeg to ico using javascript in frontend?
Alternates I have tried?
Used this repo: https://github.com/fiahfy/ico-convert but they use sharp and sharp isn't supported on client side
On googling, I got this Mozilla post, with examples, which provides the following code for conversion to ICO format (limited to Firefox browser only),
A way to convert a canvas to an ico (Mozilla only)
This uses -moz-parse to convert the canvas to ico. Windows XP doesn't
support converting from PNG to ico, so it uses bmp instead. A download
link is created by setting the download attribute. The value of the
download attribute is the name it will use as the file name.
The code:
var canvas = document.getElementById('canvas');
var d = canvas.width;
ctx = canvas.getContext('2d');
ctx.beginPath();
ctx.moveTo(d / 2, 0);
ctx.lineTo(d, d);
ctx.lineTo(0, d);
ctx.closePath();
ctx.fillStyle = 'yellow';
ctx.fill();
function blobCallback(iconName) {
return function(b) {
var a = document.createElement('a');
a.textContent = 'Download';
document.body.appendChild(a);
a.style.display = 'block';
a.download = iconName + '.ico';
a.href = window.URL.createObjectURL(b);
}
}
canvas.toBlob(blobCallback('passThisString'),
'image/vnd.microsoft.icon',
'-moz-parse-options:format=bmp;bpp=32'
);
Apart from that, I'd found no other ways to convert png/jpeg into ICO format. Alternatively, you can do the conversion on the server-side by using any of the following modules:
to-ico
image-to-icon
png-to-ico
Should you want to support every browser and have only a PNG image, the .ICO file format supports embedded PNG images as long as they are smaller than 256x256. Based on the ICO file format, I've been able to construct an ICO using a small PNG image and a hex editor. This can be replicated in JavaScript. This is my testing image file:
To convert it into an ICO, I prepended the following hex data, little-endian encoded (the bytes in the values are reversed):
00 00 01 00 - File header. Says "This file is an ICO."
01 00 - There is one image in this file.
9C - This image is 0x9C pixels wide. **This should be variable**
77 - This image is 0x77 pixels tall. **This should be variable**
00 - There is not a limited color pallette.
00 - Reserved value.
01 00 - There is one color plane in this image.
18 00 - There are 0x18 bits per pixel (24 bits per pixel is standard RGB encoding)
8A 06 00 00 - This image is 0x0000068A large. **This should be variable**
16 00 00 00 - There were 0x16 bytes before this point.
[PNG data here]
This successfully created an ISO file from the PNG. You can create a simple JavaScript script for this prepending. Looking at the PNG specification, the first 8 bytes are a header, followed by 8 bytes of IHDR chunk metadata, which starts with a 4-byte little-endian width and a 4-byte little-endian height. This can be used in our script to discover the PNG's width and height. Something like:
function pngToIco(icoFile, pngData) {
icoFile = "\x00\x00\x01\x00\x01\x00"; // First 6 bytes are constant
icoFile += pngData[15+4]; // PNG width byte
icoFile += pngData[15+8]; // PNG height byte
// Make sure PNG is less than 256x256
if (pngData[15+1] || pngData[15+2] || pngData[15+3]) {
console.log("Width over 255!"); return;
}
if (pngData[15+5] || pngData[15+6] || pngData[15+7]) {
console.log("Height over 255!"); return;
}
// Add more (probably constant) information
icoFile += "\x00\x00\x01\x00\x18\x00";
// Add encoded length
var lenBytes = pngData.length;
for (var i=0; i<4; i++) {
icoFile += String.fromCharCode(lenBytes % 256);
lenBytes >>= 4;
}
// We had 0x16 bytes before now
icoFile += "\x16\x00\x00\x00";
// Now add the png data
icoFile += pngData;
// Now we have a valid ico file!
return icoFile;
}
Here's another solution if you're willing to bend the rules of this question being a JavaScript question. If your web browser supports WebAssembly (most modern browsers do), you could use a version of the well-known library ImageMagick cross-compiled into WebAssembly. Here is what I found: https://github.com/KnicKnic/WASM-ImageMagick
This library takes in image data from a sourceBytes buffer, and returns a transformed or converted image. According to the documentation, you can use it with a syntax similar to ImageMagick's terminal syntax, with a bit of extra code (copied from documentation and modified):
<script type='module'>
import * as Magick from 'https://knicknic.github.io/wasm-imagemagick/magickApi.js';
async function converPNGToIco(pngData) {
var icoData = await Magick.Call([{ 'name': 'srcFile.png', 'content': pngData }], ["convert", "srcFile.png", "-resize", "200x200", "outFile.ico"]);
// do stuff with icoData
}
</script>
Here is a pure JavaScript version (inspired by #id01) that converts an array of png image data (each images as an array of bytes) and returns an array of bytes for .ico file
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);
}
Example how to use by converting multiple canvas images into .ico:
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>
https://jsfiddle.net/vanowm/b657yksg/
I wrote an ES6 module that pack PNG files into ICO container: PNG2ICOjs. Everything works in modern browsers without any dependency.
import { PngIcoConverter } from "../src/png2icojs.js";
// ...
const inputs = [...files].map(file => ({
png: file
}));
// Result is a Blob
const resultBlob1 = await converter.convertToBlobAsync(inputs); // Default mime type is image/x-icon
const resultBlob2 = await converter.convertToBlobAsync(inputs, "image/your-own-mime");
// Result is an Uint8Array
const resultArr = await converter.convertAsync(inputs);
this should works
github url:https://github.com/egy186/icojs
<input type="file" id="input-file" />
<script>
document.getElementById('input-file').addEventListener('change', function (evt) {
// use FileReader for converting File object to ArrayBuffer object
var reader = new FileReader();
reader.onload = function (e) {
ICO.parse(e.target.result).then(function (images) {
// logs images
console.dir(images);
})
};
reader.readAsArrayBuffer(evt.target.files[0]);
}, false);
</script>
Fully working example.
Here is JSFiddle: click (use it because this site's Code snippet do not allow me to click on a[download] link), but other code is working in code snippet - you can open links in new tab to see it.
var MyBlobBuilder = function() {
this.parts = [];
}
MyBlobBuilder.prototype.append = function(part) {
this.parts.push(part);
this.blob = undefined; // Invalidate the blob
};
MyBlobBuilder.prototype.write = function(part) {
this.append(part);
}
MyBlobBuilder.prototype.getBlob = function(atype) {
if (!this.blob) {
this.blob = new Blob(this.parts, {
type: !atype ? "text/plain" : atype
});
}
return this.blob;
};
const img = document.getElementById('input'),
a = document.getElementById('a'),
a2 = document.getElementById('a2'),
file1 = document.getElementById('file');
let imgSize = 0,
imgBlob;
img.onload = e => {
fetch(img.src).then(resp => resp.blob())
.then(blob => {
imgBlob = blob;
imgSize = blob.size;
});
};
function convertToIco(imgSize, imgBlob) {
let file = new MyBlobBuilder(),
buff;
// Write out the .ico header [00, 00]
// Reserved space
buff = new Uint8Array([0, 0]).buffer;
file.write(buff, 'binary');
// Indiciate ico file [01, 00]
buff = new Uint8Array([1, 0]).buffer;
file.write(buff, 'binary');
// Indiciate 1 image [01, 00]
buff = new Uint8Array([1, 0]).buffer;
file.write(buff, 'binary');
// Image is 50 px wide [32]
buff = new Uint8Array([img.width < 256 ? img.width : 0]).buffer;
file.write(buff, 'binary');
// Image is 50 px tall [32]
buff = new Uint8Array([img.height < 256 ? img.height : 0]).buffer;
file.write(buff, 'binary');
// Specify no color palette [00]
// TODO: Not sure if this is appropriate
buff = new Uint8Array([0]).buffer;
file.write(buff, 'binary');
// Reserved space [00]
// TODO: Not sure if this is appropriate
buff = new Uint8Array([0]).buffer;
file.write(buff, 'binary');
// Specify 1 color plane [01, 00]
// TODO: Not sure if this is appropriate
buff = new Uint8Array([1, 0]).buffer;
file.write(buff, 'binary');
// Specify 32 bits per pixel (bit depth) [20, 00]
// TODO: Quite confident in this one
buff = new Uint8Array([32, 0]).buffer;
file.write(buff, 'binary');
// Specify image size in bytes
// DEV: Assuming LE means little endian [84, 01, 00, 00] = 388 byte
// TODO: Semi-confident in this one
buff = new Uint32Array([imgSize]).buffer;
file.write(buff, 'binary');
// Specify image offset in bytes
// TODO: Not that confident in this one [16]
buff = new Uint32Array([22]).buffer;
file.write(buff, 'binary');
// Dump the .png
file.write(imgBlob, 'binary');
return file.getBlob('image/vnd.microsoft.icon');
}
function test() {
const ico = convertToIco(imgSize, imgBlob);
let url = window.URL.createObjectURL(ico);
a.href = url;
document.getElementById('result1').style.display = '';
}
file1.addEventListener('change', () => {
var file = file1.files[0];
var fileReader = new FileReader();
fileReader.onloadend = function(e) {
var arrayBuffer = e.target.result;
const blobFile = new Blob([arrayBuffer]);
const ico = convertToIco(blobFile.size, blobFile);
let url = window.URL.createObjectURL(ico);
a2.href = url;
document.getElementById('result2').style.display = '';
}
fileReader.readAsArrayBuffer(file);
});
.section {
padding: 4px;
border: 1px solid gray;
margin: 4px;
}
h3 {
margin: 0 0 4px 0;
}
<div class="section">
<h3>Convert img[src] to ICO</h3>
<div>
Example (PNG): <img src="https://gist.githubusercontent.com/twolfson/7656254/raw/c5d0dcedc0e212f177695f08d685af5aad9ff865/sprite1.png" id="input" />
</div>
<div>
<button onclick="test()">Conver to ICO</button>
</div>
<div id="result1" style="display:none">
Save ICO: <a id="a" href="#" download="example.ico">click me</a>
</div>
</div>
<div class="section">
<h3>Convert input[type=file] to ICO</h3>
<input type="file" id="file" />
<div id="result2" style="display:none">
Save ICO: <a id="a2" href="#" download="example.ico">click me</a>
</div>
</div>
P.S. Used documentation: Wikipedia.