Pixel Image

Pixelize an image

Drag & drop an image here or use the default image

1

PIXEL Image

Thanks to Claude.ai for knocking this out. I think this might help with embroidery projects, because it is good to knock down the number of colors needed.


pixel.js

// DOM elements
const canvas = document.getElementById('canvas');
const ctx = canvas.getContext('2d');
const canvasContainer = document.getElementById('canvas-container');
const pixelateSlider = document.getElementById('pixelate-slider');
const pixelateValue = document.getElementById('pixelate-value');
const defaultBtn = document.getElementById('default-btn');
const uploadBtn = document.getElementById('upload-btn');
const saveBtn = document.getElementById('save-btn');
const fileInput = document.getElementById('file-input');

// State
let originalImage = null;
let pixelSize = 1;

// Default image URL
const defaultImageUrl = "./www/p/pixel_image/test.jpg";

// Initialize
function init() {
    // Load default image on start
    loadImage(defaultImageUrl);

    // Set up event listeners
    pixelateSlider.addEventListener('input', handleSliderChange);
    defaultBtn.addEventListener('click', () => loadImage(defaultImageUrl));
    uploadBtn.addEventListener('click', () => fileInput.click());
    fileInput.addEventListener('change', handleFileUpload);
    saveBtn.addEventListener('click', saveImage);

    // Drag and drop setup
    canvasContainer.addEventListener('dragover', handleDragOver);
    canvasContainer.addEventListener('dragleave', handleDragLeave);
    canvasContainer.addEventListener('drop', handleDrop);
}

// Load and process image from URL
function loadImage(url) {
    const img = new Image();
    img.crossOrigin = "Anonymous";  // Enable saving from other domains

    img.onload = function() {
        originalImage = img;

        // Set canvas dimensions to match image
        const maxWidth = canvasContainer.clientWidth - 40;
        const maxHeight = canvasContainer.clientHeight - 40;

        // Scale to fit container while maintaining aspect ratio
        let width = img.width;
        let height = img.height;

        if (width > maxWidth) {
            const ratio = maxWidth / width;
            width = maxWidth;
            height = height * ratio;
        }

        if (height > maxHeight) {
            const ratio = maxHeight / height;
            height = maxHeight;
            width = width * ratio;
        }

        canvas.width = width;
        canvas.height = height;

        // Draw and apply current pixelation
        drawPixelatedImage();
        saveBtn.disabled = false;
    };

    img.onerror = function() {
        console.error('Error loading image');
        alert('Could not load image. Please try another one.');
    };

    img.src = url;
}

// Draw pixelated image to canvas
function drawPixelatedImage() {
    if (!originalImage) return;

    const w = canvas.width;
    const h = canvas.height;

    // Clear canvas
    ctx.clearRect(0, 0, w, h);

    if (pixelSize <= 1) {
        // Draw original image without pixelation
        ctx.drawImage(originalImage, 0, 0, w, h);
        return;
    }

    // Draw pixelated version
    ctx.imageSmoothingEnabled = false;

    // Create a temporary canvas for pixelation
    const tempCanvas = document.createElement('canvas');
    const tempCtx = tempCanvas.getContext('2d');

    // Set temp canvas size based on pixelation level
    tempCanvas.width = Math.ceil(w / pixelSize);
    tempCanvas.height = Math.ceil(h / pixelSize);

    // Draw small version (pixelated)
    tempCtx.drawImage(originalImage, 0, 0, tempCanvas.width, tempCanvas.height);

    // Draw the small version back to the main canvas scaled up
    ctx.drawImage(
        tempCanvas, 
        0, 0, tempCanvas.width, tempCanvas.height,
        0, 0, w, h
    );
}

// Handle slider change
function handleSliderChange() {
    pixelSize = parseInt(pixelateSlider.value);
    pixelateValue.textContent = pixelSize;
    drawPixelatedImage();
}

// Handle file upload from input
function handleFileUpload(e) {
    const file = e.target.files[0];
    if (file && file.type.match('image.*')) {
        const reader = new FileReader();
        reader.onload = function(event) {
            loadImage(event.target.result);
        };
        reader.readAsDataURL(file);
    }
    // Reset file input
    fileInput.value = '';
}

// Handle drag over
function handleDragOver(e) {
    e.preventDefault();
    canvasContainer.classList.add('drag-over');
}

// Handle drag leave
function handleDragLeave() {
    canvasContainer.classList.remove('drag-over');
}

// Handle drop
function handleDrop(e) {
    e.preventDefault();
    canvasContainer.classList.remove('drag-over');

    const file = e.dataTransfer.files[0];
    if (file && file.type.match('image.*')) {
        const reader = new FileReader();
        reader.onload = function(event) {
            loadImage(event.target.result);
        };
        reader.readAsDataURL(file);
    }
}

// Save the pixelated image
function saveImage() {
    if (!canvas.width) return;

    // Create download link
    const link = document.createElement('a');
    link.download = 'pixelated-image.png';
    link.href = canvas.toDataURL('image/png');
    link.click();
}

// Start the app
init();