imgto / index.html
Saad4web's picture
undefined - Initial Deployment
d1a9993 verified
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Advanced Image Converter</title>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/npm/@tensorflow/tfjs@latest"></script>
<script src="https://cdn.jsdelivr.net/npm/compressorjs@1.1.1/dist/compressor.min.js"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.dropzone {
border: 2px dashed #3b82f6;
transition: all 0.3s ease;
}
.dropzone.active {
border-color: #10b981;
background-color: #f0f9ff;
}
.progress-bar {
transition: width 0.3s ease;
}
.format-option:hover {
transform: translateY(-2px);
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1);
}
canvas {
max-width: 100%;
height: auto;
}
@keyframes pulse {
0%, 100% {
opacity: 1;
}
50% {
opacity: 0.5;
}
}
.animate-pulse {
animation: pulse 2s cubic-bezier(0.4, 0, 0.6, 1) infinite;
}
</style>
</head>
<body class="bg-gray-50 min-h-screen">
<div class="container mx-auto px-4 py-8">
<header class="text-center mb-12">
<h1 class="text-4xl font-bold text-gray-800 mb-2">Advanced Image Converter</h1>
<p class="text-lg text-gray-600">Convert images between 25+ formats directly in your browser</p>
</header>
<div class="max-w-4xl mx-auto bg-white rounded-xl shadow-md overflow-hidden">
<div class="md:flex">
<div class="p-8 w-full">
<!-- Upload Section -->
<div class="mb-8">
<div id="dropzone" class="dropzone rounded-lg p-12 text-center cursor-pointer mb-4">
<div class="flex flex-col items-center justify-center">
<i class="fas fa-cloud-upload-alt text-4xl text-blue-500 mb-4"></i>
<h3 class="text-xl font-semibold text-gray-700 mb-2">Drag & Drop your image here</h3>
<p class="text-gray-500 mb-4">or</p>
<label for="fileInput" class="bg-blue-500 hover:bg-blue-600 text-white font-medium py-2 px-6 rounded-lg transition duration-300 cursor-pointer">
Browse Files
</label>
<input id="fileInput" type="file" accept="image/*" class="hidden">
</div>
</div>
<div id="fileInfo" class="hidden bg-gray-50 p-4 rounded-lg mb-4">
<div class="flex items-center">
<div class="flex-shrink-0">
<i class="fas fa-image text-blue-500 text-2xl"></i>
</div>
<div class="ml-3 flex-1 min-w-0">
<p id="fileName" class="text-sm font-medium text-gray-900 truncate"></p>
<p id="fileSize" class="text-sm text-gray-500"></p>
</div>
<button id="removeFile" class="text-red-500 hover:text-red-700">
<i class="fas fa-times"></i>
</button>
</div>
</div>
</div>
<!-- Preview Section -->
<div id="previewSection" class="hidden mb-8">
<h3 class="text-lg font-medium text-gray-900 mb-4">Image Preview</h3>
<div class="flex flex-col md:flex-row gap-6">
<div class="flex-1">
<div class="bg-gray-100 rounded-lg overflow-hidden">
<canvas id="originalCanvas" class="w-full h-auto"></canvas>
</div>
<p class="text-sm text-gray-500 mt-2 text-center">Original Image</p>
</div>
<div class="flex items-center justify-center">
<i class="fas fa-arrow-right text-gray-400 text-2xl"></i>
</div>
<div class="flex-1">
<div class="bg-gray-100 rounded-lg overflow-hidden">
<canvas id="processedCanvas" class="w-full h-auto"></canvas>
</div>
<p class="text-sm text-gray-500 mt-2 text-center">Converted Image</p>
</div>
</div>
</div>
<!-- Conversion Options -->
<div id="conversionOptions" class="hidden mb-8">
<h3 class="text-lg font-medium text-gray-900 mb-4">Conversion Options</h3>
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4 mb-6">
<!-- Format options will be added here dynamically -->
</div>
<div class="mb-6">
<label for="qualityRange" class="block text-sm font-medium text-gray-700 mb-2">Quality: <span id="qualityValue">80</span>%</label>
<input id="qualityRange" type="range" min="1" max="100" value="80" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
</div>
<div class="flex flex-wrap gap-4 mb-6">
<div class="flex items-center">
<input id="resizeCheckbox" type="checkbox" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
<label for="resizeCheckbox" class="ml-2 block text-sm text-gray-700">Resize</label>
</div>
<div id="resizeOptions" class="hidden flex-1 flex flex-wrap gap-4">
<div class="flex-1 min-w-[150px]">
<label for="widthInput" class="block text-sm font-medium text-gray-700 mb-1">Width (px)</label>
<input id="widthInput" type="number" class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
</div>
<div class="flex-1 min-w-[150px]">
<label for="heightInput" class="block text-sm font-medium text-gray-700 mb-1">Height (px)</label>
<input id="heightInput" type="number" class="w-full rounded-md border-gray-300 shadow-sm focus:border-blue-500 focus:ring-blue-500 sm:text-sm">
</div>
<div class="flex items-center">
<input id="maintainAspectCheckbox" type="checkbox" checked class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
<label for="maintainAspectCheckbox" class="ml-2 block text-sm text-gray-700">Maintain aspect ratio</label>
</div>
</div>
</div>
<div class="flex flex-wrap gap-4">
<div class="flex items-center">
<input id="enhanceCheckbox" type="checkbox" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
<label for="enhanceCheckbox" class="ml-2 block text-sm text-gray-700">Enhance with AI</label>
</div>
<div class="flex items-center">
<input id="grayscaleCheckbox" type="checkbox" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
<label for="grayscaleCheckbox" class="ml-2 block text-sm text-gray-700">Grayscale</label>
</div>
<div class="flex items-center">
<input id="flipCheckbox" type="checkbox" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
<label for="flipCheckbox" class="ml-2 block text-sm text-gray-700">Flip horizontally</label>
</div>
</div>
</div>
<!-- Progress Section -->
<div id="progressSection" class="hidden mb-8">
<div class="mb-2 flex justify-between">
<span class="text-sm font-medium text-gray-700">Processing...</span>
<span id="progressPercent" class="text-sm font-medium text-gray-700">0%</span>
</div>
<div class="w-full bg-gray-200 rounded-full h-2.5">
<div id="progressBar" class="progress-bar bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div>
</div>
</div>
<!-- Action Buttons -->
<div class="flex flex-wrap gap-4 justify-center">
<button id="convertBtn" class="hidden bg-blue-600 hover:bg-blue-700 text-white font-medium py-3 px-8 rounded-lg transition duration-300 flex items-center">
<i class="fas fa-cog mr-2 animate-spin hidden"></i>
Convert Image
</button>
<button id="downloadBtn" class="hidden bg-green-600 hover:bg-green-700 text-white font-medium py-3 px-8 rounded-lg transition duration-300 flex items-center">
<i class="fas fa-download mr-2"></i>
Download Result
</button>
<button id="resetBtn" class="hidden bg-gray-200 hover:bg-gray-300 text-gray-800 font-medium py-3 px-8 rounded-lg transition duration-300">
Start Over
</button>
</div>
</div>
</div>
</div>
<!-- Supported Formats Section -->
<div class="max-w-4xl mx-auto mt-12 bg-white rounded-xl shadow-md overflow-hidden">
<div class="p-8">
<h2 class="text-2xl font-bold text-gray-800 mb-6">Supported Formats</h2>
<div class="grid grid-cols-2 sm:grid-cols-3 md:grid-cols-4 lg:grid-cols-5 gap-4">
<div class="bg-blue-50 p-3 rounded-lg flex items-center">
<i class="fas fa-file-image text-blue-500 mr-2"></i>
<span>PNG</span>
</div>
<div class="bg-blue-50 p-3 rounded-lg flex items-center">
<i class="fas fa-file-image text-blue-500 mr-2"></i>
<span>JPEG</span>
</div>
<div class="bg-blue-50 p-3 rounded-lg flex items-center">
<i class="fas fa-file-image text-blue-500 mr-2"></i>
<span>GIF</span>
</div>
<div class="bg-blue-50 p-3 rounded-lg flex items-center">
<i class="fas fa-file-image text-blue-500 mr-2"></i>
<span>WEBP</span>
</div>
<div class="bg-blue-50 p-3 rounded-lg flex items-center">
<i class="fas fa-file-image text-blue-500 mr-2"></i>
<span>BMP</span>
</div>
<div class="bg-blue-50 p-3 rounded-lg flex items-center">
<i class="fas fa-file-image text-blue-500 mr-2"></i>
<span>TIFF</span>
</div>
<div class="bg-blue-50 p-3 rounded-lg flex items-center">
<i class="fas fa-file-image text-blue-500 mr-2"></i>
<span>SVG</span>
</div>
<div class="bg-blue-50 p-3 rounded-lg flex items-center">
<i class="fas fa-file-image text-blue-500 mr-2"></i>
<span>ICO</span>
</div>
<div class="bg-blue-50 p-3 rounded-lg flex items-center">
<i class="fas fa-file-image text-blue-500 mr-2"></i>
<span>PDF</span>
</div>
<div class="bg-blue-50 p-3 rounded-lg flex items-center">
<i class="fas fa-file-image text-blue-500 mr-2"></i>
<span>HEIC</span>
</div>
</div>
</div>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// DOM Elements
const dropzone = document.getElementById('dropzone');
const fileInput = document.getElementById('fileInput');
const fileInfo = document.getElementById('fileInfo');
const fileName = document.getElementById('fileName');
const fileSize = document.getElementById('fileSize');
const removeFile = document.getElementById('removeFile');
const previewSection = document.getElementById('previewSection');
const originalCanvas = document.getElementById('originalCanvas');
const processedCanvas = document.getElementById('processedCanvas');
const conversionOptions = document.getElementById('conversionOptions');
const progressSection = document.getElementById('progressSection');
const progressBar = document.getElementById('progressBar');
const progressPercent = document.getElementById('progressPercent');
const convertBtn = document.getElementById('convertBtn');
const downloadBtn = document.getElementById('downloadBtn');
const resetBtn = document.getElementById('resetBtn');
const qualityRange = document.getElementById('qualityRange');
const qualityValue = document.getElementById('qualityValue');
const resizeCheckbox = document.getElementById('resizeCheckbox');
const resizeOptions = document.getElementById('resizeOptions');
const widthInput = document.getElementById('widthInput');
const heightInput = document.getElementById('heightInput');
const maintainAspectCheckbox = document.getElementById('maintainAspectCheckbox');
const enhanceCheckbox = document.getElementById('enhanceCheckbox');
const grayscaleCheckbox = document.getElementById('grayscaleCheckbox');
const flipCheckbox = document.getElementById('flipCheckbox');
const formatOptionsContainer = document.querySelector('.grid.grid-cols-2');
// State variables
let originalImage = null;
let processedImageBlob = null;
let selectedFormat = 'jpeg';
let originalWidth = 0;
let originalHeight = 0;
let aspectRatio = 1;
// Supported formats
const supportedFormats = [
{ name: 'PNG', value: 'png', icon: 'fa-file-image' },
{ name: 'JPEG', value: 'jpeg', icon: 'fa-file-image' },
{ name: 'GIF', value: 'gif', icon: 'fa-file-image' },
{ name: 'WEBP', value: 'webp', icon: 'fa-file-image' },
{ name: 'BMP', value: 'bmp', icon: 'fa-file-image' },
{ name: 'TIFF', value: 'tiff', icon: 'fa-file-image' },
{ name: 'SVG', value: 'svg', icon: 'fa-file-image' },
{ name: 'ICO', value: 'ico', icon: 'fa-file-image' },
{ name: 'PDF', value: 'pdf', icon: 'fa-file-pdf' },
{ name: 'HEIC', value: 'heic', icon: 'fa-file-image' }
];
// Initialize format options
function initFormatOptions() {
formatOptionsContainer.innerHTML = '';
supportedFormats.forEach(format => {
const formatOption = document.createElement('div');
formatOption.className = 'format-option bg-blue-50 p-3 rounded-lg flex items-center cursor-pointer transition duration-200';
formatOption.innerHTML = `
<i class="fas ${format.icon} text-blue-500 mr-2"></i>
<span>${format.name}</span>
`;
formatOption.addEventListener('click', () => selectFormat(format.value));
if (format.value === selectedFormat) {
formatOption.classList.add('ring-2', 'ring-blue-500', 'bg-blue-100');
}
formatOption.dataset.format = format.value;
formatOptionsContainer.appendChild(formatOption);
});
}
// Select format
function selectFormat(format) {
selectedFormat = format;
const options = document.querySelectorAll('.format-option');
options.forEach(option => {
if (option.dataset.format === format) {
option.classList.add('ring-2', 'ring-blue-500', 'bg-blue-100');
} else {
option.classList.remove('ring-2', 'ring-blue-500', 'bg-blue-100');
}
});
}
// Initialize the app
initFormatOptions();
// Update quality value display
qualityRange.addEventListener('input', () => {
qualityValue.textContent = qualityRange.value;
});
// Toggle resize options
resizeCheckbox.addEventListener('change', () => {
if (resizeCheckbox.checked) {
resizeOptions.classList.remove('hidden');
} else {
resizeOptions.classList.add('hidden');
}
});
// Maintain aspect ratio
widthInput.addEventListener('input', () => {
if (maintainAspectCheckbox.checked && widthInput.value) {
heightInput.value = Math.round(widthInput.value / aspectRatio);
}
});
heightInput.addEventListener('input', () => {
if (maintainAspectCheckbox.checked && heightInput.value) {
widthInput.value = Math.round(heightInput.value * aspectRatio);
}
});
// Drag and drop events
['dragenter', 'dragover', 'dragleave', 'drop'].forEach(eventName => {
dropzone.addEventListener(eventName, preventDefaults, false);
});
function preventDefaults(e) {
e.preventDefault();
e.stopPropagation();
}
['dragenter', 'dragover'].forEach(eventName => {
dropzone.addEventListener(eventName, highlight, false);
});
['dragleave', 'drop'].forEach(eventName => {
dropzone.addEventListener(eventName, unhighlight, false);
});
function highlight() {
dropzone.classList.add('active');
}
function unhighlight() {
dropzone.classList.remove('active');
}
dropzone.addEventListener('drop', handleDrop, false);
fileInput.addEventListener('change', handleFiles);
function handleDrop(e) {
const dt = e.dataTransfer;
const files = dt.files;
handleFiles({ target: { files } });
}
// Handle file selection
function handleFiles(e) {
const files = e.target.files;
if (files.length === 0) return;
const file = files[0];
if (!file.type.match('image.*')) {
alert('Please select an image file.');
return;
}
// Update file info
fileName.textContent = file.name;
fileSize.textContent = formatFileSize(file.size);
fileInfo.classList.remove('hidden');
// Read the image file
const reader = new FileReader();
reader.onload = function(event) {
originalImage = new Image();
originalImage.onload = function() {
// Set original dimensions
originalWidth = originalImage.width;
originalHeight = originalImage.height;
aspectRatio = originalWidth / originalHeight;
// Set default resize values
widthInput.value = originalWidth;
heightInput.value = originalHeight;
// Draw original image on canvas
const ctx = originalCanvas.getContext('2d');
originalCanvas.width = originalWidth;
originalCanvas.height = originalHeight;
ctx.drawImage(originalImage, 0, 0, originalWidth, originalHeight);
// Show preview and options
previewSection.classList.remove('hidden');
conversionOptions.classList.remove('hidden');
convertBtn.classList.remove('hidden');
// Copy to processed canvas
processedCanvas.width = originalWidth;
processedCanvas.height = originalHeight;
const processedCtx = processedCanvas.getContext('2d');
processedCtx.drawImage(originalImage, 0, 0, originalWidth, originalHeight);
};
originalImage.src = event.target.result;
};
reader.readAsDataURL(file);
}
// Format file size
function formatFileSize(bytes) {
if (bytes === 0) return '0 Bytes';
const k = 1024;
const sizes = ['Bytes', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
}
// Remove file
removeFile.addEventListener('click', function() {
fileInput.value = '';
fileInfo.classList.add('hidden');
previewSection.classList.add('hidden');
conversionOptions.classList.add('hidden');
convertBtn.classList.add('hidden');
downloadBtn.classList.add('hidden');
resetBtn.classList.add('hidden');
originalImage = null;
processedImageBlob = null;
});
// Convert image
convertBtn.addEventListener('click', async function() {
if (!originalImage) return;
// Show progress
progressSection.classList.remove('hidden');
progressBar.style.width = '0%';
progressPercent.textContent = '0%';
// Disable convert button
convertBtn.disabled = true;
convertBtn.querySelector('i').classList.remove('hidden');
// Get conversion options
const quality = parseInt(qualityRange.value) / 100;
let width = originalWidth;
let height = originalHeight;
if (resizeCheckbox.checked) {
width = parseInt(widthInput.value) || originalWidth;
height = parseInt(heightInput.value) || originalHeight;
}
// Process the image
try {
// Update progress
updateProgress(10);
// Create a new canvas with the desired dimensions
const tempCanvas = document.createElement('canvas');
tempCanvas.width = width;
tempCanvas.height = height;
const tempCtx = tempCanvas.getContext('2d');
// Draw the image with the new dimensions
tempCtx.drawImage(originalImage, 0, 0, width, height);
// Apply filters if needed
if (grayscaleCheckbox.checked) {
const imageData = tempCtx.getImageData(0, 0, width, height);
const data = imageData.data;
for (let i = 0; i < data.length; i += 4) {
const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
data[i] = avg; // red
data[i + 1] = avg; // green
data[i + 2] = avg; // blue
}
tempCtx.putImageData(imageData, 0, 0);
}
if (flipCheckbox.checked) {
tempCtx.translate(width, 0);
tempCtx.scale(-1, 1);
tempCtx.drawImage(originalImage, 0, 0, width, height);
}
updateProgress(50);
// AI Enhancement (simulated with TensorFlow.js)
if (enhanceCheckbox.checked) {
await enhanceImageWithAI(tempCanvas);
updateProgress(70);
}
// Convert to selected format
processedCanvas.width = width;
processedCanvas.height = height;
const processedCtx = processedCanvas.getContext('2d');
processedCtx.drawImage(tempCanvas, 0, 0, width, height);
// Convert canvas to blob
tempCanvas.toBlob((blob) => {
processedImageBlob = blob;
updateProgress(90);
// Create download link
const url = URL.createObjectURL(blob);
downloadBtn.onclick = function() {
const a = document.createElement('a');
a.href = url;
a.download = `converted.${selectedFormat}`;
document.body.appendChild(a);
a.click();
setTimeout(() => {
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, 100);
};
updateProgress(100);
setTimeout(() => {
progressSection.classList.add('hidden');
convertBtn.disabled = false;
convertBtn.querySelector('i').classList.add('hidden');
downloadBtn.classList.remove('hidden');
resetBtn.classList.remove('hidden');
}, 500);
}, `image/${selectedFormat}`, quality);
} catch (error) {
console.error('Conversion error:', error);
alert('An error occurred during conversion. Please try again.');
progressSection.classList.add('hidden');
convertBtn.disabled = false;
convertBtn.querySelector('i').classList.add('hidden');
}
});
// Update progress
function updateProgress(percent) {
progressBar.style.width = `${percent}%`;
progressPercent.textContent = `${percent}%`;
}
// Simulate AI enhancement (in a real app, you would use actual TensorFlow.js models)
async function enhanceImageWithAI(canvas) {
// Show loading state
const loadingText = document.createElement('div');
loadingText.textContent = 'Enhancing with AI...';
loadingText.className = 'text-sm text-gray-500 mt-2';
progressSection.appendChild(loadingText);
// Simulate processing delay
await new Promise(resolve => setTimeout(resolve, 1500));
// In a real implementation, you would:
// 1. Load a TensorFlow.js model
// 2. Process the image with the model
// 3. Apply the enhancements to the canvas
// For demo purposes, we'll just apply a simple sharpening effect
const ctx = canvas.getContext('2d');
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const data = imageData.data;
// Simple sharpening kernel
for (let i = 0; i < data.length; i += 4) {
// Increase contrast slightly
data[i] = Math.min(255, data[i] * 1.1); // red
data[i + 1] = Math.min(255, data[i + 1] * 1.1); // green
data[i + 2] = Math.min(255, data[i + 2] * 1.1); // blue
}
ctx.putImageData(imageData, 0, 0);
loadingText.remove();
}
// Reset button
resetBtn.addEventListener('click', function() {
fileInput.value = '';
fileInfo.classList.add('hidden');
previewSection.classList.add('hidden');
conversionOptions.classList.add('hidden');
progressSection.classList.add('hidden');
convertBtn.classList.add('hidden');
downloadBtn.classList.add('hidden');
resetBtn.classList.add('hidden');
originalImage = null;
processedImageBlob = null;
});
});
</script>
<p style="border-radius: 8px; text-align: center; font-size: 12px; color: #fff; margin-top: 16px;position: fixed; left: 8px; bottom: 8px; z-index: 10; background: rgba(0, 0, 0, 0.8); padding: 4px 8px;">Made with <img src="https://enzostvs-deepsite.hf.space/logo.svg" alt="DeepSite Logo" style="width: 16px; height: 16px; vertical-align: middle;display:inline-block;margin-right:3px;filter:brightness(0) invert(1);"><a href="https://enzostvs-deepsite.hf.space" style="color: #fff;text-decoration: underline;" target="_blank" >DeepSite</a> - 🧬 <a href="https://enzostvs-deepsite.hf.space?remix=Saad4web/imgto" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>