perf(encoder): naive parallel frame process

This commit is contained in:
wryk 2019-03-13 20:40:53 +01:00
parent 8748502ee8
commit 3229a00604
6 changed files with 185 additions and 182 deletions

5
.babelrc Normal file
View file

@ -0,0 +1,5 @@
{
"plugins": [
["@babel/plugin-transform-runtime"]
]
}

View file

@ -1 +0,0 @@
module.exports = {}

241
package-lock.json generated
View file

@ -339,43 +339,6 @@
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/helper-regex": "^7.0.0",
"regexpu-core": "^4.2.0"
},
"dependencies": {
"jsesc": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
"dev": true
},
"regexpu-core": {
"version": "4.5.3",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.3.tgz",
"integrity": "sha512-LON8666bTAlViVEPXMv65ZqiaR3rMNLz36PIaQ7D+er5snu93k0peR7FSvO0QteYbZ3GOkvfHKbGr/B1xDu9FA==",
"dev": true,
"requires": {
"regenerate": "^1.4.0",
"regenerate-unicode-properties": "^8.0.1",
"regjsgen": "^0.5.0",
"regjsparser": "^0.6.0",
"unicode-match-property-ecmascript": "^1.0.4",
"unicode-match-property-value-ecmascript": "^1.1.0"
}
},
"regjsgen": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz",
"integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==",
"dev": true
},
"regjsparser": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz",
"integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==",
"dev": true,
"requires": {
"jsesc": "~0.5.0"
}
}
}
},
"@babel/plugin-syntax-async-generators": {
@ -514,43 +477,6 @@
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/helper-regex": "^7.0.0",
"regexpu-core": "^4.1.3"
},
"dependencies": {
"jsesc": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
"dev": true
},
"regexpu-core": {
"version": "4.5.3",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.3.tgz",
"integrity": "sha512-LON8666bTAlViVEPXMv65ZqiaR3rMNLz36PIaQ7D+er5snu93k0peR7FSvO0QteYbZ3GOkvfHKbGr/B1xDu9FA==",
"dev": true,
"requires": {
"regenerate": "^1.4.0",
"regenerate-unicode-properties": "^8.0.1",
"regjsgen": "^0.5.0",
"regjsparser": "^0.6.0",
"unicode-match-property-ecmascript": "^1.0.4",
"unicode-match-property-value-ecmascript": "^1.1.0"
}
},
"regjsgen": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz",
"integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==",
"dev": true
},
"regjsparser": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz",
"integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==",
"dev": true,
"requires": {
"jsesc": "~0.5.0"
}
}
}
},
"@babel/plugin-transform-duplicate-keys": {
@ -708,17 +634,18 @@
"dev": true,
"requires": {
"regenerator-transform": "^0.13.4"
},
"dependencies": {
"regenerator-transform": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.4.tgz",
"integrity": "sha512-T0QMBjK3J0MtxjPmdIMXm72Wvj2Abb0Bd4HADdfijwMdoIsyQZ6fWC7kDFhk2YinBBEMZDL7Y7wh0J1sGx3S4A==",
"dev": true,
"requires": {
"private": "^0.1.6"
}
}
}
},
"@babel/plugin-transform-runtime": {
"version": "7.3.4",
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-runtime/-/plugin-transform-runtime-7.3.4.tgz",
"integrity": "sha512-PaoARuztAdd5MgeVjAxnIDAIUet5KpogqaefQvPOmPYCxYoaPhautxDh3aO8a4xHsKgT/b9gSxR0BKK1MIewPA==",
"dev": true,
"requires": {
"@babel/helper-module-imports": "^7.0.0",
"@babel/helper-plugin-utils": "^7.0.0",
"resolve": "^1.8.1",
"semver": "^5.5.1"
}
},
"@babel/plugin-transform-shorthand-properties": {
@ -777,43 +704,6 @@
"@babel/helper-plugin-utils": "^7.0.0",
"@babel/helper-regex": "^7.0.0",
"regexpu-core": "^4.1.3"
},
"dependencies": {
"jsesc": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
"dev": true
},
"regexpu-core": {
"version": "4.5.3",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.3.tgz",
"integrity": "sha512-LON8666bTAlViVEPXMv65ZqiaR3rMNLz36PIaQ7D+er5snu93k0peR7FSvO0QteYbZ3GOkvfHKbGr/B1xDu9FA==",
"dev": true,
"requires": {
"regenerate": "^1.4.0",
"regenerate-unicode-properties": "^8.0.1",
"regjsgen": "^0.5.0",
"regjsparser": "^0.6.0",
"unicode-match-property-ecmascript": "^1.0.4",
"unicode-match-property-value-ecmascript": "^1.1.0"
}
},
"regjsgen": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz",
"integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==",
"dev": true
},
"regjsparser": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz",
"integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==",
"dev": true,
"requires": {
"jsesc": "~0.5.0"
}
}
}
},
"@babel/preset-env": {
@ -865,26 +755,12 @@
"invariant": "^2.2.2",
"js-levenshtein": "^1.1.3",
"semver": "^5.3.0"
},
"dependencies": {
"browserslist": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.4.2.tgz",
"integrity": "sha512-ISS/AIAiHERJ3d45Fz0AVYKkgcy+F/eJHzKEvv1j0wwKGKD9T3BrwKr/5g45L+Y4XIK5PlTqefHciRFcfE1Jxg==",
"dev": true,
"requires": {
"caniuse-lite": "^1.0.30000939",
"electron-to-chromium": "^1.3.113",
"node-releases": "^1.1.8"
}
}
}
},
"@babel/runtime": {
"version": "7.3.4",
"resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.3.4.tgz",
"integrity": "sha512-IvfvnMdSaLBateu0jfsYIpZTxAc2cKEXEMiezGGN75QcBcecDUKd3PgLAncT0oOgxKy8dd8hrJKj9MfzgfZd6g==",
"dev": true,
"requires": {
"regenerator-runtime": "^0.12.0"
},
@ -892,8 +768,7 @@
"regenerator-runtime": {
"version": "0.12.1",
"resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.12.1.tgz",
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg==",
"dev": true
"integrity": "sha512-odxIc1/vDlo4iZcfXqRYFj0vpXFNoGdKMAUieAlFYO6m/nl5e9KR/beGf41z4a1FI+aQgtjhuaSlDxQ0hmkrHg=="
}
}
},
@ -5570,12 +5445,11 @@
}
},
"p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
"integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
"dev": true,
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.2.0.tgz",
"integrity": "sha512-pZbTJpoUsCzV48Mc9Nh51VbwO0X9cuPFE8gYwx9BTCt9SF8/b7Zljd2fVgOxhIF/HDTKgpVzs+GPhyKfjLLFRQ==",
"requires": {
"p-try": "^1.0.0"
"p-try": "^2.0.0"
}
},
"p-locate": {
@ -5585,13 +5459,29 @@
"dev": true,
"requires": {
"p-limit": "^1.1.0"
},
"dependencies": {
"p-limit": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/p-limit/-/p-limit-1.3.0.tgz",
"integrity": "sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==",
"dev": true,
"requires": {
"p-try": "^1.0.0"
}
},
"p-try": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
"dev": true
}
}
},
"p-try": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-1.0.0.tgz",
"integrity": "sha1-y8ec26+P1CKOE/Yh8rGiN8GyB7M=",
"dev": true
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/p-try/-/p-try-2.0.0.tgz",
"integrity": "sha512-hMp0onDKIajHfIkdRk3P4CdCmErkYAxxDtP3Wx/4nZ3aGlau2VKh3mZpcuFkH27WQkL/3WBCPOktzA9ZOAnMQQ=="
},
"pako": {
"version": "1.0.10",
@ -6516,6 +6406,11 @@
"integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==",
"dev": true
},
"promises-progress": {
"version": "1.0.3",
"resolved": "https://registry.npmjs.org/promises-progress/-/promises-progress-1.0.3.tgz",
"integrity": "sha512-qeVptRZbEqo/JhWVmaAGIG14QnAXXyLj7EI+HKhduqKCl3mMVslMeqxC3tPyxy3N7ffqwbe56vrxqweyKHe1Yg=="
},
"proto-list": {
"version": "1.2.4",
"resolved": "https://registry.npmjs.org/proto-list/-/proto-list-1.2.4.tgz",
@ -6667,9 +6562,9 @@
"integrity": "sha512-1G6jJVDWrt0rK99kBjvEtziZNCICAuvIPkSiUFIQxVP06RCVpq3dmDo2oi6ABpYaDYaTRr67BEhL8r1wgEZZKg=="
},
"regenerate-unicode-properties": {
"version": "8.0.1",
"resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.0.1.tgz",
"integrity": "sha512-HTjMafphaH5d5QDHuwW8Me6Hbc/GhXg8luNqTkPVwZ/oCZhnoifjWhGYsu2BzepMELTlbnoVcXvV0f+2uDDvoQ==",
"version": "8.0.2",
"resolved": "https://registry.npmjs.org/regenerate-unicode-properties/-/regenerate-unicode-properties-8.0.2.tgz",
"integrity": "sha512-SbA/iNrBUf6Pv2zU8Ekv1Qbhv92yxL4hiDa2siuxs4KKn4oOoMDHXjAf7+Nz9qinUQ46B1LcWEi/PhJfPWpZWQ==",
"dev": true,
"requires": {
"regenerate": "^1.4.0"
@ -6681,6 +6576,15 @@
"integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg==",
"dev": true
},
"regenerator-transform": {
"version": "0.13.4",
"resolved": "https://registry.npmjs.org/regenerator-transform/-/regenerator-transform-0.13.4.tgz",
"integrity": "sha512-T0QMBjK3J0MtxjPmdIMXm72Wvj2Abb0Bd4HADdfijwMdoIsyQZ6fWC7kDFhk2YinBBEMZDL7Y7wh0J1sGx3S4A==",
"dev": true,
"requires": {
"private": "^0.1.6"
}
},
"regex-not": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/regex-not/-/regex-not-1.0.2.tgz",
@ -6703,6 +6607,43 @@
"integrity": "sha512-lv0M6+TkDVniA3aD1Eg0DVpfU/booSu7Eev3TDO/mZKHBfVjgCGTV4t4buppESEYDtkArYFOxTJWv6S5C+iaNw==",
"dev": true
},
"regexpu-core": {
"version": "4.5.4",
"resolved": "https://registry.npmjs.org/regexpu-core/-/regexpu-core-4.5.4.tgz",
"integrity": "sha512-BtizvGtFQKGPUcTy56o3nk1bGRp4SZOTYrDtGNlqCQufptV5IkkLN6Emw+yunAJjzf+C9FQFtvq7IoA3+oMYHQ==",
"dev": true,
"requires": {
"regenerate": "^1.4.0",
"regenerate-unicode-properties": "^8.0.2",
"regjsgen": "^0.5.0",
"regjsparser": "^0.6.0",
"unicode-match-property-ecmascript": "^1.0.4",
"unicode-match-property-value-ecmascript": "^1.1.0"
},
"dependencies": {
"jsesc": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/jsesc/-/jsesc-0.5.0.tgz",
"integrity": "sha1-597mbjXW/Bb3EP6R1c9p9w8IkR0=",
"dev": true
},
"regjsgen": {
"version": "0.5.0",
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.5.0.tgz",
"integrity": "sha512-RnIrLhrXCX5ow/E5/Mh2O4e/oa1/jW0eaBKTSy3LaCj+M3Bqvm97GWDp2yUtzIs4LEn65zR2yiYGFqb2ApnzDA==",
"dev": true
},
"regjsparser": {
"version": "0.6.0",
"resolved": "https://registry.npmjs.org/regjsparser/-/regjsparser-0.6.0.tgz",
"integrity": "sha512-RQ7YyokLiQBomUJuUG8iGVvkgOLxwyZM8k6d3q5SAXpg4r5TZJZigKFvC6PpD+qQ98bCDC5YelPeA3EucDoNeQ==",
"dev": true,
"requires": {
"jsesc": "~0.5.0"
}
}
}
},
"regjsgen": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/regjsgen/-/regjsgen-0.2.0.tgz",

View file

@ -9,14 +9,19 @@
"lint-fix": "eslint --ext .js,vue . --fix"
},
"dependencies": {
"@babel/runtime": "^7.3.4",
"eventemitter3": "^3.1.0",
"gif-writer": "^0.9.3",
"objectFitPolyfill": "^2.1.1",
"p-limit": "^2.2.0",
"postcss-modules": "^1.4.1",
"promises-progress": "^1.0.3",
"vue": "^2.6.6",
"vuex": "^3.0.1"
},
"devDependencies": {
"@babel/core": "^7.3.4",
"@babel/plugin-transform-runtime": "^7.3.4",
"@vue/component-compiler-utils": "^2.6.0",
"autoprefixer": "^9.4.10",
"eslint": "^5.15.1",

View file

@ -1,10 +1,9 @@
import {
GifWriter,
MedianCutColorReducer,
IndexedColorImage
} from 'gif-writer'
import { GifWriter } from 'gif-writer'
import pLimit from 'p-limit'
onmessage = (event) => {
onmessage = handleMessage
async function handleMessage (event) {
const { imageDataList, imageWidth, imageHeight, paletteSize, delayTime } = event.data
const outputStream = new OutputStream()
@ -21,15 +20,24 @@ onmessage = (event) => {
writer.writeLoopControlInfo(0)
const indexedColorImages = imageDataList.map((imageData, index, { length }) => {
const indexedColorImage = imageDataToIndexedColorImage(imageData, paletteSize)
postProgressMessage(calcProgress(0, 0.9, length, index + 1))
return indexedColorImage
console.time('quantization')
const limit = pLimit(8)
const promises = imageDataList
.map(imageData => limit(() => convertImageDataToIndexedColorImage(imageData, paletteSize)))
const progressPromises = promisesProgress(promises, function (value) {
postProgressMessage(calcProgress(0, 0.9, value))
})
const indexedColorImages = await Promise.all(progressPromises)
console.timeEnd('quantization')
indexedColorImages.forEach((indexedColorImage, index, { length }) => {
writer.writeTableBasedImageWithGraphicControl(indexedColorImage, { delayTimeInMS: delayTime })
postProgressMessage(calcProgress(0.9, 1, length, index + 1))
postProgressMessage(calcProgress(0.9, 1, (index + 1) / length))
})
writer.writeTrailer()
@ -51,29 +59,40 @@ class OutputStream {
}
}
function imageDataToIndexedColorImage (imageData, paletteSize) {
var reducer = new MedianCutColorReducer(imageData, paletteSize)
var paletteData = reducer.process()
var dat = Array.prototype.slice.call(imageData.data)
function promisesProgress (promises, progress) {
let complete = 0
var indexedColorImageData = []
for (var idx = 0, len = dat.length; idx < len; idx += 4) {
var d = dat.slice(idx, idx + 4) // r,g,b,a
indexedColorImageData.push(reducer.map(d[0], d[1], d[2]))
}
return new IndexedColorImage(
{
width: imageData.width,
height: imageData.height
},
indexedColorImageData,
paletteData
)
return promises.map(p => {
return p.then(x => {
progress(++complete / promises.length)
return p
})
})
}
function calcProgress (from, to, steps, current) {
function convertImageDataToIndexedColorImage (imageData, paletteSize) {
return new Promise((resolve, reject) => {
const worker = new Worker('/services/quantize-color.worker.js')
worker.onerror = reject
worker.onmessageerror = reject
worker.onmessage = event => {
resolve(event.data)
}
worker.postMessage({
imageData,
paletteSize
})
})
}
function calcProgress (from, to, value) {
return from + ((to - from) * value)
}
function calcProgress2 (from, to, steps, current) {
return from + ((to - from) / steps * current)
}

View file

@ -0,0 +1,34 @@
import { MedianCutColorReducer, IndexedColorImage } from 'gif-writer'
onmessage = handleMessage
function handleMessage (event) {
const { imageData, paletteSize } = event.data
const indexedColorImage = convertImageDataToIndexedColorImage(imageData, paletteSize)
postMessage(indexedColorImage)
}
function convertImageDataToIndexedColorImage (imageData, paletteSize) {
const reducer = new MedianCutColorReducer(imageData, paletteSize)
const paletteData = reducer.process()
const data = Array.from(imageData.data)
const indexedColorImageData = []
for (let index = 0, length = data.length; index < length; index += 4) {
const [r, g, b, a] = data.slice(index, index + 4) // r,g,b,a
indexedColorImageData.push(reducer.map(r, g, b))
}
return new IndexedColorImage(
{
width: imageData.width,
height: imageData.height
},
indexedColorImageData,
paletteData
)
}