You can edit the below JavaScript code to customize the image tool.
Apply Changes
async function processImage(
originalImg,
outputWidthChars = 80,
fontSize = 10,
fontFamily = "'SBL Hebrew', 'Ezra SIL', 'David Libre', 'Frank Ruhl Libre', 'Arial Hebrew', Arial, 'Courier New', Courier, monospace",
backgroundColor = "#FFFFFF",
textColor = "#000000",
charSetString = " 'וירןךאלתקדהבגעזטחנפףצץמסשם" // Sparse (light) to Dense (dark)
) {
const imgWidth = originalImg.width;
const imgHeight = originalImg.height;
if (imgWidth === 0 || imgHeight === 0) {
const emptyCanvas = document.createElement('canvas');
emptyCanvas.width = 1;
emptyCanvas.height = 1;
return emptyCanvas; // Return a minimal canvas for invalid input
}
// Determine effective character set
const effectiveCharSet = (charSetString && charSetString.length >= 2) ? charSetString : " 'וירןךאלתקדהבגעזטחנפףצץמסשם";
const charSetLength = effectiveCharSet.length;
// 1. Create a temporary canvas to draw the image and get pixel data
const tempCanvas = document.createElement('canvas');
tempCanvas.width = imgWidth;
tempCanvas.height = imgHeight;
const tempCtx = tempCanvas.getContext('2d', { willReadFrequently: true });
// Fill temp canvas with background color (handles transparent images)
// then draw the image on top.
tempCtx.fillStyle = backgroundColor;
tempCtx.fillRect(0, 0, imgWidth, imgHeight);
tempCtx.drawImage(originalImg, 0, 0, imgWidth, imgHeight);
// 2. Calculate dimensions of the character grid and image blocks
const actualOutputWidthChars = Math.max(1, Math.floor(outputWidthChars));
// Calculate block size in pixels of the original image
const blockPixelWidth = imgWidth / actualOutputWidthChars;
// Calculate output height in characters to maintain aspect ratio
const actualOutputHeightChars = Math.max(1, Math.floor(imgHeight / blockPixelWidth));
// Recalculate blockPixelHeight based on actualOutputHeightChars to ensure full image coverage
const blockPixelHeight = imgHeight / actualOutputHeightChars;
// 3. Create the output canvas for drawing character art
const outputCanvas = document.createElement('canvas');
const outputCtx = outputCanvas.getContext('2d');
// Estimate character cell size for canvas dimensioning and drawing
// These are heuristics. Monospace fonts are more predictable but Hebrew can vary.
const cellWidth = fontSize * 0.65; // Adjust this ratio if characters are wider/narrower
const cellHeight = fontSize; // Typically, font size itself is a good measure for cell height
outputCanvas.width = Math.max(1, Math.floor(actualOutputWidthChars * cellWidth));
outputCanvas.height = Math.max(1, Math.floor(actualOutputHeightChars * cellHeight));
// Prepare output canvas: fill background, set font properties
outputCtx.fillStyle = backgroundColor;
outputCtx.fillRect(0, 0, outputCanvas.width, outputCanvas.height);
outputCtx.font = `${fontSize}px ${fontFamily}`;
outputCtx.fillStyle = textColor;
outputCtx.textAlign = 'center';
outputCtx.textBaseline = 'middle';
// Luminance function
function getLuminance(r, g, b) {
return 0.299 * r + 0.587 * g + 0.114 * b;
}
// 4. Main loop: iterate over character grid cells
for (let charY = 0; charY < actualOutputHeightChars; charY++) {
for (let charX = 0; charX < actualOutputWidthChars; charX++) {
// Determine the region in the original image for this character cell
const imgStartX = Math.floor(charX * blockPixelWidth);
const imgStartY = Math.floor(charY * blockPixelHeight);
const imgEndX = Math.floor((charX + 1) * blockPixelWidth);
const imgEndY = Math.floor((charY + 1) * blockPixelHeight);
const currentBlockActualWidth = Math.max(1, imgEndX - imgStartX);
const currentBlockActualHeight = Math.max(1, imgEndY - imgStartY);
// Ensure we don't read outside canvas bounds if originalImg is smaller than calculated blocks
if (imgStartX + currentBlockActualWidth > imgWidth || imgStartY + currentBlockActualHeight > imgHeight) {
continue;
}
const imageData = tempCtx.getImageData(imgStartX, imgStartY, currentBlockActualWidth, currentBlockActualHeight);
const data = imageData.data;
let sumBrightness = 0;
const numPixelsInBlock = currentBlockActualWidth * currentBlockActualHeight;
for (let i = 0; i < data.length; i += 4) {
sumBrightness += getLuminance(data[i], data[i + 1], data[i + 2]);
}
const avgBrightness = numPixelsInBlock > 0 ? sumBrightness / numPixelsInBlock : 0; // 0-255
// Map brightness to a character.
// Character set is sparse (light) to dense (dark).
// High brightness (light image area) -> sparse char (lower index).
// Low brightness (dark image area) -> dense char (higher index).
let charIndex = Math.floor(((255 - avgBrightness) / 255) * (charSetLength - 1));
// Clamp index to be safe
charIndex = Math.max(0, Math.min(charSetLength - 1, charIndex));
const charToDraw = effectiveCharSet[charIndex];
// Calculate drawing position for the character
const drawX = (charX + 0.5) * cellWidth;
const drawY = (charY + 0.5) * cellHeight;
outputCtx.fillText(charToDraw, drawX, drawY);
}
}
return outputCanvas;
}
Apply Changes