heic-to 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.tool-versions +1 -0
- package/README.md +72 -0
- package/dist/heic-to.js +84 -0
- package/dist/heic-to.min.js +1 -0
- package/esbuild.mjs +64 -0
- package/example/index.html +76 -0
- package/package.json +24 -0
- package/src/index.js +75 -0
- package/src/lib/libheif.js +39 -0
- package/src/worker.js +49 -0
package/esbuild.mjs
ADDED
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
import * as esbuild from 'esbuild';
|
|
2
|
+
import fs from 'fs'
|
|
3
|
+
|
|
4
|
+
// Build heic-to.js
|
|
5
|
+
|
|
6
|
+
await esbuild.build({
|
|
7
|
+
entryPoints: [
|
|
8
|
+
'src/worker.js'
|
|
9
|
+
],
|
|
10
|
+
bundle: true,
|
|
11
|
+
minify: false,
|
|
12
|
+
target: 'es6',
|
|
13
|
+
platform: 'browser',
|
|
14
|
+
outfile: 'tmp/worker.js',
|
|
15
|
+
external: ['fs', 'path']
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
const workerFileContent = fs.readFileSync('tmp/worker.js', 'utf8')
|
|
19
|
+
|
|
20
|
+
esbuild.build({
|
|
21
|
+
entryPoints: [
|
|
22
|
+
'src/index.js'
|
|
23
|
+
],
|
|
24
|
+
bundle: true,
|
|
25
|
+
minify: false,
|
|
26
|
+
target: 'es6',
|
|
27
|
+
platform: 'browser',
|
|
28
|
+
outfile: 'dist/heic-to.js',
|
|
29
|
+
format: 'esm',
|
|
30
|
+
define: {
|
|
31
|
+
WORKER_FILE_CONTENT: JSON.stringify(workerFileContent)
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
|
|
35
|
+
// Build heic-to.min.js
|
|
36
|
+
|
|
37
|
+
await esbuild.build({
|
|
38
|
+
entryPoints: [
|
|
39
|
+
'src/worker.js'
|
|
40
|
+
],
|
|
41
|
+
bundle: true,
|
|
42
|
+
minify: true,
|
|
43
|
+
target: 'es6',
|
|
44
|
+
platform: 'browser',
|
|
45
|
+
outfile: 'tmp/worker.min.js',
|
|
46
|
+
external: ['fs', 'path']
|
|
47
|
+
});
|
|
48
|
+
|
|
49
|
+
const workerFileMinifyContent = fs.readFileSync('tmp/worker.min.js', 'utf8')
|
|
50
|
+
|
|
51
|
+
esbuild.build({
|
|
52
|
+
entryPoints: [
|
|
53
|
+
'src/index.js'
|
|
54
|
+
],
|
|
55
|
+
bundle: true,
|
|
56
|
+
minify: true,
|
|
57
|
+
target: 'es6',
|
|
58
|
+
platform: 'browser',
|
|
59
|
+
outfile: 'dist/heic-to.min.js',
|
|
60
|
+
format: 'esm',
|
|
61
|
+
define: {
|
|
62
|
+
WORKER_FILE_CONTENT: JSON.stringify(workerFileMinifyContent)
|
|
63
|
+
}
|
|
64
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
<!DOCTYPE html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="UTF-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
|
6
|
+
<title>heic-to</title>
|
|
7
|
+
<style>
|
|
8
|
+
body {
|
|
9
|
+
display: flex;
|
|
10
|
+
justify-content: center;
|
|
11
|
+
align-items: center;
|
|
12
|
+
flex-direction: column;
|
|
13
|
+
height: 100vh;
|
|
14
|
+
}
|
|
15
|
+
input {
|
|
16
|
+
border: 1px solid black;
|
|
17
|
+
padding: 10px;
|
|
18
|
+
}
|
|
19
|
+
label {
|
|
20
|
+
display: block;;
|
|
21
|
+
padding-top: 20px;
|
|
22
|
+
padding-bottom: 20px;
|
|
23
|
+
color: red;
|
|
24
|
+
}
|
|
25
|
+
.images {
|
|
26
|
+
width: 200px;
|
|
27
|
+
height: 200px;
|
|
28
|
+
}
|
|
29
|
+
img {
|
|
30
|
+
display: block;
|
|
31
|
+
width: 100%;
|
|
32
|
+
}
|
|
33
|
+
</style>
|
|
34
|
+
</head>
|
|
35
|
+
<body>
|
|
36
|
+
|
|
37
|
+
<h1>heic-to</h1>
|
|
38
|
+
|
|
39
|
+
<main>
|
|
40
|
+
<input type="file">
|
|
41
|
+
<label></label>
|
|
42
|
+
<div class="images"></div>
|
|
43
|
+
</main>
|
|
44
|
+
|
|
45
|
+
<script type="module">
|
|
46
|
+
import {heicTo, isHeic} from '../dist/heic-to.js'
|
|
47
|
+
|
|
48
|
+
const imagesContainer = document.querySelector(".images")
|
|
49
|
+
const messageLabel = document.querySelector("label")
|
|
50
|
+
const field = document.querySelector("input")
|
|
51
|
+
|
|
52
|
+
field.addEventListener('change', async (event) => {
|
|
53
|
+
|
|
54
|
+
let imageDom = document.querySelector("img")
|
|
55
|
+
if (!imageDom) {
|
|
56
|
+
imageDom = document.createElement('img')
|
|
57
|
+
imagesContainer.appendChild(imageDom)
|
|
58
|
+
}
|
|
59
|
+
imageDom.src = ''
|
|
60
|
+
imageDom.alt = "Loading..."
|
|
61
|
+
messageLabel.textContent = "Loading..."
|
|
62
|
+
|
|
63
|
+
const image = field.files[0]
|
|
64
|
+
if (await isHeic(image)) {
|
|
65
|
+
messageLabel.textContent = "It's a HEIC image"
|
|
66
|
+
const heifImage = await heicTo({blob: image, type: 'image/jpeg', quality: 0.5})
|
|
67
|
+
imageDom.src = URL.createObjectURL(heifImage)
|
|
68
|
+
} else {
|
|
69
|
+
messageLabel.textContent = "It's not a HEIC image"
|
|
70
|
+
imageDom.src = URL.createObjectURL(image)
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
})
|
|
74
|
+
</script>
|
|
75
|
+
</body>
|
|
76
|
+
</html>
|
package/package.json
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "heic-to",
|
|
3
|
+
"version": "1.0.0",
|
|
4
|
+
"description": "Converting HEIF/HEIF image formats to PNG/JPEG in the browser",
|
|
5
|
+
"main": "dist/heic-to.js",
|
|
6
|
+
"scripts": {
|
|
7
|
+
"build": "node esbuild.mjs",
|
|
8
|
+
"s": "npx http-server -o ./example"
|
|
9
|
+
},
|
|
10
|
+
"repository": {
|
|
11
|
+
"type": "git",
|
|
12
|
+
"url": "https://github.com/hoppergee/heic-to.git"
|
|
13
|
+
},
|
|
14
|
+
"keywords": [
|
|
15
|
+
"heic", "heif", "jpeg", "jpg", "png", "conversion", "convert", "image", "decoder"
|
|
16
|
+
],
|
|
17
|
+
"author": "Hopper Gee",
|
|
18
|
+
"license": "LGPL-3.0",
|
|
19
|
+
"homepage": "https://github.com/hoppergee/heic-to",
|
|
20
|
+
"devDependencies": {
|
|
21
|
+
"esbuild": "0.23.0",
|
|
22
|
+
"http-server": "^14.1.1"
|
|
23
|
+
}
|
|
24
|
+
}
|
package/src/index.js
ADDED
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
|
|
2
|
+
const isHeic = async (file) => {
|
|
3
|
+
const buffer = await file.arrayBuffer()
|
|
4
|
+
const slicedBuffer = buffer.slice(8, 12)
|
|
5
|
+
const brandMajor = new TextDecoder('utf-8')
|
|
6
|
+
.decode(slicedBuffer)
|
|
7
|
+
.replace('\0', ' ')
|
|
8
|
+
.trim();
|
|
9
|
+
|
|
10
|
+
switch (brandMajor) {
|
|
11
|
+
case 'mif1':
|
|
12
|
+
return true; // {ext: 'heic', mime: 'image/heif'};
|
|
13
|
+
case 'msf1':
|
|
14
|
+
return true; // {ext: 'heic', mime: 'image/heif-sequence'};
|
|
15
|
+
case 'heic':
|
|
16
|
+
case 'heix':
|
|
17
|
+
return true; // {ext: 'heic', mime: 'image/heic'};
|
|
18
|
+
case 'hevc':
|
|
19
|
+
case 'hevx':
|
|
20
|
+
return true; // {ext: 'heic', mime: 'image/heic-sequence'};
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return false;
|
|
24
|
+
};
|
|
25
|
+
|
|
26
|
+
const workerFileContent = WORKER_FILE_CONTENT
|
|
27
|
+
const workerBlob = new Blob([workerFileContent], {type: 'application/javascript'})
|
|
28
|
+
const worker = new Worker(URL.createObjectURL(workerBlob))
|
|
29
|
+
|
|
30
|
+
const decodeBuffer = async (buffer) => {
|
|
31
|
+
return new Promise((resolve, reject) => {
|
|
32
|
+
const id = (Math.random() * new Date().getTime()).toString();
|
|
33
|
+
const message = { id, buffer };
|
|
34
|
+
worker.postMessage(message);
|
|
35
|
+
worker.addEventListener(
|
|
36
|
+
"message",
|
|
37
|
+
(message) => {
|
|
38
|
+
if (message.data.id === id) {
|
|
39
|
+
if (message.data.error) {
|
|
40
|
+
return reject(message.data.error);
|
|
41
|
+
}
|
|
42
|
+
return resolve(message.data.imageData);
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
);
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
const encodeByCanvas = async (imageBuffer) => {
|
|
50
|
+
const imageData = await decodeBuffer(imageBuffer)
|
|
51
|
+
|
|
52
|
+
const canvas = document.createElement('canvas');
|
|
53
|
+
canvas.width = imageData.width;
|
|
54
|
+
canvas.height = imageData.height;
|
|
55
|
+
|
|
56
|
+
const ctx = canvas.getContext('2d')
|
|
57
|
+
ctx.putImageData(imageData, 0, 0)
|
|
58
|
+
return canvas;
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const heicTo = async ({blob, type, quality}) => {
|
|
62
|
+
const imageBuffer = await blob.arrayBuffer()
|
|
63
|
+
const canvas = await encodeByCanvas(imageBuffer);
|
|
64
|
+
return await new Promise((resolve, reject) => canvas.toBlob(blob => {
|
|
65
|
+
if (blob != null)
|
|
66
|
+
resolve(blob);
|
|
67
|
+
else
|
|
68
|
+
reject(`Can't convert canvas to blob.`);
|
|
69
|
+
}, type, quality));
|
|
70
|
+
};
|
|
71
|
+
|
|
72
|
+
export {
|
|
73
|
+
isHeic,
|
|
74
|
+
heicTo
|
|
75
|
+
}
|