thelip commited on
Commit
d516afb
·
verified ·
1 Parent(s): 79429ac

Add 2 files

Browse files
Files changed (2) hide show
  1. README.md +7 -5
  2. index.html +684 -19
README.md CHANGED
@@ -1,10 +1,12 @@
1
  ---
2
- title: Tosvg
3
- emoji: 🚀
4
- colorFrom: green
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: tosvg
3
+ emoji: 🐳
4
+ colorFrom: purple
5
+ colorTo: purple
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,684 @@
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>Advanced Photo to SVG Converter</title>
7
+ <script src="https://cdn.tailwindcss.com"></script>
8
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/potrace.min.js"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/quantize.min.js"></script>
10
+ <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css">
11
+ <style>
12
+ .dropzone {
13
+ border: 3px dashed rgba(59, 130, 246, 0.5);
14
+ transition: all 0.3s ease;
15
+ }
16
+ .dropzone.active {
17
+ border-color: rgba(59, 130, 246, 1);
18
+ background-color: rgba(59, 130, 246, 0.1);
19
+ }
20
+ .canvas-container {
21
+ position: relative;
22
+ overflow: hidden;
23
+ }
24
+ .comparison-slider {
25
+ position: absolute;
26
+ top: 0;
27
+ left: 0;
28
+ width: 50%;
29
+ height: 100%;
30
+ overflow: hidden;
31
+ resize: horizontal;
32
+ min-width: 10px;
33
+ max-width: calc(100% - 10px);
34
+ cursor: ew-resize;
35
+ }
36
+ .comparison-slider::-webkit-resizer {
37
+ display: none;
38
+ }
39
+ .slider-handle {
40
+ position: absolute;
41
+ right: -5px;
42
+ top: 50%;
43
+ transform: translateY(-50%);
44
+ width: 10px;
45
+ height: 40px;
46
+ background-color: rgba(255, 255, 255, 0.8);
47
+ border-radius: 5px;
48
+ cursor: ew-resize;
49
+ z-index: 10;
50
+ }
51
+ .progress-bar {
52
+ height: 5px;
53
+ transition: width 0.3s ease;
54
+ }
55
+ .svg-preview {
56
+ border: 1px solid #e5e7eb;
57
+ background-image: url('data:image/svg+xml;utf8,<svg width="20" height="20" xmlns="http://www.w3.org/2000/svg"><rect width="10" height="10" fill="%23f3f4f6"/><rect x="10" y="10" width="10" height="10" fill="%23f3f4f6"/></svg>');
58
+ background-size: 20px 20px;
59
+ }
60
+ .tooltip {
61
+ position: relative;
62
+ }
63
+ .tooltip-text {
64
+ visibility: hidden;
65
+ width: 200px;
66
+ background-color: #333;
67
+ color: #fff;
68
+ text-align: center;
69
+ border-radius: 6px;
70
+ padding: 5px;
71
+ position: absolute;
72
+ z-index: 1;
73
+ bottom: 125%;
74
+ left: 50%;
75
+ transform: translateX(-50%);
76
+ opacity: 0;
77
+ transition: opacity 0.3s;
78
+ }
79
+ .tooltip:hover .tooltip-text {
80
+ visibility: visible;
81
+ opacity: 1;
82
+ }
83
+ </style>
84
+ </head>
85
+ <body class="bg-gray-50 min-h-screen">
86
+ <div class="container mx-auto px-4 py-8">
87
+ <div class="text-center mb-8">
88
+ <h1 class="text-3xl font-bold text-gray-800 mb-2">Advanced Photo to SVG Converter</h1>
89
+ <p class="text-gray-600">Transform your photos into high-quality, realistic SVG vector graphics</p>
90
+ </div>
91
+
92
+ <div class="bg-white rounded-xl shadow-lg overflow-hidden mb-8">
93
+ <div class="p-6">
94
+ <div class="grid grid-cols-1 lg:grid-cols-2 gap-6">
95
+ <!-- Upload Section -->
96
+ <div class="space-y-4">
97
+ <div class="flex items-center justify-between">
98
+ <h2 class="text-xl font-semibold text-gray-800">Upload Image</h2>
99
+ <div class="flex space-x-2">
100
+ <button id="sample-btn" class="px-3 py-1 bg-blue-100 text-blue-600 rounded-md text-sm hover:bg-blue-200 transition">
101
+ <i class="fas fa-image mr-1"></i> Sample
102
+ </button>
103
+ <button id="reset-btn" class="px-3 py-1 bg-gray-100 text-gray-600 rounded-md text-sm hover:bg-gray-200 transition">
104
+ <i class="fas fa-redo mr-1"></i> Reset
105
+ </button>
106
+ </div>
107
+ </div>
108
+
109
+ <div id="dropzone" class="dropzone rounded-lg p-8 text-center cursor-pointer">
110
+ <div class="flex flex-col items-center justify-center space-y-3">
111
+ <i class="fas fa-cloud-upload-alt text-4xl text-blue-500"></i>
112
+ <h3 class="text-lg font-medium text-gray-700">Drag & drop your photo here</h3>
113
+ <p class="text-sm text-gray-500">or click to browse files</p>
114
+ <input type="file" id="file-input" class="hidden" accept="image/*">
115
+ <button id="browse-btn" class="mt-2 px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 transition">
116
+ Select Image
117
+ </button>
118
+ </div>
119
+ </div>
120
+
121
+ <div class="bg-gray-50 p-4 rounded-lg">
122
+ <h4 class="text-sm font-medium text-gray-700 mb-2">Conversion Settings</h4>
123
+ <div class="space-y-4">
124
+ <div>
125
+ <label class="block text-sm text-gray-600 mb-1">Detail Level</label>
126
+ <input id="detail-slider" type="range" min="1" max="10" value="5" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
127
+ <div class="flex justify-between text-xs text-gray-500 mt-1">
128
+ <span>Low</span>
129
+ <span>Medium</span>
130
+ <span>High</span>
131
+ </div>
132
+ </div>
133
+
134
+ <div>
135
+ <label class="block text-sm text-gray-600 mb-1">Color Palette</label>
136
+ <select id="color-select" class="w-full p-2 border border-gray-300 rounded-md text-sm">
137
+ <option value="auto">Auto-detect (recommended)</option>
138
+ <option value="2">2 colors</option>
139
+ <option value="4">4 colors</option>
140
+ <option value="8">8 colors</option>
141
+ <option value="16">16 colors</option>
142
+ <option value="32">32 colors</option>
143
+ <option value="64">64 colors</option>
144
+ <option value="128">128 colors</option>
145
+ <option value="256">256 colors</option>
146
+ </select>
147
+ </div>
148
+
149
+ <div class="flex items-center space-x-4">
150
+ <div class="flex-1">
151
+ <label class="block text-sm text-gray-600 mb-1">Edge Threshold</label>
152
+ <input id="edge-slider" type="range" min="1" max="100" value="50" class="w-full h-2 bg-gray-200 rounded-lg appearance-none cursor-pointer">
153
+ </div>
154
+ <div class="tooltip">
155
+ <i class="fas fa-info-circle text-gray-400"></i>
156
+ <span class="tooltip-text">Adjusts how sensitive the converter is to edges in the image</span>
157
+ </div>
158
+ </div>
159
+
160
+ <div class="flex items-center">
161
+ <input id="smooth-checkbox" type="checkbox" class="h-4 w-4 text-blue-600 focus:ring-blue-500 border-gray-300 rounded">
162
+ <label for="smooth-checkbox" class="ml-2 block text-sm text-gray-700">Smooth curves</label>
163
+ </div>
164
+ </div>
165
+ </div>
166
+
167
+ <button id="convert-btn" class="w-full py-3 bg-blue-600 text-white rounded-md font-medium hover:bg-blue-700 transition flex items-center justify-center space-x-2">
168
+ <i class="fas fa-magic"></i>
169
+ <span>Convert to SVG</span>
170
+ </button>
171
+ </div>
172
+
173
+ <!-- Preview Section -->
174
+ <div class="space-y-4">
175
+ <h2 class="text-xl font-semibold text-gray-800">Preview</h2>
176
+
177
+ <div id="preview-container" class="bg-gray-100 rounded-lg flex items-center justify-center" style="min-height: 300px;">
178
+ <p class="text-gray-500">Your SVG preview will appear here</p>
179
+ </div>
180
+
181
+ <div id="progress-container" class="hidden">
182
+ <div class="flex justify-between text-sm text-gray-600 mb-1">
183
+ <span>Processing image...</span>
184
+ <span id="progress-percent">0%</span>
185
+ </div>
186
+ <div class="w-full bg-gray-200 rounded-full h-2.5">
187
+ <div id="progress-bar" class="progress-bar bg-blue-600 h-2.5 rounded-full" style="width: 0%"></div>
188
+ </div>
189
+ </div>
190
+
191
+ <div id="comparison-container" class="canvas-container hidden" style="height: 300px;">
192
+ <img id="original-image" class="w-full h-full object-contain" src="" alt="Original">
193
+ <div class="comparison-slider">
194
+ <svg id="svg-output" class="w-full h-full" viewBox=""></svg>
195
+ <div class="slider-handle"></div>
196
+ </div>
197
+ </div>
198
+
199
+ <div id="download-section" class="hidden bg-blue-50 p-4 rounded-lg">
200
+ <h4 class="text-sm font-medium text-blue-800 mb-2">Conversion Complete!</h4>
201
+ <div class="flex flex-col sm:flex-row space-y-2 sm:space-y-0 sm:space-x-2">
202
+ <button id="download-svg" class="flex-1 py-2 bg-white border border-blue-500 text-blue-600 rounded-md hover:bg-blue-50 transition flex items-center justify-center space-x-2">
203
+ <i class="fas fa-download"></i>
204
+ <span>Download SVG</span>
205
+ </button>
206
+ <button id="copy-svg" class="flex-1 py-2 bg-white border border-blue-500 text-blue-600 rounded-md hover:bg-blue-50 transition flex items-center justify-center space-x-2">
207
+ <i class="far fa-copy"></i>
208
+ <span>Copy SVG Code</span>
209
+ </button>
210
+ </div>
211
+ </div>
212
+
213
+ <div id="stats-section" class="hidden bg-gray-50 p-4 rounded-lg">
214
+ <div class="grid grid-cols-3 gap-4 text-center">
215
+ <div>
216
+ <p class="text-xs text-gray-500">Original Size</p>
217
+ <p id="original-size" class="font-medium">-</p>
218
+ </div>
219
+ <div>
220
+ <p class="text-xs text-gray-500">SVG Size</p>
221
+ <p id="svg-size" class="font-medium">-</p>
222
+ </div>
223
+ <div>
224
+ <p class="text-xs text-gray-500">Reduction</p>
225
+ <p id="reduction" class="font-medium">-</p>
226
+ </div>
227
+ </div>
228
+ </div>
229
+ </div>
230
+ </div>
231
+ </div>
232
+ </div>
233
+
234
+ <div class="bg-white rounded-xl shadow-lg overflow-hidden mb-8 hidden" id="advanced-section">
235
+ <div class="p-6">
236
+ <h2 class="text-xl font-semibold text-gray-800 mb-4">Advanced SVG Options</h2>
237
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-3 gap-4">
238
+ <div>
239
+ <label class="block text-sm text-gray-600 mb-1">Optimization Level</label>
240
+ <select id="optimize-select" class="w-full p-2 border border-gray-300 rounded-md text-sm">
241
+ <option value="0">None</option>
242
+ <option value="1" selected>Basic</option>
243
+ <option value="2">Aggressive</option>
244
+ <option value="3">Maximum</option>
245
+ </select>
246
+ </div>
247
+ <div>
248
+ <label class="block text-sm text-gray-600 mb-1">Background</label>
249
+ <select id="bg-select" class="w-full p-2 border border-gray-300 rounded-md text-sm">
250
+ <option value="transparent">Transparent</option>
251
+ <option value="white">White</option>
252
+ <option value="black">Black</option>
253
+ <option value="custom">Custom Color</option>
254
+ </select>
255
+ <input id="bg-color" type="color" class="hidden mt-2 w-full h-8" value="#ffffff">
256
+ </div>
257
+ <div>
258
+ <label class="block text-sm text-gray-600 mb-1">Output Quality</label>
259
+ <select id="quality-select" class="w-full p-2 border border-gray-300 rounded-md text-sm">
260
+ <option value="low">Low (faster)</option>
261
+ <option value="medium" selected>Medium</option>
262
+ <option value="high">High</option>
263
+ <option value="ultra">Ultra (slowest)</option>
264
+ </select>
265
+ </div>
266
+ </div>
267
+ </div>
268
+ </div>
269
+
270
+ <div class="bg-white rounded-xl shadow-lg overflow-hidden hidden" id="svg-preview-section">
271
+ <div class="p-6">
272
+ <h2 class="text-xl font-semibold text-gray-800 mb-4">SVG Preview</h2>
273
+ <div class="svg-preview rounded-lg overflow-auto p-4" style="max-height: 500px;">
274
+ <pre id="svg-code" class="text-xs text-gray-800 bg-white p-2 overflow-auto"></pre>
275
+ </div>
276
+ </div>
277
+ </div>
278
+ </div>
279
+
280
+ <script>
281
+ document.addEventListener('DOMContentLoaded', function() {
282
+ // DOM elements
283
+ const fileInput = document.getElementById('file-input');
284
+ const dropzone = document.getElementById('dropzone');
285
+ const browseBtn = document.getElementById('browse-btn');
286
+ const convertBtn = document.getElementById('convert-btn');
287
+ const previewContainer = document.getElementById('preview-container');
288
+ const progressContainer = document.getElementById('progress-container');
289
+ const progressBar = document.getElementById('progress-bar');
290
+ const progressPercent = document.getElementById('progress-percent');
291
+ const comparisonContainer = document.getElementById('comparison-container');
292
+ const originalImage = document.getElementById('original-image');
293
+ const svgOutput = document.getElementById('svg-output');
294
+ const downloadSection = document.getElementById('download-section');
295
+ const downloadSvg = document.getElementById('download-svg');
296
+ const copySvg = document.getElementById('copy-svg');
297
+ const statsSection = document.getElementById('stats-section');
298
+ const originalSize = document.getElementById('original-size');
299
+ const svgSize = document.getElementById('svg-size');
300
+ const reduction = document.getElementById('reduction');
301
+ const sampleBtn = document.getElementById('sample-btn');
302
+ const resetBtn = document.getElementById('reset-btn');
303
+ const advancedSection = document.getElementById('advanced-section');
304
+ const svgPreviewSection = document.getElementById('svg-preview-section');
305
+ const svgCode = document.getElementById('svg-code');
306
+ const detailSlider = document.getElementById('detail-slider');
307
+ const colorSelect = document.getElementById('color-select');
308
+ const edgeSlider = document.getElementById('edge-slider');
309
+ const smoothCheckbox = document.getElementById('smooth-checkbox');
310
+ const optimizeSelect = document.getElementById('optimize-select');
311
+ const bgSelect = document.getElementById('bg-select');
312
+ const bgColor = document.getElementById('bg-color');
313
+ const qualitySelect = document.getElementById('quality-select');
314
+
315
+ // Variables
316
+ let originalFile = null;
317
+ let originalImageData = null;
318
+ let svgData = null;
319
+
320
+ // Event listeners
321
+ browseBtn.addEventListener('click', () => fileInput.click());
322
+ fileInput.addEventListener('change', handleFileSelect);
323
+ dropzone.addEventListener('dragover', handleDragOver);
324
+ dropzone.addEventListener('dragleave', handleDragLeave);
325
+ dropzone.addEventListener('drop', handleDrop);
326
+ convertBtn.addEventListener('click', convertToSvg);
327
+ downloadSvg.addEventListener('click', downloadSvgFile);
328
+ copySvg.addEventListener('click', copySvgToClipboard);
329
+ sampleBtn.addEventListener('click', loadSampleImage);
330
+ resetBtn.addEventListener('click', resetConverter);
331
+ bgSelect.addEventListener('change', toggleBgColorPicker);
332
+
333
+ // Functions
334
+ function handleFileSelect(e) {
335
+ const file = e.target.files[0] || (e.dataTransfer && e.dataTransfer.files[0]);
336
+ if (!file) return;
337
+
338
+ if (!file.type.match('image.*')) {
339
+ alert('Please select an image file');
340
+ return;
341
+ }
342
+
343
+ processImageFile(file);
344
+ }
345
+
346
+ function handleDragOver(e) {
347
+ e.preventDefault();
348
+ e.stopPropagation();
349
+ dropzone.classList.add('active');
350
+ }
351
+
352
+ function handleDragLeave(e) {
353
+ e.preventDefault();
354
+ e.stopPropagation();
355
+ dropzone.classList.remove('active');
356
+ }
357
+
358
+ function handleDrop(e) {
359
+ e.preventDefault();
360
+ e.stopPropagation();
361
+ dropzone.classList.remove('active');
362
+
363
+ const file = e.dataTransfer.files[0];
364
+ if (!file.type.match('image.*')) {
365
+ alert('Please drop an image file');
366
+ return;
367
+ }
368
+
369
+ processImageFile(file);
370
+ }
371
+
372
+ function processImageFile(file) {
373
+ originalFile = file;
374
+
375
+ // Display original image
376
+ const reader = new FileReader();
377
+ reader.onload = function(e) {
378
+ originalImage.src = e.target.result;
379
+
380
+ // Show file info
381
+ originalSize.textContent = formatFileSize(file.size);
382
+
383
+ // Create Image object to get dimensions
384
+ const img = new Image();
385
+ img.onload = function() {
386
+ previewContainer.innerHTML = `
387
+ <div class="text-center">
388
+ <img src="${e.target.result}" class="max-h-64 mx-auto" alt="Preview">
389
+ <p class="mt-2 text-sm text-gray-600">${img.width} × ${img.height} pixels</p>
390
+ </div>
391
+ `;
392
+
393
+ // Show advanced options
394
+ advancedSection.classList.remove('hidden');
395
+ };
396
+ img.src = e.target.result;
397
+ };
398
+ reader.readAsDataURL(file);
399
+ }
400
+
401
+ function convertToSvg() {
402
+ if (!originalFile) {
403
+ alert('Please select an image first');
404
+ return;
405
+ }
406
+
407
+ // Show progress
408
+ progressContainer.classList.remove('hidden');
409
+ previewContainer.classList.add('hidden');
410
+ comparisonContainer.classList.add('hidden');
411
+ downloadSection.classList.add('hidden');
412
+ statsSection.classList.add('hidden');
413
+ svgPreviewSection.classList.add('hidden');
414
+
415
+ // Update progress
416
+ updateProgress(10);
417
+
418
+ // Get settings
419
+ const detailLevel = parseInt(detailSlider.value);
420
+ const colorCount = colorSelect.value === 'auto' ? 'auto' : parseInt(colorSelect.value);
421
+ const edgeThreshold = parseInt(edgeSlider.value) / 100;
422
+ const smoothCurves = smoothCheckbox.checked;
423
+ const optimizeLevel = parseInt(optimizeSelect.value);
424
+ const quality = qualitySelect.value;
425
+
426
+ // Create canvas to process image
427
+ const img = new Image();
428
+ img.onload = function() {
429
+ updateProgress(30);
430
+
431
+ const canvas = document.createElement('canvas');
432
+ const ctx = canvas.getContext('2d');
433
+ canvas.width = img.width;
434
+ canvas.height = img.height;
435
+
436
+ // Draw image to canvas
437
+ ctx.drawImage(img, 0, 0);
438
+
439
+ // Get image data
440
+ originalImageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
441
+
442
+ updateProgress(50);
443
+
444
+ // Process image in chunks to avoid blocking UI
445
+ setTimeout(() => {
446
+ // Apply color quantization if needed
447
+ let quantizedData = originalImageData;
448
+ if (colorCount !== 'auto') {
449
+ quantizedData = quantizeImage(originalImageData, colorCount);
450
+ }
451
+
452
+ updateProgress(70);
453
+
454
+ // Convert to SVG using Potrace
455
+ const potrace = new Potrace();
456
+ potrace.setParameters({
457
+ threshold: edgeThreshold,
458
+ color: '#000000',
459
+ background: '#ffffff',
460
+ steps: detailLevel * 10,
461
+ turdSize: Math.max(1, 10 - detailLevel),
462
+ alphaMax: smoothCurves ? 1 : 0,
463
+ optCurve: smoothCurves,
464
+ optTolerance: smoothCurves ? 0.2 : 0
465
+ });
466
+
467
+ potrace.loadImageData(quantizedData);
468
+
469
+ updateProgress(90);
470
+
471
+ // Get SVG output
472
+ svgData = potrace.getSVG();
473
+
474
+ // Optimize SVG
475
+ svgData = optimizeSvg(svgData, optimizeLevel);
476
+
477
+ // Set viewBox
478
+ const viewBox = `0 0 ${canvas.width} ${canvas.height}`;
479
+ svgOutput.setAttribute('viewBox', viewBox);
480
+ svgOutput.innerHTML = svgData;
481
+
482
+ // Display results
483
+ displayResults(svgData, canvas.width, canvas.height);
484
+
485
+ updateProgress(100);
486
+
487
+ // Hide progress after a delay
488
+ setTimeout(() => {
489
+ progressContainer.classList.add('hidden');
490
+ }, 500);
491
+ }, 100);
492
+ };
493
+
494
+ img.src = URL.createObjectURL(originalFile);
495
+ }
496
+
497
+ function quantizeImage(imageData, colorCount) {
498
+ const pixels = imageData.data;
499
+ const pixelCount = imageData.width * imageData.height;
500
+ const pixelArray = [];
501
+
502
+ // Convert image data to array of pixels
503
+ for (let i = 0; i < pixelCount; i++) {
504
+ const offset = i * 4;
505
+ pixelArray.push([
506
+ pixels[offset],
507
+ pixels[offset + 1],
508
+ pixels[offset + 2],
509
+ pixels[offset + 3]
510
+ ]);
511
+ }
512
+
513
+ // Quantize colors
514
+ const cmap = MMCQ.quantize(pixelArray, colorCount);
515
+ const palette = cmap.palette();
516
+
517
+ // Create new image data with quantized colors
518
+ const newImageData = new ImageData(imageData.width, imageData.height);
519
+ const newPixels = newImageData.data;
520
+
521
+ for (let i = 0; i < pixelCount; i++) {
522
+ const offset = i * 4;
523
+ const pixel = pixelArray[i];
524
+ const color = cmap.map(pixel);
525
+
526
+ newPixels[offset] = color[0];
527
+ newPixels[offset + 1] = color[1];
528
+ newPixels[offset + 2] = color[2];
529
+ newPixels[offset + 3] = color[3] || 255;
530
+ }
531
+
532
+ return newImageData;
533
+ }
534
+
535
+ function optimizeSvg(svgData, level) {
536
+ // Basic optimizations (always applied)
537
+ svgData = svgData.replace(/\s+/g, ' '); // Remove extra whitespace
538
+ svgData = svgData.replace(/>\s+</g, '><'); // Remove spaces between tags
539
+
540
+ if (level >= 1) {
541
+ // Remove unnecessary attributes
542
+ svgData = svgData.replace(/xmlns:xlink="[^"]*"/g, '');
543
+ svgData = svgData.replace(/xlink:href="[^"]*"/g, '');
544
+ }
545
+
546
+ if (level >= 2) {
547
+ // Shorten path commands
548
+ svgData = svgData.replace(/ ([mlhvcsqtaz]) /g, '$1');
549
+ svgData = svgData.replace(/(\d)-(\d)/g, '$1 -$2'); // Fix negative numbers
550
+ }
551
+
552
+ if (level >= 3) {
553
+ // More aggressive optimizations
554
+ svgData = svgData.replace(/<g>\s*<\/g>/g, ''); // Remove empty groups
555
+ svgData = svgData.replace(/fill-rule="evenodd"/g, ''); // Remove default fill-rule
556
+ }
557
+
558
+ return svgData;
559
+ }
560
+
561
+ function displayResults(svgData, width, height) {
562
+ // Show comparison
563
+ comparisonContainer.classList.remove('hidden');
564
+
565
+ // Show download options
566
+ downloadSection.classList.remove('hidden');
567
+
568
+ // Show stats
569
+ const svgSizeBytes = new Blob([svgData]).size;
570
+ svgSize.textContent = formatFileSize(svgSizeBytes);
571
+
572
+ const reductionPercent = ((originalFile.size - svgSizeBytes) / originalFile.size * 100).toFixed(1);
573
+ reduction.textContent = `${reductionPercent}%`;
574
+
575
+ statsSection.classList.remove('hidden');
576
+
577
+ // Show SVG code preview
578
+ svgCode.textContent = svgData;
579
+ svgPreviewSection.classList.remove('hidden');
580
+ }
581
+
582
+ function updateProgress(percent) {
583
+ progressBar.style.width = `${percent}%`;
584
+ progressPercent.textContent = `${percent}%`;
585
+ }
586
+
587
+ function downloadSvgFile() {
588
+ if (!svgData) return;
589
+
590
+ const blob = new Blob([svgData], { type: 'image/svg+xml' });
591
+ const url = URL.createObjectURL(blob);
592
+
593
+ const a = document.createElement('a');
594
+ a.href = url;
595
+ a.download = originalFile.name.replace(/\.[^/.]+$/, '') + '.svg';
596
+ document.body.appendChild(a);
597
+ a.click();
598
+ document.body.removeChild(a);
599
+ URL.revokeObjectURL(url);
600
+ }
601
+
602
+ function copySvgToClipboard() {
603
+ if (!svgData) return;
604
+
605
+ navigator.clipboard.writeText(svgData)
606
+ .then(() => {
607
+ const originalText = copySvg.querySelector('span').textContent;
608
+ copySvg.querySelector('span').textContent = 'Copied!';
609
+
610
+ setTimeout(() => {
611
+ copySvg.querySelector('span').textContent = originalText;
612
+ }, 2000);
613
+ })
614
+ .catch(err => {
615
+ console.error('Failed to copy SVG: ', err);
616
+ alert('Failed to copy SVG to clipboard');
617
+ });
618
+ }
619
+
620
+ function loadSampleImage() {
621
+ // Sample image URL (replace with your own)
622
+ const sampleImageUrl = 'https://images.unsplash.com/photo-1506748686214-e9df14d4d9d0?ixlib=rb-1.2.1&auto=format&fit=crop&w=800&h=600&q=80';
623
+
624
+ // Fetch the sample image
625
+ fetch(sampleImageUrl)
626
+ .then(response => response.blob())
627
+ .then(blob => {
628
+ const file = new File([blob], 'sample-image.jpg', { type: 'image/jpeg' });
629
+ processImageFile(file);
630
+ })
631
+ .catch(error => {
632
+ console.error('Error loading sample image:', error);
633
+ alert('Failed to load sample image');
634
+ });
635
+ }
636
+
637
+ function resetConverter() {
638
+ // Reset UI
639
+ fileInput.value = '';
640
+ previewContainer.innerHTML = '<p class="text-gray-500">Your SVG preview will appear here</p>';
641
+ progressContainer.classList.add('hidden');
642
+ comparisonContainer.classList.add('hidden');
643
+ downloadSection.classList.add('hidden');
644
+ statsSection.classList.add('hidden');
645
+ svgPreviewSection.classList.add('hidden');
646
+ advancedSection.classList.add('hidden');
647
+
648
+ // Reset variables
649
+ originalFile = null;
650
+ originalImageData = null;
651
+ svgData = null;
652
+
653
+ // Reset form values
654
+ detailSlider.value = 5;
655
+ colorSelect.value = 'auto';
656
+ edgeSlider.value = 50;
657
+ smoothCheckbox.checked = true;
658
+ optimizeSelect.value = '1';
659
+ bgSelect.value = 'transparent';
660
+ qualitySelect.value = 'medium';
661
+ bgColor.classList.add('hidden');
662
+ }
663
+
664
+ function toggleBgColorPicker() {
665
+ if (bgSelect.value === 'custom') {
666
+ bgColor.classList.remove('hidden');
667
+ } else {
668
+ bgColor.classList.add('hidden');
669
+ }
670
+ }
671
+
672
+ function formatFileSize(bytes) {
673
+ if (bytes === 0) return '0 Bytes';
674
+
675
+ const k = 1024;
676
+ const sizes = ['Bytes', 'KB', 'MB', 'GB'];
677
+ const i = Math.floor(Math.log(bytes) / Math.log(k));
678
+
679
+ return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
680
+ }
681
+ });
682
+ </script>
683
+ <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=thelip/tosvg" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
684
+ </html>