ocirema commited on
Commit
00a2e8f
·
verified ·
1 Parent(s): 5b79375

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +6 -4
  2. index.html +601 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Americo Foto
3
- emoji: 😻
4
  colorFrom: red
5
- colorTo: pink
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
1
  ---
2
+ title: americo-foto
3
+ emoji: 🐳
4
  colorFrom: red
5
+ colorTo: blue
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite
10
  ---
11
 
12
+ Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
index.html CHANGED
@@ -1,19 +1,601 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ <!DOCTYPE html>
2
+ <html lang="en">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>Image Editor Pro</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
9
+ <style>
10
+ .slider-container {
11
+ width: 100%;
12
+ margin: 10px 0;
13
+ }
14
+ .slider-container input[type="range"] {
15
+ width: 100%;
16
+ height: 8px;
17
+ -webkit-appearance: none;
18
+ background: #ddd;
19
+ border-radius: 5px;
20
+ outline: none;
21
+ }
22
+ .slider-container input[type="range"]::-webkit-slider-thumb {
23
+ -webkit-appearance: none;
24
+ width: 18px;
25
+ height: 18px;
26
+ background: #4f46e5;
27
+ border-radius: 50%;
28
+ cursor: pointer;
29
+ }
30
+ .filter-option {
31
+ transition: all 0.2s ease;
32
+ }
33
+ .filter-option:hover {
34
+ transform: scale(1.05);
35
+ box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
36
+ }
37
+ .filter-option.active {
38
+ border: 3px solid #4f46e5;
39
+ }
40
+ canvas {
41
+ max-width: 100%;
42
+ max-height: 70vh;
43
+ box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1), 0 4px 6px -2px rgba(0, 0, 0, 0.05);
44
+ }
45
+ .tool-btn {
46
+ transition: all 0.2s ease;
47
+ }
48
+ .tool-btn:hover {
49
+ background-color: #e0e7ff;
50
+ }
51
+ .tool-btn.active {
52
+ background-color: #4f46e5;
53
+ color: white;
54
+ }
55
+ </style>
56
+ </head>
57
+ <body class="bg-gray-100 font-sans">
58
+ <div class="container mx-auto px-4 py-8">
59
+ <header class="mb-8 text-center">
60
+ <h1 class="text-4xl font-bold text-indigo-700 mb-2">Image Editor Pro</h1>
61
+ <p class="text-gray-600">Edit your images with powerful tools right in your browser</p>
62
+ </header>
63
+
64
+ <div class="bg-white rounded-xl shadow-lg overflow-hidden">
65
+ <div class="flex flex-col lg:flex-row">
66
+ <!-- Sidebar with tools -->
67
+ <div class="w-full lg:w-64 bg-gray-50 p-4 border-r border-gray-200">
68
+ <div class="mb-6">
69
+ <h3 class="font-semibold text-gray-700 mb-3 flex items-center">
70
+ <i class="fas fa-upload mr-2"></i> Upload Image
71
+ </h3>
72
+ <input type="file" id="imageInput" accept="image/*" class="hidden">
73
+ <label for="imageInput" class="block w-full bg-indigo-600 hover:bg-indigo-700 text-white py-2 px-4 rounded-md text-center cursor-pointer transition">
74
+ Choose Image
75
+ </label>
76
+ </div>
77
+
78
+ <div class="mb-6">
79
+ <h3 class="font-semibold text-gray-700 mb-3 flex items-center">
80
+ <i class="fas fa-sliders-h mr-2"></i> Adjustments
81
+ </h3>
82
+ <div class="space-y-4">
83
+ <div class="slider-container">
84
+ <label class="block text-sm text-gray-600 mb-1">Brightness</label>
85
+ <input type="range" id="brightness" min="-100" max="100" value="0">
86
+ </div>
87
+ <div class="slider-container">
88
+ <label class="block text-sm text-gray-600 mb-1">Contrast</label>
89
+ <input type="range" id="contrast" min="-100" max="100" value="0">
90
+ </div>
91
+ <div class="slider-container">
92
+ <label class="block text-sm text-gray-600 mb-1">Saturation</label>
93
+ <input type="range" id="saturation" min="-100" max="100" value="0">
94
+ </div>
95
+ <div class="slider-container">
96
+ <label class="block text-sm text-gray-600 mb-1">Temperature</label>
97
+ <input type="range" id="temperature" min="-100" max="100" value="0">
98
+ </div>
99
+ </div>
100
+ </div>
101
+
102
+ <div class="mb-6">
103
+ <h3 class="font-semibold text-gray-700 mb-3 flex items-center">
104
+ <i class="fas fa-magic mr-2"></i> Filters
105
+ </h3>
106
+ <div class="grid grid-cols-3 gap-2">
107
+ <div class="filter-option p-2 rounded-md cursor-pointer" data-filter="none">
108
+ <div class="w-full h-12 bg-gray-200 rounded-md"></div>
109
+ <p class="text-xs text-center mt-1">Original</p>
110
+ </div>
111
+ <div class="filter-option p-2 rounded-md cursor-pointer" data-filter="grayscale">
112
+ <div class="w-full h-12 bg-gray-400 rounded-md"></div>
113
+ <p class="text-xs text-center mt-1">Grayscale</p>
114
+ </div>
115
+ <div class="filter-option p-2 rounded-md cursor-pointer" data-filter="sepia">
116
+ <div class="w-full h-12 bg-amber-200 rounded-md"></div>
117
+ <p class="text-xs text-center mt-1">Sepia</p>
118
+ </div>
119
+ <div class="filter-option p-2 rounded-md cursor-pointer" data-filter="invert">
120
+ <div class="w-full h-12 bg-gray-800 rounded-md"></div>
121
+ <p class="text-xs text-center mt-1">Invert</p>
122
+ </div>
123
+ <div class="filter-option p-2 rounded-md cursor-pointer" data-filter="vintage">
124
+ <div class="w-full h-12 bg-yellow-700 rounded-md"></div>
125
+ <p class="text-xs text-center mt-1">Vintage</p>
126
+ </div>
127
+ <div class="filter-option p-2 rounded-md cursor-pointer" data-filter="blur">
128
+ <div class="w-full h-12 bg-blue-100 rounded-md"></div>
129
+ <p class="text-xs text-center mt-1">Blur</p>
130
+ </div>
131
+ </div>
132
+ </div>
133
+ </div>
134
+
135
+ <!-- Main content area -->
136
+ <div class="flex-1 p-6">
137
+ <div class="flex justify-between items-center mb-6">
138
+ <h2 class="text-xl font-semibold text-gray-800">Editor</h2>
139
+ <div class="flex space-x-2">
140
+ <button id="resetBtn" class="bg-gray-200 hover:bg-gray-300 text-gray-800 py-2 px-4 rounded-md transition">
141
+ <i class="fas fa-undo mr-2"></i>Reset
142
+ </button>
143
+ <button id="downloadBtn" class="bg-indigo-600 hover:bg-indigo-700 text-white py-2 px-4 rounded-md transition">
144
+ <i class="fas fa-download mr-2"></i>Download
145
+ </button>
146
+ </div>
147
+ </div>
148
+
149
+ <div class="flex justify-center mb-6">
150
+ <div class="relative">
151
+ <canvas id="canvas" class="border border-gray-200 rounded-lg"></canvas>
152
+ <div id="cropOverlay" class="absolute inset-0 border-2 border-white border-dashed pointer-events-none hidden"></div>
153
+ </div>
154
+ </div>
155
+
156
+ <div class="bg-gray-50 p-4 rounded-lg">
157
+ <h3 class="font-semibold text-gray-700 mb-3 flex items-center">
158
+ <i class="fas fa-cut mr-2"></i> Crop & Rotate
159
+ </h3>
160
+ <div class="flex flex-wrap gap-2">
161
+ <button id="cropBtn" class="tool-btn bg-gray-200 hover:bg-gray-300 text-gray-800 py-2 px-4 rounded-md transition">
162
+ <i class="fas fa-crop mr-2"></i>Crop
163
+ </button>
164
+ <button id="rotateLeftBtn" class="tool-btn bg-gray-200 hover:bg-gray-300 text-gray-800 py-2 px-4 rounded-md transition">
165
+ <i class="fas fa-undo mr-2"></i>Rotate Left
166
+ </button>
167
+ <button id="rotateRightBtn" class="tool-btn bg-gray-200 hover:bg-gray-300 text-gray-800 py-2 px-4 rounded-md transition">
168
+ <i class="fas fa-redo mr-2"></i>Rotate Right
169
+ </button>
170
+ <button id="flipHorizontalBtn" class="tool-btn bg-gray-200 hover:bg-gray-300 text-gray-800 py-2 px-4 rounded-md transition">
171
+ <i class="fas fa-arrows-alt-h mr-2"></i>Flip H
172
+ </button>
173
+ <button id="flipVerticalBtn" class="tool-btn bg-gray-200 hover:bg-gray-300 text-gray-800 py-2 px-4 rounded-md transition">
174
+ <i class="fas fa-arrows-alt-v mr-2"></i>Flip V
175
+ </button>
176
+ </div>
177
+ </div>
178
+ </div>
179
+ </div>
180
+ </div>
181
+
182
+ <footer class="mt-12 text-center text-gray-500 text-sm">
183
+ <p>Image Editor Pro &copy; 2023 - All rights reserved</p>
184
+ </footer>
185
+ </div>
186
+
187
+ <script>
188
+ document.addEventListener('DOMContentLoaded', function() {
189
+ // Canvas setup
190
+ const canvas = document.getElementById('canvas');
191
+ const ctx = canvas.getContext('2d');
192
+ let image = null;
193
+ let originalImageData = null;
194
+ let isCropping = false;
195
+ let cropStartX, cropStartY, cropEndX, cropEndY;
196
+ let isDragging = false;
197
+
198
+ // UI elements
199
+ const imageInput = document.getElementById('imageInput');
200
+ const brightnessSlider = document.getElementById('brightness');
201
+ const contrastSlider = document.getElementById('contrast');
202
+ const saturationSlider = document.getElementById('saturation');
203
+ const temperatureSlider = document.getElementById('temperature');
204
+ const filterOptions = document.querySelectorAll('.filter-option');
205
+ const resetBtn = document.getElementById('resetBtn');
206
+ const downloadBtn = document.getElementById('downloadBtn');
207
+ const cropBtn = document.getElementById('cropBtn');
208
+ const rotateLeftBtn = document.getElementById('rotateLeftBtn');
209
+ const rotateRightBtn = document.getElementById('rotateRightBtn');
210
+ const flipHorizontalBtn = document.getElementById('flipHorizontalBtn');
211
+ const flipVerticalBtn = document.getElementById('flipVerticalBtn');
212
+ const cropOverlay = document.getElementById('cropOverlay');
213
+
214
+ // Current filter state
215
+ let currentFilter = 'none';
216
+ let adjustments = {
217
+ brightness: 0,
218
+ contrast: 0,
219
+ saturation: 0,
220
+ temperature: 0
221
+ };
222
+
223
+ // Load image
224
+ imageInput.addEventListener('change', function(e) {
225
+ if (e.target.files && e.target.files[0]) {
226
+ const reader = new FileReader();
227
+ reader.onload = function(event) {
228
+ image = new Image();
229
+ image.onload = function() {
230
+ // Set canvas dimensions to match image
231
+ canvas.width = image.width;
232
+ canvas.height = image.height;
233
+
234
+ // Draw the image
235
+ ctx.drawImage(image, 0, 0);
236
+
237
+ // Store original image data
238
+ originalImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
239
+
240
+ // Reset adjustments
241
+ resetAdjustments();
242
+ };
243
+ image.src = event.target.result;
244
+ };
245
+ reader.readAsDataURL(e.target.files[0]);
246
+ }
247
+ });
248
+
249
+ // Apply adjustments
250
+ function applyAdjustments() {
251
+ if (!image) return;
252
+
253
+ // Reset to original image
254
+ ctx.putImageData(originalImageData, 0, 0);
255
+
256
+ // Get current image data
257
+ const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
258
+ const data = imageData.data;
259
+
260
+ // Apply brightness
261
+ if (adjustments.brightness !== 0) {
262
+ const brightness = adjustments.brightness / 100;
263
+ for (let i = 0; i < data.length; i += 4) {
264
+ data[i] = data[i] + (255 * brightness);
265
+ data[i + 1] = data[i + 1] + (255 * brightness);
266
+ data[i + 2] = data[i + 2] + (255 * brightness);
267
+ }
268
+ }
269
+
270
+ // Apply contrast
271
+ if (adjustments.contrast !== 0) {
272
+ const contrast = (adjustments.contrast / 100) + 1;
273
+ const intercept = 128 * (1 - contrast);
274
+ for (let i = 0; i < data.length; i += 4) {
275
+ data[i] = data[i] * contrast + intercept;
276
+ data[i + 1] = data[i + 1] * contrast + intercept;
277
+ data[i + 2] = data[i + 2] * contrast + intercept;
278
+ }
279
+ }
280
+
281
+ // Apply saturation
282
+ if (adjustments.saturation !== 0) {
283
+ const saturation = adjustments.saturation / 100;
284
+ for (let i = 0; i < data.length; i += 4) {
285
+ const r = data[i];
286
+ const g = data[i + 1];
287
+ const b = data[i + 2];
288
+ const gray = 0.2989 * r + 0.5870 * g + 0.1140 * b;
289
+ data[i] = gray + (r - gray) * (1 + saturation);
290
+ data[i + 1] = gray + (g - gray) * (1 + saturation);
291
+ data[i + 2] = gray + (b - gray) * (1 + saturation);
292
+ }
293
+ }
294
+
295
+ // Apply temperature
296
+ if (adjustments.temperature !== 0) {
297
+ const temperature = adjustments.temperature / 100;
298
+ for (let i = 0; i < data.length; i += 4) {
299
+ data[i] = data[i] + (255 * temperature);
300
+ data[i + 2] = data[i + 2] - (255 * temperature);
301
+ }
302
+ }
303
+
304
+ // Apply filter
305
+ applyFilter(data);
306
+
307
+ // Put the modified data back
308
+ ctx.putImageData(imageData, 0, 0);
309
+ }
310
+
311
+ // Apply selected filter
312
+ function applyFilter(data) {
313
+ switch(currentFilter) {
314
+ case 'grayscale':
315
+ for (let i = 0; i < data.length; i += 4) {
316
+ const avg = (data[i] + data[i + 1] + data[i + 2]) / 3;
317
+ data[i] = avg;
318
+ data[i + 1] = avg;
319
+ data[i + 2] = avg;
320
+ }
321
+ break;
322
+ case 'sepia':
323
+ for (let i = 0; i < data.length; i += 4) {
324
+ const r = data[i];
325
+ const g = data[i + 1];
326
+ const b = data[i + 2];
327
+ data[i] = Math.min(255, (r * 0.393) + (g * 0.769) + (b * 0.189));
328
+ data[i + 1] = Math.min(255, (r * 0.349) + (g * 0.686) + (b * 0.168));
329
+ data[i + 2] = Math.min(255, (r * 0.272) + (g * 0.534) + (b * 0.131));
330
+ }
331
+ break;
332
+ case 'invert':
333
+ for (let i = 0; i < data.length; i += 4) {
334
+ data[i] = 255 - data[i];
335
+ data[i + 1] = 255 - data[i + 1];
336
+ data[i + 2] = 255 - data[i + 2];
337
+ }
338
+ break;
339
+ case 'vintage':
340
+ for (let i = 0; i < data.length; i += 4) {
341
+ const r = data[i];
342
+ const g = data[i + 1];
343
+ const b = data[i + 2];
344
+ data[i] = (r * 0.3) + (g * 0.59) + (b * 0.11);
345
+ data[i + 1] = (r * 0.3) + (g * 0.59) + (b * 0.11);
346
+ data[i + 2] = (r * 0.3) + (g * 0.59) + (b * 0.11);
347
+ data[i] += 40;
348
+ data[i + 1] += 20;
349
+ data[i + 2] -= 20;
350
+ }
351
+ break;
352
+ case 'blur':
353
+ // Simple box blur (for demo purposes)
354
+ const tempCanvas = document.createElement('canvas');
355
+ const tempCtx = tempCanvas.getContext('2d');
356
+ tempCanvas.width = canvas.width;
357
+ tempCanvas.height = canvas.height;
358
+ tempCtx.putImageData(new ImageData(data, canvas.width), 0, 0);
359
+
360
+ ctx.filter = 'blur(4px)';
361
+ ctx.drawImage(tempCanvas, 0, 0);
362
+ ctx.filter = 'none';
363
+ return;
364
+ }
365
+ }
366
+
367
+ // Reset all adjustments
368
+ function resetAdjustments() {
369
+ brightnessSlider.value = 0;
370
+ contrastSlider.value = 0;
371
+ saturationSlider.value = 0;
372
+ temperatureSlider.value = 0;
373
+
374
+ adjustments = {
375
+ brightness: 0,
376
+ contrast: 0,
377
+ saturation: 0,
378
+ temperature: 0
379
+ };
380
+
381
+ // Reset filter
382
+ currentFilter = 'none';
383
+ filterOptions.forEach(option => {
384
+ option.classList.remove('active');
385
+ if (option.dataset.filter === 'none') {
386
+ option.classList.add('active');
387
+ }
388
+ });
389
+
390
+ // Redraw original image
391
+ if (originalImageData) {
392
+ ctx.putImageData(originalImageData, 0, 0);
393
+ }
394
+ }
395
+
396
+ // Event listeners for adjustments
397
+ brightnessSlider.addEventListener('input', function() {
398
+ adjustments.brightness = parseInt(this.value);
399
+ applyAdjustments();
400
+ });
401
+
402
+ contrastSlider.addEventListener('input', function() {
403
+ adjustments.contrast = parseInt(this.value);
404
+ applyAdjustments();
405
+ });
406
+
407
+ saturationSlider.addEventListener('input', function() {
408
+ adjustments.saturation = parseInt(this.value);
409
+ applyAdjustments();
410
+ });
411
+
412
+ temperatureSlider.addEventListener('input', function() {
413
+ adjustments.temperature = parseInt(this.value);
414
+ applyAdjustments();
415
+ });
416
+
417
+ // Filter selection
418
+ filterOptions.forEach(option => {
419
+ option.addEventListener('click', function() {
420
+ filterOptions.forEach(opt => opt.classList.remove('active'));
421
+ this.classList.add('active');
422
+ currentFilter = this.dataset.filter;
423
+ applyAdjustments();
424
+ });
425
+ });
426
+
427
+ // Reset button
428
+ resetBtn.addEventListener('click', resetAdjustments);
429
+
430
+ // Download button
431
+ downloadBtn.addEventListener('click', function() {
432
+ if (!image) return;
433
+
434
+ const link = document.createElement('a');
435
+ link.download = 'edited-image.png';
436
+ link.href = canvas.toDataURL('image/png');
437
+ link.click();
438
+ });
439
+
440
+ // Crop functionality
441
+ cropBtn.addEventListener('click', function() {
442
+ isCropping = !isCropping;
443
+ cropBtn.classList.toggle('active', isCropping);
444
+ cropOverlay.classList.toggle('hidden', !isCropping);
445
+
446
+ if (!isCropping) {
447
+ // Perform the crop
448
+ if (cropStartX && cropStartY && cropEndX && cropEndY) {
449
+ const x = Math.min(cropStartX, cropEndX);
450
+ const y = Math.min(cropStartY, cropEndY);
451
+ const width = Math.abs(cropEndX - cropStartX);
452
+ const height = Math.abs(cropEndY - cropStartY);
453
+
454
+ if (width > 10 && height > 10) { // Minimum size
455
+ // Create a temporary canvas for the cropped image
456
+ const tempCanvas = document.createElement('canvas');
457
+ const tempCtx = tempCanvas.getContext('2d');
458
+ tempCanvas.width = width;
459
+ tempCanvas.height = height;
460
+
461
+ // Draw the cropped portion
462
+ tempCtx.drawImage(canvas, x, y, width, height, 0, 0, width, height);
463
+
464
+ // Resize the main canvas
465
+ canvas.width = width;
466
+ canvas.height = height;
467
+
468
+ // Draw the cropped image
469
+ ctx.drawImage(tempCanvas, 0, 0);
470
+
471
+ // Update the original image data
472
+ originalImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
473
+ }
474
+ }
475
+
476
+ // Reset crop coordinates
477
+ cropStartX = cropStartY = cropEndX = cropEndY = null;
478
+ }
479
+ });
480
+
481
+ // Canvas mouse events for cropping
482
+ canvas.addEventListener('mousedown', function(e) {
483
+ if (!isCropping) return;
484
+
485
+ const rect = canvas.getBoundingClientRect();
486
+ cropStartX = e.clientX - rect.left;
487
+ cropStartY = e.clientY - rect.top;
488
+ isDragging = true;
489
+ });
490
+
491
+ canvas.addEventListener('mousemove', function(e) {
492
+ if (!isCropping || !isDragging) return;
493
+
494
+ const rect = canvas.getBoundingClientRect();
495
+ cropEndX = e.clientX - rect.left;
496
+ cropEndY = e.clientY - rect.top;
497
+
498
+ // Update crop overlay
499
+ if (cropStartX && cropStartY) {
500
+ const left = Math.min(cropStartX, cropEndX);
501
+ const top = Math.min(cropStartY, cropEndY);
502
+ const width = Math.abs(cropEndX - cropStartX);
503
+ const height = Math.abs(cropEndY - cropStartY);
504
+
505
+ cropOverlay.style.left = `${left}px`;
506
+ cropOverlay.style.top = `${top}px`;
507
+ cropOverlay.style.width = `${width}px`;
508
+ cropOverlay.style.height = `${height}px`;
509
+ }
510
+ });
511
+
512
+ canvas.addEventListener('mouseup', function() {
513
+ isDragging = false;
514
+ });
515
+
516
+ canvas.addEventListener('mouseleave', function() {
517
+ isDragging = false;
518
+ });
519
+
520
+ // Rotate and flip buttons
521
+ rotateLeftBtn.addEventListener('click', function() {
522
+ if (!image) return;
523
+
524
+ // Create a temporary canvas
525
+ const tempCanvas = document.createElement('canvas');
526
+ const tempCtx = tempCanvas.getContext('2d');
527
+
528
+ // Swap width and height
529
+ tempCanvas.width = canvas.height;
530
+ tempCanvas.height = canvas.width;
531
+
532
+ // Rotate and draw
533
+ tempCtx.translate(tempCanvas.width / 2, tempCanvas.height / 2);
534
+ tempCtx.rotate(-Math.PI / 2);
535
+ tempCtx.drawImage(canvas, -canvas.width / 2, -canvas.height / 2);
536
+
537
+ // Update main canvas
538
+ canvas.width = tempCanvas.width;
539
+ canvas.height = tempCanvas.height;
540
+ ctx.drawImage(tempCanvas, 0, 0);
541
+
542
+ // Update original image data
543
+ originalImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
544
+ applyAdjustments();
545
+ });
546
+
547
+ rotateRightBtn.addEventListener('click', function() {
548
+ if (!image) return;
549
+
550
+ // Create a temporary canvas
551
+ const tempCanvas = document.createElement('canvas');
552
+ const tempCtx = tempCanvas.getContext('2d');
553
+
554
+ // Swap width and height
555
+ tempCanvas.width = canvas.height;
556
+ tempCanvas.height = canvas.width;
557
+
558
+ // Rotate and draw
559
+ tempCtx.translate(tempCanvas.width / 2, tempCanvas.height / 2);
560
+ tempCtx.rotate(Math.PI / 2);
561
+ tempCtx.drawImage(canvas, -canvas.width / 2, -canvas.height / 2);
562
+
563
+ // Update main canvas
564
+ canvas.width = tempCanvas.width;
565
+ canvas.height = tempCanvas.height;
566
+ ctx.drawImage(tempCanvas, 0, 0);
567
+
568
+ // Update original image data
569
+ originalImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
570
+ applyAdjustments();
571
+ });
572
+
573
+ flipHorizontalBtn.addEventListener('click', function() {
574
+ if (!image) return;
575
+
576
+ ctx.save();
577
+ ctx.scale(-1, 1);
578
+ ctx.drawImage(canvas, -canvas.width, 0);
579
+ ctx.restore();
580
+
581
+ // Update original image data
582
+ originalImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
583
+ applyAdjustments();
584
+ });
585
+
586
+ flipVerticalBtn.addEventListener('click', function() {
587
+ if (!image) return;
588
+
589
+ ctx.save();
590
+ ctx.scale(1, -1);
591
+ ctx.drawImage(canvas, 0, -canvas.height);
592
+ ctx.restore();
593
+
594
+ // Update original image data
595
+ originalImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
596
+ applyAdjustments();
597
+ });
598
+ });
599
+ </script>
600
+ <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=ocirema/americo-foto" style="color: #fff;text-decoration: underline;" target="_blank" >🧬 Remix</a></p></body>
601
+ </html>