Esse site utiliza cookies
Nós armazenamos dados temporariamente para melhorar a sua experiência de navegação e recomendar conteúdo do seu interesse.
Ao utilizar os nossos serviços, você concorda com as nossas políticas de privacidade.
Esse site utiliza cookies
Nós armazenamos dados temporariamente para melhorar a sua experiência de navegação e recomendar conteúdo do seu interesse.
Ao utilizar os nossos serviços, você concorda com as nossas políticas de privacidade.
Postado em 16 fevereiro 2023
Atualizado em 16 fevereiro 2023
Arte de texto ou Ascii art é uma técnica de desenhar imagens usando caracteres. Esse projeto irá converter uma imagem normal para arte de texto usando javascript puro. A conversão é feita em 3 etapas:
Quanto mais escuro for o pixel, menos espaço branco terá o caracter. Esse assunto será discutido mais adiante. O valor inicial usado como referência será a cor do pixel que é representada em RGB.
Esse projeto usa javascript puro como linguagem de programação. Porém, o servidor roda no node.js e o gerenciamento de bibliotecas é feito com o npm. A única biblioteca usada será o http-server, usado para iniciar o servidor.
O projeto usará HTML e CSS no front-end e javascript no back-end. É importante levar em consideração que a fonte utilizada no projeto tenha caracteres do mesmo tamanho para que não haja distorção da imagem de texto.
A imagem carregada no servidor será desenhada no canvas. As informações do canvas serão usadas para desenhar a arte de texto.
Primeiro de tudo, criamos o diretório do projeto e instalamos a biblioteca responsável pelo servidor. Nesse projeto, alguns diretórios e arquivos são criados pelo terminal.
mkdir ascii_art
cd ascii_art
npm install -g http-server
Logo em seguida, criamos o arquivo index.html, arquivo raiz do projeto responsável por exibir o formulário ao usuário.
touch index.html
Após criar o arquivo raiz, criamo a caixa de entrada (input) para o upload do arquivo de imagem e o canvas para exibir a imagem.
<!DOCTYPE html>
<html lang="pt-BR">
<head>
<meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Conversor de Ascii Art</title>
<link rel="stylesheet" href="main.css" />
</head>
<body>
<h1>Conversor de Ascii Art</h1>
<p>
<input id="file" type="file" name="picture" style="width: 100%;" />
</p>
<pre id="asciiImage"></pre>
<canvas id="preview"></canvas>
<script src="main.js"></script>
</body>
</html>
No código acima, também temos um tag “pre”, local onde a arte de texto será extraída. Em seguida, criamos o arquivo CSS com o nome de “main.css”, responsável pelo estilo do HTML acima.
pre {
font-family: 'Courier New', 'monospace';
margin: 1rem auto;
font-size: 1pt;
line-height: 1pt;
letter-spacing: 1pt;
}
* {
margin: 0;
}
body {
padding: 2rem 3rem;
font-family: 'VT323', monospace;
line-height: 3rem;
font-size: 18px;
}
header {
display: flex;
align-items: baseline;
font-size: 18px;
}
A porção mais importante do arquivo CSS acima é o “pre”. Atributos como tipo de fonte, tamanho e espaço entre os caracteres são definidos. Caso esses atributos estejam com um valor desequilibrado definido, a arte de texto sofre deformações.
Criamos um arquivo chamado “main.js” e escrevemos o algoritmo de arte de texto. Primeiro, obtemos a instância dos elementos principais do HTML usando javascript.
let canvas = document.getElementById("preview");
let file = document.getElementById('file');
let asciiImage = document.getElementById('asciiImage');
let context = canvas.getContext("2d");
Os elementos acima se comportarão da seguinte maneira:
Elemento | Papel |
---|---|
canvas | Exibi imagem original |
file | Abriga a instância dos arquivos carregado |
asciiImage | Exibi a arte de texto |
context | Obtenção de pixels e tamanho de imagem |
Em seguida, queremos exibir o conteúdo da imagem carregada no canvas. Usamos a função do javascript “onchange” para detectar mudanças de estado.
file.onchange = function(e) {
// Mesmo selecionando múltiplas imagens, só trabalharemos com a primeira
let file = e.target.files[0];
let reader = new FileReader();
reader.onload = function(event) {
let image = new Image();
image.onload = function() {
// Muda o tamanho do canvas para o mesmo tamanho que a imagem
canvas.width = image.width;
canvas.height = image.height;
// Imagem está em base64 quando carregada
// drawImage desenha a imagem no canvas
context.drawImage(image, 0, 0);
};
// Passa o conteúdo carregado para a imagem e detecta a mudança
image.src = event.target.result;
};
// Finaliza
reader.readAsDataURL(file);
};
Uma vez que o código acima tenha sido implementado, já é possível testar o resultado. Usamos o comando abaixo no diretório do projeto para iniciar o servidor.
http-server
Após carregar a imagem, temos uma simples imagem colorida como mostra o resultado abaixo:
Seguindo adiante, definimos o “ramp”, variável que armazena os caracteres que representam a densidade de cada pixel da imagem. Quanto mais escuro for a imagem, maior será o caracter utilizado. No site de Paul Borke, ele explica um pouco sobre como funciona.
const rampForGray = " .'`^\",:;Il!i><~+_-?][}{1)(|\/tfjrxnuvczXYUJCLQ0OZmwqpdbkhao*#MW&8%B@$";
A variável acima é uma string. Cada pixel da imagem será representa com um dos caracteres disponíveis na string acima. Os pixels brancos (branco é a cor menos densa possível) serão representados com o espaço que fica na extremidade esquerda do valor da variável “rampForGray”. Quanto mais preto for o pixel da imagem, mais denso ele será, usando um caracter mais próxima a extremidade direita da string acima.
Porém, antes de medir a intensidade do cinza, temos que converter a imagem de colorida para cinza. Isso é possível fazendo uma simples conta com o valor RGB de cada pixel. Existem algumas fórmulas que podem ser usadas para mudar a imagem para cinza, duas delas são:
Qualquer uma das duas fórmulas irá funcionar. RGB representa a iniciante das cores vermelho, verde e azul.
// Formula para deixar imagem colorida em cinza
const convertToGray = function(r, g, b) {
return (r + g + b) / 3;
};
const convertImageToGray = function(width, height) {
let imageData = context.getImageData(0, 0, width, height);
for (let i = 0; i < imageData.data.length; i += 4) {
// Obtem o RGB do pixel atual
let r = imageData.data[i];
let g = imageData.data[i + 1];
let b = imageData.data[i + 2];
// Transforma a cor do pixel em cinza
let gray = convertToGray(r, g, b);
imageData.data[i] = gray;
imageData.data[i + 1] = gray;
imageData.data[i + 2] = gray;
}
context.putImageData(imageData, 0, 0);
}
A função acima irá transformar a imagem colorida em cinza. “getImageData” irá obter um array contendo os pixels da imagem. No loop, adicionamos 4 a variável “i”, pois cada pixel ocupa 4 elementos no array.
Após definir a função acima, basta invocarmos a mesma logo após a função “drawImage”, como mostra abaixo:
...
context.drawImage(image, 0, 0);
convertImageToGray(canvas.width, canvas.height)
...
O resultado será uma imagem colorida convertida para uma imagem cinza.
Após a conversão basta converter cada pixel para texto usando a intensidade do cinza como valor referente. A variável “gray” usada dentro da função “convertImageToGray” pode ter um valor de 0 até 255. Quanto mais claro o pixel é mais alto é o valor. Portanto, zero é a cor totalmente preta e o 255 é a cor totalmente branca. Usando esse valor do “gray”, criamos uma faixa (range) entre 0 e o tamanho da string da variável “rampForGray”. O tamanho da variável é obtida com a linha abaixo:
const rampLength = rampForGray.length;
Usando uma função de mapeamento obtida no StackOverflow, temos a seguinte função pronta para o uso:
function mapRange(value, low1, high1, low2, high2) {
return low2 + (high2 - low2) * (value - low1) / (high1 - low1);
}
A função acima será usada para ajustar um range de 0 até 255 para 0 até “rampLength”. Dentro da função “convertImageToGray” adicionamos mais código:
const charIndex = mapRange(gray, 0, 255, 0, rampLength);
// Arredonda o número
const charIndexfloor = Math.floor(charIndex);
// Seleciona o caracter ideal para a intensidade da imagem
const character = rampForGray.charAt(charIndexfloor);
// Adiciona o caracter do pixel na imagem
asciiCharacters += character;
Antes do loop, iniciamos a variável “asciiCharacters” como uma string vazia:
asciiCharacters = "";
Temos que iniciar uma nova linha quando necessário. Isso pode ser feito com uma simples condição ainda dentro do loop usando o atributo “width” da imagem.
if (i % (imageData.width * 4) == 0 && i != 0) {
asciiCharacters += "\n";
}
Multiplicamos o tamanho da imagem por 4, pois são elementos por pixel. Caso a contagem dos elementos de uma linha dividido pelo tamanho da imagem multiplicado por 4 dê um valor zero, temos uma nova linha adicionada ao string.
Finalmente, após o loop acabar, passamos a string para o elemento no HTML.
asciiImage.textContent = asciiCharacters;
Ao carregarmos a imagem veremos que a arte de texto ficará extremamente larga. Isso acontece pelo grande número de pixels por linha. Podemos diminuir a largura da imagem ignorando alguns pixels. Podemos mudar o loop da seguinte maneira:
...
for (let i = 0; i < imageData.data.length; i += (4 * 4)) {
...
Multiplicamos o número de elementos por pixel que é 4, por 4. Isso fará com que usemos apenas 1 pixel a cada 4 pixels, assim os outros 3 pixels são ignorados. Assim, podemos diminuir a largura da arte de texto. O único problema disso, é que a imagem perde informação, perdendo alguns detalhes quando muito complexa.
O resultado final do código pode ser conferido no github. Esse projeto nos faz entender como a estrutura de pixels de imagens funcionam no HTML e javascript. Cada pixel armazenado no array da imagem possui 4 elementos que representam o RGBA da imagem. É possível usar o valor da intensidade das cores para representar cada pixel da imagem usando diversos conjuntos de caracteres.
Postagens mais vistas
Os 5 principais componentes do computador são a unidade de controle, unidade aritmética e lógica, memória, dispositivo de entrada e dispositivo de saída.
A porta é um número de 16 bits que é adicionado no final do endereço IP, insinuando qual aplicativo está vinculado e atuando nessa porta.
Rede local de computadores (LAN) é um conjunto de computadores ou dispositivos conectados uns aos outros de forma isolada em um pequeno local.