95 lines
3.6 KiB
TypeScript
95 lines
3.6 KiB
TypeScript
export type GrsaiGptImageSizeBucket = "1K" | "2K" | "4K";
|
|
|
|
type GrsaiGptImageSizeOption = {
|
|
bucket: GrsaiGptImageSizeBucket;
|
|
width: number;
|
|
height: number;
|
|
};
|
|
|
|
const GRSAI_GPT_IMAGE_SIZE_OPTIONS: GrsaiGptImageSizeOption[] = [
|
|
{ bucket: "1K", width: 1024, height: 1024 },
|
|
{ bucket: "1K", width: 1774, height: 887 },
|
|
{ bucket: "1K", width: 887, height: 1774 },
|
|
{ bucket: "1K", width: 1536, height: 1024 },
|
|
{ bucket: "1K", width: 1024, height: 1536 },
|
|
{ bucket: "2K", width: 2048, height: 2048 },
|
|
{ bucket: "2K", width: 2048, height: 1152 },
|
|
{ bucket: "2K", width: 1152, height: 2048 },
|
|
{ bucket: "2K", width: 2048, height: 1360 },
|
|
{ bucket: "2K", width: 1360, height: 2048 },
|
|
{ bucket: "2K", width: 2048, height: 880 },
|
|
{ bucket: "2K", width: 880, height: 2048 },
|
|
{ bucket: "2K", width: 2048, height: 688 },
|
|
{ bucket: "2K", width: 688, height: 2048 },
|
|
{ bucket: "2K", width: 2048, height: 1024 },
|
|
{ bucket: "2K", width: 1024, height: 2048 },
|
|
{ bucket: "4K", width: 2880, height: 2880 },
|
|
{ bucket: "4K", width: 3840, height: 2160 },
|
|
{ bucket: "4K", width: 2160, height: 3840 },
|
|
{ bucket: "4K", width: 3504, height: 2336 },
|
|
{ bucket: "4K", width: 2336, height: 3504 },
|
|
{ bucket: "4K", width: 3840, height: 1648 },
|
|
{ bucket: "4K", width: 1648, height: 3840 },
|
|
{ bucket: "4K", width: 3840, height: 1280 },
|
|
{ bucket: "4K", width: 1280, height: 3840 },
|
|
{ bucket: "4K", width: 3840, height: 1920 },
|
|
{ bucket: "4K", width: 1920, height: 3840 },
|
|
];
|
|
|
|
const GRSAI_GPT_IMAGE_SIZE_VALUES = new Set(
|
|
GRSAI_GPT_IMAGE_SIZE_OPTIONS.map((option) => `${option.width}x${option.height}`)
|
|
);
|
|
|
|
function normalizeGrsaiGptImageBucket(imageSize?: string): GrsaiGptImageSizeBucket {
|
|
const normalized = String(imageSize || "").trim().toUpperCase();
|
|
if (normalized === "1K" || normalized === "2K" || normalized === "4K") {
|
|
return normalized;
|
|
}
|
|
return "2K";
|
|
}
|
|
|
|
function parseAspectRatioValue(aspectRatio?: string): number {
|
|
const normalized = String(aspectRatio || "").trim().toLowerCase();
|
|
if (!normalized || normalized === "auto") {
|
|
return 1;
|
|
}
|
|
|
|
const [widthRaw, heightRaw] = normalized.split(":").map((part) => Number(part));
|
|
if (!Number.isFinite(widthRaw) || !Number.isFinite(heightRaw) || widthRaw <= 0 || heightRaw <= 0) {
|
|
return 1;
|
|
}
|
|
|
|
return widthRaw / heightRaw;
|
|
}
|
|
|
|
export function getGrsaiGptImageOutputSize(aspectRatio?: string, imageSize?: string) {
|
|
const bucket = normalizeGrsaiGptImageBucket(imageSize);
|
|
const targetRatio = parseAspectRatioValue(aspectRatio);
|
|
const candidates = GRSAI_GPT_IMAGE_SIZE_OPTIONS.filter((option) => option.bucket === bucket);
|
|
const best = candidates.reduce((currentBest, option) => {
|
|
const currentDelta = Math.abs(currentBest.width / currentBest.height - targetRatio);
|
|
const optionDelta = Math.abs(option.width / option.height - targetRatio);
|
|
if (optionDelta < currentDelta) {
|
|
return option;
|
|
}
|
|
if (optionDelta === currentDelta) {
|
|
const currentArea = currentBest.width * currentBest.height;
|
|
const optionArea = option.width * option.height;
|
|
return optionArea > currentArea ? option : currentBest;
|
|
}
|
|
return currentBest;
|
|
}, candidates[0]);
|
|
|
|
return { width: best.width, height: best.height };
|
|
}
|
|
|
|
export function resolveGrsaiGptImageSize(aspectRatio?: string, imageSize?: string): string {
|
|
const requestedSize = String(imageSize || "").trim().toLowerCase();
|
|
if (/^\d+x\d+$/.test(requestedSize) && GRSAI_GPT_IMAGE_SIZE_VALUES.has(requestedSize)) {
|
|
return requestedSize;
|
|
}
|
|
|
|
const { width, height } = getGrsaiGptImageOutputSize(aspectRatio, imageSize);
|
|
return `${width}x${height}`;
|
|
}
|