UNPKG

@canvacord/beta

Version:

Simple & easy to use image manipulation module for beginners.

362 lines (361 loc) 18.5 kB
"use strict"; /* * Taken from https://github.com/geraintluff/canvas-sketch/blob/master/sketch.js */ Object.defineProperty(exports, "__esModule", { value: true }); exports.Sketcher = void 0; // @ts-nocheck const module_1 = require("../Canvas/module"); var Sketcher = (function () { function Sketcher(width, height) { var thisSketcher = this; this.width = width; this.height = height; this.levelSteps = 2; this.textureCanvases = null; this.textureImageDatas = null; this.lineThickness = 1; this.maxTextures = NaN; this.lineLength = Math.sqrt(width * height) * 0.2; this.darkeningFactor = 0.1; this.lineAlpha = 0.1; this.lineDensity = 0.5; this.lightness = 4; this.edgeBlurAmount = 4; this.edgeAmount = 0.5; /* noop */ this.progressUpdate = function () { }; this.sendProgressUpdate = function (proportion, message) { }; this.preparationFunctions = []; var totalPrepFunctions = 1; var startedInit = false; this.requiredColours = null; var whenReadyFunctions = []; this.whenReady = function (callback) { if (!startedInit) { this.createTextures(); totalPrepFunctions = this.preparationFunctions.length; startedInit = true; var intervalKey = setInterval(function () { if (thisSketcher.preparationFunctions.length == 0) { clearInterval(intervalKey); while (whenReadyFunctions.length > 0) { whenReadyFunctions.shift()(); } thisSketcher.whenReady = function (callback) { callback(); }; thisSketcher.progressUpdate = function () { }; thisSketcher.progressUpdateFunctions = null; } else { var message = thisSketcher.preparationFunctions.shift()(); var proportion = 1 - thisSketcher.preparationFunctions.length / totalPrepFunctions; thisSketcher.sendProgressUpdate(proportion, message); } }, 10); } whenReadyFunctions.push(callback); return this; }; } Sketcher.prototype = { transformCanvas: function (canvas, greyscale) { var context = canvas.getContext("2d"); var width = canvas.width; var height = canvas.height; var imageData = context.getImageData(0, 0, width, height); var pixels = imageData.data; this.sendProgressUpdate(0, "Calculating required textures"); var pixelCodes = {}; for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { var index = (x + y * width) * 4; var pixelCode = pixels[index] + ":" + pixels[index + 1] + ":" + pixels[index + 2]; pixelCodes[pixelCode] = true; } } while (true) { this.requiredColours = {}; for (var key in pixelCodes) { var parts = key.split(":"); var red = parseInt(parts[0]); var green = parseInt(parts[1]); var blue = parseInt(parts[2]); var redIndex = Math.round(red / 255 * (this.levelSteps - 1)); var greenIndex = Math.round(green / 255 * (this.levelSteps - 1)); var blueIndex = Math.round(blue / 255 * (this.levelSteps - 1)); for (var ri = -1; ri <= 1; ri++) { for (var gi = -1; gi <= 1; gi++) { for (var bi = -1; bi <= 1; bi++) { var key = (redIndex + ri) + ":" + (greenIndex + gi) + ":" + (blueIndex + bi); this.requiredColours[key] = true; } } } } if (Object.keys(this.requiredColours).length > this.maxTextures && this.levelSteps > 2) { this.levelSteps--; continue; } break; } var thisSketcher = this; this.whenReady(function () { thisSketcher.transformCanvasInner(canvas, greyscale); }); return this; }, transformCanvasInner: function transformCanvasInner(canvas, greyscale) { var context = canvas.getContext("2d"); var width = canvas.width; var height = canvas.height; var imageData = context.getImageData(0, 0, width, height); var pixels = imageData.data; var edges = []; for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { var index = x + y * width; edges[index * 3] = pixels[index * 4]; edges[index * 3 + 1] = pixels[index * 4 + 1]; edges[index * 3 + 2] = pixels[index * 4 + 2]; } } this.sendProgressUpdate(1, "Calculating edges"); var edges = this.calculateStandardDeviation(edges, this.edgeBlurAmount); this.sendProgressUpdate(1, "Assembling final image"); for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { var index = x + y * width; var red = pixels[index * 4]; var green = pixels[index * 4 + 1]; var blue = pixels[index * 4 + 2]; var rgb = this.getPixel(index, red, green, blue); if (greyscale) { var value = Math.round((rgb.red + rgb.green + rgb.blue) / 3); rgb.red = rgb.green = rgb.blue = value; } var edgeFactor = Math.max(0, (255 - edges[x + y * width] * this.edgeAmount) / 255); var edgeFactor = Math.min(1, Math.max(0.5, edgeFactor * edgeFactor)); pixels[index * 4] = Math.round(rgb.red * edgeFactor); pixels[index * 4 + 1] = Math.round(rgb.green * edgeFactor); pixels[index * 4 + 2] = Math.round(rgb.blue * edgeFactor); } } context.putImageData(imageData, 0, 0); }, calculateStandardDeviation: function (inputRgb, blurAmount) { var width = this.width; var height = this.height; var vsum = []; var vsum2 = []; for (var x = 0; x < width; x++) { var totals = [0, 0, 0]; var totals2 = [0, 0, 0]; for (var y = 0; y < height; y++) { var index = x + y * width; totals[0] += inputRgb[index * 3 + 0]; totals[1] += inputRgb[index * 3 + 1]; totals[2] += inputRgb[index * 3 + 2]; totals2[0] += inputRgb[index * 3 + 0] * inputRgb[index * 3 + 0]; totals2[1] += inputRgb[index * 3 + 1] * inputRgb[index * 3 + 1]; totals2[2] += inputRgb[index * 3 + 2] * inputRgb[index * 3 + 2]; vsum[index] = totals.slice(0); vsum2[index] = totals2.slice(0); } } var hsum = []; var hsum2 = []; for (var y = 0; y < height; y++) { var totals = [0, 0, 0]; var totals2 = [0, 0, 0]; for (var x = 0; x < width; x++) { var index = x + y * width; var startIndex = x + Math.max(0, Math.round(y - blurAmount / 2)) * width; var endIndex = x + Math.min(height - 1, Math.round(y + blurAmount / 2)) * width; totals[0] += (vsum[endIndex][0] - vsum[startIndex][0]) / (endIndex - startIndex) * width; totals[1] += (vsum[endIndex][1] - vsum[startIndex][1]) / (endIndex - startIndex) * width; totals[2] += (vsum[endIndex][2] - vsum[startIndex][2]) / (endIndex - startIndex) * width; totals2[0] += (vsum2[endIndex][0] - vsum2[startIndex][0]) / (endIndex - startIndex) * width; totals2[1] += (vsum2[endIndex][1] - vsum2[startIndex][1]) / (endIndex - startIndex) * width; totals2[2] += (vsum2[endIndex][2] - vsum2[startIndex][2]) / (endIndex - startIndex) * width; hsum[index] = totals.slice(0); hsum2[index] = totals2.slice(0); } } var sd = []; for (var x = 0; x < width; x++) { for (var y = 0; y < height; y++) { var index = x + y * width; var startIndex = Math.max(0, Math.round(x - blurAmount / 2)) + y * width; var endIndex = Math.min(width - 1, Math.round(x + blurAmount / 2)) + y * width; var avgR = (hsum[endIndex][0] - hsum[startIndex][0]) / (endIndex - startIndex); var avgG = (hsum[endIndex][1] - hsum[startIndex][1]) / (endIndex - startIndex); var avgB = (hsum[endIndex][2] - hsum[startIndex][2]) / (endIndex - startIndex); var avgR2 = (hsum2[endIndex][0] - hsum2[startIndex][0]) / (endIndex - startIndex); var avgG2 = (hsum2[endIndex][1] - hsum2[startIndex][1]) / (endIndex - startIndex); var avgB2 = (hsum2[endIndex][2] - hsum2[startIndex][2]) / (endIndex - startIndex); sd[index] = Math.sqrt((avgR2 + avgG2 + avgB2) - (avgR * avgR + avgG * avgG + avgB * avgB)); if (isNaN(sd[index])) { sd[index] = 0; } } } return sd; }, createTextures: function createTextures() { var thisSketcher = this; var width = this.width; var height = this.height; var steps = this.levelSteps; var canvases = this.textureCanvases = []; var imageDatas = this.textureImageDatas = []; var thickness = this.lineThickness; var length = this.lineLength; var darkeningFactor = 1 - this.darkeningFactor; var alpha = this.lineAlpha; var densityFactor = this.lineDensity * 2; var lightness = this.lightness; for (var ri = -1; ri <= steps; ri++) { canvases[ri] = {}; imageDatas[ri] = {}; for (var gi = -1; gi <= steps; gi++) { canvases[ri][gi] = {}; imageDatas[ri][gi] = {}; } } for (var key in this.requiredColours) { var parts = key.split(":"); var ri = parseInt(parts[0]); var gi = parseInt(parts[1]); var bi = parseInt(parts[2]); var red = 255 * ri / (steps - 1); var green = 255 * gi / (steps - 1); var blue = 255 * bi / (steps - 1); var value = (red + green + blue) / 3 / 255; red = Math.min(255, Math.max(0, red)); green = Math.min(255, Math.max(0, green)); blue = Math.min(255, Math.max(0, blue)); var minimum = 1 - Math.min(red, green, blue) / 255; if (minimum > 0) { var scaling = Math.pow(1 / minimum, 1.0 / lightness); var displayRed = Math.round((255 - (255 - red) * scaling) * darkeningFactor); var displayGreen = Math.round((255 - (255 - green) * scaling) * darkeningFactor); var displayBlue = Math.round((255 - (255 - blue) * scaling) * darkeningFactor); var colour = "rgb(" + displayRed + "," + displayGreen + "," + displayBlue + ")"; } else { var displayRed = Math.round(red * darkeningFactor); var displayGreen = Math.round(green * darkeningFactor); var displayBlue = Math.round(blue * darkeningFactor); var colour = "rgb(" + displayRed + "," + displayGreen + "," + displayBlue + ")"; } if (Math.abs(green - blue) > 0.1 || Math.abs(2 * red - green - blue) > 0.1) { var hue = Math.atan2(Math.sqrt(3) * (green - blue), 2 * red - green - blue); var maxRgb = Math.max(255 - red, 255 - green, 255 - blue); var minRgb = Math.min(255 - red, 255 - green, 255 - blue); var saturation = (maxRgb - minRgb) / maxRgb; if (saturation == 0) { hue = Math.random() * Math.PI * 2; } } else { var hue = 0; var saturation = 0; } (function (ri, gi, bi, hue, saturation, thickness, length, minimum, colour, alpha, densityFactor) { thisSketcher.preparationFunctions.push(function () { var angleVariation = Math.PI * (0.1 + 0.9 * Math.pow(1 - saturation, 3)); var canvas = directionalStrokes(width, height, hue / 2 + Math.PI * 0.3, angleVariation, thickness, length, minimum * densityFactor, colour, alpha); canvases[ri][gi][bi] = canvas; imageDatas[ri][gi][bi] = canvas.getContext("2d").getImageData(0, 0, width, height); return [ri, gi, bi].join(":"); }); })(ri, gi, bi, hue, saturation, thickness, length, minimum, colour, alpha, densityFactor); } }, getPixel: function getPixel(pixelIndex, r, g, b) { var imageDatas = this.textureImageDatas; pixelIndex *= 4; var redIndex = r / 255 * (this.levelSteps - 1); var greenIndex = g / 255 * (this.levelSteps - 1); var blueIndex = b / 255 * (this.levelSteps - 1); var redBlend = redIndex; var greenBlend = greenIndex; var blueBlend = blueIndex; redIndex = Math.round(redIndex); greenIndex = Math.round(greenIndex); blueIndex = Math.round(blueIndex); var blendTotal = 0; for (var ri = -1; ri <= 1; ri++) { for (var gi = -1; gi <= 1; gi++) { for (var bi = -1; bi <= 1; bi++) { var blend = (0.75 - Math.abs(redIndex + ri - redBlend) / 2) * (0.75 - Math.abs(greenIndex + gi - greenBlend) / 2) * (0.75 - Math.abs(blueIndex + bi - blueBlend) / 2); if (blend < 0) { throw new Error("debug"); } blendTotal += blend; } } } var red = 0; var green = 0; var blue = 0; for (var ri = -1; ri <= 1; ri++) { for (var gi = -1; gi <= 1; gi++) { for (var bi = -1; bi <= 1; bi++) { var blend = (0.75 - Math.abs(redIndex + ri - redBlend) / 2) * (0.75 - Math.abs(greenIndex + gi - greenBlend) / 2) * (0.75 - Math.abs(blueIndex + bi - blueBlend) / 2); blend /= blendTotal; var imageData = imageDatas[redIndex + ri][greenIndex + gi][blueIndex + bi]; if (imageData == undefined) { throw new Error("debug me!"); } red += imageData.data[pixelIndex] * blend; green += imageData.data[pixelIndex + 1] * blend; blue += imageData.data[pixelIndex + 2] * blend; } } } var brighteningFactor = 1 - (1 - (this.levelSteps + 1) / this.levelSteps) * 0.25; return { red: Math.min(255, Math.round(red * brighteningFactor)), green: Math.min(255, Math.round(green * brighteningFactor)), blue: Math.min(255, Math.round(blue * brighteningFactor)) }; } }; function directionalStrokes(width, height, angle, angleVariation, thickness, length, density, lineStyle, alpha) { var count = density * width * height / length / thickness / alpha; var canvas = module_1.createCanvas(width, height); var context = canvas.getContext("2d"); context.strokeStyle = lineStyle; context.globalAlpha = 1; context.fillStyle = "#FFFFFF"; context.fillRect(0, 0, width, height); context.globalAlpha = alpha; context.lineWidth = thickness; for (var i = 0; i < count; i++) { var lineAngle = angle + Math.round(Math.random() * 2 - 1) / 2 * angleVariation; var midX = Math.random() * width; var midY = Math.random() * height; var deltaX = length / 2 * Math.cos(lineAngle); var deltaY = length / 2 * Math.sin(lineAngle); var startX = midX + deltaX; var endX = midX - deltaX; var startY = midY + deltaY; var endY = midY - deltaY; context.beginPath(); context.moveTo(startX, startY); context.lineTo(endX, endY); context.stroke(); } return canvas; } return Sketcher; })(); exports.Sketcher = Sketcher; exports.default = Sketcher;