newspaces-are-good / index.html
swapit's picture
Add 3 files
7a50916 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 Editor | Creative Studio</title>
<script src="https://cdn.tailwindcss.com"></script>
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
<style>
.editor-container {
background: linear-gradient(135deg, #f5f7fa 0%, #c3cfe2 100%);
}
.tool-btn {
transition: all 0.3s ease;
}
.tool-btn:hover {
transform: translateY(-3px);
box-shadow: 0 10px 20px rgba(0,0,0,0.1);
}
.preview-box {
box-shadow: 0 20px 25px -5px rgba(0,0,0,0.1), 0 10px 10px -5px rgba(0,0,0,0.04);
}
.slider-thumb::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 20px;
height: 20px;
border-radius: 50%;
background: #4f46e5;
cursor: pointer;
}
.tab-active {
border-bottom: 3px solid #4f46e5;
}
.image-thumbnail {
transition: all 0.2s ease;
}
.image-thumbnail:hover {
transform: scale(1.05);
box-shadow: 0 4px 6px rgba(0,0,0,0.1);
}
.image-thumbnail.active {
border: 2px solid #4f46e5;
}
.prompt-box {
min-height: 100px;
resize: vertical;
}
.pose-preview {
background-size: contain;
background-repeat: no-repeat;
background-position: center;
}
.processing-overlay {
position: absolute;
top: 0;
left: 0;
right: 0;
bottom: 0;
background: rgba(0,0,0,0.7);
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: white;
z-index: 10;
}
.face-landmarks {
position: absolute;
width: 20px;
height: 20px;
background: rgba(255,0,0,0.5);
border-radius: 50%;
transform: translate(-50%, -50%);
}
.selection-box {
position: absolute;
border: 2px dashed #4f46e5;
background: rgba(79, 70, 229, 0.2);
z-index: 5;
}
.predictive-options {
position: absolute;
background: white;
border-radius: 4px;
box-shadow: 0 2px 10px rgba(0,0,0,0.1);
z-index: 20;
padding: 5px;
}
.predictive-option {
padding: 5px 10px;
cursor: pointer;
border-radius: 3px;
}
.predictive-option:hover {
background: #f3f4f6;
}
.effect-thumb {
background-size: cover;
background-position: center;
}
</style>
</head>
<body class="bg-gray-50">
<!-- Header -->
<header class="bg-white shadow-sm">
<div class="container mx-auto px-4 py-4 flex justify-between items-center">
<div class="flex items-center space-x-2">
<i class="fas fa-camera-retro text-indigo-600 text-2xl"></i>
<h1 class="text-xl font-bold text-gray-800">Advanced Image Editor</h1>
</div>
<nav class="hidden md:flex space-x-8">
<a href="#" class="text-gray-600 hover:text-indigo-600 font-medium">Home</a>
<a href="#" class="text-gray-600 hover:text-indigo-600 font-medium">Features</a>
<a href="#" class="text-gray-600 hover:text-indigo-600 font-medium">Tutorials</a>
<a href="#" class="text-gray-600 hover:text-indigo-600 font-medium">Pricing</a>
</nav>
<div class="flex items-center space-x-4">
<button class="px-4 py-2 rounded-md text-gray-600 hover:bg-gray-100">
<i class="fas fa-user-circle text-xl"></i>
</button>
<button class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">
Upgrade
</button>
</div>
</div>
</header>
<!-- Main Content -->
<main class="container mx-auto px-4 py-8">
<div class="flex flex-col lg:flex-row gap-8">
<!-- Tools Panel -->
<div class="w-full lg:w-1/4 bg-white rounded-xl shadow-md p-6 h-fit">
<div class="flex border-b mb-6">
<button id="image-tab" class="tab-active px-4 py-2 font-medium text-indigo-600">Image</button>
<button id="video-tab" class="px-4 py-2 font-medium text-gray-500 hover:text-indigo-600">Video</button>
</div>
<div id="image-tools">
<!-- Multi Image Upload -->
<div class="mb-6">
<h3 class="font-medium text-gray-700 mb-3">Upload Reference Images</h3>
<div class="border-2 border-dashed border-gray-300 rounded-lg p-4 text-center cursor-pointer hover:bg-gray-50 mb-3" id="upload-area">
<i class="fas fa-cloud-upload-alt text-3xl text-gray-400 mb-2"></i>
<p class="text-gray-500">Drag & drop files here</p>
<p class="text-sm text-gray-400 mt-1">or click to browse</p>
<input type="file" id="file-upload" class="hidden" accept="image/*" multiple>
</div>
<div id="thumbnail-container" class="grid grid-cols-3 gap-2 mt-2 max-h-40 overflow-y-auto"></div>
</div>
<!-- AI Prompts -->
<div class="mb-6">
<h3 class="font-medium text-gray-700 mb-3">AI Prompts</h3>
<textarea id="ai-prompt" class="w-full border border-gray-300 rounded-md p-2 text-sm prompt-box" placeholder="Describe the changes you want (e.g. 'make breasts larger', 'enhance curves', 'face swap with second image')"></textarea>
<div class="mt-2 grid grid-cols-2 gap-2">
<button onclick="applyPrompt('Increase breast size by 20%, enhance curves')" class="px-2 py-1 bg-gray-100 text-xs rounded hover:bg-gray-200">Plastic Surgery</button>
<button onclick="applyPrompt('Slim waist, enhance hips, tone stomach')" class="px-2 py-1 bg-gray-100 text-xs rounded hover:bg-gray-200">Body Reshape</button>
<button onclick="showFaceSwapModal()" class="px-2 py-1 bg-gray-100 text-xs rounded hover:bg-gray-200">Face Swap</button>
<button onclick="applyPrompt('Smooth skin, remove blemishes, even skin tone')" class="px-2 py-1 bg-gray-100 text-xs rounded hover:bg-gray-200">Skin Smoothing</button>
</div>
</div>
<!-- Text & Object Removal -->
<div class="mb-6">
<h3 class="font-medium text-gray-700 mb-3">Text & Object Removal</h3>
<div class="grid grid-cols-3 gap-3">
<button class="tool-btn flex flex-col items-center p-3 bg-gray-100 rounded-lg hover:bg-indigo-50" onclick="startTextRemoval()">
<i class="fas fa-font text-indigo-600 text-xl mb-1"></i>
<span class="text-xs text-gray-700">Remove Text</span>
</button>
<button class="tool-btn flex flex-col items-center p-3 bg-gray-100 rounded-lg hover:bg-indigo-50" onclick="startObjectSelection()">
<i class="fas fa-object-group text-indigo-600 text-xl mb-1"></i>
<span class="text-xs text-gray-700">Remove Object</span>
</button>
<button class="tool-btn flex flex-col items-center p-3 bg-gray-100 rounded-lg hover:bg-indigo-50" onclick="startPredictiveEdit()">
<i class="fas fa-brain text-indigo-600 text-xl mb-1"></i>
<span class="text-xs text-gray-700">Predictive Edit</span>
</button>
</div>
</div>
<!-- Face Tools -->
<div class="mb-6">
<h3 class="font-medium text-gray-700 mb-3">Face Tools</h3>
<div class="grid grid-cols-3 gap-3">
<button class="tool-btn flex flex-col items-center p-3 bg-gray-100 rounded-lg hover:bg-indigo-50" onclick="showFaceSwapModal()">
<i class="fas fa-user-edit text-indigo-600 text-xl mb-1"></i>
<span class="text-xs text-gray-700">Face Swap</span>
</button>
<button class="tool-btn flex flex-col items-center p-3 bg-gray-100 rounded-lg hover:bg-indigo-50" onclick="applyPrompt('Make face expression more happy, slight smile')">
<i class="fas fa-smile text-indigo-600 text-xl mb-1"></i>
<span class="text-xs text-gray-700">Expressions</span>
</button>
<button class="tool-btn flex flex-col items-center p-3 bg-gray-100 rounded-lg hover:bg-indigo-50" onclick="applyPrompt('Beautify face: enhance eyes, perfect lips, symmetrical features')">
<i class="fas fa-magic text-indigo-600 text-xl mb-1"></i>
<span class="text-xs text-gray-700">Beautify</span>
</button>
</div>
</div>
<!-- Pose Maker -->
<div class="mb-6">
<h3 class="font-medium text-gray-700 mb-3">Pose Maker</h3>
<div class="grid grid-cols-3 gap-2 mb-3">
<div class="pose-preview h-16 bg-gray-100 rounded cursor-pointer" onclick="applyPose('standing')" style="background-image: url('https://i.imgur.com/JQlE0gP.png')"></div>
<div class="pose-preview h-16 bg-gray-100 rounded cursor-pointer" onclick="applyPose('sitting')" style="background-image: url('https://i.imgur.com/5XkJQqG.png')"></div>
<div class="pose-preview h-16 bg-gray-100 rounded cursor-pointer" onclick="applyPose('dancing')" style="background-image: url('https://i.imgur.com/8zJqWQk.png')"></div>
<div class="pose-preview h-16 bg-gray-100 rounded cursor-pointer" onclick="applyPose('yoga')" style="background-image: url('https://i.imgur.com/3mJQkqG.png')"></div>
<div class="pose-preview h-16 bg-gray-100 rounded cursor-pointer" onclick="applyPose('running')" style="background-image: url('https://i.imgur.com/7XkJQqG.png')"></div>
<div class="pose-preview h-16 bg-gray-100 rounded cursor-pointer" onclick="applyPose('flexing')" style="background-image: url('https://i.imgur.com/9zJqWQk.png')"></div>
</div>
<button class="w-full py-2 bg-indigo-50 text-indigo-600 rounded-md text-sm hover:bg-indigo-100" onclick="showCustomPoseModal()">
<i class="fas fa-plus mr-1"></i> Custom Pose
</button>
</div>
<!-- Body Tools -->
<div class="mb-6">
<h3 class="font-medium text-gray-700 mb-3">Body Tools</h3>
<div class="space-y-4">
<div>
<label class="block text-sm text-gray-600 mb-1">Breast Size</label>
<input type="range" class="w-full slider-thumb" id="breast-slider" min="-50" max="50" value="0" oninput="updateBodyParam('breast', this.value)">
<div class="flex justify-between text-xs text-gray-500">
<span>Smaller</span>
<span>Larger</span>
</div>
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">Hip Width</label>
<input type="range" class="w-full slider-thumb" id="hip-slider" min="-50" max="50" value="0" oninput="updateBodyParam('hip', this.value)">
<div class="flex justify-between text-xs text-gray-500">
<span>Narrower</span>
<span>Wider</span>
</div>
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">Waist Slimness</label>
<input type="range" class="w-full slider-thumb" id="waist-slider" min="-50" max="50" value="0" oninput="updateBodyParam('waist', this.value)">
<div class="flex justify-between text-xs text-gray-500">
<span>Wider</span>
<span>Slimmer</span>
</div>
</div>
<div>
<label class="block text-sm text-gray-600 mb-1">Leg Length</label>
<input type="range" class="w-full slider-thumb" id="leg-slider" min="-50" max="50" value="0" oninput="updateBodyParam('leg', this.value)">
<div class="flex justify-between text-xs text-gray-500">
<span>Shorter</span>
<span>Longer</span>
</div>
</div>
</div>
</div>
<!-- Effects -->
<div class="mb-6">
<h3 class="font-medium text-gray-700 mb-3">Effects</h3>
<div class="grid grid-cols-4 gap-2">
<div class="effect-thumb bg-gray-200 rounded-md h-16 cursor-pointer hover:ring-2 hover:ring-indigo-500" style="background-image: url('https://i.imgur.com/JQlE0gP.png')" onclick="applyEffect('vintage')"></div>
<div class="effect-thumb bg-gray-300 rounded-md h-16 cursor-pointer hover:ring-2 hover:ring-indigo-500" style="background-image: url('https://i.imgur.com/5XkJQqG.png')" onclick="applyEffect('bw')"></div>
<div class="effect-thumb bg-gray-400 rounded-md h-16 cursor-pointer hover:ring-2 hover:ring-indigo-500" style="background-image: url('https://i.imgur.com/8zJqWQk.png')" onclick="applyEffect('warm')"></div>
<div class="effect-thumb bg-gray-500 rounded-md h-16 cursor-pointer hover:ring-2 hover:ring-indigo-500" style="background-image: url('https://i.imgur.com/3mJQkqG.png')" onclick="applyEffect('cool')"></div>
</div>
</div>
</div>
<div id="video-tools" class="hidden">
<!-- Video tools content -->
<div class="text-center py-8 text-gray-500">
<i class="fas fa-video text-3xl mb-2"></i>
<p>Video editing coming soon!</p>
</div>
</div>
</div>
<!-- Editor Area -->
<div class="w-full lg:w-2/4 editor-container rounded-xl p-6">
<div class="preview-box bg-white rounded-lg overflow-hidden relative" style="height: 500px;">
<div class="absolute inset-0 flex items-center justify-center" id="placeholder">
<div class="text-center">
<i class="fas fa-image text-gray-300 text-5xl mb-3"></i>
<p class="text-gray-400">Upload an image to start editing</p>
</div>
</div>
<img id="preview-image" src="" alt="" class="hidden w-full h-full object-contain">
<video id="preview-video" controls class="hidden w-full h-full object-contain"></video>
<div id="processing-overlay" class="processing-overlay hidden">
<i class="fas fa-spinner fa-spin text-4xl mb-4"></i>
<p id="processing-text">Processing your image...</p>
<div class="w-full bg-gray-200 rounded-full h-2.5 mt-4 max-w-md">
<div id="progress-bar" class="bg-indigo-600 h-2.5 rounded-full" style="width: 0%"></div>
</div>
</div>
</div>
<div class="mt-6 flex justify-center space-x-4">
<button id="undo-btn" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300 disabled:opacity-50" disabled>
<i class="fas fa-undo mr-2"></i> Undo
</button>
<button id="redo-btn" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300 disabled:opacity-50" disabled>
<i class="fas fa-redo mr-2"></i> Redo
</button>
<button id="reset-btn" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300" onclick="resetEditor()">
<i class="fas fa-trash-alt mr-2"></i> Reset
</button>
<button id="process-btn" class="px-6 py-2 bg-purple-600 text-white rounded-md hover:bg-purple-700" onclick="processImage()">
<i class="fas fa-cogs mr-2"></i> Process
</button>
<button id="save-btn" class="px-6 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700" onclick="saveImage()">
<i class="fas fa-download mr-2"></i> Save
</button>
</div>
</div>
<!-- Layers Panel -->
<div class="w-full lg:w-1/4 bg-white rounded-xl shadow-md p-6 h-fit">
<div class="flex justify-between items-center mb-4">
<h3 class="font-medium text-gray-700">Reference Images</h3>
<button class="text-indigo-600 hover:text-indigo-800" onclick="document.getElementById('file-upload').click()">
<i class="fas fa-plus"></i>
</button>
</div>
<div id="reference-container" class="space-y-3 max-h-64 overflow-y-auto">
<!-- Reference images will appear here -->
</div>
<div class="mt-6">
<h3 class="font-medium text-gray-700 mb-3">Text Removal Settings</h3>
<div class="mb-3">
<label class="block text-sm text-gray-600 mb-1">Detection Sensitivity</label>
<input type="range" id="text-sensitivity" class="w-full slider-thumb" min="0" max="100" value="50">
</div>
<div class="mb-3">
<label class="block text-sm text-gray-600 mb-1">Blending Quality</label>
<input type="range" id="blend-quality" class="w-full slider-thumb" min="0" max="100" value="75">
</div>
<button onclick="applyTextRemoval()" class="w-full py-2 bg-indigo-600 text-white rounded-md text-sm hover:bg-indigo-700">
Remove Selected Text
</button>
</div>
<div class="mt-6">
<h3 class="font-medium text-gray-700 mb-3">Object Removal Settings</h3>
<div class="mb-3">
<label class="block text-sm text-gray-600 mb-1">Selection Precision</label>
<input type="range" id="object-precision" class="w-full slider-thumb" min="0" max="100" value="50">
</div>
<div class="mb-3">
<label class="block text-sm text-gray-600 mb-1">Content-Aware Fill</label>
<select id="fill-method" class="w-full p-2 border border-gray-300 rounded-md text-sm">
<option value="smart">Smart Fill</option>
<option value="blur">Background Blur</option>
<option value="extend">Edge Extension</option>
</select>
</div>
<button onclick="applyObjectRemoval()" class="w-full py-2 bg-indigo-600 text-white rounded-md text-sm hover:bg-indigo-700">
Remove Selected Object
</button>
</div>
<div class="mt-6">
<h3 class="font-medium text-gray-700 mb-3">Predictive AI</h3>
<div class="mb-3">
<label class="block text-sm text-gray-600 mb-1">AI Creativity</label>
<input type="range" id="ai-creativity" class="w-full slider-thumb" min="0" max="100" value="60">
</div>
<div class="mb-3">
<label class="block text-sm text-gray-600 mb-1">Style Match</label>
<input type="range" id="style-match" class="w-full slider-thumb" min="0" max="100" value="80">
</div>
<button onclick="applyPredictiveEdit()" class="w-full py-2 bg-purple-600 text-white rounded-md text-sm hover:bg-purple-700">
Apply Predictive Edit
</button>
</div>
<div class="mt-6">
<h3 class="font-medium text-gray-700 mb-3">Export Options</h3>
<div class="space-y-3">
<div class="flex items-center">
<input type="radio" id="format-jpg" name="export-format" class="mr-2" checked>
<label for="format-jpg" class="text-sm">JPG</label>
</div>
<div class="flex items-center">
<input type="radio" id="format-png" name="export-format" class="mr-2">
<label for="format-png" class="text-sm">PNG</label>
</div>
<div class="flex items-center">
<input type="radio" id="format-webp" name="export-format" class="mr-2">
<label for="format-webp" class="text-sm">WebP</label>
</div>
</div>
</div>
</div>
</div>
</main>
<!-- Face Swap Modal -->
<div id="face-swap-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50">
<div class="bg-white rounded-lg p-6 w-full max-w-2xl">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium">Face Swap</h3>
<button onclick="hideFaceSwapModal()" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="grid grid-cols-2 gap-4 mb-4">
<div>
<h4 class="text-sm font-medium mb-2">Source Face</h4>
<select id="modal-source-face" class="w-full p-2 border border-gray-300 rounded-md text-sm mb-2" onchange="updateFacePreview('source')">
<option value="">Select reference image</option>
</select>
<div class="border rounded-md h-40 bg-gray-100 flex items-center justify-center relative" id="source-face-preview">
<span class="text-gray-400">No image selected</span>
</div>
</div>
<div>
<h4 class="text-sm font-medium mb-2">Target Face</h4>
<select id="modal-target-face" class="w-full p-2 border border-gray-300 rounded-md text-sm mb-2" onchange="updateFacePreview('target')">
<option value="">Select reference image</option>
</select>
<div class="border rounded-md h-40 bg-gray-100 flex items-center justify-center relative" id="target-face-preview">
<span class="text-gray-400">No image selected</span>
</div>
</div>
</div>
<div class="mb-4">
<label class="block text-sm text-gray-600 mb-1">Face Alignment Strength</label>
<input type="range" id="face-align-strength" class="w-full slider-thumb" min="0" max="100" value="75">
</div>
<div class="flex justify-end space-x-3">
<button onclick="hideFaceSwapModal()" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300">
Cancel
</button>
<button onclick="applyModalFaceSwap()" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">
Apply Face Swap
</button>
</div>
</div>
</div>
<!-- Custom Pose Modal -->
<div id="custom-pose-modal" class="fixed inset-0 bg-black bg-opacity-50 flex items-center justify-center hidden z-50">
<div class="bg-white rounded-lg p-6 w-full max-w-2xl">
<div class="flex justify-between items-center mb-4">
<h3 class="text-lg font-medium">Create Custom Pose</h3>
<button onclick="hideCustomPoseModal()" class="text-gray-500 hover:text-gray-700">
<i class="fas fa-times"></i>
</button>
</div>
<div class="mb-4">
<label class="block text-sm text-gray-600 mb-1">Pose Description</label>
<textarea id="pose-description" class="w-full border border-gray-300 rounded-md p-2 text-sm" placeholder="Describe the pose you want (e.g. 'arms raised, left leg forward, slight torso twist')"></textarea>
</div>
<div class="mb-4">
<label class="block text-sm text-gray-600 mb-1">Pose Strength</label>
<input type="range" id="pose-strength" class="w-full slider-thumb" min="0" max="100" value="50">
</div>
<div class="flex justify-end space-x-3">
<button onclick="hideCustomPoseModal()" class="px-4 py-2 bg-gray-200 text-gray-700 rounded-md hover:bg-gray-300">
Cancel
</button>
<button onclick="applyCustomPose()" class="px-4 py-2 bg-indigo-600 text-white rounded-md hover:bg-indigo-700">
Create Pose
</button>
</div>
</div>
</div>
<!-- Footer -->
<footer class="bg-gray-800 text-white py-8 mt-12">
<div class="container mx-auto px-4">
<div class="grid grid-cols-1 md:grid-cols-4 gap-8">
<div>
<h3 class="text-lg font-medium mb-4">Advanced Image Editor</h3>
<p class="text-gray-400 text-sm">Professional photo editing tools with AI-powered enhancements.</p>
</div>
<div>
<h4 class="font-medium mb-4">Features</h4>
<ul class="space-y-2 text-sm text-gray-400">
<li><a href="#" class="hover:text-white">Face Swap</a></li>
<li><a href="#" class="hover:text-white">Body Reshaping</a></li>
<li><a href="#" class="hover:text-white">AI Enhancements</a></li>
<li><a href="#" class="hover:text-white">Pose Maker</a></li>
</ul>
</div>
<div>
<h4 class="font-medium mb-4">Company</h4>
<ul class="space-y-2 text-sm text-gray-400">
<li><a href="#" class="hover:text-white">About Us</a></li>
<li><a href="#" class="hover:text-white">Careers</a></li>
<li><a href="#" class="hover:text-white">Blog</a></li>
<li><a href="#" class="hover:text-white">Contact</a></li>
</ul>
</div>
<div>
<h4 class="font-medium mb-4">Legal</h4>
<ul class="space-y-2 text-sm text-gray-400">
<li><a href="#" class="hover:text-white">Terms of Service</a></li>
<li><a href="#" class="hover:text-white">Privacy Policy</a></li>
<li><a href="#" class="hover:text-white">Content Policy</a></li>
<li><a href="#" class="hover:text-white">Community Guidelines</a></li>
</ul>
</div>
</div>
<div class="border-t border-gray-700 mt-8 pt-6 text-sm text-gray-400">
<div class="flex flex-col md:flex-row justify-between items-center">
<p>© 2023 Advanced Image Editor. All rights reserved.</p>
<div class="flex space-x-6 mt-4 md:mt-0">
<a href="#" class="hover:text-white"><i class="fab fa-facebook-f"></i></a>
<a href="#" class="hover:text-white"><i class="fab fa-twitter"></i></a>
<a href="#" class="hover:text-white"><i class="fab fa-instagram"></i></a>
<a href="#" class="hover:text-white"><i class="fab fa-youtube"></i></a>
</div>
</div>
</div>
</div>
</footer>
<script>
// Global variables
let uploadedImages = [];
let currentImageIndex = 0;
let editHistory = [];
let currentHistoryIndex = -1;
let bodyParams = {
breast: 0,
hip: 0,
waist: 0,
leg: 0
};
// Selection variables
let isSelecting = false;
let selectionStartX = 0;
let selectionStartY = 0;
let selectionBox = null;
let currentSelectionType = null; // 'text' or 'object'
let predictiveOptionsVisible = false;
// Tab switching functionality
document.getElementById('image-tab').addEventListener('click', function() {
this.classList.add('tab-active');
document.getElementById('video-tab').classList.remove('tab-active');
document.getElementById('image-tools').classList.remove('hidden');
document.getElementById('video-tools').classList.add('hidden');
});
document.getElementById('video-tab').addEventListener('click', function() {
this.classList.add('tab-active');
document.getElementById('image-tab').classList.remove('tab-active');
document.getElementById('video-tools').classList.remove('hidden');
document.getElementById('image-tools').classList.add('hidden');
});
// File upload functionality
const fileUpload = document.getElementById('file-upload');
const uploadArea = document.getElementById('upload-area');
const thumbnailContainer = document.getElementById('thumbnail-container');
const referenceContainer = document.getElementById('reference-container');
const previewImage = document.getElementById('preview-image');
const previewVideo = document.getElementById('preview-video');
const placeholder = document.getElementById('placeholder');
// Handle drag and drop
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('bg-indigo-50', 'border-indigo-300');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('bg-indigo-50', 'border-indigo-300');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('bg-indigo-50', 'border-indigo-300');
if (e.dataTransfer.files.length > 0) {
fileUpload.files = e.dataTransfer.files;
handleFileUpload();
}
});
uploadArea.addEventListener('click', () => fileUpload.click());
fileUpload.addEventListener('change', handleFileUpload);
function handleFileUpload() {
const files = fileUpload.files;
if (!files || files.length === 0) return;
for (let i = 0; i < files.length; i++) {
const file = files[i];
if (!file.type.startsWith('image/')) continue;
const reader = new FileReader();
reader.onload = function(event) {
const imageData = {
id: Date.now() + i,
src: event.target.result,
name: file.name,
originalSrc: event.target.result
};
uploadedImages.push(imageData);
// Add to thumbnail container
const thumbnail = document.createElement('div');
thumbnail.className = `image-thumbnail relative ${uploadedImages.length === 1 ? 'active' : ''}`;
thumbnail.innerHTML = `
<img src="${imageData.src}" class="w-full h-full object-cover rounded">
<div class="absolute inset-0 flex items-center justify-center bg-black bg-opacity-0 hover:bg-opacity-30 transition-all duration-200">
<button onclick="setActiveImage(${imageData.id})" class="text-white opacity-0 hover:opacity-100">
<i class="fas fa-check-circle text-xl"></i>
</button>
</div>
`;
thumbnailContainer.appendChild(thumbnail);
// Add to reference container
const referenceItem = document.createElement('div');
referenceItem.className = 'flex items-center p-2 bg-gray-50 rounded-md';
referenceItem.innerHTML = `
<div class="w-10 h-10 bg-gray-200 rounded-md mr-3 overflow-hidden">
<img src="${imageData.src}" class="w-full h-full object-cover">
</div>
<div class="flex-1 truncate">
<p class="text-sm font-medium truncate">${file.name}</p>
<p class="text-xs text-gray-500">${(file.size / 1024).toFixed(1)} KB</p>
</div>
<button onclick="removeImage(${imageData.id})" class="text-gray-400 hover:text-gray-600 ml-2">
<i class="fas fa-times"></i>
</button>
`;
referenceContainer.appendChild(referenceItem);
// Update select options
updateSelectOptions();
// Set first image as active
if (uploadedImages.length === 1) {
setActiveImage(imageData.id);
}
};
reader.readAsDataURL(file);
}
}
function setActiveImage(id) {
const imageData = uploadedImages.find(img => img.id === id);
if (!imageData) return;
currentImageIndex = uploadedImages.findIndex(img => img.id === id);
previewImage.src = imageData.src;
previewImage.classList.remove('hidden');
previewVideo.classList.add('hidden');
placeholder.classList.add('hidden');
// Update active thumbnail
document.querySelectorAll('.image-thumbnail').forEach((thumb, index) => {
if (index === currentImageIndex) {
thumb.classList.add('active');
} else {
thumb.classList.remove('active');
}
});
// Save to history
saveToHistory();
}
function removeImage(id) {
const index = uploadedImages.findIndex(img => img.id === id);
if (index === -1) return;
uploadedImages.splice(index, 1);
// Remove from thumbnail container
thumbnailContainer.children[index].remove();
// Remove from reference container
referenceContainer.children[index].remove();
// Update select options
updateSelectOptions();
// If we removed the active image
if (currentImageIndex === index) {
if (uploadedImages.length > 0) {
// Set next available image as active
const newIndex = Math.min(index, uploadedImages.length - 1);
setActiveImage(uploadedImages[newIndex].id);
} else {
// No images left
previewImage.src = '';
previewImage.classList.add('hidden');
placeholder.classList.remove('hidden');
}
} else if (currentImageIndex > index) {
currentImageIndex--;
}
saveToHistory();
}
function updateSelectOptions() {
const modalSourceFace = document.getElementById('modal-source-face');
const modalTargetFace = document.getElementById('modal-target-face');
// Clear existing options except first
[modalSourceFace, modalTargetFace].forEach(select => {
while (select.options.length > 1) {
select.remove(1);
}
});
// Add new options
uploadedImages.forEach((img, index) => {
const option = document.createElement('option');
option.value = img.id;
option.textContent = `Image ${index + 1}`;
const option2 = option.cloneNode(true);
modalSourceFace.appendChild(option);
modalTargetFace.appendChild(option2);
});
}
// History management
function saveToHistory() {
if (uploadedImages.length === 0) return;
// Truncate history if we're not at the end
if (currentHistoryIndex < editHistory.length - 1) {
editHistory = editHistory.slice(0, currentHistoryIndex + 1);
}
// Save current state
const historyItem = {
imageSrc: uploadedImages[currentImageIndex].src,
bodyParams: {...bodyParams}
};
editHistory.push(historyItem);
currentHistoryIndex = editHistory.length - 1;
// Update undo/redo buttons
document.getElementById('undo-btn').disabled = currentHistoryIndex <= 0;
document.getElementById('redo-btn').disabled = currentHistoryIndex >= editHistory.length - 1;
}
function undo() {
if (currentHistoryIndex <= 0) return;
currentHistoryIndex--;
applyHistoryState();
}
function redo() {
if (currentHistoryIndex >= editHistory.length - 1) return;
currentHistoryIndex++;
applyHistoryState();
}
function applyHistoryState() {
const historyItem = editHistory[currentHistoryIndex];
// Update image
previewImage.src = historyItem.imageSrc;
uploadedImages[currentImageIndex].src = historyItem.imageSrc;
// Update body params
bodyParams = {...historyItem.bodyParams};
document.getElementById('breast-slider').value = bodyParams.breast;
document.getElementById('hip-slider').value = bodyParams.hip;
document.getElementById('waist-slider').value = bodyParams.waist;
document.getElementById('leg-slider').value = bodyParams.leg;
// Update buttons
document.getElementById('undo-btn').disabled = currentHistoryIndex <= 0;
document.getElementById('redo-btn').disabled = currentHistoryIndex >= editHistory.length - 1;
}
// Reset button functionality
function resetEditor() {
if (uploadedImages.length === 0) return;
previewImage.src = uploadedImages[currentImageIndex].originalSrc;
uploadedImages[currentImageIndex].src = uploadedImages[currentImageIndex].originalSrc;
// Reset sliders
document.getElementById('breast-slider').value = 0;
document.getElementById('hip-slider').value = 0;
document.getElementById('waist-slider').value = 0;
document.getElementById('leg-slider').value = 0;
bodyParams = { breast: 0, hip: 0, waist: 0, leg: 0 };
// Clear history
editHistory = [];
currentHistoryIndex = -1;
document.getElementById('undo-btn').disabled = true;
document.getElementById('redo-btn').disabled = true;
saveToHistory();
}
// Process button functionality
function processImage() {
if (!previewImage.src) {
alert('Please upload an image first');
return;
}
const promptBox = document.getElementById('ai-prompt');
const prompt = promptBox.value;
if (!prompt && Object.values(bodyParams).every(val => val === 0)) {
alert('Please enter a prompt or adjust body parameters');
return;
}
// Show processing overlay
const overlay = document.getElementById('processing-overlay');
const progressBar = document.getElementById('progress-bar');
const processingText = document.getElementById('processing-text');
overlay.classList.remove('hidden');
processingText.textContent = 'Processing your image...';
// Simulate processing with progress
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 10;
if (progress > 100) progress = 100;
progressBar.style.width = `${progress}%`;
if (progress === 100) {
clearInterval(interval);
processingText.textContent = 'Finalizing results...';
setTimeout(() => {
overlay.classList.add('hidden');
progressBar.style.width = '0%';
// In a real app, this would call your AI processing API
// For demo, we'll just modify the image slightly
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
// Apply simple filter to simulate processing
if (prompt.includes('breast') || bodyParams.breast !== 0) {
// Simulate breast enhancement
ctx.fillStyle = 'rgba(255, 200, 200, 0.1)';
ctx.beginPath();
ctx.ellipse(canvas.width * 0.4, canvas.height * 0.4,
canvas.width * 0.1 * (1 + bodyParams.breast/100),
canvas.height * 0.15 * (1 + bodyParams.breast/100),
0, 0, Math.PI * 2);
ctx.fill();
ctx.beginPath();
ctx.ellipse(canvas.width * 0.6, canvas.height * 0.4,
canvas.width * 0.1 * (1 + bodyParams.breast/100),
canvas.height * 0.15 * (1 + bodyParams.breast/100),
0, 0, Math.PI * 2);
ctx.fill();
}
if (prompt.includes('hip') || bodyParams.hip !== 0) {
// Simulate hip enhancement
ctx.fillStyle = 'rgba(200, 200, 255, 0.1)';
ctx.beginPath();
ctx.ellipse(canvas.width * 0.5, canvas.height * 0.6,
canvas.width * 0.15 * (1 + bodyParams.hip/100),
canvas.height * 0.1 * (1 + bodyParams.hip/100),
0, 0, Math.PI * 2);
ctx.fill();
}
if (prompt.includes('waist') || bodyParams.waist !== 0) {
// Simulate waist enhancement
ctx.fillStyle = 'rgba(255, 255, 200, 0.1)';
ctx.beginPath();
ctx.ellipse(canvas.width * 0.5, canvas.height * 0.5,
canvas.width * 0.1 * (1 - bodyParams.waist/200),
canvas.height * 0.15 * (1 - bodyParams.waist/200),
0, 0, Math.PI * 2);
ctx.fill();
}
// Apply effects based on prompt
if (prompt.includes('smooth') || prompt.includes('skin')) {
// Simulate skin smoothing
ctx.filter = 'blur(1px)';
ctx.drawImage(canvas, 0, 0);
ctx.filter = 'none';
}
// Update the image
const newSrc = canvas.toDataURL('image/jpeg');
previewImage.src = newSrc;
uploadedImages[currentImageIndex].src = newSrc;
saveToHistory();
};
img.src = previewImage.src;
alert('Processing complete!');
}, 500);
}
}, 100);
}
// Save button functionality
function saveImage() {
if (!previewImage.src && !previewVideo.src) {
alert('Please upload an image or video first');
return;
}
// Get selected format
let format = 'jpg';
if (document.getElementById('format-png').checked) format = 'png';
if (document.getElementById('format-webp').checked) format = 'webp';
// Simulate download
const link = document.createElement('a');
if (previewImage.src) {
link.href = previewImage.src;
link.download = `edited-image.${format}`;
} else {
link.href = previewVideo.src;
link.download = 'edited-video.mp4';
}
document.body.appendChild(link);
link.click();
document.body.removeChild(link);
}
// Body parameter updates
function updateBodyParam(param, value) {
bodyParams[param] = parseInt(value);
saveToHistory();
}
// AI Prompt functions
function applyPrompt(prompt) {
document.getElementById('ai-prompt').value = prompt;
processImage();
}
// Face Swap Functions
function showFaceSwapModal() {
if (uploadedImages.length < 2) {
alert('You need at least 2 images to perform face swap');
return;
}
document.getElementById('face-swap-modal').classList.remove('hidden');
}
function hideFaceSwapModal() {
document.getElementById('face-swap-modal').classList.add('hidden');
}
function updateFacePreview(type) {
const select = type === 'source' ?
document.getElementById('modal-source-face') :
document.getElementById('modal-target-face');
const preview = document.getElementById(`${type}-face-preview`);
const selectedId = select.value;
if (!selectedId) {
preview.innerHTML = '<span class="text-gray-400">No image selected</span>';
return;
}
const imageData = uploadedImages.find(img => img.id == selectedId);
if (!imageData) return;
// Clear previous preview
preview.innerHTML = '';
// Add image to preview
const img = document.createElement('img');
img.src = imageData.src;
img.className = 'w-full h-full object-contain';
preview.appendChild(img);
// Simulate face detection with dots
for (let i = 0; i < 5; i++) {
const dot = document.createElement('div');
dot.className = 'face-landmarks';
dot.style.left = `${50 + Math.random() * 20}%`;
dot.style.top = `${40 + Math.random() * 20}%`;
preview.appendChild(dot);
}
}
function applyModalFaceSwap() {
const sourceId = document.getElementById('modal-source-face').value;
const targetId = document.getElementById('modal-target-face').value;
if (!sourceId || !targetId) {
alert('Please select both source and target faces');
return;
}
if (sourceId === targetId) {
alert('Source and target faces must be different');
return;
}
const strength = document.getElementById('face-align-strength').value;
// Show processing
const overlay = document.getElementById('processing-overlay');
const progressBar = document.getElementById('progress-bar');
const processingText = document.getElementById('processing-text');
overlay.classList.remove('hidden');
processingText.textContent = 'Swapping faces...';
// Simulate face swap with progress
let progress = 0;
const interval = setInterval(() => {
progress += Math.random() * 15;
if (progress > 100) progress = 100;
progressBar.style.width = `${progress}%`;
if (progress === 100) {
clearInterval(interval);
// In a real app, this would call your face swap API
// For demo, we'll just blend the images slightly
const sourceImg = uploadedImages.find(img => img.id == sourceId);
const targetImg = uploadedImages.find(img => img.id == targetId);
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
// Draw source face over target
const sourceFace = new Image();
sourceFace.onload = function() {
// Simulate face swap by blending
ctx.globalAlpha = strength / 100;
ctx.drawImage(sourceFace,
canvas.width * 0.3, canvas.height * 0.2,
canvas.width * 0.4, canvas.height * 0.4);
ctx.globalAlpha = 1.0;
// Update the image
const newSrc = canvas.toDataURL('image/jpeg');
previewImage.src = newSrc;
uploadedImages[currentImageIndex].src = newSrc;
hideFaceSwapModal();
overlay.classList.add('hidden');
progressBar.style.width = '0%';
saveToHistory();
};
sourceFace.src = sourceImg.src;
};
img.src = targetImg.src;
}
}, 100);
}
// Pose functions
function applyPose(poseType) {
alert(`Applying ${poseType} pose to the image`);
// In a real app, this would call pose estimation/application API
processImage();
}
function showCustomPoseModal() {
document.getElementById('custom-pose-modal').classList.remove('hidden');
}
function hideCustomPoseModal() {
document.getElementById('custom-pose-modal').classList.add('hidden');
}
function applyCustomPose() {
const description = document.getElementById('pose-description').value;
const strength = document.getElementById('pose-strength').value;
if (!description) {
alert('Please describe the pose you want to create');
return;
}
alert(`Creating custom pose: ${description} with strength ${strength}`);
hideCustomPoseModal();
processImage();
}
// Text/Object Removal functions
function startTextRemoval() {
if (!previewImage.src) {
alert('Please upload an image first');
return;
}
currentSelectionType = 'text';
alert('Click and drag to select text to remove');
setupSelection();
}
function startObjectSelection() {
if (!previewImage.src) {
alert('Please upload an image first');
return;
}
currentSelectionType = 'object';
alert('Click and drag to select object to remove');
setupSelection();
}
function setupSelection() {
const previewBox = document.querySelector('.preview-box');
// Remove existing selection box if any
if (selectionBox) {
previewBox.removeChild(selectionBox);
}
// Create new selection box
selectionBox = document.createElement('div');
selectionBox.className = 'selection-box hidden';
previewBox.appendChild(selectionBox);
// Set up event listeners
previewBox.onmousedown = startSelection;
previewBox.onmousemove = updateSelection;
previewBox.onmouseup = endSelection;
}
function startSelection(e) {
if (!currentSelectionType) return;
isSelecting = true;
const rect = e.target.getBoundingClientRect();
selectionStartX = e.clientX - rect.left;
selectionStartY = e.clientY - rect.top;
selectionBox.style.left = `${selectionStartX}px`;
selectionBox.style.top = `${selectionStartY}px`;
selectionBox.style.width = '0px';
selectionBox.style.height = '0px';
selectionBox.classList.remove('hidden');
}
function updateSelection(e) {
if (!isSelecting) return;
const rect = e.target.getBoundingClientRect();
const currentX = e.clientX - rect.left;
const currentY = e.clientY - rect.top;
const width = currentX - selectionStartX;
const height = currentY - selectionStartY;
selectionBox.style.width = `${Math.abs(width)}px`;
selectionBox.style.height = `${Math.abs(height)}px`;
if (width < 0) {
selectionBox.style.left = `${currentX}px`;
}
if (height < 0) {
selectionBox.style.top = `${currentY}px`;
}
}
function endSelection() {
isSelecting = false;
// Remove event listeners
const previewBox = document.querySelector('.preview-box');
previewBox.onmousedown = null;
previewBox.onmousemove = null;
previewBox.onmouseup = null;
}
function applyTextRemoval() {
if (!selectionBox || selectionBox.classList.contains('hidden')) {
alert('Please select text to remove first');
return;
}
const sensitivity = document.getElementById('text-sensitivity').value;
const quality = document.getElementById('blend-quality').value;
alert(`Removing selected text with sensitivity ${sensitivity} and quality ${quality}`);
// Simulate text removal
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
// Get selection box coordinates
const rect = selectionBox.getBoundingClientRect();
const previewRect = document.querySelector('.preview-box').getBoundingClientRect();
const x = (rect.left - previewRect.left) / previewRect.width * canvas.width;
const y = (rect.top - previewRect.top) / previewRect.height * canvas.height;
const width = rect.width / previewRect.width * canvas.width;
const height = rect.height / previewRect.height * canvas.height;
// Remove text by filling with surrounding colors
ctx.fillStyle = ctx.getImageData(x, y, 1, 1).data;
ctx.fillRect(x, y, width, height);
// Update the image
const newSrc = canvas.toDataURL('image/jpeg');
previewImage.src = newSrc;
uploadedImages[currentImageIndex].src = newSrc;
// Hide selection box
selectionBox.classList.add('hidden');
currentSelectionType = null;
saveToHistory();
};
img.src = previewImage.src;
}
function applyObjectRemoval() {
if (!selectionBox || selectionBox.classList.contains('hidden')) {
alert('Please select object to remove first');
return;
}
const precision = document.getElementById('object-precision').value;
const method = document.getElementById('fill-method').value;
alert(`Removing selected object with precision ${precision} using ${method} method`);
// Simulate object removal
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
// Get selection box coordinates
const rect = selectionBox.getBoundingClientRect();
const previewRect = document.querySelector('.preview-box').getBoundingClientRect();
const x = (rect.left - previewRect.left) / previewRect.width * canvas.width;
const y = (rect.top - previewRect.top) / previewRect.height * canvas.height;
const width = rect.width / previewRect.width * canvas.width;
const height = rect.height / previewRect.height * canvas.height;
// Remove object based on selected method
if (method === 'blur') {
// Blur method
const imageData = ctx.getImageData(x, y, width, height);
for (let i = 0; i < imageData.data.length; i += 4) {
const avg = (imageData.data[i] + imageData.data[i+1] + imageData.data[i+2]) / 3;
imageData.data[i] = avg;
imageData.data[i+1] = avg;
imageData.data[i+2] = avg;
}
ctx.putImageData(imageData, x, y);
} else if (method === 'extend') {
// Edge extension method
const edgeColor = ctx.getImageData(x, y, 1, 1).data;
ctx.fillStyle = `rgb(${edgeColor[0]}, ${edgeColor[1]}, ${edgeColor[2]})`;
ctx.fillRect(x, y, width, height);
} else {
// Smart fill (default)
const surrounding = ctx.getImageData(x-1, y-1, width+2, height+2);
for (let i = 0; i < surrounding.data.length; i += 4) {
surrounding.data[i] = 255 - surrounding.data[i]; // Invert colors for demo
}
ctx.putImageData(surrounding, x-1, y-1);
}
// Update the image
const newSrc = canvas.toDataURL('image/jpeg');
previewImage.src = newSrc;
uploadedImages[currentImageIndex].src = newSrc;
// Hide selection box
selectionBox.classList.add('hidden');
currentSelectionType = null;
saveToHistory();
};
img.src = previewImage.src;
}
// Predictive Edit functions
function startPredictiveEdit() {
if (!previewImage.src) {
alert('Please upload an image first');
return;
}
currentSelectionType = 'predictive';
alert('Click and drag to select area for predictive edit');
setupSelection();
}
function applyPredictiveEdit() {
if (!selectionBox || selectionBox.classList.contains('hidden')) {
alert('Please select area for predictive edit first');
return;
}
const creativity = document.getElementById('ai-creativity').value;
const styleMatch = document.getElementById('style-match').value;
alert(`Applying predictive edit with creativity ${creativity} and style match ${styleMatch}`);
// Simulate predictive edit
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
// Get selection box coordinates
const rect = selectionBox.getBoundingClientRect();
const previewRect = document.querySelector('.preview-box').getBoundingClientRect();
const x = (rect.left - previewRect.left) / previewRect.width * canvas.width;
const y = (rect.top - previewRect.top) / previewRect.height * canvas.height;
const width = rect.width / previewRect.width * canvas.width;
const height = rect.height / previewRect.height * canvas.height;
// Apply "predictive" effect (just a color shift for demo)
const imageData = ctx.getImageData(x, y, width, height);
for (let i = 0; i < imageData.data.length; i += 4) {
// Shift colors based on creativity
imageData.data[i] = Math.min(255, imageData.data[i] * (1 + creativity/200)); // Red
imageData.data[i+1] = Math.min(255, imageData.data[i+1] * (1 + creativity/300)); // Green
imageData.data[i+2] = Math.min(255, imageData.data[i+2] * (1 + creativity/400)); // Blue
}
ctx.putImageData(imageData, x, y);
// Update the image
const newSrc = canvas.toDataURL('image/jpeg');
previewImage.src = newSrc;
uploadedImages[currentImageIndex].src = newSrc;
// Hide selection box
selectionBox.classList.add('hidden');
currentSelectionType = null;
saveToHistory();
};
img.src = previewImage.src;
}
// Effect functions
function applyEffect(effectType) {
if (!previewImage.src) {
alert('Please upload an image first');
return;
}
// Simulate applying effect
const canvas = document.createElement('canvas');
const ctx = canvas.getContext('2d');
const img = new Image();
img.onload = function() {
canvas.width = img.width;
canvas.height = img.height;
ctx.drawImage(img, 0, 0);
// Apply different effects based on type
const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
switch(effectType) {
case 'vintage':
// Sepia effect
for (let i = 0; i < imageData.data.length; i += 4) {
const r = imageData.data[i];
const g = imageData.data[i+1];
const b = imageData.data[i+2];
imageData.data[i] = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189));
imageData.data[i+1] = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168));
imageData.data[i+2] = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131));
}
break;
case 'bw':
// Black and white
for (let i = 0; i < imageData.data.length; i += 4) {
const avg = (imageData.data[i] + imageData.data[i+1] + imageData.data[i+2]) / 3;
imageData.data[i] = avg;
imageData.data[i+1] = avg;
imageData.data[i+2] = avg;
}
break;
case 'warm':
// Warm filter
for (let i = 0; i < imageData.data.length; i += 4) {
imageData.data[i] = Math.min(255, imageData.data[i] * 1.2); // Boost red
imageData.data[i+2] = imageData.data[i+2] * 0.8; // Reduce blue
}
break;
case 'cool':
// Cool filter
for (let i = 0; i < imageData.data.length; i += 4) {
imageData.data[i] = imageData.data[i] * 0.8; // Reduce red
imageData.data[i+2] = Math.min(255, imageData.data[i+2] * 1.2); // Boost blue
}
break;
}
ctx.putImageData(imageData, 0, 0);
// Update the image
const newSrc = canvas.toDataURL('image/jpeg');
previewImage.src = newSrc;
uploadedImages[currentImageIndex].src = newSrc;
saveToHistory();
};
img.src = previewImage.src;
}
// Initialize undo/redo buttons
document.getElementById('undo-btn').addEventListener('click', undo);
document.getElementById('redo-btn').addEventListener('click', redo);
</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=swapit/newspaces-are-good" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>