deep-fried-meme / index.html
Jaspior's picture
Add 3 files
496276f verified
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Deep Fryer 2025 🌶️🔥</title>
<script src="https://cdn.tailwindcss.com"></script>
<link href="https://fonts.googleapis.com/css2?family=Impact&family=Inter:wght@400;700&family=Comic+Neue:ital,wght@0,400;0,700;1,400;1,700&display=swap" rel="stylesheet">
<style>
body {
font-family: 'Inter', sans-serif;
background-color: #1a1a1a; /* Fundo mais escuro */
}
.impact-font {
font-family: 'Impact', sans-serif;
}
.comic-neue-font {
font-family: 'Comic Neue', cursive;
}
.container-glow {
border: 3px solid #7e22ce; /* Roxo mais suave */
box-shadow: 0 0 15px #a855f7, 0 0 30px #7e22ce inset;
}
.h1-glow {
color: #facc15; /* Amarelo */
text-shadow: 2px 2px 0 #c026d3, -2px -2px 0 #14b8a6; /* Magenta e Teal */
}
.subtitle-color {
color: #14b8a6; /* Teal */
}
.upload-area-style {
border: 3px dashed #c026d3; /* Magenta */
transition: all 0.3s;
}
.upload-area-style:hover {
background-color: rgba(192, 38, 211, 0.1);
border-color: #facc15; /* Amarelo */
}
.upload-area-style.dragover {
background-color: rgba(20, 184, 166, 0.1); /* Teal */
border-color: #14b8a6;
}
input[type="range"] {
-webkit-appearance: none;
appearance: none;
width: 100%;
height: 12px;
background: #4a044e; /* Roxo escuro */
outline: none;
border-radius: 6px;
transition: background 0.3s;
}
input[type="range"]::-webkit-slider-thumb {
-webkit-appearance: none;
appearance: none;
width: 24px;
height: 24px;
background: #14b8a6; /* Teal */
cursor: pointer;
border-radius: 50%;
border: 2px solid #facc15; /* Amarelo */
}
input[type="range"]::-moz-range-thumb {
width: 24px;
height: 24px;
background: #14b8a6; /* Teal */
cursor: pointer;
border-radius: 50%;
border: 2px solid #facc15; /* Amarelo */
}
.custom-btn {
background-color: #c026d3; /* Magenta */
color: white;
transition: all 0.3s;
}
.custom-btn:hover {
background-color: #a855f7; /* Roxo mais claro */
transform: scale(1.05);
}
.rage-btn-style {
background: linear-gradient(45deg, #c026d3, #facc15, #14b8a6);
background-size: 200% 200%;
animation: rage_anim 1s infinite;
}
@keyframes rage_anim {
0% { background-position: 0% 50%; }
50% { background-position: 100% 50%; }
100% { background-position: 0% 50%; }
}
/* Custom Modal Styles */
.modal {
display: none; /* Hidden by default */
position: fixed; /* Stay in place */
z-index: 100; /* Sit on top */
left: 0;
top: 0;
width: 100%; /* Full width */
height: 100%; /* Full height */
overflow: auto; /* Enable scroll if needed */
background-color: rgba(0,0,0,0.6); /* Black w/ opacity */
}
.modal-content {
background-color: #2d2d2d;
margin: 15% auto; /* 15% from the top and centered */
padding: 20px;
border: 1px solid #888;
width: 80%; /* Could be more or less, depending on screen size */
max-width: 500px;
border-radius: 8px;
text-align: center;
color: #f0f0f0;
}
.modal-close-btn {
background-color: #c026d3;
color: white;
padding: 8px 16px;
border-radius: 5px;
cursor: pointer;
margin-top: 15px;
}
</style>
</head>
<body class="bg-gray-900 text-purple-400 p-4 md:p-8">
<div id="customModal" class="modal">
<div class="modal-content">
<p id="modalMessage">Modal Message Here</p>
<button id="modalCloseBtn" class="modal-close-btn">OK</button>
</div>
</div>
<div class="container mx-auto max-w-3xl p-6 bg-gray-800 rounded-lg container-glow">
<h1 class="text-5xl md:text-6xl font-bold text-center mb-2 impact-font h1-glow">DEEP FRYER 2025</h1>
<p class="text-lg text-center mb-8 subtitle-color">MAKE YOUR MEMES EXTRA SPICY 🌶️🔥</p>
<div class="grid grid-cols-1 sm:grid-cols-3 gap-4 mb-6 items-center">
<label class="flex items-center space-x-2 text-yellow-400">
<span>Dumb Mode:</span>
<input type="checkbox" id="dumbModeToggle" class="form-checkbox h-5 w-5 text-purple-500 rounded">
</label>
<label class="flex items-center space-x-2 text-yellow-400">
<span>VHS Effect:</span>
<input type="checkbox" id="vhsToggle" class="form-checkbox h-5 w-5 text-purple-500 rounded">
</label>
<label class="flex items-center space-x-2 text-yellow-400">
<span>Broken Screen:</span>
<input type="checkbox" id="brokenToggle" class="form-checkbox h-5 w-5 text-purple-500 rounded">
</label>
</div>
<div id="uploadArea" class="upload-area-style p-8 text-center mb-6 rounded-md cursor-pointer bg-gray-700">
<p class="text-lg">DRAG & DROP YOUR IMAGE HERE</p>
<p class="text-sm text-gray-400">OR CLICK TO SELECT (MAX 5MB)</p>
<input type="file" id="fileInput" accept="image/*" class="hidden">
</div>
<div class="preview-container relative mb-6 border-3 border-yellow-400 min-h-[300px] bg-black rounded-md overflow-hidden">
<div id="placeholder" class="placeholder-text absolute inset-0 flex items-center justify-center text-yellow-400 text-xl">YOUR FRIED MEME WILL APPEAR HERE</div>
<canvas id="previewCanvas" class="hidden max-w-full mx-auto block cursor-pointer"></canvas>
<div id="scanlines" class="absolute top-0 left-0 w-full h-full pointer-events-none opacity-0" style="background: linear-gradient(rgba(18, 16, 16, 0) 50%, rgba(0, 0, 0, 0.25) 50%); background-size: 100% 4px;"></div>
<div id="brokenScreen" class="absolute top-0 left-0 w-full h-full pointer-events-none opacity-0 mix-blend-overlay" style="background-image: url('data:image/svg+xml;utf8,<svg xmlns=\"http://www.w3.org/2000/svg\" width=\"100\" height=\"100\" viewBox=\"0 0 100 100\"><path d=\"M0,0 L100,0 L100,100 L0,100 Z\" fill=\"none\" stroke=\"rgba(0,0,0,0.5)\" stroke-width=\"2\"/><path d=\"M20,20 L80,20 L80,80 L20,80 Z\" fill=\"none\" stroke=\"rgba(0,0,0,0.3)\" stroke-width=\"1\"/><path d=\"M30,30 L70,30 L70,70 L30,70 Z\" fill=\"none\" stroke=\"rgba(0,0,0,0.2)\" stroke-width=\"0.5\"/></svg>'); background-size: 20px 20px;"></div>
<img id="memeOverlay" class="absolute pointer-events-none z-10 hidden">
<img id="msPaintDoodle" class="absolute pointer-events-none z-20 opacity-0">
</div>
<div class="controls grid grid-cols-1 md:grid-cols-2 gap-x-6 gap-y-4 mb-6">
<div class="control-group">
<label for="compression" class="block mb-1 text-yellow-400">JPEG Compression: <span id="compressionValue" class="text-purple-300">0</span>%</label>
<input type="range" id="compression" min="0" max="100" value="0">
</div>
<div class="control-group">
<label for="saturation" class="block mb-1 text-yellow-400">Saturation: <span id="saturationValue" class="text-purple-300">100</span>%</label>
<input type="range" id="saturation" min="0" max="200" value="100">
</div>
<div class="control-group">
<label for="contrast" class="block mb-1 text-yellow-400">Contrast: <span id="contrastValue" class="text-purple-300">100</span>%</label>
<input type="range" id="contrast" min="0" max="200" value="100">
</div>
<div class="control-group">
<label for="sharpness" class="block mb-1 text-yellow-400">Sharpness: <span id="sharpnessValue" class="text-purple-300">0</span>%</label>
<input type="range" id="sharpness" min="0" max="100" value="0">
</div>
<div class="control-group">
<label for="noise" class="block mb-1 text-yellow-400">Noise/Grain: <span id="noiseValue" class="text-purple-300">0</span>%</label>
<input type="range" id="noise" min="0" max="100" value="0">
</div>
<div class="control-group">
<label for="hue" class="block mb-1 text-yellow-400">Hue Shift: <span id="hueValue" class="text-purple-300">0</span>°</label>
<input type="range" id="hue" min="0" max="360" value="0">
</div>
</div>
<div class="flex flex-col sm:flex-row items-center gap-4 mb-6">
<label for="reapplyCount" class="text-yellow-400">Reapply Filters <input type="number" id="reapplyCount" value="1" min="1" max="10" class="bg-gray-700 border border-purple-500 text-white rounded px-2 py-1 w-16 text-center"> times</label>
<button id="reapplyBtn" class="custom-btn font-semibold py-2 px-4 rounded-md w-full sm:w-auto">Apply N Times</button>
</div>
<div class="buttons grid grid-cols-1 sm:grid-cols-2 gap-4 mb-8">
<button id="rageMode" class="custom-btn rage-btn-style font-semibold py-3 px-6 rounded-md impact-font text-xl">RAGE MODE</button>
<button id="downloadBtn" class="custom-btn font-semibold py-3 px-6 rounded-md">DOWNLOAD FRIED MEME</button>
</div>
<div class="social-buttons flex gap-4 justify-center">
<a href="#" class="social-btn w-12 h-12 rounded-full flex items-center justify-center text-white text-2xl transition-transform hover:scale-110" style="background-color: #1DA1F2;">𝕏</a>
<a href="#" class="social-btn w-12 h-12 rounded-full flex items-center justify-center text-white text-2xl transition-transform hover:scale-110" style="background-color: #001935;">T</a>
<a href="#" class="social-btn w-12 h-12 rounded-full flex items-center justify-center text-white text-2xl transition-transform hover:scale-110" style="background-color: #00B488;">V</a>
</div>
</div>
<script>
document.addEventListener('DOMContentLoaded', function() {
// Elements
const uploadArea = document.getElementById('uploadArea');
const fileInput = document.getElementById('fileInput');
const previewCanvas = document.getElementById('previewCanvas');
const ctx = previewCanvas.getContext('2d', { willReadFrequently: true });
const compressionSlider = document.getElementById('compression');
const saturationSlider = document.getElementById('saturation');
const contrastSlider = document.getElementById('contrast');
const sharpnessSlider = document.getElementById('sharpness');
const noiseSlider = document.getElementById('noise');
const hueSlider = document.getElementById('hue');
const compressionValueEl = document.getElementById('compressionValue');
const saturationValueEl = document.getElementById('saturationValue');
const contrastValueEl = document.getElementById('contrastValue');
const sharpnessValueEl = document.getElementById('sharpnessValue');
const noiseValueEl = document.getElementById('noiseValue');
const hueValueEl = document.getElementById('hueValue');
const downloadBtn = document.getElementById('downloadBtn');
const rageModeBtn = document.getElementById('rageMode');
const dumbModeToggle = document.getElementById('dumbModeToggle');
const vhsToggle = document.getElementById('vhsToggle');
const brokenToggle = document.getElementById('brokenToggle');
const scanlines = document.getElementById('scanlines');
const brokenScreen = document.getElementById('brokenScreen');
const memeOverlay = document.getElementById('memeOverlay');
const msPaintDoodle = document.getElementById('msPaintDoodle');
// Meme templates elements removed
const placeholder = document.getElementById('placeholder');
const body = document.body;
const reapplyCountInput = document.getElementById('reapplyCount');
const reapplyBtn = document.getElementById('reapplyBtn');
const customModal = document.getElementById('customModal');
const modalMessage = document.getElementById('modalMessage');
const modalCloseBtn = document.getElementById('modalCloseBtn');
// State
let originalImage = null;
let currentImageForReapply = null;
let isProcessing = false;
// activeDeformation removed as it was not fully used
const sliders = [
{ el: compressionSlider, neutral: 0, valEl: compressionValueEl, unit: '%' },
{ el: saturationSlider, neutral: 100, valEl: saturationValueEl, unit: '%' },
{ el: contrastSlider, neutral: 100, valEl: contrastValueEl, unit: '%' },
{ el: sharpnessSlider, neutral: 0, valEl: sharpnessValueEl, unit: '%' },
{ el: noiseSlider, neutral: 0, valEl: noiseValueEl, unit: '%' },
{ el: hueSlider, neutral: 0, valEl: hueValueEl, unit: '°' }
];
// --- Custom Modal Functions ---
function showModal(message) {
modalMessage.textContent = message;
customModal.style.display = 'block';
}
modalCloseBtn.onclick = function() {
customModal.style.display = 'none';
}
window.onclick = function(event) {
if (event.target == customModal) {
customModal.style.display = 'none';
}
}
function customAlert(message) {
showModal(message);
}
// --- Initialize ---
function resetAllSliders() {
sliders.forEach(s => {
s.el.value = s.neutral;
updateSliderValueDisplay(s.el, s.valEl, s.unit);
});
if (originalImage) processImage(true, 1); // Redraw original if image exists, 1 iteration
}
function updateSliderValueDisplay(slider, valueEl, unit) {
if (valueEl) valueEl.textContent = slider.value;
if (unit && valueEl) valueEl.textContent += unit;
}
sliders.forEach(s => {
s.el.addEventListener('input', () => {
updateSliderValueDisplay(s.el, s.valEl, s.unit);
if (originalImage) processImage(false, 1); // Process on slider change
});
updateSliderValueDisplay(s.el, s.valEl, s.unit);
});
resetAllSliders();
// Event Listeners
uploadArea.addEventListener('click', () => fileInput.click());
uploadArea.addEventListener('dragover', (e) => {
e.preventDefault();
uploadArea.classList.add('dragover');
});
uploadArea.addEventListener('dragleave', () => {
uploadArea.classList.remove('dragover');
});
uploadArea.addEventListener('drop', (e) => {
e.preventDefault();
uploadArea.classList.remove('dragover');
if (e.dataTransfer.files.length) {
handleFile(e.dataTransfer.files[0]);
}
});
fileInput.addEventListener('change', (e) => {
if (e.target.files.length) {
handleFile(e.target.files[0]);
}
});
dumbModeToggle.addEventListener('change', () => {
body.classList.toggle('comic-neue-font', dumbModeToggle.checked);
body.classList.toggle('inter-font', !dumbModeToggle.checked);
});
vhsToggle.addEventListener('change', () => {
scanlines.style.opacity = vhsToggle.checked ? '0.5' : '0';
});
brokenToggle.addEventListener('change', () => {
brokenScreen.style.opacity = brokenToggle.checked ? '0.2' : '0';
});
downloadBtn.addEventListener('click', downloadImage);
rageModeBtn.addEventListener('click', activateRageMode);
reapplyBtn.addEventListener('click', applyFiltersMultipleTimes);
previewCanvas.addEventListener('click', handleCanvasClick);
// --- Core Functions ---
function handleFile(file) {
if (file.size > 5 * 1024 * 1024) {
customAlert('File too big! Max 5MB please.');
return;
}
const reader = new FileReader();
reader.onload = function(e) {
const img = new Image();
img.onload = function() {
originalImage = img;
currentImageForReapply = null;
placeholder.classList.add('hidden');
previewCanvas.classList.remove('hidden');
resetAllSliders();
};
img.onerror = () => customAlert('Error loading image. Please try another file.');
img.src = e.target.result;
};
reader.onerror = () => customAlert('Error reading file. Please try again.');
reader.readAsDataURL(file);
}
function loadImageFromUrl(url) { // This function was used by meme templates, can be kept or removed
const img = new Image();
img.crossOrigin = 'Anonymous';
img.onload = function() {
originalImage = img;
currentImageForReapply = null;
placeholder.classList.add('hidden');
previewCanvas.classList.remove('hidden');
resetAllSliders();
};
img.onerror = () => customAlert('Error loading template image. Check CORS or URL.');
img.src = url;
}
function applyFiltersMultipleTimes() {
if (!originalImage) {
customAlert('Please upload an image first!');
return;
}
const count = parseInt(reapplyCountInput.value, 10);
if (isNaN(count) || count < 1) {
customAlert('Please enter a valid number of times to reapply.');
return;
}
// Start from the original image, then apply filters 'count' times sequentially.
processImage(true, count);
}
function processImage(startFromOriginal = true, iterations = 1) {
if (!originalImage || isProcessing) return;
if (!originalImage.complete || originalImage.naturalWidth === 0) {
setTimeout(() => processImage(startFromOriginal, iterations), 100);
return;
}
isProcessing = true;
const previewContainerWidth = previewCanvas.parentElement.clientWidth;
const aspectRatio = originalImage.naturalWidth / originalImage.naturalHeight;
let canvasWidth = previewContainerWidth;
let canvasHeight = previewContainerWidth / aspectRatio;
const maxHeight = Math.min(500, window.innerHeight * 0.6);
if (canvasHeight > maxHeight) {
canvasHeight = maxHeight;
canvasWidth = canvasHeight * aspectRatio;
}
if (canvasWidth > previewContainerWidth) { // Should not happen if previewContainerWidth is respected
canvasWidth = previewContainerWidth;
canvasHeight = canvasWidth / aspectRatio;
}
previewCanvas.width = Math.round(canvasWidth); // Use integers for canvas dimensions
previewCanvas.height = Math.round(canvasHeight);
ctx.clearRect(0, 0, previewCanvas.width, previewCanvas.height);
if (startFromOriginal || !currentImageForReapply) {
ctx.drawImage(originalImage, 0, 0, previewCanvas.width, previewCanvas.height);
} else {
// Draw the previously processed image data (currentImageForReapply) to the canvas
// This ensures that single slider adjustments build upon the last processed state.
ctx.putImageData(currentImageForReapply, 0, 0);
}
for (let iter = 0; iter < iterations; iter++) {
let imageData = ctx.getImageData(0, 0, previewCanvas.width, previewCanvas.height);
let data = imageData.data;
const compression = parseFloat(compressionSlider.value);
const saturation = parseFloat(saturationSlider.value) / 100;
const contrast = parseFloat(contrastSlider.value) / 100;
const sharpness = parseFloat(sharpnessSlider.value) / 100;
const currentNoise = parseFloat(noiseSlider.value) / 100; // Renamed to avoid conflict
const hueShift = parseFloat(hueSlider.value);
for (let i = 0; i < data.length; i += 4) {
let r = data[i];
let g = data[i + 1];
let b = data[i + 2];
if (saturation !== 1) {
const gray = 0.299 * r + 0.587 * g + 0.114 * b;
r = gray + saturation * (r - gray);
g = gray + saturation * (g - gray);
b = gray + saturation * (b - gray);
}
if (contrast !== 1) {
r = 128 + contrast * (r - 128);
g = 128 + contrast * (g - 128);
b = 128 + contrast * (b - 128);
}
if (hueShift !== 0) {
const hsv = rgbToHsv(r, g, b);
hsv.h = (hsv.h + hueShift) % 360;
if (hsv.h < 0) hsv.h += 360;
const rgbRes = hsvToRgb(hsv.h, hsv.s, hsv.v); // Renamed to avoid conflict
r = rgbRes.r;
g = rgbRes.g;
b = rgbRes.b;
}
if (compression > 0) {
const compFactor = Math.pow(compression / 100, 2);
const depth = Math.max(1, Math.floor(255 * (1 - compFactor * 0.9)));
r = Math.floor(r / depth) * depth;
g = Math.floor(g / depth) * depth;
b = Math.floor(b / depth) * depth;
}
// Apply Noise directly to current pixel data
if (currentNoise > 0) {
const noiseAmount = Math.pow(currentNoise, 2);
const randomFactor = (Math.random() - 0.5) * 2 * 50 * noiseAmount;
r += randomFactor;
g += randomFactor;
b += randomFactor;
}
data[i] = Math.max(0, Math.min(255, r));
data[i + 1] = Math.max(0, Math.min(255, g));
data[i + 2] = Math.max(0, Math.min(255, b));
}
ctx.putImageData(imageData, 0, 0); // Put data back after pixel loop for this iteration
// Sharpness applied after pixel loop for this iteration
if (sharpness > 0) {
const sharpAmount = Math.pow(sharpness, 1.5); // Removed /100, let's try direct value
ctx.filter = `contrast(${1 + sharpAmount * 0.05}) brightness(${1 + sharpAmount * 0.005})`; // Reduced effect
// Draw the canvas onto itself to apply the filter
// Create a temporary canvas to avoid issues with drawing source and destination being the same in some browsers
const tempSharpCanvas = document.createElement('canvas');
tempSharpCanvas.width = previewCanvas.width;
tempSharpCanvas.height = previewCanvas.height;
const tempSharpCtx = tempSharpCanvas.getContext('2d');
tempSharpCtx.drawImage(previewCanvas, 0, 0); // Copy current state
ctx.clearRect(0,0, previewCanvas.width, previewCanvas.height); // Clear main canvas
ctx.filter = `contrast(${1 + sharpAmount * 0.05}) brightness(${1 + sharpAmount * 0.005})`;
ctx.drawImage(tempSharpCanvas, 0, 0); // Draw filtered image back
ctx.filter = 'none'; // Reset filter
}
}
currentImageForReapply = ctx.getImageData(0, 0, previewCanvas.width, previewCanvas.height);
applyOverlaysAndEasterEggs(previewCanvas.width, previewCanvas.height);
isProcessing = false;
}
function applyOverlaysAndEasterEggs(canvasWidth, canvasHeight) {
const allMaxed = sliders.every(s => parseFloat(s.el.value) === parseFloat(s.el.max) || (s.el.id === 'hue' && s.el.value !== '0'));
const harambeChance = Math.random() < 0.01;
const sliderIntensities = sliders.map(s => {
const val = parseFloat(s.el.value);
const neutral = parseFloat(s.neutral);
const max = parseFloat(s.el.max);
const min = parseFloat(s.el.min); // Assuming min is 0 for most, or neutral for others
if (max === min) return 0;
return Math.abs(val - neutral) / Math.max(Math.abs(max - neutral), Math.abs(min - neutral));
});
const avgIntensity = sliderIntensities.reduce((a,b) => a + b, 0) / sliders.length;
if (avgIntensity > 0.6) {
msPaintDoodle.src = getRandomDoodle();
msPaintDoodle.style.opacity = String(Math.min(1, (avgIntensity - 0.6) * 2.5));
msPaintDoodle.style.left = `${Math.random() * (canvasWidth - 100)}px`;
msPaintDoodle.style.top = `${Math.random() * (canvasHeight - 100)}px`;
msPaintDoodle.style.transform = `rotate(${Math.random() * 30 - 15}deg)`;
msPaintDoodle.style.width = `${50 + Math.random() * 100}px`;
msPaintDoodle.classList.remove('hidden');
} else {
msPaintDoodle.classList.add('hidden');
msPaintDoodle.style.opacity = '0';
}
if (allMaxed) {
memeOverlay.src = Math.random() > 0.5 ?
'https://i.imgur.com/4ZQ4Z4Q.jpg' :
'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 200 100"><text x="10" y="70" font-family="Impact" font-size="50" fill="%23FF00FF" stroke="%23FFFF00" stroke-width="2">S🅱️IN</text></svg>';
memeOverlay.classList.remove('hidden');
memeOverlay.style.width = `${canvasWidth * (Math.random() * 0.3 + 0.2)}px`;
memeOverlay.style.left = `${Math.random() * canvasWidth * 0.5}px`;
memeOverlay.style.top = `${Math.random() * canvasHeight * 0.5}px`;
memeOverlay.style.transform = `rotate(${Math.random() * 40 - 20}deg)`;
} else if (harambeChance) {
memeOverlay.src = 'https://i.imgur.com/5ZQ5Z5Q.jpg';
memeOverlay.classList.remove('hidden');
memeOverlay.style.width = `${canvasWidth}px`;
memeOverlay.style.height = `${canvasHeight}px`;
memeOverlay.style.left = '0';
memeOverlay.style.top = '0';
memeOverlay.style.opacity = '0.3';
memeOverlay.style.transform = `none`;
} else {
memeOverlay.classList.add('hidden');
}
}
function handleCanvasClick(event) {
if (!originalImage || isProcessing) return;
const rect = previewCanvas.getBoundingClientRect();
const scaleX = previewCanvas.width / rect.width;
const scaleY = previewCanvas.height / rect.height;
const clickX = (event.clientX - rect.left) * scaleX;
const clickY = (event.clientY - rect.top) * scaleY;
applyBulgeEffect(clickX, clickY);
}
function applyBulgeEffect(centerX, centerY) {
if (!currentImageForReapply && originalImage) { // If no processing done yet, use original
ctx.clearRect(0,0,previewCanvas.width, previewCanvas.height);
ctx.drawImage(originalImage, 0,0, previewCanvas.width, previewCanvas.height);
currentImageForReapply = ctx.getImageData(0,0,previewCanvas.width, previewCanvas.height);
} else if (!currentImageForReapply && !originalImage) {
customAlert("Please upload an image first.");
return;
}
isProcessing = true;
// Use currentImageForReapply as the source, which holds the latest state of the canvas
let imageData = ctx.getImageData(0, 0, previewCanvas.width, previewCanvas.height); // Get current canvas state
let srcData = new Uint8ClampedArray(imageData.data); // Clone current canvas data to read from
let dstData = imageData.data; // Modify this directly (it's a reference to the canvas's data)
const width = previewCanvas.width;
const height = previewCanvas.height;
const radius = Math.min(width, height) / (Math.random() * 2 + 3); // Random radius (3 to 5)
const strength = (Math.random() * 0.3 + 0.4); // Random strength (0.4 to 0.7 for bulge)
for (let y = 0; y < height; y++) {
for (let x = 0; x < width; x++) {
const dx = x - centerX;
const dy = y - centerY;
const distance = Math.sqrt(dx * dx + dy * dy);
const destIdx = (y * width + x) * 4;
if (distance < radius) {
const normalizedDistance = distance / radius;
const bulgeFactor = Math.pow(normalizedDistance, strength);
const srcX = Math.floor(centerX + dx * bulgeFactor);
const srcY = Math.floor(centerY + dy * bulgeFactor);
if (srcX >= 0 && srcX < width && srcY >= 0 && srcY < height) {
const srcIdx = (srcY * width + srcX) * 4;
dstData[destIdx] = srcData[srcIdx];
dstData[destIdx + 1] = srcData[srcIdx + 1];
dstData[destIdx + 2] = srcData[srcIdx + 2];
dstData[destIdx + 3] = srcData[srcIdx + 3];
}
}
}
}
ctx.putImageData(imageData, 0, 0);
currentImageForReapply = ctx.getImageData(0, 0, previewCanvas.width, previewCanvas.height);
isProcessing = false;
applyOverlaysAndEasterEggs(previewCanvas.width, previewCanvas.height); // Re-apply overlays if needed
}
// --- Utility Functions ---
function rgbToHsv(r, g, b) {
r /= 255, g /= 255, b /= 255;
const max = Math.max(r, g, b), min = Math.min(r, g, b);
let h, s, v = max;
const d = max - min;
s = max === 0 ? 0 : d / max;
if (max === min) {
h = 0;
} else {
switch (max) {
case r: h = (g - b) / d + (g < b ? 6 : 0); break;
case g: h = (b - r) / d + 2; break;
case b: h = (r - g) / d + 4; break;
}
h /= 6;
}
return { h: h * 360, s: s, v: v };
}
function hsvToRgb(h, s, v) {
h /= 360;
let r, g, b;
const i = Math.floor(h * 6);
const f = h * 6 - i;
const p = v * (1 - s);
const q = v * (1 - f * s);
const t = v * (1 - (1 - f) * s);
switch (i % 6) {
case 0: r = v, g = t, b = p; break;
case 1: r = q, g = v, b = p; break;
case 2: r = p, g = v, b = t; break;
case 3: r = p, g = q, b = v; break;
case 4: r = t, g = p, b = v; break;
case 5: r = v, g = p, b = q; break;
}
return { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255) };
}
function getRandomDoodle() {
const doodles = [
'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><circle cx="50" cy="50" r="40" fill="%23facc15" stroke="black" stroke-width="2"/><circle cx="35" cy="40" r="5" fill="black"/><circle cx="65" cy="40" r="5" fill="black"/><path d="M30,65 Q50,80 70,65" fill="none" stroke="black" stroke-width="3"/></svg>',
'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text x="10" y="70" font-family="Comic Neue" font-size="60" fill="red">🔥</text></svg>',
'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text x="5" y="60" font-family="Comic Neue" font-size="20" fill="%2314b8a6" transform="rotate(-10 50 50)">W E W</text><text x="25" y="80" font-family="Comic Neue" font-size="20" fill="%23c026d3" transform="rotate(5 50 50)">L A D</text></svg>',
'data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 100 100"><text x="20" y="75" font-family="Impact" font-size="80" fill="%23c026d3" stroke="black" stroke-width="1">🅱️</text></svg>'
];
return doodles[Math.floor(Math.random() * doodles.length)];
}
function downloadImage() {
if (!currentImageForReapply && !originalImage) {
customAlert('Please upload and process an image first!');
return;
}
const tempCanvas = document.createElement('canvas');
const tempCtx = tempCanvas.getContext('2d');
let imageToDownload = currentImageForReapply || null;
if (!imageToDownload && originalImage) { // If no processing, prepare original image for download
tempCanvas.width = originalImage.naturalWidth;
tempCanvas.height = originalImage.naturalHeight;
tempCtx.drawImage(originalImage, 0, 0);
imageToDownload = tempCtx.getImageData(0,0, tempCanvas.width, tempCanvas.height);
} else if (imageToDownload) {
tempCanvas.width = imageToDownload.width;
tempCanvas.height = imageToDownload.height;
tempCtx.putImageData(imageToDownload, 0, 0);
} else {
customAlert('No image to download.');
return;
}
// Add overlays to the download version too if they are visible
if (scanlines.style.opacity !== '0' && parseFloat(scanlines.style.opacity) > 0) {
tempCtx.fillStyle = 'rgba(0,0,0,0.15)'; // Slightly less opaque for download
for(let i=0; i < tempCanvas.height; i+=4) {
if (i % 8 >= 2) {
tempCtx.fillRect(0, i, tempCanvas.width, 2);
}
}
}
if (brokenScreen.style.opacity !== '0' && parseFloat(brokenScreen.style.opacity) > 0) {
tempCtx.globalAlpha = Math.min(0.2, parseFloat(brokenScreen.style.opacity)); // Cap opacity
tempCtx.strokeStyle = 'rgba(0,0,0,0.3)';
tempCtx.lineWidth = 1;
for(let i=0; i < Math.max(tempCanvas.width, tempCanvas.height); i+= (Math.random()*10 + 15) ) { // Randomize grid
tempCtx.beginPath();
tempCtx.moveTo(i + (Math.random()*10-5),0); tempCtx.lineTo(i + (Math.random()*10-5), tempCanvas.height);
tempCtx.moveTo(0,i + (Math.random()*10-5)); tempCtx.lineTo(tempCanvas.width, i + (Math.random()*10-5));
tempCtx.stroke();
}
tempCtx.globalAlpha = 1.0;
}
// MemeOverlay and MSPaintDoodle are img elements, need to draw them if visible
const overlaysToDraw = [];
if(!msPaintDoodle.classList.contains('hidden') && msPaintDoodle.src && parseFloat(msPaintDoodle.style.opacity) > 0) {
overlaysToDraw.push({element: msPaintDoodle, isSVG: msPaintDoodle.src.startsWith('data:image/svg+xml')});
}
if (!memeOverlay.classList.contains('hidden') && memeOverlay.src && parseFloat(memeOverlay.style.opacity || 1) > 0) {
overlaysToDraw.push({element: memeOverlay, isSVG: memeOverlay.src.startsWith('data:image/svg+xml')});
}
let overlaysDrawn = 0;
const totalOverlays = overlaysToDraw.length;
if (totalOverlays === 0) {
finalizeDownload(tempCanvas);
return;
}
overlaysToDraw.forEach(overlayData => {
const overlayElement = overlayData.element;
const img = new Image();
img.crossOrigin = "Anonymous";
img.onload = () => {
const prevRect = previewCanvas.getBoundingClientRect(); // Preview canvas rect
const tempRect = tempCanvas; // Target canvas (already sized)
// Calculate scale factor from preview to download canvas
const scaleX = tempRect.width / prevRect.width;
const scaleY = tempRect.height / prevRect.height;
// Get computed style for precise positioning if possible
const computedStyle = window.getComputedStyle(overlayElement);
let left = parseFloat(computedStyle.left);
let top = parseFloat(computedStyle.top);
let width = parseFloat(computedStyle.width);
let height = parseFloat(computedStyle.height);
// Fallback to style attributes if computed are NaN
if (isNaN(left)) left = parseFloat(overlayElement.style.left) || 0;
if (isNaN(top)) top = parseFloat(overlayElement.style.top) || 0;
if (isNaN(width)) width = parseFloat(overlayElement.style.width) || img.naturalWidth;
if (isNaN(height)) height = parseFloat(overlayElement.style.height) || img.naturalHeight;
const drawX = left * scaleX;
const drawY = top * scaleY;
const drawWidth = width * scaleX;
const drawHeight = height * scaleY;
tempCtx.globalAlpha = parseFloat(computedStyle.opacity) || 1;
// Simple rotation handling (extract from transform: rotate(Xdeg))
const transform = computedStyle.transform;
let angle = 0;
if (transform && transform !== 'none') {
const match = transform.match(/rotate\(([^deg)]+)deg\)/);
if (match && match[1]) {
angle = parseFloat(match[1]) * Math.PI / 180; // to radians
}
}
if (angle !== 0) {
tempCtx.translate(drawX + drawWidth / 2, drawY + drawHeight / 2);
tempCtx.rotate(angle);
tempCtx.drawImage(img, -drawWidth / 2, -drawHeight / 2, drawWidth, drawHeight);
tempCtx.rotate(-angle); // Rotate back
tempCtx.translate(-(drawX + drawWidth / 2), -(drawY + drawHeight / 2)); // Translate back
} else {
tempCtx.drawImage(img, drawX, drawY, drawWidth, drawHeight);
}
tempCtx.globalAlpha = 1.0; // Reset alpha
overlaysDrawn++;
if (overlaysDrawn === totalOverlays) {
finalizeDownload(tempCanvas);
}
};
img.onerror = () => {
console.error("Failed to load overlay for download:", overlayElement.src);
overlaysDrawn++;
if (overlaysDrawn === totalOverlays) {
finalizeDownload(tempCanvas);
}
};
img.src = overlayElement.src;
});
}
function finalizeDownload(canvasToDownload) {
canvasToDownload.toBlob(function(blob) {
if (!blob) {
customAlert("Failed to create image blob for download.");
return;
}
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = `DEEPFRY_${Date.now()}.jpg`;
document.body.appendChild(a);
a.click();
document.body.removeChild(a);
URL.revokeObjectURL(url);
}, 'image/jpeg', 0.3 + (parseFloat(compressionSlider.value)/100 * -0.25));
}
function activateRageMode() {
sliders.forEach(s => {
if (s.el.id === 'saturation' || s.el.id === 'contrast') {
s.el.value = s.el.max;
} else if (s.el.id === 'hue') {
s.el.value = String(Math.floor(Math.random() * 180 + 90));
} else {
s.el.value = String(Math.floor(Math.random() * 50 + 50));
}
updateSliderValueDisplay(s.el, s.valEl, s.unit);
});
vhsToggle.checked = Math.random() > 0.3;
brokenToggle.checked = Math.random() > 0.3;
dumbModeToggle.checked = Math.random() > 0.5;
scanlines.style.opacity = vhsToggle.checked ? '0.7' : '0';
brokenScreen.style.opacity = brokenToggle.checked ? '0.3' : '0';
body.classList.toggle('comic-neue-font', dumbModeToggle.checked);
body.classList.toggle('inter-font', !dumbModeToggle.checked);
if (originalImage) processImage(true, Math.floor(Math.random() * 2 + 2));
const container = document.querySelector('.container');
container.style.animation = 'rage_anim 0.1s infinite alternate';
setTimeout(() => {
container.style.animation = '';
}, 1000 + Math.random() * 1000);
}
previewCanvas.classList.add('hidden');
placeholder.classList.remove('hidden');
if (!body.classList.contains('inter-font')) body.classList.add('inter-font');
});
</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=Jaspior/deep-fried-meme" style="color: #fff;text-decoration: underline;" target="_blank" >Remix</a></p></body>
</html>