Skip to content

Commit

Permalink
chore: refactor image-shrink types and unwrap promise chains
Browse files Browse the repository at this point in the history
  • Loading branch information
nd0ut committed Feb 16, 2024
1 parent 77e053e commit a7a08b6
Show file tree
Hide file tree
Showing 18 changed files with 229 additions and 216 deletions.
4 changes: 2 additions & 2 deletions packages/image-shrink/src/utils/IccProfile/getIccProfile.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { readJpegChunks } from '../image/JPEG/readJpegChunks'

export const getIccProfile = async (file: File) => {
export const getIccProfile = async (blob: Blob) => {
const iccProfile: DataView[] = []
const { promiseReadJpegChunks, stack } = readJpegChunks()

return await promiseReadJpegChunks(file)
return await promiseReadJpegChunks(blob)
.then(() => {
stack.forEach(({ marker, view }) => {
if (marker === 0xe2) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { replaceJpegChunk } from '../image/JPEG/replaceJpegChunk'

export const MARKER = 0xe2
export const replaceIccProfile = (
blob: Blob | File,
iccProfiles: DataView[]
) => {
export const replaceIccProfile = (blob: Blob, iccProfiles: DataView[]) => {
return replaceJpegChunk(
blob,
MARKER,
Expand Down
14 changes: 3 additions & 11 deletions packages/image-shrink/src/utils/IccProfile/stripIccProfile.ts
Original file line number Diff line number Diff line change
@@ -1,17 +1,9 @@
import { replaceIccProfile } from './replaceIccProfile'
import { imageLoader } from '../image/imageLoader'

export const stripIccProfile = async (
inputFile: File
): Promise<HTMLImageElement> => {
export const stripIccProfile = async (blob: Blob): Promise<Blob> => {
try {
const file = await replaceIccProfile(inputFile, [])
const image = await imageLoader(URL.createObjectURL(file as Blob))

URL.revokeObjectURL(image.src)

return image
return await replaceIccProfile(blob, [])
} catch (e) {
throw new Error(`Failed to strip ICC profile and not image ${e}`)
throw new Error(`Failed to strip ICC profile: ${e}`)
}
}
38 changes: 21 additions & 17 deletions packages/image-shrink/src/utils/canvas/canvasResize.ts
Original file line number Diff line number Diff line change
@@ -1,24 +1,28 @@
import { createCanvas } from './createCanvas'

export const canvasResize = (img: CanvasImageSource, w: number, h: number) => {
return new Promise<HTMLCanvasElement>((resolve, reject) => {
try {
const { ctx, canvas } = createCanvas()
export const canvasResize = async (
img: CanvasImageSource,
w: number,
h: number
) => {
try {
const { ctx, canvas } = createCanvas()

canvas.width = w
canvas.height = h
canvas.width = w
canvas.height = h

ctx.imageSmoothingQuality = 'high'
ctx.drawImage(img, 0, 0, w, h)
ctx.imageSmoothingQuality = 'high'
ctx.drawImage(img, 0, 0, w, h)

// @ts-expect-error TODO: fix this
img.src = '//:0' // for image
// @ts-expect-error TODO: fix this
img.width = img.height = 1 // for canvas

resolve(canvas)
} catch (e) {
reject(`Failed to resize image. ${e}`)
if (img instanceof HTMLImageElement) {
img.src = '//:0' // free memory
}
if (img instanceof HTMLCanvasElement) {
img.width = img.height = 1 // free memory
}
})

return canvas
} catch (e) {
throw new Error('Canvas resize error', { cause: e })
}
}
17 changes: 13 additions & 4 deletions packages/image-shrink/src/utils/canvas/canvasToBlob.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,17 @@
export const canvasToBlob = (
canvas: HTMLCanvasElement,
type: string,
quality: number | undefined,
callback: BlobCallback
): void => {
return canvas.toBlob(callback, type, quality)
quality: number | undefined
): Promise<Blob> => {
return new Promise((resolve, reject) => {
const callback: BlobCallback = (blob) => {
if (!blob) {
reject('Failed to convert canvas to blob')
return
}
resolve(blob)
}
canvas.toBlob(callback, type, quality)
canvas.width = canvas.height = 1
})
}
35 changes: 16 additions & 19 deletions packages/image-shrink/src/utils/canvas/testCanvasSize.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,25 +16,22 @@ function wrapAsync<A extends unknown[], R>(fn: (...args: A) => R) {
const squareTest = wrapAsync(memoize(canvasTest, memoKeySerializer))
const dimensionTest = wrapAsync(memoize(canvasTest, memoKeySerializer))

export const testCanvasSize = (w: number, h: number) => {
return new Promise((resolve, reject) => {
const testSquareSide = sizes.squareSide.find((side) => side * side >= w * h)
const testDimension = sizes.dimension.find((side) => side >= w && side >= h)
export const testCanvasSize = async (w: number, h: number) => {
const testSquareSide = sizes.squareSide.find((side) => side * side >= w * h)
const testDimension = sizes.dimension.find((side) => side >= w && side >= h)

if (!testSquareSide || !testDimension) {
reject()
return
}
if (!testSquareSide || !testDimension) {
throw new Error('Not supported')
}

Promise.all([
squareTest(testSquareSide, testSquareSide),
dimensionTest(testDimension, 1)
]).then(([squareSupported, dimensionSupported]) => {
if (squareSupported && dimensionSupported) {
resolve(true)
} else {
reject()
}
})
})
const [squareSupported, dimensionSupported] = await Promise.all([
squareTest(testSquareSide, testSquareSide),
dimensionTest(testDimension, 1)
])

if (squareSupported && dimensionSupported) {
return true
} else {
throw new Error('Not supported')
}
}
14 changes: 6 additions & 8 deletions packages/image-shrink/src/utils/exif/findExifOrientation.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// TODO: rename to littleEndian
export const findExifOrientation = (
exif: DataView,
exifCallback: (offset: number, little: boolean) => void
exifCallback: (offset: number, littleEndian: boolean) => void
) => {
let j, little, offset, ref
if (
Expand All @@ -10,28 +9,27 @@ export const findExifOrientation = (
exif.getUint32(0) !== 0x45786966 ||
exif.getUint16(4) !== 0
) {
return null
return
}
if (exif.getUint16(6) === 0x4949) {
little = true
} else if (exif.getUint16(6) === 0x4d4d) {
little = false
} else {
return null
return
}
if (exif.getUint16(8, little) !== 0x002a) {
return null
return
}
offset = 8 + exif.getUint32(10, little)
const count = exif.getUint16(offset - 2, little)
for (j = 0, ref = count; ref >= 0 ? j < ref : j > ref; ref >= 0 ? ++j : --j) {
if (exif.byteLength < offset + 10) {
return null
return
}
if (exif.getUint16(offset, little) === 0x0112) {
return exifCallback(offset + 8, little)
exifCallback(offset + 8, little)
}
offset += 12
}
return null
}
17 changes: 8 additions & 9 deletions packages/image-shrink/src/utils/exif/getExif.ts
Original file line number Diff line number Diff line change
@@ -1,27 +1,26 @@
import { readJpegChunks } from '../image/JPEG/readJpegChunks'

export const getExif = async (file: File) => {
let isExif: DataView | null = null
export const getExif = async (blob: Blob) => {
let exif: DataView | null = null

const { promiseReadJpegChunks, stack } = readJpegChunks()
return promiseReadJpegChunks(file)
return promiseReadJpegChunks(blob)
.then(() => {
stack.forEach(({ marker, view }) => {
if (!isExif && marker === 0xe1) {
if (!exif && marker === 0xe1) {
if (view.byteLength >= 14) {
if (
// check for "Exif\0"
view.getUint32(0) === 0x45786966 &&
view.getUint16(4) === 0
) {
isExif = view
return isExif
exif = view
return
}
}
}

return isExif
})
return exif
})
.catch(() => isExif)
.catch(() => exif)
}
3 changes: 2 additions & 1 deletion packages/image-shrink/src/utils/exif/isBrowserApplyExif.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// 2x1 pixel image 90CW rotated with orientation header
const base64ImageSrc =
'data:image/jpg;base64,' +
'/9j/4AAQSkZJRgABAQEASABIAAD/4QA6RXhpZgAATU0AKgAAAAgAAwESAAMAAAABAAYAAAEo' +
Expand All @@ -7,7 +8,7 @@ const base64ImageSrc =

let isApplied: boolean | undefined = undefined

export const isBrowserApplyExif = () => {
export const isBrowserApplyExifOrientation = () => {
return new Promise<boolean>((resolve) => {
if (isApplied !== undefined) {
resolve(isApplied)
Expand Down
10 changes: 4 additions & 6 deletions packages/image-shrink/src/utils/exif/replaceExif.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,20 +2,18 @@ import { replaceJpegChunk } from '../image/JPEG/replaceJpegChunk'
import { findExifOrientation } from './findExifOrientation'

export const setExifOrientation = (exif: DataView, orientation: number) => {
// TODO: rename to littleEndian
findExifOrientation(exif, (offset, little) =>
exif.setUint16(offset, orientation, little)
findExifOrientation(exif, (offset, littleEndian) =>
exif.setUint16(offset, orientation, littleEndian)
)
}
export const replaceExif = async (
// TODO: rename to blob
file: Blob,
blob: Blob,
exif: DataView,
isExifApplied: boolean
) => {
if (isExifApplied) {
setExifOrientation(exif, 1)
}

return replaceJpegChunk(file, 0xe1, [exif.buffer])
return replaceJpegChunk(blob, 0xe1, [exif.buffer])
}
37 changes: 18 additions & 19 deletions packages/image-shrink/src/utils/image/JPEG/readJpegChunks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ type TChunk = {

export const readJpegChunks = () => {
const stack: TChunk[] = []
// TODO: rename to blob
const promiseReadJpegChunks = (file: Blob) =>
new Promise((resolve, reject) => {
let pos: number
const readToView = (file: Blob, cb: (view: DataView) => void) => {
const promiseReadJpegChunks = (blob: Blob) =>
new Promise<boolean>((resolve, reject) => {
let pos = 2
const readToView = (blob: Blob, cb: (view: DataView) => void) => {
const reader = new FileReader()

reader.addEventListener('load', () => {
Expand All @@ -22,12 +21,11 @@ export const readJpegChunks = () => {
reject(`Reader error: ${e}`)
})

reader.readAsArrayBuffer(file)
reader.readAsArrayBuffer(blob)
}

// @ts-expect-error TODO: fix this
const readNext = () =>
readToView(file.slice(pos, pos + 128), (view: DataView) => {
readToView(blob.slice(pos, pos + 128), (view: DataView) => {
let i, j, ref
for (
i = j = 0, ref = view.byteLength;
Expand All @@ -40,34 +38,36 @@ export const readJpegChunks = () => {
}
}

return readNextChunk()
readNextChunk()
})

// @ts-expect-error TODO: fix this
const readNextChunk = () => {
const startPos = pos

return readToView(file.slice(pos, (pos += 4)), (view: DataView) => {
return readToView(blob.slice(pos, (pos += 4)), (view: DataView) => {
if (view.byteLength !== 4 || view.getUint8(0) !== 0xff) {
return reject('Corrupted')
reject('Corrupted')
return
}

const marker = view?.getUint8(1)

if (marker === 0xda) {
return resolve(true)
resolve(true)
return
}

const length = view.getUint16(2) - 2
return readToView(
file.slice(pos, (pos += length)),
blob.slice(pos, (pos += length)),
(view: DataView) => {
if (view.byteLength !== length) {
return reject('Corrupted')
reject('Corrupted')
return
}

stack.push({ startPos, length, marker, view })
return readNext()
readNext()
}
)
})
Expand All @@ -77,13 +77,12 @@ export const readJpegChunks = () => {
reject('Not Support')
}

pos = 2
readToView(file.slice(0, 2), function (view: DataView) {
readToView(blob.slice(0, 2), (view: DataView) => {
if (view.getUint16(0) !== 0xffd8) {
reject('Not jpeg')
}

return readNext()
readNext()
})
})

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ export const replaceJpegChunk = (
marker: number,
chunks: ArrayBuffer[]
) => {
return new Promise((resolve, reject) => {
return new Promise<Blob>((resolve, reject) => {
const oldChunkPos: number[] = []
const oldChunkLength: number[] = []

Expand Down
Loading

0 comments on commit a7a08b6

Please sign in to comment.