Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Chore/image-shrink-types #521

Merged
merged 1 commit into from
Feb 26, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading