diff --git a/src/components/image-converter.tsx b/src/components/image-converter.tsx index e01ef5f..bdf7e20 100644 --- a/src/components/image-converter.tsx +++ b/src/components/image-converter.tsx @@ -4,8 +4,6 @@ import { useState, useRef, ChangeEvent, useEffect } from "react"; import { Card, CardContent, - CardDescription, - CardFooter, CardHeader, CardTitle, } from "@/components/ui/card"; @@ -31,6 +29,7 @@ import { AccordionTrigger, } from "@/components/ui/accordion"; import { Slider } from "@/components/ui/slider"; +import { ObjectPositionControl } from "./object-position-control"; export function ImageConverter() { const [images, setImages] = useState([]); @@ -47,6 +46,9 @@ export function ImageConverter() { const [counterStart, setCounterStart] = useState(1); const [counterDigits, setCounterDigits] = useState(3); + const [scaleMode, setScaleMode] = useState<'fill' | 'cover' | 'contain'>('fill'); + const [objectPosition, setObjectPosition] = useState('center center'); + const [isConverting, setIsConverting] = useState(false); const [convertingIndex, setConvertingIndex] = useState(null); const [isDraggingOver, setIsDraggingOver] = useState(false); @@ -167,7 +169,49 @@ export function ImageConverter() { const ctx = canvas.getContext("2d"); if (ctx) { - ctx.drawImage(img, 0, 0, targetWidth, targetHeight); + const sWidth = img.naturalWidth; + const sHeight = img.naturalHeight; + const dWidth = targetWidth; + const dHeight = targetHeight; + + if (scaleMode === 'fill' || !width || !height) { + ctx.drawImage(img, 0, 0, dWidth, dHeight); + } else { + const sourceRatio = sWidth / sHeight; + const targetRatio = dWidth / dHeight; + let sx = 0, sy = 0, sRenderWidth = sWidth, sRenderHeight = sHeight; + let dx = 0, dy = 0, dRenderWidth = dWidth, dRenderHeight = dHeight; + const [hPos, vPos] = objectPosition.split(' '); + + if (scaleMode === 'cover') { + if (sourceRatio > targetRatio) { + sRenderHeight = sHeight; + sRenderWidth = sHeight * targetRatio; + if (hPos === 'center') sx = (sWidth - sRenderWidth) / 2; + if (hPos === 'right') sx = sWidth - sRenderWidth; + } else { + sRenderWidth = sWidth; + sRenderHeight = sWidth / targetRatio; + if (vPos === 'center') sy = (sHeight - sRenderHeight) / 2; + if (vPos === 'bottom') sy = sHeight - sRenderHeight; + } + ctx.drawImage(img, sx, sy, sRenderWidth, sRenderHeight, 0, 0, dWidth, dHeight); + } else if (scaleMode === 'contain') { + if (sourceRatio > targetRatio) { + dRenderWidth = dWidth; + dRenderHeight = dWidth / sourceRatio; + if (vPos === 'center') dy = (dHeight - dRenderHeight) / 2; + if (vPos === 'bottom') dy = dHeight - dRenderHeight; + } else { + dRenderHeight = dHeight; + dRenderWidth = dHeight * sourceRatio; + if (hPos === 'center') dx = (dWidth - dRenderWidth) / 2; + if (hPos === 'right') dx = dWidth - dRenderWidth; + } + ctx.drawImage(img, 0, 0, sWidth, sHeight, dx, dy, dRenderWidth, dRenderHeight); + } + } + const mimeType = `image/${format}`; const dataUrl = canvas.toDataURL(mimeType, format === 'png' ? undefined : quality / 100); const link = document.createElement("a"); @@ -250,7 +294,7 @@ export function ImageConverter() {

Image Settings

- Adjust resolution for all uploaded images. + Adjust resolution and scaling for all images.

@@ -265,6 +309,23 @@ export function ImageConverter() { setHeight(e.target.value)} /> +
+ + +
+ {scaleMode !== 'fill' && ( +
+ + setObjectPosition(pos)} /> +
+ )} diff --git a/src/components/object-position-control.tsx b/src/components/object-position-control.tsx new file mode 100644 index 0000000..57c5031 --- /dev/null +++ b/src/components/object-position-control.tsx @@ -0,0 +1,46 @@ +"use client"; + +import { cn } from "@/lib/utils"; + +type Position = + | "left top" + | "center top" + | "right top" + | "left center" + | "center center" + | "right center" + | "left bottom" + | "center bottom" + | "right bottom"; + +const positions: Position[] = [ + "left top", "center top", "right top", + "left center", "center center", "right center", + "left bottom", "center bottom", "right bottom", +]; + +interface ObjectPositionControlProps { + value: string; + onChange: (value: Position) => void; +} + +export function ObjectPositionControl({ value, onChange }: ObjectPositionControlProps) { + return ( +
+ {positions.map((pos) => ( +
+ ); +} \ No newline at end of file