Skip to content

Commit

Permalink
refactor: improve code style
Browse files Browse the repository at this point in the history
  • Loading branch information
Jack-Works committed Oct 11, 2022
1 parent c33a1c4 commit df9e3d7
Show file tree
Hide file tree
Showing 24 changed files with 216 additions and 284 deletions.
5 changes: 5 additions & 0 deletions .changeset/modern-keys-bathe.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@dimensiondev/stego-js': minor
---

make exported type readonly as possible
3 changes: 2 additions & 1 deletion .vscode/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,5 +2,6 @@
"editor.formatOnSave": true,
"editor.codeActionsOnSave": {
"source.fixAll": true
}
},
"cSpell.words": ["stego", "unshuffle"]
}
12 changes: 11 additions & 1 deletion src/cli/cli.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
#!/usr/bin/env node
import meow from 'meow'
import { createReadStream } from 'fs'
import { rs2Buf } from '../utils/helper.js'
import { encode, decode, AlgorithmVersion } from '../node.js'
import {
CLI_NAME,
Expand All @@ -15,6 +14,17 @@ import {
DEFAULT_ALGORITHM_VERSION,
} from '../constant.js'
import { normalizeFlags, validateFlags, flags2Options, flags } from './flag.js'
import { Readable } from 'stream'

function rs2Buf(rs: Readable) {
return new Promise<Buffer>((resolve, reject) => {
const buffers: Uint8Array[] = []

rs.on('data', (c) => buffers.push(c))
rs.on('end', () => resolve(Buffer.concat(buffers)))
rs.on('error', (err) => reject(err))
})
}

const cli = meow(
`Usage
Expand Down
11 changes: 7 additions & 4 deletions src/cli/flag.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,10 +126,13 @@ export const flags = {
} as const

export function normalizeFlags(rawFlags: TypedFlags<typeof flags>) {
const { encode, decode, mask, tolerance } = rawFlags
const { encode, decode, mask, tolerance, transform } = rawFlags
return {
...rawFlags,
tolerance: tolerance === TOLERANCE_NOT_SET ? DEFAULT_TOLERANCE[rawFlags.algorithmVersion].transform : tolerance,
tolerance:
tolerance === TOLERANCE_NOT_SET
? DEFAULT_TOLERANCE[rawFlags.algorithmVersion as AlgorithmVersion][transform as TransformAlgorithm]
: tolerance,
encode: encode && !decode,
decode,
mask: mask ? resolvePath(process.cwd(), mask) : '',
Expand Down Expand Up @@ -159,8 +162,8 @@ export function validateFlags({
if (isNaN(copies) || copies <= 0 || copies % 2 === 0 || copies > 31) {
return '-c, --copies should be a postive odd number and less than 31'
}
// the valiadation for transform algorithm should prior to tolerance,
// becasue tolerance validation depends on transform algorithm
// the validation for transform algorithm should prior to tolerance,
// because tolerance validation depends on transform algorithm
if (!Object.values(TransformAlgorithm).includes(transform)) {
return 'unknown transform algorithm'
}
Expand Down
16 changes: 8 additions & 8 deletions src/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ export const DEFAULT_COPIES = 3
export const DEFAULT_PARAM_COPIES = 9
export const DEFAULT_SIZE = 8
export const TOLERANCE_NOT_SET = -1
export const DEFAULT_TOLERANCE: Record<string, Record<string, number>> = {
export const DEFAULT_TOLERANCE: Readonly<Record<AlgorithmVersion, Record<TransformAlgorithm, number>>> = Object.freeze({
[AlgorithmVersion.V1]: {
[TransformAlgorithm.DCT]: 100,
[TransformAlgorithm.FastDCT]: 500,
Expand All @@ -23,26 +23,26 @@ export const DEFAULT_TOLERANCE: Record<string, Record<string, number>> = {
[TransformAlgorithm.FFT1D]: 30,
[TransformAlgorithm.FFT2D]: 150,
},
}
export const MAX_TOLERANCE: Record<string, number> = {
})
export const MAX_TOLERANCE: Readonly<Record<TransformAlgorithm, number>> = Object.freeze({
[TransformAlgorithm.DCT]: 5000,
[TransformAlgorithm.FastDCT]: 5000,
[TransformAlgorithm.FFT1D]: 5000,
[TransformAlgorithm.FFT2D]: 50000,
}
})
export const DEFAULT_FAKE_MASK_PIXELS = false
export const DEFAULT_EXHAUST_PIXELS = true
export const DEFAULT_CROP_EDGE_PIXELS = true
export const DEFAULT_ALGORITHM_VERSION = AlgorithmVersion.V2

export const DEFAULT_MASK = [
export const DEFAULT_MASK = Object.freeze([
137, 80, 78, 71, 13, 10, 26, 10, 0, 0, 0, 13, 73, 72, 68, 82, 0, 0, 0, 1, 0, 0, 0, 1, 1, 3, 0, 0, 0, 37, 219, 86, 202,
0, 0, 0, 1, 115, 82, 71, 66, 1, 217, 201, 44, 127, 0, 0, 0, 9, 112, 72, 89, 115, 0, 0, 11, 19, 0, 0, 11, 19, 1, 0,
154, 156, 24, 0, 0, 0, 3, 80, 76, 84, 69, 255, 255, 255, 167, 196, 27, 200, 0, 0, 0, 10, 73, 68, 65, 84, 120, 156, 99,
96, 0, 0, 0, 2, 0, 1, 72, 175, 164, 113, 0, 0, 0, 0, 73, 69, 78, 68, 174, 66, 96, 130,
]
])

export const SEED = [
export const SEED = Object.freeze([
76221, 13388, 20800, 80672, 15974, 87005, 71203, 84444, 16928, 51335, 94092, 83586, 37656, 2240, 26283, 1887, 93419,
96857, 20866, 21797, 42065, 39781, 50192, 24399, 98969, 54274, 38815, 45159, 36824,
]
])
4 changes: 2 additions & 2 deletions src/dct/fastdct.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,8 @@

// DCT type II, unscaled. Algorithm by Byeong Gi Lee, 1984.
// See: http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.118.3056&rep=rep1&type=pdf#page=34
export function transform(vector: Array<number>): void {
const n: number = vector.length
export function transform(vector: number[]): void {
const n = vector.length
if (n <= 0 || (n & (n - 1)) !== 0) throw 'Length must be power of 2'
transformInternal(vector, 0, n, new Float64Array(n))
}
Expand Down
22 changes: 9 additions & 13 deletions src/dct/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
const ONE_SQUARE_ROOT_OF_TWO = 1 / Math.sqrt(2)

// type-II DCT
export function dct(nums: number[], size: number = 8) {
export function dct(numbers: number[], size: number = 8) {
const coefficients: number[] = []

for (let v = 0; v < size; v += 1) {
Expand All @@ -16,7 +16,9 @@ export function dct(nums: number[], size: number = 8) {
for (let y = 0; y < size; y += 1) {
for (let x = 0; x < size; x += 1) {
sum +=
nums[y * size + x] * Math.cos(((2 * x + 1) * u * Math.PI) / 16) * Math.cos(((2 * y + 1) * v * Math.PI) / 16)
numbers[y * size + x] *
Math.cos(((2 * x + 1) * u * Math.PI) / 16) *
Math.cos(((2 * y + 1) * v * Math.PI) / 16)
}
}
coefficients.push((sum * au * av) / 4)
Expand All @@ -25,13 +27,13 @@ export function dct(nums: number[], size: number = 8) {

// in-place update
for (let i = 0; i < coefficients.length; i += 1) {
nums[i] = coefficients[i]
numbers[i] = coefficients[i]
}
}

// type-III DCT
export function idct(coefficients: number[], size: number = 8) {
const nums: number[] = []
const numbers: number[] = []

for (let y = 0; y < size; y += 1) {
for (let x = 0; x < size; x += 1) {
Expand All @@ -50,18 +52,12 @@ export function idct(coefficients: number[], size: number = 8) {
Math.cos(((2 * y + 1) * v * Math.PI) / 16)
}
}
nums.push(sum / 4)
numbers.push(sum / 4)
}
}

// in-place update
for (let i = 0; i < nums.length; i += 1) {
coefficients[i] = nums[i]
for (let i = 0; i < numbers.length; i += 1) {
coefficients[i] = numbers[i]
}
}

export const QUANTIZATION_MATRIX = [
16, 11, 10, 16, 24, 40, 51, 61, 12, 12, 14, 19, 26, 58, 60, 55, 14, 13, 16, 24, 40, 57, 69, 56, 14, 17, 22, 29, 51,
87, 80, 62, 18, 22, 37, 56, 68, 109, 103, 77, 24, 35, 55, 64, 81, 104, 113, 92, 49, 64, 78, 87, 103, 121, 120, 101,
72, 92, 95, 98, 112, 100, 103, 99,
]
82 changes: 37 additions & 45 deletions src/dom.ts
Original file line number Diff line number Diff line change
@@ -1,58 +1,50 @@
import { proxy } from './utils/expose.js'
import { imgType } from './utils/helper.js'
import { createAPI } from './utils/expose.js'
import { getImageType } from './utils/helper.js'
import { preprocessImage } from './utils/image.js'
import { AlgorithmVersion } from './utils/stego-params.js'
import * as v1 from './v1/index.js'
import * as v2 from './v2/index.js'

export { imgType as getImageType }
export { getImageType }
export * from './utils/types.js'
export * from './constant.js'

const { encode, decode } = proxy({
algoithms: { [AlgorithmVersion.V1]: v1, [AlgorithmVersion.V2]: v2 },
methods: {
toImageData(data) {
const type = imgType(new Uint8Array(data.slice(0, 8)))
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
export const { encode, decode } = createAPI({
toImageData(data) {
const type = getImageType(new Uint8Array(data.slice(0, 8)))
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))
})
},
async toBuffer(imgData, height = imgData.height, width = imgData.width) {
const canvas = createCanvas(width, height)
canvas.getContext('2d')!.putImageData(imgData, 0, 0, 0, 0, width, height)
if (isOffscreenCanvas(canvas)) {
return toArrayBuffer(await canvas.convertToBlob({ type: 'image/png' }))
}
return new Promise<ArrayBuffer>((resolve, reject) => {
const callback: BlobCallback = (blob) => {
if (blob) {
resolve(toArrayBuffer(blob))
} else {
reject(new Error('fail to generate array buffer'))
}
element.addEventListener('error', reject)
element.src = url
})
},
async toBuffer(imgData, height = imgData.height, width = imgData.width) {
const canvas = createCanvas(width, height)
canvas.getContext('2d')!.putImageData(imgData, 0, 0, 0, 0, width, height)
if (isOffscreenCanvas(canvas)) {
return toArrayBuffer(await canvas.convertToBlob({ type: 'image/png' }))
}
return new Promise<ArrayBuffer>((resolve, reject) => {
const callback: BlobCallback = (blob) => {
if (blob) {
resolve(toArrayBuffer(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)
},
}
canvas.toBlob(callback, 'image/png')
})
},
preprocessImage(data) {
return preprocessImage(data, (w, h) => createCanvas(w, h).getContext('2d')?.createImageData(w, h) ?? null)
},
})

export { encode, decode }

function toArrayBuffer(blob: Blob) {
return new Promise<ArrayBuffer>((resolve, reject) => {
const reader = new FileReader()
Expand Down
76 changes: 34 additions & 42 deletions src/node.ts
Original file line number Diff line number Diff line change
@@ -1,57 +1,49 @@
import { JsColorType, Transformer } from '@napi-rs/image'
import { proxy } from './utils/expose.js'
import { imgType } from './utils/helper.js'
import { createAPI } from './utils/expose.js'
import { getImageType } from './utils/helper.js'
import { preprocessImage } from './utils/image.js'
import { AlgorithmVersion } from './utils/stego-params.js'
import * as v1 from './v1/index.js'
import * as v2 from './v2/index.js'

export { imgType as getImageType }
export { getImageType }
export * from './utils/types.js'
export * from './constant.js'

const { encode, decode } = proxy({
algoithms: { [AlgorithmVersion.V1]: v1, [AlgorithmVersion.V2]: v2 },
methods: {
async toImageData(data) {
let transformer = new Transformer(Buffer.from(data))
let { width, height, colorType } = await transformer.metadata()
export const { encode, decode } = createAPI({
async toImageData(data) {
let transformer = new Transformer(Buffer.from(data))
let { width, height, colorType } = await transformer.metadata()

if (colorType !== JsColorType.Rgba8 && colorType !== JsColorType.Rgb8) {
transformer = new Transformer(await transformer.png())
;({ width, height, colorType } = await transformer.metadata())
}
if (colorType !== JsColorType.Rgba8 && colorType !== JsColorType.Rgb8) {
transformer = new Transformer(await transformer.png())
;({ width, height, colorType } = await transformer.metadata())
}

if (colorType !== JsColorType.Rgba8 && colorType !== JsColorType.Rgb8) {
throw new TypeError('Cannot convert the given image to rgba8 format.')
}
let rgb: Uint8ClampedArray = new Uint8ClampedArray(await transformer.rawPixels())
if (colorType === JsColorType.Rgb8) rgb = rgb_to_rgba(rgb)
if (colorType !== JsColorType.Rgba8 && colorType !== JsColorType.Rgb8) {
throw new TypeError('Cannot convert the given image to rgba8 format.')
}
let rgb: Uint8ClampedArray = new Uint8ClampedArray(await transformer.rawPixels())
if (colorType === JsColorType.Rgb8) rgb = rgb_to_rgba(rgb)

const imageData: ImageData = {
width,
height,
colorSpace: 'srgb',
data: rgb,
}
return imageData
},
async toBuffer(imgData, height = imgData.height, width = imgData.width) {
return (await Transformer.fromRgbaPixels(imgData.data, width, height).crop(0, 0, width, height).png()).buffer
},
preprocessImage(data) {
return preprocessImage(data, (width, height) => ({
height,
width,
colorSpace: 'srgb',
data: new Uint8ClampedArray(width * height * 4),
}))
},
const imageData: ImageData = {
width,
height,
colorSpace: 'srgb',
data: rgb,
}
return imageData
},
async toBuffer(imgData, height = imgData.height, width = imgData.width) {
return (await Transformer.fromRgbaPixels(imgData.data, width, height).crop(0, 0, width, height).png()).buffer
},
preprocessImage(data) {
return preprocessImage(data, (width, height) => ({
height,
width,
colorSpace: 'srgb',
data: new Uint8ClampedArray(width * height * 4),
}))
},
})

export { encode, decode }

function rgb_to_rgba(array: Uint8ClampedArray) {
const next = new Uint8ClampedArray((array.length / 3) * 4)
for (var old_index = 0, new_index = 0; old_index < array.length; old_index += 3, new_index += 4) {
Expand Down
Loading

0 comments on commit df9e3d7

Please # to comment.