Skip to content

Commit

Permalink
feat: improve support for worker and service worker
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack-Works committed Oct 11, 2022
1 parent 5cf3d71 commit 1dd3b8c
Show file tree
Hide file tree
Showing 5 changed files with 102 additions and 47 deletions.
5 changes: 5 additions & 0 deletions .changeset/spotty-jobs-pretend.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@dimensiondev/stego-js': minor
---

improve support for worker and service worker
45 changes: 38 additions & 7 deletions example/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,7 @@
</style>

<script>
// the global fr
const fr = new FileReader()
</script>

<script>
const openFile = (event) => {
const input = event.target
const fr2 = new FileReader()
Expand All @@ -38,8 +34,6 @@
fr.readAsArrayBuffer(input.files[0])
fr2.readAsDataURL(input.files[0])
}
</script>
<script>
const scrollRange = (event) => {
document.getElementById('tolerancePreviewer').textContent = event.target.value
}
Expand Down Expand Up @@ -139,9 +133,14 @@
<section>
<span>Tolerance:</span>
<span>1-5000</span>
<input id="tolerance" type="range" min="1" max="5000" step="10" value="100" onchange="scrollRange(event)" />
<input id="tolerance" type="number" min="1" max="5000" value="100" onchange="scrollRange(event)" />
<span id="tolerancePreviewer"></span>
</section>
<section>
<span>Web Compatibility:</span>
<label>Uses OffscreenCanvas <input type="checkbox" id="offscreenCanvas" /></label>
<label>Uses createImageBitmap <input type="checkbox" id="imageBitmap" /></label>
</section>
<section>
<input
id="password"
Expand All @@ -165,6 +164,38 @@
</section>
</content>

<script>
const _offscreenCanvas = self.OffscreenCanvas
const _imageBitMap = self.ImageBitmap
const _createImageBitMap = self.createImageBitmap
const canvasCheckbox = document.getElementById('offscreenCanvas')
const bitmapCheckbox = document.getElementById('imageBitmap')
if (_offscreenCanvas) {
canvasCheckbox.checked = true
canvasCheckbox.onchange = () => {
if (self.OffscreenCanvas) delete self.OffscreenCanvas
else self.OffscreenCanvas = _offscreenCanvas
}
} else {
canvasCheckbox.disabled = true
}

if (_createImageBitMap) {
bitmapCheckbox.checked = true
bitmapCheckbox.onchange = () => {
if (self.ImageBitmap) {
delete self.ImageBitmap
delete self.createImageBitmap
} else {
self.ImageBitmap = _imageBitMap
self.createImageBitmap = _createImageBitMap
}
}
} else {
bitmapCheckbox.disabled = true
}
</script>

<script>
function getOptions() {
return {
Expand Down
91 changes: 55 additions & 36 deletions src/dom.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,63 +8,82 @@ export * from './constant.js'

export const { encode, decode } = createAPI({
toImageData(_data) {
const data = new Uint8Array(_data)
const type = getImageType(data)
const blob = new Blob([data], { type })
const url = URL.createObjectURL(blob)
return new Promise((resolve, reject) => {
const element = new Image()
element.addEventListener('load', () => {
const { width, height } = element
const ctx = createCanvas(width, height).getContext('2d')!
ctx.drawImage(element, 0, 0, width, height)
resolve(ctx.getImageData(0, 0, width, height))
})
element.addEventListener('error', reject)
element.src = url
return new Promise<ImageData>((resolve) => {
const data = new Uint8Array(_data)
const type = getImageType(data)
const blob = new Blob([data], { type })
resolve(getImageData(blob))
})
},
async toBuffer(imgData, height = imgData.height, width = imgData.width) {
async toPNG(imgData, height = imgData.height, width = imgData.width) {
const canvas = createCanvas(width, height)
canvas.getContext('2d')!.putImageData(imgData, 0, 0, 0, 0, width, height)
const context = canvas.getContext('2d')!
context.putImageData(imgData, 0, 0, 0, 0, width, height)

if (isOffscreenCanvas(canvas)) {
return toUint8ClampedArray(await canvas.convertToBlob({ type: 'image/png' }))
return canvas.convertToBlob({ type: 'image/png' }).then(toUint8Array)
} else {
return new Promise<Uint8Array>((resolve, reject) => {
canvas.toBlob((blob) => {
if (blob) resolve(toUint8Array(blob))
else reject(new Error('fail to convert to png'))
}, 'image/png')
})
}
return new Promise<Uint8ClampedArray>((resolve, reject) => {
const callback: BlobCallback = (blob) => {
if (blob) {
resolve(toUint8ClampedArray(blob))
} else {
reject(new Error('fail to generate array buffer'))
}
}
canvas.toBlob(callback, 'image/png')
})
},
preprocessImage(data) {
return preprocessImage(data, (w, h) => createCanvas(w, h).getContext('2d')?.createImageData(w, h) ?? null)
},
})

function toUint8ClampedArray(blob: Blob) {
return new Promise<Uint8ClampedArray>((resolve, reject) => {
function toUint8Array(blob: Blob) {
return new Promise<Uint8Array>((resolve, reject) => {
const reader = new FileReader()
reader.addEventListener('load', () => resolve(new Uint8ClampedArray(reader.result as ArrayBuffer)))
reader.addEventListener('load', () => resolve(new Uint8Array(reader.result as ArrayBuffer)))
reader.addEventListener('error', () => reject(new Error('fail to generate array buffer')))
reader.readAsArrayBuffer(blob)
})
}

function createCanvas(width: number, height: number) {
if (typeof OffscreenCanvas !== 'undefined') {
return new OffscreenCanvas(width, height)
let canvas: HTMLCanvasElement | OffscreenCanvas
if (typeof OffscreenCanvas === 'function') {
canvas = new OffscreenCanvas(width, height)
} else {
canvas = document.createElement('canvas')
canvas.width = width
canvas.height = height
}
const canvas = document.createElement('canvas')
canvas.width = width
canvas.height = height
return canvas
}

async function getImageData(imageBlob: Blob) {
let width: number, height: number
let image: ImageBitmap | HTMLImageElement
if (typeof createImageBitmap === 'function') {
image = await createImageBitmap(imageBlob)
width = image.width
height = image.height
} else {
const url = URL.createObjectURL(imageBlob)
image = await new Promise<HTMLImageElement>((resolve, reject) => {
const element = new Image()
element.addEventListener('load', () => {
width = element.width
height = element.height
resolve(element)
})
element.addEventListener('error', reject)
element.src = url
}).finally(() => URL.revokeObjectURL(url))
}

const canvas = createCanvas(width!, height!)
const ctx = canvas.getContext('2d')!
ctx.drawImage(image, 0, 0)
return ctx.getImageData(0, 0, width!, height!)
}

function isOffscreenCanvas(value: any): value is OffscreenCanvas {
return value?.[Symbol.toStringTag] === 'OffscreenCanvas'
return typeof OffscreenCanvas === 'function' && value instanceof OffscreenCanvas
}
4 changes: 2 additions & 2 deletions src/node.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,8 @@ export const { encode, decode } = createAPI({
}
return imageData
},
async toBuffer(imgData, height = imgData.height, width = imgData.width) {
return new Uint8ClampedArray(
async toPNG(imgData, height = imgData.height, width = imgData.width) {
return new Uint8Array(
(await Transformer.fromRgbaPixels(imgData.data, width, height).crop(0, 0, width, height).png()).buffer,
)
},
Expand Down
4 changes: 2 additions & 2 deletions src/utils/expose.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,15 +17,15 @@ export type Decoder = (imgData: ImageData, maskData: Uint8ClampedArray, options:

export interface IO {
toImageData(data: ArrayBufferLike | ArrayLike<number>): Promise<ImageData>
toBuffer(imgData: ImageData, height?: number, width?: number): Promise<Uint8ClampedArray>
toPNG(imgData: ImageData, height?: number, width?: number): Promise<Uint8Array>
preprocessImage(data: ImageData): ImageData
}

const algorithms = {
[AlgorithmVersion.V1]: v1,
[AlgorithmVersion.V2]: v2,
}
export function createAPI({ preprocessImage, toBuffer, toImageData }: IO) {
export function createAPI({ preprocessImage, toPNG: toBuffer, toImageData }: IO) {
return {
async encode(image: ArrayBuffer, mask: ArrayBuffer, options: EncodeOptions) {
const { data, height, width } = await algorithms[options.version].encode(
Expand Down

0 comments on commit 1dd3b8c

Please # to comment.