55 lines
2.2 KiB
TypeScript
55 lines
2.2 KiB
TypeScript
export const IMAGE_ASPECT_IDS = ['auto', '16:9', '9:16', '1:1'] as const
|
|
export type ImageAspectId = (typeof IMAGE_ASPECT_IDS)[number]
|
|
|
|
export const IMAGE_FILL_INSTRUCTION =
|
|
'Extend the photograph to fill the empty edges of the frame. Do not stretch or squash the original image. Keep the original subject, pose, and framing. Match lighting, perspective, and texture in the filled areas.'
|
|
|
|
export function parseImageAspect(raw: unknown): ImageAspectId {
|
|
const value = String(raw || 'auto').trim()
|
|
return value === '16:9' || value === '9:16' || value === '1:1' ? value : 'auto'
|
|
}
|
|
|
|
export function imageAspectRatio(aspect: Exclude<ImageAspectId, 'auto'>) {
|
|
if (aspect === '16:9') return 16 / 9
|
|
if (aspect === '9:16') return 9 / 16
|
|
return 1
|
|
}
|
|
|
|
export function generateSizeForAspect(aspect: ImageAspectId) {
|
|
if (aspect === '16:9') return { width: 1280, height: 768, aspect: '16:9' as const }
|
|
if (aspect === '9:16') return { width: 768, height: 1280, aspect: '9:16' as const }
|
|
return { width: 1024, height: 1024, aspect: '1:1' as const }
|
|
}
|
|
|
|
export function composeFillPrompt(userPrompt: string, padded: boolean) {
|
|
const body = String(userPrompt || '').trim()
|
|
if (!padded) return body
|
|
return body ? `${IMAGE_FILL_INSTRUCTION}\n\n${body}` : IMAGE_FILL_INSTRUCTION
|
|
}
|
|
|
|
export function snapImagePad(value: number) {
|
|
return Math.max(32, Math.round(value / 32) * 32)
|
|
}
|
|
|
|
export function padExtents(srcW: number, srcH: number, aspect: Exclude<ImageAspectId, 'auto'>) {
|
|
if (!srcW || !srcH) return null
|
|
const target = imageAspectRatio(aspect)
|
|
const src = srcW / srcH
|
|
if (Math.abs(src - target) < 0.03) return null
|
|
let canvasW = srcW
|
|
let canvasH = srcH
|
|
if (src > target) {
|
|
canvasW = snapImagePad(srcW)
|
|
canvasH = snapImagePad(canvasW / target)
|
|
} else {
|
|
canvasH = snapImagePad(srcH)
|
|
canvasW = snapImagePad(canvasH * target)
|
|
}
|
|
if (canvasW < srcW) canvasW = snapImagePad(srcW)
|
|
if (canvasH < srcH) canvasH = snapImagePad(srcH)
|
|
const x = Math.max(0, Math.floor((canvasW - srcW) / 2))
|
|
const y = Math.max(0, Math.floor((canvasH - srcH) / 2))
|
|
if (x === 0 && y === 0 && canvasW === srcW && canvasH === srcH) return null
|
|
return { canvasW, canvasH, x, y, srcW, srcH }
|
|
}
|