246 lines
7.5 KiB
JavaScript
246 lines
7.5 KiB
JavaScript
|
|
const canvas = document.getElementById('canvas');
|
|
const ctx = canvas.getContext('2d');
|
|
|
|
const overlayCanvas = document.getElementById('overlayCanvas');
|
|
const overlayCtx = overlayCanvas.getContext('2d');
|
|
|
|
const image = document.getElementById('image');
|
|
|
|
let drawing = false;
|
|
let penSize = document.getElementById('penSize').value;
|
|
let lastX = 0;
|
|
let lastY = 0;
|
|
|
|
image.onload = drawImageOnCanvas;
|
|
|
|
function drawImageOnCanvas() {
|
|
canvas.width = image.width
|
|
canvas.height = image.height;
|
|
|
|
overlayCanvas.width = image.width
|
|
overlayCanvas.height = image.height;
|
|
|
|
ctx.drawImage(image, 0, 0, image.width, image.height);
|
|
|
|
document.getElementById('canvas-container').style.visibility='visible';
|
|
document.getElementById('toolbox').style.visibility='visible';
|
|
CentralSpaceHideProcessing();
|
|
HideThumbs(); // More visual space
|
|
};
|
|
|
|
// Adjust pen size
|
|
document.getElementById('penSize').addEventListener('input', function () {
|
|
penSize = this.value;
|
|
});
|
|
|
|
// Start drawing
|
|
canvas.addEventListener('mousedown', (e) => {
|
|
drawing = true;
|
|
[lastX, lastY] = getMousePos(e);
|
|
draw(e);
|
|
});
|
|
|
|
canvas.addEventListener('mouseup', () => drawing = false);
|
|
canvas.addEventListener('mousemove', draw);
|
|
|
|
function draw(e) {
|
|
|
|
if (document.getElementById('penSize').disabled) {return false;}
|
|
|
|
const [x, y] = getMousePos(e);
|
|
const mode = document.getElementById('editMode').value;
|
|
|
|
e.preventDefault();
|
|
|
|
// Clear the previous brush preview
|
|
overlayCtx.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height);
|
|
|
|
if (!drawing)
|
|
{
|
|
// Not drawing, draw a pen preview only.
|
|
// Draw a circle at the current mouse position
|
|
|
|
// Set dashed pattern for visibility on any background
|
|
overlayCtx.setLineDash([5, 5]); // Alternating dash pattern
|
|
overlayCtx.lineWidth = 2;
|
|
|
|
// Draw a white circle outline
|
|
overlayCtx.strokeStyle = 'white';
|
|
overlayCtx.beginPath();
|
|
overlayCtx.arc(x, y, penSize / 2, 0, Math.PI * 2);
|
|
overlayCtx.stroke();
|
|
|
|
// Draw a black circle outline over the white to create contrast
|
|
overlayCtx.strokeStyle = 'black';
|
|
overlayCtx.lineWidth = 1;
|
|
overlayCtx.beginPath();
|
|
overlayCtx.arc(x, y, penSize / 2, 0, Math.PI * 2);
|
|
overlayCtx.stroke();
|
|
return;
|
|
}
|
|
|
|
ctx.lineWidth = penSize;
|
|
ctx.globalCompositeOperation = 'destination-out';
|
|
ctx.strokeStyle = "black";
|
|
ctx.lineCap = 'round';
|
|
ctx.lineJoin = 'round';
|
|
|
|
if (mode=="clone" || mode=="white" || mode=="black")
|
|
{
|
|
// Add shadow for blur effect
|
|
ctx.lineWidth = penSize/3;
|
|
ctx.shadowColor = 'black'; // Set shadow color to match stroke
|
|
ctx.shadowBlur = penSize/2; // Increase this value for stronger blur
|
|
}
|
|
|
|
ctx.beginPath();
|
|
ctx.moveTo(lastX, lastY);
|
|
ctx.lineTo(x, y);
|
|
ctx.stroke();
|
|
|
|
[lastX, lastY] = [x, y];
|
|
}
|
|
|
|
// Get mouse position relative to the canvas
|
|
function getMousePos(e) {
|
|
const rect = canvas.getBoundingClientRect();
|
|
return [
|
|
e.clientX - rect.left,
|
|
e.clientY - rect.top
|
|
];
|
|
}
|
|
|
|
// Hide the brush preview when the mouse leaves the main canvas
|
|
canvas.addEventListener('mouseleave', () => {
|
|
overlayCtx.clearRect(0, 0, overlayCanvas.width, overlayCanvas.height);
|
|
drawing=false;
|
|
});
|
|
|
|
// Submit canvas as mask
|
|
document.getElementById('submitBtn').addEventListener('click', async () => {
|
|
|
|
|
|
const tempCanvas = document.createElement('canvas');
|
|
const tempCtx = tempCanvas.getContext('2d');
|
|
tempCanvas.width = 1024;
|
|
tempCanvas.height = 1024;
|
|
tempCtx.drawImage(canvas, 0, 0);
|
|
|
|
const mask = tempCanvas.toDataURL('image/png');
|
|
const prompt = document.getElementById('prompt').value;
|
|
|
|
CentralSpaceShowProcessing(0,defaultLoadingMessage);
|
|
|
|
// Send mask and image to the backend via AJAX
|
|
|
|
// Prepare form data
|
|
const formData = new URLSearchParams({
|
|
...csrf_pair,
|
|
mask: mask,
|
|
imageType: document.getElementById('downloadType').value,
|
|
mode: document.getElementById('editMode').value,
|
|
prompt: prompt,
|
|
ajax: true
|
|
});
|
|
|
|
const response = await fetch(submit_url, {
|
|
method: 'POST',
|
|
body: formData
|
|
});
|
|
|
|
const result = await response.json(); // Parse the response as JSON
|
|
|
|
// Check if a valid image URL is returned
|
|
if (result.image_base64) {
|
|
|
|
const base64Image = result.image_base64; // Extract the base64 image data
|
|
|
|
// Update the image src behind the canvas with the new one
|
|
image.src = `data:image/png;base64,${base64Image}`;
|
|
|
|
// Redraw the canvas with the new image once it loads
|
|
image.onload = () => {
|
|
|
|
// I have no idea why this is necessary or why it does anything, but without it the following drawImage() fails to draw anything.
|
|
canvas.width = canvas.width;
|
|
canvas.height = canvas.height;
|
|
|
|
ctx.drawImage(image, 0, 0); // Draw new image
|
|
|
|
document.getElementById('downloadOptions').style.visibility='visible';
|
|
};
|
|
} else {
|
|
console.error('Failed to get a valid image URL from OpenAI.');
|
|
styledalert('OpenAI',result.error.message);
|
|
}
|
|
CentralSpaceHideProcessing();
|
|
});
|
|
|
|
// Get the mode selector and add an event listener
|
|
document.getElementById('editMode').addEventListener('change', () => {
|
|
const mode = document.getElementById('editMode').value;
|
|
const brush = document.getElementById('penSize');
|
|
const prompt = document.getElementById('prompt');
|
|
brush.disabled=(mode=='generate' || mode=='variation');
|
|
prompt.disabled=(mode=='white' || mode=='black' || mode=='variation');
|
|
});
|
|
|
|
|
|
// Get the download button and add an event listener
|
|
document.getElementById('downloadBtn').addEventListener('click', () => {
|
|
// Convert the canvas content to a data URL (PNG format)
|
|
const dataURL = canvas.toDataURL(document.getElementById('downloadType').value);
|
|
const downloadAction=document.getElementById('downloadAction').value;
|
|
|
|
if (downloadAction=='download')
|
|
{
|
|
// Download the file (or really, save, as it's already on the user's system)
|
|
|
|
// Create a temporary link element
|
|
const link = document.createElement('a');
|
|
link.href = dataURL;
|
|
link.download = 'ai_edited.' + document.getElementById('downloadType').value.split('/')[1]; // The name of the downloaded image
|
|
|
|
// Programmatically click the link to trigger the download
|
|
link.click();
|
|
}
|
|
|
|
if (downloadAction=='alternative' || downloadAction=='new')
|
|
{
|
|
// Save as alternative file
|
|
CentralSpaceShowProcessing(0);
|
|
|
|
// Prepare form data
|
|
const formData = new URLSearchParams({
|
|
...csrf_pair,
|
|
ajax: true,
|
|
imageData: dataURL,
|
|
imageType: document.getElementById('downloadType').value
|
|
});
|
|
|
|
fetch(downloadAction=='alternative' ? alternative_url : save_new_url, {
|
|
method: 'POST',
|
|
body: formData
|
|
})
|
|
.then(response => response.json())
|
|
.then(result => {
|
|
console.log('Image submitted successfully:', result);
|
|
|
|
if (downloadAction=='alternative')
|
|
{
|
|
window.location.href=view_url;
|
|
}
|
|
else
|
|
{
|
|
window.location.href=view_new_url + result['resource'];
|
|
}
|
|
})
|
|
.catch(error => {
|
|
alert('Error submitting image:' + error);
|
|
});
|
|
}
|
|
|
|
|
|
});
|