mirror of
https://github.com/GuerillaStudio/souvenir.git
synced 2024-11-08 10:51:53 +00:00
refact(encode): rewrite with tasks
This commit is contained in:
parent
b5e48f7566
commit
6e83c88c04
2 changed files with 115 additions and 77 deletions
|
@ -1,76 +1,103 @@
|
|||
import EventEmitter from 'eventemitter3'
|
||||
import genericPool from 'generic-pool'
|
||||
import { promisesProgress, calcProgress } from '/services/util.js'
|
||||
import { calcProgress } from '/services/util.js'
|
||||
import { task, do as taskDo, waitAll } from 'folktale/concurrency/task'
|
||||
|
||||
import { GIF_PALETTE_SIZE } from '/constants.js'
|
||||
|
||||
export function encode ({ imageDataList, imageWidth, imageHeight, delayTime }, { boomerangEffect }) {
|
||||
const emitter = new EventEmitter()
|
||||
export function encode ({ imageDataList, imageWidth, imageHeight, delayTime }, { boomerangEffect }, progressCallback) {
|
||||
return taskDo(function * () {
|
||||
const indexedColorImageList = yield quantizeColorImageDataList(
|
||||
imageDataList,
|
||||
GIF_PALETTE_SIZE,
|
||||
(value) => progressCallback(calcProgress(0, 0.9, value))
|
||||
)
|
||||
|
||||
const quantizeColorWorkerPool = genericPool.createPool({
|
||||
create: () => new Worker('/services/quantize-color.worker.js'),
|
||||
destroy: worker => worker.terminate()
|
||||
}, {
|
||||
min: 0,
|
||||
max: 2
|
||||
return writeBlob(
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
indexedColorImageList,
|
||||
delayTime,
|
||||
boomerangEffect,
|
||||
(value) => progressCallback(calcProgress(0.9, 1, value))
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
function quantizeColorImageDataList (imageDataList, paletteSize, progressCallback) {
|
||||
let complete = 0
|
||||
|
||||
const tasks = imageDataList
|
||||
.map(imageData => quantizeColorImageData(imageData, paletteSize))
|
||||
.map(task => {
|
||||
return task.map(x => {
|
||||
progressCallback(++complete / imageDataList.length)
|
||||
return x
|
||||
})
|
||||
})
|
||||
|
||||
const indexedColorImagesP = imageDataList
|
||||
.map(async imageData => {
|
||||
const worker = await quantizeColorWorkerPool.acquire()
|
||||
return waitAll(tasks)
|
||||
}
|
||||
|
||||
function quantizeColorImageData (imageData, paletteSize) {
|
||||
return task((resolver) => {
|
||||
const worker = new Worker('/services/quantize-color.worker.js')
|
||||
|
||||
resolver.cleanup(() => {
|
||||
worker.terminate()
|
||||
})
|
||||
|
||||
worker.onerror = resolver.reject
|
||||
worker.onmessageerror = resolver.reject
|
||||
|
||||
const indexedColorImage = await new Promise((resolve, reject) => {
|
||||
worker.onerror = reject
|
||||
worker.onmessageerror = reject
|
||||
worker.onmessage = event => {
|
||||
resolve(event.data)
|
||||
resolver.resolve(event.data)
|
||||
}
|
||||
|
||||
worker.postMessage({
|
||||
imageData,
|
||||
paletteSize: GIF_PALETTE_SIZE
|
||||
paletteSize
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
await quantizeColorWorkerPool.release(worker)
|
||||
return indexedColorImage
|
||||
function writeBlob (
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
indexedColorImages,
|
||||
delayTime,
|
||||
boomerangEffect,
|
||||
onProgress
|
||||
) {
|
||||
return task((resolver) => {
|
||||
const worker = new Worker('/services/write-gif.worker.js')
|
||||
|
||||
resolver.cleanup(() => {
|
||||
worker.terminate()
|
||||
})
|
||||
|
||||
const progressPromises = promisesProgress(indexedColorImagesP, function (value) {
|
||||
emitter.emit('progress', calcProgress(0, 0.9, value))
|
||||
})
|
||||
worker.onerror = resolver.reject
|
||||
worker.onmessageerror = resolver.reject
|
||||
|
||||
Promise.all(progressPromises).then(indexedColorImages => {
|
||||
const writeWorker = new Worker('/services/write-gif.worker.js')
|
||||
|
||||
writeWorker.onerror = error => emitter.emit('error', error)
|
||||
writeWorker.onmessageerror = error => emitter.emit('error', error)
|
||||
|
||||
writeWorker.onmessage = event => {
|
||||
worker.onmessage = event => {
|
||||
const { type, payload } = event.data
|
||||
|
||||
switch (type) {
|
||||
default:
|
||||
emitter.emit('error', new Error(`Unexpected worker message with type ${type}`))
|
||||
break
|
||||
|
||||
case 'progress':
|
||||
emitter.emit('progress', calcProgress(0.9, 1, payload.value))
|
||||
break
|
||||
onProgress(payload.value)
|
||||
return
|
||||
|
||||
case 'done':
|
||||
const byteArray = new Uint8Array(payload.buffer)
|
||||
const blob = new Blob([byteArray], { type: 'image/gif' })
|
||||
|
||||
emitter.emit('done', {
|
||||
blob,
|
||||
resolver.resolve({
|
||||
blob: new Blob([new Uint8Array(payload.buffer)], { type: 'image/gif' }),
|
||||
createdAt: new Date()
|
||||
})
|
||||
break
|
||||
return
|
||||
|
||||
default:
|
||||
resolver.reject(new Error(`Unexpected worker message with type ${type}`))
|
||||
}
|
||||
}
|
||||
|
||||
writeWorker.postMessage({
|
||||
worker.postMessage({
|
||||
imageWidth,
|
||||
imageHeight,
|
||||
indexedColorImages,
|
||||
|
@ -78,7 +105,4 @@ export function encode ({ imageDataList, imageWidth, imageHeight, delayTime }, {
|
|||
boomerangEffect
|
||||
})
|
||||
})
|
||||
.catch(error => emitter.emit('error', error))
|
||||
|
||||
return emitter
|
||||
}
|
||||
|
|
|
@ -35,6 +35,7 @@ export default {
|
|||
},
|
||||
data: () => ({
|
||||
encoding: false,
|
||||
encodingExecution: null,
|
||||
encodingProgress: 0,
|
||||
objectUrl: null,
|
||||
downloadReady: false
|
||||
|
@ -54,6 +55,7 @@ export default {
|
|||
},
|
||||
methods: {
|
||||
back () {
|
||||
this.cancelEncode()
|
||||
this.$router.push({ name: 'preview' })
|
||||
},
|
||||
backHome () {
|
||||
|
@ -66,23 +68,29 @@ export default {
|
|||
URL.revokeObjectURL(this.objectUrl)
|
||||
this.$store.commit('updateGif', null)
|
||||
},
|
||||
startEncoding () {
|
||||
startEncode () {
|
||||
this.encoding = true
|
||||
const encoding = encode(this.capture, { boomerangEffect: this.boomerang })
|
||||
|
||||
encoding.once('error', error => {
|
||||
console.error(error)
|
||||
this.encoding = false
|
||||
this.encodingProgress = 0
|
||||
})
|
||||
|
||||
encoding.on('progress', value => {
|
||||
this.encodingExecution = encode(
|
||||
this.capture,
|
||||
{ boomerangEffect: this.boomerang },
|
||||
(value) => {
|
||||
this.encodingProgress = value
|
||||
})
|
||||
}
|
||||
).run()
|
||||
|
||||
encoding.once('done', gif => {
|
||||
const cleanup = () => {
|
||||
this.encoding = false
|
||||
this.encodingProgress = 0
|
||||
this.encodingExecution = null
|
||||
}
|
||||
|
||||
this.encodingExecution.listen({
|
||||
onCancelled: cleanup,
|
||||
onRejected: cleanup,
|
||||
onResolved: (gif) => {
|
||||
cleanup()
|
||||
this.$store.commit('updateGif', gif)
|
||||
this.fillGIF()
|
||||
this.downloadReady = true
|
||||
|
@ -99,14 +107,20 @@ export default {
|
|||
once: true
|
||||
})
|
||||
}
|
||||
}
|
||||
})
|
||||
},
|
||||
cancelEncode () {
|
||||
if (this.encodingExecution) {
|
||||
this.encodingExecution.cancel()
|
||||
}
|
||||
}
|
||||
},
|
||||
created () {
|
||||
if (!this.capture) {
|
||||
this.$router.push({ name: 'home' })
|
||||
} else if (!this.gif) {
|
||||
this.startEncoding()
|
||||
this.startEncode()
|
||||
} else {
|
||||
this.fillGIF()
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue