pixai / src /shared /image-options.ts
ghuser1's picture
Upload 69 files
f703ba6 verified
import type {
GenerateImageInput,
ImageBackground,
ImageInputFidelity,
ImageModeration,
ImageOutputFormat,
ImageQuality,
ImageRatio
} from './types'
export const DEFAULT_MODEL = 'gpt-image-2'
export const DEFAULT_IMAGE_OUTPUT_FORMAT: ImageOutputFormat = 'png'
export const IMAGE_RATIOS: ImageRatio[] = ['1:1', '3:2', '2:3', '4:3', '3:4', '16:9', '9:16', '21:9', '9:21']
export const IMAGE_QUALITIES: GenerateImageInput['quality'][] = ['auto', 'low', 'medium', 'high']
export const IMAGE_OUTPUT_FORMATS: ImageOutputFormat[] = ['jpeg', 'png', 'webp']
export const IMAGE_BACKGROUNDS: ImageBackground[] = ['auto', 'opaque']
export const IMAGE_MODERATIONS: ImageModeration[] = ['auto', 'low']
export const IMAGE_INPUT_FIDELITIES: ImageInputFidelity[] = ['low', 'high']
export const IMAGE_QUALITY_LABELS: Record<ImageQuality, string> = {
auto: 'θ‡ͺ动',
low: '低',
medium: 'δΈ­',
high: '高'
}
export const IMAGE_OUTPUT_FORMAT_LABELS: Record<ImageOutputFormat, string> = {
png: 'PNG',
jpeg: 'JPEG',
webp: 'WebP'
}
export const IMAGE_BACKGROUND_LABELS: Record<ImageBackground, string> = {
auto: 'θ‡ͺ动',
opaque: 'δΈι€ζ˜Ž'
}
export const IMAGE_MODERATION_LABELS: Record<ImageModeration, string> = {
auto: 'θ‡ͺ动',
low: '低'
}
export const IMAGE_INPUT_FIDELITY_LABELS: Record<ImageInputFidelity, string> = {
low: '低',
high: '高'
}
export type ImageSizeOption = {
value: string
label: string
}
const imageSizeOptionsByRatio: Record<ImageRatio, ImageSizeOption[]> = {
'1:1': [
{ value: '1024x1024', label: '标准 1024Γ—1024' },
{ value: '1536x1536', label: 'ι«˜ζΈ… 1536Γ—1536' },
{ value: '2048x2048', label: '2K 2048Γ—2048' },
{ value: '2560x2560', label: 'ι«˜εˆ† 2560Γ—2560' }
],
'3:2': [
{ value: '1536x1024', label: '标准 1536Γ—1024' },
{ value: '3072x2048', label: 'ι«˜εˆ† 3072Γ—2048' }
],
'2:3': [
{ value: '1024x1536', label: '标准 1024Γ—1536' },
{ value: '2048x3072', label: 'ι«˜εˆ† 2048Γ—3072' }
],
'4:3': [
{ value: '1024x768', label: '标准 1024Γ—768' },
{ value: '1536x1152', label: 'ι«˜ζΈ… 1536Γ—1152' },
{ value: '2048x1536', label: 'ι«˜εˆ† 2048Γ—1536' },
{ value: '3072x2304', label: 'ι«˜εˆ† 3072Γ—2304' }
],
'3:4': [
{ value: '768x1024', label: '标准 768Γ—1024' },
{ value: '1152x1536', label: 'ι«˜ζΈ… 1152Γ—1536' },
{ value: '1536x2048', label: 'ι«˜εˆ† 1536Γ—2048' },
{ value: '2304x3072', label: 'ι«˜εˆ† 2304Γ—3072' }
],
'16:9': [
{ value: '1280x720', label: '720P 1280Γ—720' },
{ value: '1536x864', label: '1536Γ—864' },
{ value: '1792x1008', label: '标准 1792Γ—1008' },
{ value: '2048x1152', label: '2K 2048Γ—1152' },
{ value: '2560x1440', label: '2K 2560Γ—1440' },
{ value: '3072x1728', label: 'ι«˜εˆ† 3072Γ—1728' },
{ value: '3840x2160', label: '4K 3840Γ—2160' }
],
'9:16': [
{ value: '720x1280', label: '720P 720Γ—1280' },
{ value: '864x1536', label: '864Γ—1536' },
{ value: '1008x1792', label: '标准 1008Γ—1792' },
{ value: '1152x2048', label: '2K 1152Γ—2048' },
{ value: '1440x2560', label: '2K 1440Γ—2560' },
{ value: '1728x3072', label: 'ι«˜εˆ† 1728Γ—3072' },
{ value: '2160x3840', label: '4K 2160Γ—3840' }
],
'21:9': [
{ value: '1344x576', label: '标准 1344Γ—576' },
{ value: '1792x768', label: 'ι«˜εˆ† 1792Γ—768' },
{ value: '2240x960', label: 'ι«˜εˆ† 2240Γ—960' },
{ value: '2688x1152', label: 'ι«˜εˆ† 2688Γ—1152' },
{ value: '3136x1344', label: 'ι«˜εˆ† 3136Γ—1344' },
{ value: '3584x1536', label: 'ι«˜εˆ† 3584Γ—1536' }
],
'9:21': [
{ value: '576x1344', label: '标准 576Γ—1344' },
{ value: '768x1792', label: 'ι«˜εˆ† 768Γ—1792' },
{ value: '960x2240', label: 'ι«˜εˆ† 960Γ—2240' },
{ value: '1152x2688', label: 'ι«˜εˆ† 1152Γ—2688' },
{ value: '1344x3136', label: 'ι«˜εˆ† 1344Γ—3136' },
{ value: '1536x3584', label: 'ι«˜εˆ† 1536Γ—3584' }
]
}
export function getImageSizeOptions(ratio: ImageRatio): ImageSizeOption[] {
return imageSizeOptionsByRatio[ratio]
}
export function getDefaultImageSize(ratio: ImageRatio): string {
return getImageSizeOptions(ratio).find((option) => option.label.startsWith('标准'))?.value
|| getImageSizeOptions(ratio)[0]?.value
|| ratioToSize(ratio)
}
export function isImageSizeCompatible(ratio: ImageRatio, size: string): boolean {
return getImageSizeOptions(ratio).some((option) => option.value === size)
}
const ratioSizeMap: Record<ImageRatio, string> = {
'1:1': '1024x1024',
'3:2': '1536x1024',
'2:3': '1024x1536',
'4:3': '1024x768',
'3:4': '768x1024',
'16:9': '1792x1008',
'9:16': '1008x1792',
'21:9': '1344x576',
'9:21': '576x1344'
}
export function ratioToSize(ratio: ImageRatio): string {
return ratioSizeMap[ratio]
}
export function formatImageQuality(quality: ImageQuality): string {
return IMAGE_QUALITY_LABELS[quality] || quality
}
export function formatImageSize(size: string): string {
return size.replace('x', 'Γ—')
}
function toOptionalNumber(value: number | null | undefined): number | undefined {
return typeof value === 'number' && Number.isFinite(value) ? value : undefined
}
export function buildImageEndpoint(baseURL: string): string {
const normalized = baseURL.trim().replace(/\/+$/, '')
return `${normalized}/v1/images/generations`
}
export function buildImageEditEndpoint(baseURL: string): string {
const normalized = baseURL.trim().replace(/\/+$/, '')
return `${normalized}/v1/images/edits`
}
export function supportsImageInputFidelity(model: string): boolean {
return !model.trim().toLowerCase().startsWith('gpt-image-2')
}
export function buildImageRequestBody(input: GenerateImageInput): Record<string, unknown> {
const body: Record<string, unknown> = {
prompt: input.prompt.trim(),
model: input.model.trim() || DEFAULT_MODEL,
size: input.size.trim() || getDefaultImageSize(input.ratio),
quality: input.quality,
n: Math.min(10, Math.max(1, input.n || 1))
}
if (input.outputFormat) body.output_format = input.outputFormat
if (toOptionalNumber(input.outputCompression) != null) body.output_compression = input.outputCompression
if (input.background) body.background = input.background
if (input.moderation) body.moderation = input.moderation
if (input.stream) body.stream = true
if (toOptionalNumber(input.partialImages) != null) body.partial_images = input.partialImages
return body
}