JavaScript
语言:
JaveScriptBabelCoffeeScript
确定
var ColorPalette;
(function(ColorPalette) {
ColorPalette["Monochrome"] = "none";
ColorPalette["Grey2Bit"] = "grey2bit";
ColorPalette["Grey4Bit"] = "grey4bit";
ColorPalette["Grey8Bit"] = "grey8bit";
ColorPalette["Color3Bit"] = "color3bit";
ColorPalette["Color4Bit"] = "color4bit";
ColorPalette["ColorFull"] = "color";
})(ColorPalette || (ColorPalette = {}));
var AsciiArtGenerator = /** @class */ (function() {
function AsciiArtGenerator() {
var _this = this;
this.settings = {
charSet: ' !"#$%&\'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_`abcdefghijklmnopqrstuvwxyz{|}~',
url: '/assets/coolgirl.jpg',
charSamples: 1,
size: 50,
contrast: 0,
brightness: 0,
alpha: 0,
ColorPalette: ColorPalette.Grey2Bit,
debug: false,
isDemoRunning: true,
saveAsHtml: function() {
_this.asciiElement.style.setProperty('--width', _this.width.toString());
_this.asciiElement.style.setProperty('--height', _this.height.toString());
var blob = new Blob([_this.asciiElement.outerHTML]);
_this.exportElement.href = URL.createObjectURL(blob);
_this.exportElement.click();
}
};
this.demoDirection = -1;
this.demoSettings = [{
url: '/assets/coolgirl.jpg',
size: 50,
charSamples: 1
}, {
url: '/assets/coolboy.jpg',
size: 60,
charSamples: 1
}];
this.demoIndex = 0;
this.isImageLoaded = false;
this.charRegions = {};
this.colorMap = [];
this.valueMap = [];
this.normalizedMap = [];
this.width = 0;
this.height = 0;
this.cachedUrls = {};
this.colorPalettes = {};
var elements = this.elements;
this.asciiElement = elements.asciiElement;
this.exportElement = elements.exportElement;
this.debugImageElement = elements.debugImageElement;
this.debugCharsElement = elements.debugCharsElement;
this.initGui();
this.generatePalettes();
this.analyzeCharRegions();
this.loadFromUrl();
this.initDemo();
}
AsciiArtGenerator.prototype.initDemo = function() {
var _this = this;
this.demo();
var stopDemo = function() {
_this.settings.isDemoRunning = false;
window.removeEventListener('mousedown', stopDemo);
};
window.addEventListener('mousedown', stopDemo);
};
AsciiArtGenerator.prototype.initGui = function() {
var _this = this;
var gui = new dat.GUI();
gui.add(this.settings, 'charSet').onChange(function() {
_this.analyzeCharRegions();
_this.generate();
});
gui.add(this.settings, 'url').listen().onChange(function() {
return _this.loadFromUrl();
});
gui.add(this.settings, 'charSamples', 1, 3, 1).listen().onChange(function() {
_this.analyzeCharRegions();
_this.loadFromUrl();
});
gui.add(this.settings, 'size', 10, 150, 1).listen().onChange(function() {
return _this.loadFromUrl();
});
gui.add(this.settings, 'contrast', -1, 1, 0.01).onChange(function() {
_this.normalizeValueMap();
_this.generate();
});
gui.add(this.settings, 'brightness', -1, 1, 0.01).listen().onChange(function() {
_this.normalizeValueMap();
_this.generate();
});
gui.add(this.settings, 'alpha', -1, 1, 0.01).onChange(function() {
return _this.generate();
});
gui.add(this.settings, 'ColorPalette', ColorPalette).onChange(function() {
_this.generate();
});
gui.add(this.settings, 'debug').onChange(function() {
_this.analyzeCharRegions();
_this.loadFromUrl();
});
gui.add(this.settings, 'isDemoRunning').listen().onChange(function() {
if (_this.settings.isDemoRunning) {
_this.demo();
}
});
gui.add(this.settings, 'saveAsHtml');
};
Object.defineProperty(AsciiArtGenerator.prototype, "elements", {
get: function() {
var asciiElement = document.getElementById('ascii');
if (!asciiElement)
throw '#ascii Element is missing';
var exportElement = document.getElementById('export');
if (!exportElement)
throw '#export Element is missing';
var debugImageElement = document.getElementById('debug-image');
if (!debugImageElement)
throw '#debug-image Element is missing';
var debugCharsElement = document.getElementById('debug-chars');
if (!debugCharsElement)
throw '#debug-chars Element is missing';
return {
asciiElement: asciiElement,
exportElement: exportElement,
debugImageElement: debugImageElement,
debugCharsElement: debugCharsElement
};
},
enumerable: true,
configurable: true
});
AsciiArtGenerator.prototype.generatePalettes = function() {
this.colorPalettes[ColorPalette.Grey2Bit] = [
[0, 0, 0],
[104, 104, 104],
[184, 184, 184],
[255, 255, 255],
];
this.colorPalettes[ColorPalette.Grey4Bit] = [];
for (var i = 0; i < 16; i += 1) {
this.colorPalettes[ColorPalette.Grey4Bit].push([i * 17, i * 17, i * 17]);
}
this.colorPalettes[ColorPalette.Grey8Bit] = [];
for (var i = 0; i < 256; i += 1) {
this.colorPalettes[ColorPalette.Grey8Bit].push([i, i, i]);
}
this.colorPalettes[ColorPalette.Color3Bit] = [
[0, 0, 0],
[0, 249, 45],
[0, 252, 254],
[255, 48, 21],
[255, 62, 253],
[254, 253, 52],
[16, 37, 251],
[255, 255, 255],
];
this.colorPalettes[ColorPalette.Color4Bit] = this.colorPalettes[ColorPalette.Color3Bit].slice();
for (var i = 1; i < 8; i += 1) {
this.colorPalettes[ColorPalette.Color4Bit].push([i * 32, i * 32, i * 32]);
}
};
AsciiArtGenerator.prototype.analyzeChar = function(char) {
var canvas = document.createElement('canvas');
canvas.width = 12;
canvas.height = 12;
var ctx = canvas.getContext('2d');
if (!ctx)
throw 'context creation failed';
ctx.font = '12px monospace';
ctx.fillText(char, 2, 10);
var data = ctx.getImageData(0, 0, 12, 12).data;
var values = [];
var size = 12 / this.settings.charSamples;
for (var cellY = 0; cellY < this.settings.charSamples; cellY += 1) {
for (var cellX = 0; cellX < this.settings.charSamples; cellX += 1) {
var value = 0;
for (var posY = 0; posY < size; posY += 1) {
for (var posX = 0; posX < size; posX += 1) {
value += data[(cellX * size + posX + (cellY * size + posY) * 12) * 4 + 3];
}
}
values.push(value / (size * size) / 255);
}
}
if (this.settings.debug) {
this.debugCharsElement.appendChild(canvas);
for (var cellX = 0; cellX < this.settings.charSamples; cellX += 1) {
for (var cellY = 0; cellY < this.settings.charSamples; cellY += 1) {
ctx.fillStyle = "rgba(255, 0, 255, " + values[cellX + cellY * this.settings.charSamples] + ")";
ctx.fillRect(cellX * size, cellY * size, size, size);
}
}
console.log({
char: char,
values: values
});
}
this.charRegions[char] = values;
};
AsciiArtGenerator.prototype.normalizeCharRegions = function() {
var min = 1;
var max = 0;
for (var char in this.charRegions) {
// let value = 0;
for (var _i = 0, _a = this.charRegions[char]; _i < _a.length; _i++) {
var region = _a[_i];
if (min > region)
min = region;
if (max < region)
max = region;
}
// value /= this.settings.charSamples * this.settings.charSamples;
// if (min > value) min = value;
// if (max < value) max = value;
}
if (max > 0 && min != max) {
var diff = max - min;
for (var char in this.charRegions) {
var regions = this.charRegions[char];
for (var index = 0; index < regions.length; index += 1) {
regions[index] = (regions[index] - min) * (1 / diff);
}
}
}
if (this.settings.debug) {
console.log({
min: min,
max: max,
charRegions: this.charRegions
});
}
};
AsciiArtGenerator.prototype.analyzeCharRegions = function() {
this.clearElement(this.debugCharsElement);
this.charRegions = {};
for (var _i = 0, _a = this.settings.charSet; _i < _a.length; _i++) {
var char = _a[_i];
this.analyzeChar(char);
}
this.normalizeCharRegions();
};
AsciiArtGenerator.prototype.loadFromUrl = function() {
var _this = this;
this.isImageLoaded = false;
if (this.cachedUrls[this.settings.url]) {
this.onImageLoaded(this.cachedUrls[this.settings.url]);
} else {
var img_1 = document.createElement('img');
img_1.crossOrigin = 'Anonymous';
img_1.src = this.settings.url;
img_1.addEventListener('load', function() {
_this.cachedUrls[_this.settings.url] = img_1;
_this.onImageLoaded(img_1);
});
img_1.addEventListener('error', function() {
var urls = Object.keys(_this.cachedUrls);
if (urls.length > 0) {
_this.onImageLoaded(_this.cachedUrls[urls[urls.length - 1]]);
}
});
}
};
AsciiArtGenerator.prototype.onImageLoaded = function(img) {
this.width = this.settings.size;
this.height = ~~((img.height / img.width) * this.width / 1.9);
var canvas = document.createElement('canvas');
canvas.width = this.width * this.settings.charSamples;
canvas.height = this.height * this.settings.charSamples;
var ctx = canvas.getContext('2d');
if (!ctx)
throw 'context creation failed';
ctx.drawImage(img, 0, 0, this.width * this.settings.charSamples, this.height * this.settings.charSamples);
this.clearElement(this.debugImageElement);
if (this.settings.debug) {
this.debugImageElement.appendChild(canvas);
console.log({
img: img,
width: this.width,
height: this.height
});
}
document.body.style.setProperty('--width', this.width.toString());
document.body.style.setProperty('--height', this.height.toString());
this.generateValueMap(ctx);
this.isImageLoaded = true;
};
AsciiArtGenerator.prototype.generateValueMap = function(ctx) {
this.valueMap = [];
this.colorMap = [];
var data = Array.from(ctx.getImageData(0, 0, this.width * this.settings.charSamples, this.height * this.settings.charSamples).data);
var rowLength = this.width * this.settings.charSamples * 4;
for (var cellY = 0; cellY < this.height; cellY += 1) {
for (var cellX = 0; cellX < this.width; cellX += 1) {
var cell = [];
var pos = (cellX * this.settings.charSamples) * 4 + (cellY * this.settings.charSamples) * rowLength;
this.colorMap.push(data.slice(pos, pos + 4));
for (var posY = 0; posY < this.settings.charSamples; posY += 1) {
for (var posX = 0; posX < this.settings.charSamples; posX += 1) {
var pos_1 = (cellX * this.settings.charSamples + posX) * 4 + (cellY * this.settings.charSamples + posY) * rowLength;
var alpha = data[pos_1 + 3] / 255;
var values = data.slice(pos_1, pos_1 + 3);
var value = 1 - ((values[0] + values[1] + values[2]) / 765 * (alpha) + 1 - alpha);
if (this.settings.debug) {
ctx.fillStyle = "rgba(255, 0, 255, " + value + ")";
ctx.fillRect(cellX * this.settings.charSamples + posX, cellY * this.settings.charSamples + posY, 1, 1);
}
cell.push(value);
}
}
this.valueMap.push(cell);
}
}
if (this.settings.debug) {
console.log({
valueMap: this.valueMap,
colorMap: this.colorMap
});
}
this.normalizeValueMap();
this.generate();
};
AsciiArtGenerator.prototype.normalizeValueMap = function() {
var min = 1;
var max = 0;
for (var _i = 0, _a = this.valueMap; _i < _a.length; _i++) {
var regions = _a[_i];
// const value = 0;
for (var _b = 0, regions_1 = regions; _b < regions_1.length; _b++) {
var region = regions_1[_b];
if (min > region)
min = region;
if (max < region)
max = region;
}
}
if (max > 0 && min != max) {
var diff = max - min;
this.normalizedMap = [];
for (var _c = 0, _d = this.valueMap; _c < _d.length; _c++) {
var regions = _d[_c];
var normals = Array.from(regions);
for (var index = 0; index < normals.length; index += 1) {
normals[index] = (normals[index] - min) * (1 / diff);
normals[index] = (this.settings.contrast + 1) * (normals[index] - 0.5) + 0.5 + this.settings.brightness;
}
this.normalizedMap.push(normals);
}
} else {
this.normalizedMap = this.valueMap;
}
if (this.settings.debug) {
console.log({
min: min,
max: max,
valueMap: this.valueMap
});
}
};
AsciiArtGenerator.prototype.getClosestChar = function(values) {
var minDiff = Number.MAX_VALUE;
var minChar = '';
for (var char in this.charRegions) {
var regions = this.charRegions[char];
var diff = 0;
for (var index = 0; index < regions.length; index++) {
diff += Math.abs(regions[index] - values[index]);
}
if (diff < minDiff) {
minDiff = diff;
minChar = char;
}
}
return minChar;
};
AsciiArtGenerator.prototype.clearElement = function(element) {
while (element.firstChild) {
element.removeChild(element.firstChild);
}
};
AsciiArtGenerator.prototype.arrayToRgba = function(color) {
var r = color[3] > 0 ? ~~color[0] : 255;
var g = color[3] > 0 ? ~~color[1] : 255;
var b = color[3] > 0 ? ~~color[2] : 255;
var a = Math.max(0, Math.min(1, color[3] / 255 + this.settings.alpha));
return "rgba(" + r + "," + g + "," + b + "," + a + ")";
};
AsciiArtGenerator.prototype.getCharColor = function(color) {
if (this.settings.ColorPalette === ColorPalette.ColorFull) {
return this.arrayToRgba(color);
} else {
var closestColor = [0, 0, 0];
var minDiff = Number.MAX_VALUE;
for (var _i = 0, _a = this.colorPalettes[this.settings.ColorPalette]; _i < _a.length; _i++) {
var paletteColor = _a[_i];
var diff = Math.abs(color[0] - paletteColor[0]) + Math.abs(color[1] - paletteColor[1]) + Math.abs(color[2] - paletteColor[2]);
if (diff < minDiff) {
minDiff = diff;
closestColor = paletteColor;
}
}
return this.arrayToRgba(closestColor.concat([color[3]]));
}
};
AsciiArtGenerator.prototype.generate = function() {
this.clearElement(this.asciiElement);
for (var cellY = 0; cellY < this.height; cellY += 1) {
for (var cellX = 0; cellX < this.width; cellX += 1) {
var cell = document.createElement('div');
if (this.settings.ColorPalette !== ColorPalette.Monochrome) {
cell.style.color = this.getCharColor(this.colorMap[cellX + cellY * this.width]);
}
cell.innerHTML = this.getClosestChar(this.normalizedMap[cellX + cellY * this.width]).replace(' ', ' ');
this.asciiElement.appendChild(cell);
}
}
};
AsciiArtGenerator.prototype.demo = function() {
var _this = this;
if (this.settings.isDemoRunning) {
if (this.isImageLoaded) {
this.settings.brightness += 0.05 * this.demoDirection;
this.normalizeValueMap();
this.generate();
if (this.settings.brightness >= 0.5 || this.settings.brightness <= -1) {
this.demoDirection *= -1;
}
if (this.settings.brightness <= -1) {
this.demoIndex = (this.demoIndex + 1) % this.demoSettings.length;
this.settings.url = this.demoSettings[this.demoIndex].url;
this.settings.size = this.demoSettings[this.demoIndex].size;
this.settings.charSamples = this.demoSettings[this.demoIndex].charSamples;
this.analyzeCharRegions();
this.loadFromUrl();
}
}
requestAnimationFrame(function() {
return _this.demo();
});
}
};
return AsciiArtGenerator;
}());
var generator = new AsciiArtGenerator();
console.log(generator);