Hướng dẫn ascii to html converter

announcement check out our new project!

We just created something new for all science fans – SCIURLS – a neat science news aggragator. Check it out!

Pro tips Master online ascii tools

You can pass input to this tool via

?input

query argument and it will automatically compute output. Here's how to type it in your browser's address bar. Click to try!

https://onlineasciitools.com/convert-ascii-to-html-entities

?input

=It%27s%20all%20entities%2C%20dude%21&hex=true

Coming soon These ascii tools are on the way

ASCII Art Editor

Draw ASCII art in a neat browser-based editor.

ANSI Art Editor

Draw ANSI art in a neat browser-based editor.

Show NFO Files

Draw the contents of NFO files.

Convert ASCII to JPG

Create a JPG image from ASCII art.

Convert JPG to ASCII

Create ASCII art from a JPG image.

Convert ASCII to PNG

Create a PNG image from ASCII art.

Convert PNG to ASCII

Create ASCII art from a PNG image.

Convert ASCII to GIF

Create a GIF image (single frame) from ASCII art.

Convert GIF to ASCII

Create ASCII art from a GIF image.

Create an ASCII Animation

Create an animated GIF from multiple ASCII art frames.

Convert ASCII to Chemical Elements

Use chemical element names for ASCII values.

Convert ASCII to Scan Codes

Convert ASCII characters to scan codes.

Convert Scan Codes to ASCII

Convert scan codes to ASCII characters.

Convert ASCII to BCD

Convert ASCII code to BCD code.

Convert BCD to ASCII

Convert BCD code to ASCII code.

Convert ASCII to EBCDIC

Convert ASCII charset to EBCDIC charset.

Convert EBCDIC to ASCII

Convert EBCDIC charset to ASCII charset.

Convert ASCII to PETSCII

Convert ASCII data to PETSCII (CBM ASCII) data.

Convert PETSCII to ASCII

Convert PETSCII (CBM ASCII) text to ASCII text.

Convert ASCII to ATASCII

Convert ASCII data to ATASCII (ATARI ASCII) data.

Convert ATASCII to ASCII

Convert ATASCII (ATARI ASCII) data to ASCII data.

Rotate ASCII Characters

Rotate ASCII chars to the left or to the right.

Truncate ASCII Data

Make the input ASCII data shorter.

Slice ASCII Data

Extract a slice from ASCII data.

Clamp ASCII Data

Set upper and lower ASCII code bounds.

Randomize ASCII Order

Randomize the order of ASCII characters.

Reverse ASCII Characters

Reverse the order of ASCII chars.

Convert ASCII to Integer

Convert ASCII characters to integers.

Convert Integer to ASCII

Convert integers to ASCII values.

Convert ASCII to Keycodes

Convert ASCII characters to keyboard keycodes.

Convert Keycodes to ASCII

Convert keyboard keycodes to ASCII values.

Find ASCII Sum

Calculate the sum of individual ASCII code points.

Find ASCII Difference

Calculate the difference of individual ASCII code points.

Find ASCII Length

Calculate the length of an ASCII string.

Create a Table

Draw a table using ASCII characters as border elements.

Draw ASCII Tree

Draw a folder structure or a binary graph.

Draw ASCII Waves

Generate digital waveforms with ASCII characters.

Draw PETSCII Table

Draw a PETSCII (CBM ASCII) character set table.

Draw ATASCII Table

Draw an ATASCII (ATARI ASCII) character set table.

Draw an ASCII Spiral

Make all ASCII symbols go in a spiral.

Draw an ASCII Art Smiley

Draw various ASCII art smiley faces.

Debug ASCII Data

Visualize ASCII data at the byte and bit level.

Let Zalgo Damage ASCII

Unleash Zalgo on ASCII data.

Dạo gần đây tự nhiên rộ lên cái trend convert ảnh sang ASCII art (ảnh dưới dạng các ký tự ASCII). Sau khi tìm hiểu thì mình thấy cách phổ biến nhất mà mọi người thường làm là lên google search "convert image to ASCII art" rồi upload ảnh lên, sau đó copy các ký tự ASCII về rồi in ra hoặc dùng một lib nào đó. Những cách đó cũng được thôi, nhưng bạn sẽ không thể custom cũng như là không hiểu các tool đó họ làm như thế nào.

Bạn nào nôn nóng có thể xem trước demo hoặc Github Repo

Trong bài viết này mình sẽ hướng dẫn các bạn convert từ ảnh sang ASCII art chỉ bằng javascript và không dùng bất kỳ thư viện nào cả. Ok bắt đầu nhé.

Có một bài post thú vị các bạn có thể xem How do ASCII art image conversion algorithms work?

Chuyển sang ASCII art cơ bản có 2 bước:

  1. Chuyển ảnh màu sang ảnh trắng đen (gray colors)

  2. Map từng pixel sang các ký tự dựa trên giá trị grayscale

Ví dụ, @ thì tối màu hơn +, cũng tối màu hơn ..

Trong bài viết này, mình sẽ triển khai thuật toán này bằng JS thuần và chạy trên trình duyệt.

Upload ảnh sang Canvas

Hướng dẫn ascii to html converter
Niiya Mayu cho bác nào muốn hỏi

Bước đầu tiên chúng ta cần cho phép người dùng upload ảnh, vì thế ta cần một input file. Sau đó mình sẽ phân tích ảnh sang từng pixel, mình sẽ dùng một canvas. Dưới đây là HTML mà mình sử dụng.

html

<!DOCTYPE html>

<html lang="en">

<head>

<meta charset="UTF-8" />

<meta name="viewport" content="width=device-width, initial-scale=1.0" />

<meta http-equiv="X-UA-Compatible" content="ie=edge" />

<title>Ascii Art Converter</title>

</head>

<body>

<h2>Ascii Art Converter</h2>

<input type="file" name="picture" />

<canvas id="preview"></canvas>

</body>

</html>

Tại bước này, mình có thể upload một ảnh lên input nhưng chưa có gì xảy ra cả. Đó là bởi vì mình cần chuyển file ảnh đó sang canvas element. Mình sẽ sử dụng FileReader API:

js

const canvas = document.getElementById('preview')

const fileInput = document.querySelector('input[type="file"]')

const context = canvas.getContext('2d')

fileInput.onchange = e => {

const file = e.target.files[0]

const reader = new FileReader()

reader.onload = event => {

const image = new Image()

image.onload = () => {

canvas.width = image.width

canvas.height = image.height

context.drawImage(image, 0, 0)

}

image.src = event.target.result

}

reader.readAsDataURL(file)

}

Khi input thay đổi, mình sẽ tạo một FileReader object mới, điều này sẽ giúp mình đọc file và chuyển vào canvas. Mình đã đặt size canvas bằng với size ảnh upload để tránh bị giảm chất lượng. 2 tham số cuối của drawImage xác định nơi đặt ảnh trong canvas. Trong trường hợp này, mình muốn vẽ một bức 'ảnh' từ vị trí góc trên cùng bên trái. (tọa độ [0,0]).

Một khi mình đã nhúng đoạn script này vào trang HTML, mình có thể upload một bức ảnh bất kì và nó sẽ hiển thị trong canvas element.

Hướng dẫn ascii to html converter

Chuyển ảnh sang ảnh trắng đen Gray Colors

Bây giờ bức ảnh đã được upload, mình cần chuyển nó sang ảnh trắng đen. Màu của mỗi pixel được tạo từ 3 màu cơ bản: red, green, blue như trong giá trị hexadicimal color css (#RRGGBB).

Một cách đơn giản để tính được thang màu xám tương ứng là lấy trung bình 3 giá trị này. Tuy nhiên, mắt con người chúng ta thì không nhạy cảm như nhau với 3 màu này. Ví dụ, mắt chúng ta rất nhạy cảm với màu green, trong khi blue thì sẽ hời hợt 1 chút. Do đó, chúng ta cần cân nhắc sử dụng các màu với các tỉ lệ khác nhau. Sau khi nghiên cứu chi tiết Grayscale Wikipedia Page, mình quyết định tính giá trị grayscale dựa trên công thức dưới đây:

bash

GrayScale = 0.21 R + 0.72 G + 0.07 B

Vì thế mình cần lặp qua mỗi pixel của bức ảnh và thay thế nó bằng giá trị grayscale. canvas API cung cấp function getImageData để chúng ta phân tách từng pixel của bức ảnh.

js

const toGrayScale = (r, g, b) => 0.21 * r + 0.72 * g + 0.07 * b

const convertToGrayScales = (context, width, height) => {

const imageData = context.getImageData(0, 0, width, height)

const grayScales = []

for (let i = 0; i < imageData.data.length; i += 4) {

const r = imageData.data[i]

const g = imageData.data[i + 1]

const b = imageData.data[i + 2]

const grayScale = toGrayScale(r, g, b)

imageData.data[i] = imageData.data[i + 1] = imageData.data[i + 2] = grayScale

grayScales.push(grayScale)

}

context.putImageData(imageData, 0, 0)

return grayScales

}

getImageData sẽ cho ra một object chứa thuộc tính data là một Uint8ClampedArray. Data này là một mảng một chiều chứa các giá trị red, green, blue, alpha (RGBA) theo thứ tự, với các số nguyên có giá trị từ 0 - 255. Vì thế mình lặp qua mảng bằng cách tăng lên 4 mỗi lần lặp, lấy giá trị RGB từ 3 phần tử đầu tiên, tính toán tỉ lệ gray và sau đó tiếp tục cho đến hết.

Lưu ý là RGBA khác RGBa nha. Với RGBA thì A là alpha đại diện cho độ trong suốt có giá trị là số nguyên từ 0 - 255. Còn RGBa được sử dụng phổ biến trong CSS với a cũng là alpha nhưng giá trị là số thập phân từ 0 - 1. Nghe có vẻ RGBa có thể biểu thị được alpha nhiều hơn vì không giới hạn về mặt lý thuyết nhưng điều này về mặt phần cứng thì không phải vậy. Có sự khác biệt nào giữa alpha 0.5000000.500001? Mình nghĩ là không. Mình nghĩ rằng từ 0.0 - 1.0 thì CSS sẽ chuyển sang 0 - 255 dựa trên giá trị nào gần hơn.

Trong đoạn code này, mình đã thay đổi image data gốc. Nghĩa là function convertToGrayScales() không được thuần khiết (impure).

Sau đó, mình chỉ cần gọi convertToGrayScales tại dòng cuối cùng của image.onload listener. Và bây giờ ảnh được upload sẽ chuyển sang màu xám.

Map các Pixel sang các ký tự tương ứng

Để hiển thị ảnh sử dụng chuỗi các ký tự ASCII, bước tiếp theo chúng ta cần làm là chọn mỗi ký tự cho mỗi pixel ảnh. Một số ký tự thì tối màu hơn số còn lại. Ví dụ @ thì tối hơn ., bởi vì nó chiếm nhiều không gian màn hình hơn. Chuỗi ký tự dưới đây thường được dùng để biểu diễn các cấp độ của màu xám.

69 cấp độ màu xám

sh

grayRamp1 = "$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/\|()1{}[]?-_+~<>i!lI;:,\"^`'. "

hoặc 10 cấp độ màu xám

sh

grayRamp2 = "@%#*+=-:. "

Càng nhiều cấp độ màu thì ASCII art của chúng ta sẽ hiển thị được nhiều sắc thái màu hơn (không liên quan độ nét nha).

Map một giá trị gray scale với ký tự tương đương.

js

const grayRamp = '$@B%8&WM#*oahkbdpqwmZO0QLCJUYXzcvunxrjft/|()1{}[]?-_+~<>i!lI;:,"^`\'. '

const rampLength = grayRamp.length

const getCharacterForGrayScale = grayScale => grayRamp[Math.ceil(((rampLength - 1) * grayScale) / 255)]

Bây giờ chúng ta cùng chuyển ảnh sang các ký tự nào:

js

const asciiImage = document.querySelector('pre#ascii')

const drawAscii = grayScales => {

const ascii = grayScales.reduce((asciiImage, grayScale) => {

return asciiImage + getCharacterForGrayScale(grayScale)

}, '')

asciiImage.textContent = ascii

}

Mình dùng thẻ <pre> để giữ đúng tỉ lệ của bức ảnh kết hợp với đó là dùng monospaced font.

Gọi hàm drawAscii ở cuối cùng của image.onload callback, mình nhận được kết quả như dưới đây:

Hướng dẫn ascii to html converter

Thoạt nhìn, nó có vẻ không hoạt động. Nhưng nếu bạn scroll ngang, bạn để ý thấy một số chuỗi xuất hiện trên màng hình. Bức ảnh của chúng ta có vẻ hiển thị trên 1 dòng. Thực sự thì tất cả các pixel được biểu diễn trên 1 mảng 1 chiều. Vậy nên mình cần cho nó xuống dòng dựa trên giá trị chiều rộng pixel bức ảnh width.

js

const drawAscii = (grayScales, width) => {

const ascii = grayScales.reduce((asciiImage, grayScale, index) => {

let nextChars = getCharacterForGrayScale(grayScale)

if ((index + 1) % width === 0) {

nextChars += '\n'

}

return asciiImage + nextChars

}, '')

asciiImage.textContent = ascii

}

Kết quả bây giờ tốt hơn nhiều rồi, ngoại trừ việc nó quá chi tiết (tức là độ phân giải quá cao)

Hướng dẫn ascii to html converter

Các ký tự ASCII đại diện cho ảnh thực sự khổng lồ. Mình đã map mỗi pixel cho một ký tự. Thử vẽ một bức tranh nhỏ với 10x10 pixel sẽ tốn 10 dòng với 10 ký tự. Chúng quá lớn.

Mình có thể giữ bức tranh bằng ký tự khổng lồ này và giảm font-size lại cho bằng bức ảnh đã được upload. Nhưng mà điều này không tối ưu lắm, nó làm trình duyệt render hơi khá lâu và nếu như bạn chia sẽ ảnh ASCII này cho người khác bằng email thì có thể bị giới hạn ký tự.

Làm giảm độ phân giải ảnh ASCII

Phương thức drawImage của canvas còn nhận vào 2 tham số nữa đó là output width và height. Vì thế chúng ta có thể điều chỉnh lại kích thước ảnh đầu ra. Mình không rõ tiến trình xử lý ảnh phía dưới nó xử lý như thế nào để giảm độ phân giải nhưng mình đoán là nó là subsampling process.

Ok, cùng giảm độ phân giải ảnh nào.

js

const MAXIMUM_WIDTH = 80

const MAXIMUM_HEIGHT = 80

const clampDimensions = (width, height) => {

if (height > MAXIMUM_HEIGHT) {

const reducedWidth = Math.floor((width * MAXIMUM_HEIGHT) / height)

return [reducedWidth, MAXIMUM_HEIGHT]

}

if (width > MAXIMUM_WIDTH) {

const reducedHeight = Math.floor((height * MAXIMUM_WIDTH) / width)

return [MAXIMUM_WIDTH, reducedHeight]

}

return [width, height]

}

Mình tập trung vào chiều cao trước tiên. Thực sự để một bức ảnh được đánh giá cao thì người dùng không cần phải cuộn thanh scrollbar. Cũng lưu ý là mình giữ nguyên tỉ lệ ảnh để tránh ảnh bị biến dạng.

Bây giờ mình có thể cập nhật image.onload để sử dụng giá trị widthheight mới.

js

image.onload = () => {

const [width, height] = clampDimensions(image.width, image.height)

canvas.width = width

canvas.height = height

context.drawImage(image, 0, 0, width, height)

const grayScales = convertToGrayScales(context, width, height)

drawAscii(grayScales, width)

}

Bây giờ, mình upload bức ảnh cô nàng hot girl của mình, và đây là kết quả:

text

JvxZZcUbaZYQ0pUvxx|rv{+_?<>~-|(UCp0wwpkkk0XOvJC0wbhkdZCQZZpk#W8

Qu[|Z(_ChahmXpLXJuzU]/?[>>?[]/uUCCpdddbhkpcCLmZUZwqwmZpqZ0Obo#W

OZc)Or!1ObaqXCCQL0puYn1{?~}1(|cUQ0doaahkbbmUL0LYZwwqphkq0LZdhoo

CLQuCviitmaqvcmpZmOXqXn[(1[|rjc0Odqk*ahbbhkCzYYfXOdk*#qZCJmbdZm

zObmqJ]?[{xmmmbh0oqhZxzxt1xxjczJLOdko#hhaahqUnvXCZdk*#dbqZmb0QO

rQoooL({+-/CpmkbkkwaqZuxj{YcuUULZmpkhMMaaaopJYcUmppwkomOwZwphao

UQwkaqJLXuzXQd*hhkhoppCnr|JUYQJJqmdohW&#haahmCZZdbmZbhZ0wmpk#&8

LQ0qd0(cZXtfuk*kododbq0rntJCXC0Qqqqbk#MW#haoqCpbh#hwk*bpdqpb#W8

ZQuCmZvJpwQ0O#k##aMdbmQvn)XLr00JOdZoo*M&WaaohpaaaMaqaopqq0OpoM%

cjtjUZwZmpZvO&ah#*#hkwOrz{L0jZLmwkwh**M&8#oo*ko###dmkd0LQLCOko&

/1cnQOLOpkwtmM#o*WMoawwXJ[Zdvzpdpddk*oM&8Wh#*akbbkOZpZXvYJZZqh#

jjLp0CObaOZwo*#Ma&MahqqYL]mkvvqOpbwhao*8%&##oamOOOJOZOYXQZZL0b*

rt|fxYZkkmpk#*oWh8#oamqQQ{rkcnXqdhhhaoWM8%M*MobJYYYUcczCmZCQqba

Y|}{cwqpdbhd#W##*MM#*mqOQ})kL{r0kbohhkoM8B%#**mUvvuxYJLOZO0pbdk

Q()CqL0pbhhb#W##&8&M*qq0Q}]Uo{}Um#aph#*o#%%M*MqXzu/rCJUCL0Zbhbb

QcXk0nYwpwdhWW*&8%&M*qqUQ1?va|[(Ckapa*MMM8B8M*awLzJCJcvJ0L0qkkk

kbaar(cQmQmbWW##%B8MkZQxL{[rkX[[1QddwmqdhMB&W#hqXx00UYXUJULZphk

M&&0)tJZppZbWWMM%%&#dqmLpYcuZk|?)nzJObqCmo8%MWabpZQJXXYUQQLLmkb

#W#QzZr)uQZbMWMWB8&opmmQ0UXczZ1[|rXh*qOqJQ#B8&*#omLUuYYJmZLObah

ookdOQt]1JCZ#&W&B%&mcrrXZqJcx/[[(xzZwcnJxnbB%&W#oputvUzzUQ0mdkh

adOmUX(i-CcLkWMWB%MCnrYOqoYzx/]_1fxx/)(1}(mB%&&akbLjjXcuvYY0kod

aakqv{>;-uunp&WWBB#XtrLYuXr|t)->-}1(1}?~~]LBB&8akbdUxvvvcXJma#q

khk0t~><+[]-ua&&BBMz1[(rt)1({]+;l~___<il>]CBB88apZOnjuvuuzXLbhh

bbpc??[))-_?tkWW%B&J}__?]]--_+>,"!>>>lII>}O@B8&*qUCn|nvnnvzUqdd

dJvJ1]{1}_~_}JoM%@%m{~>>>>>><<i,^I><iIIl<)k@@%8#bJrttjvnnuzU0wO

mn1Yt-|[]->+[X**%BBWt+>i!l!l>+i"`:<_<i!i+t#@B8M#bqYrvtvcvzJCLmq

Jc/}|??()?-}|1O*8%B%L[~>!lli+_i^'^l~+~<~]uW@@88Mddm0YzXczJOZZZw

O0U[[]?jcj{1txm*W%%%oj?~>ii<__<:"I~i!<~-1Q%BB8%MdwCYYccvzQmOOmk

U00r]?]c00CJLCmoM88%801-~<<~~[}}_?)iIi~]/p%B%&%WhqOQQJcjxJwwZZb

|)}}_-]/fXuLOUpMo&888kr}-+~>!_|f/1+Il+-}z8B@%&8&M*apZZZYrUOqwwZ

<!;I~}){-/j0bbk#oW8888O(?_<>!l+{)1{{)f|fp%B@%&8&#hbpOZqbOOmZZmZ

!I!+1/|}/YLqqhd*#M&8&8MY1-+_--fzuxzj_~10WBBB%W%&WkkbmZwbdwZwq0m

[}1{++1]cpbwUc0o#M&&88&WJ(]1rzvvvfI"`^;r*%@%&M8WMMhhbddbppqkkpd

-f/_ll!_xLmLvfmao#M8W8888Zxfffnc(>i!>iII]aB8W#8WM&ahbdpppbdkhbb

?x?I~{1|})xuUnOao#M&W&888%bJnrx)?<!<_(~!I(*8&W&M###kdddZOphhpwp

1}l,+(Xc?1XLn{Ch*M#WWWW88%8hQvjj_:^^"!I;!l{*M*M##**kk*#bZh**k0m

]i;<-?)))YZx+?Qha#a#8888&%%#kwU]:Il:"^`^^":_a*#oh#appa#a#ba#bZk

<I+)]+--[x(_-1Qah###&888888Mhq/l>}t]~>I^``^;|baabdUXunnvLzLpbdk

_~-11ttjct}1(|OaaWM#W8%8&88Maw>+{_?<!l;^'`^,>JqbJ|}>^..I++(Zdmw

1{1numkzrf/n|_Xa*M*W#&&888&Mop_|+:^`'''...'""!Cw|v;' ',<zm

(fuCZhar)/j(~1QaMM#&#8&88W&#k(]_:I!;,,"^'.'`^":xul^. .f

{}rLmwQj(}!:^~Zo&M#W#W&&8&#hw)[<!~l`'. .``^^;-:`'

??{/rj(}!^. `1Zo&M#M#&&8&W*mJv/i_:,lI:^'..'`'``^,"`.

__++]]1, ^<Jph&##MM&8&W##Jnx/-!I+[[?+>:``' .''``'.

_+-?}+` '>{Qh**W##MW&WM*oYf(t]-]jcvuj{_!,`'...''`. .

?----, '`I?/QhM**#WWW&W##aU1{t??(cXzvr/)?i:"`..''''. . ...

_+~+>. `,~[upk##*W&&W&W#opx[]))|juczzvr/)?>:"^''.'''. . ....'

_++{I '`i[rckh*a*&&8&8Mkah|]-})(/fxczXznf1]>I:"`...''.........

]]]]. '^:_(vmhhooWMW88WMhhJ}~+-}1(/fnvzXXcr1?~!I,`...`''''''..'

(){}..^,+[jzpao*MM&&WWM#kwx?>>~]1)|/rucYYXcr1?_>l,'.'^^^^^`''''

(((,.'":_(nwdhao#MW8W#MkpU|_!!<-[)1)|rvYJUXvj{]_~l"'.`^,:;,```'

[{(`'"ll]j0qbka##WW&WMadUt?illi+]][[)/xzYUYznj)]_i;^..':!<l^``'

1))`'""!{cwkddaM#WWW*hqLr?!IIl!<_??]})tnvzXzvxu]]~l:^^"Ii}<"``'

)1{``,,}tUqddko*WMWMkmUf?l:::;I!~+_-]})/frnuuxx+]_>!IIl>!1!^'''

|1[^'::<|Cqmqbo*WM#hwjn]i,"^",I!<~+_-]}{1|/jrrr_??<!lI!ill,'..'

(1>`'^:>jObdmbk#*Mapn+_!,``^^";!<+++-?]]][{)|/}[]?>l;;Ill;^. .

{1l`'`I<uLdqdbh*MopC}i""'''`^":I>+_-?-_+++-][[~_?]iI:;;IlI^.

)(l```<[nJmppdk#akQr<^``..''`^,;!+-?]_~>ii<~~!Ii-]>I::;II;".

))l``^,?rOJqdmkhdwY|> '....'`^:l~-]_<!lll!!,:;l~[>l::;IIIl. .

({I``,,-ru0qpZ*dwQx~' . ...'^";>?]<l;:::;;^^":!->l;:;I;Il". .

)]I^`,,-juQ0LLapQu1I ..'`^:!-?l:,"""""`^^":_>!;:;I;;l!'..

)1I^^^"i)CJLLObJcr~' . ..`^,I__I"^^^^..'.'`"~>!I::;;;ll^..

))l"^^,>]vQJzwXOu}l. .. .''^";+~,^````'''. '`!>!I;;I;;II;..

1[I""^,I]jXxtOLvt_, .'...''`";<>,^``''^'. .',>!I;IIIIlll`

{_;""^^:-txxXCUf-I: .`'..''^":i!"^```.. .^i!l;IIIIll!"

[~::""^,_ttrJOx|+^^ ``''`^":i!^^^` '.. '>!lI;IIIll!i'

?<:,,"^,<})/Xx/[;'` .^''``^":i!""^'` .I!II;IlIIl!!'

}+:,,"^,i{frz)f[^'` .,``^^",;>i,"``' . .,!l;IIllIlll'

}+:,""",i[tLucn?,'' .,",:I<<;"'. .' ."!l;;Illl!!;.

1-:,""":<|uj(C)+^. 'I',:Il+_I^.`'. ."ilIIIll!!!:.

}+:,""",>]vxuu]+`.. '`"..:I!])i'`..` .:<!lIIll!!I`.

}~:,,,",l~/rLr?+"'.... . ''. "!~(u^^`''. ...'I+i!IIl!!l,..

]~::::,,I>}txY?>,^`'''.....^' 'i-j+"^^:" ...''^I+>!lll!!;..'

)_:::,:,;>?(rzji;""^^^`'````^:lIi{}`..'" ..''``^"l+>i!llll" '`

|?;:;:,:;i-{tux~l;:,,""^^^`"I"^,`_>`,`:`..''`^"",,i-~i!!ll:..`^

/[;:;:::;!_[(rX1illI;::,""""";"::'';'^,.'`^^",:,:;<?~>i!!l^ .^"

)?;;;:::;l<?1fcriii!lII;:::;I;l". .'.,``^"",,;;;;I<?+>i!!: `,^

[+;;;:,:Il<_[|ux<<>ii!lllIII:,!!Il`'>l:;;IIIIIlII:!<~>i!!,..,:"

}~;;::::Il>+])xzc-~>iiiiiii!!ii<!:;I<!!>>>iiiiii<,:><>i!!`.`;,"

)+;;;;::;li~?{r0qJ/+<<<><<~~+<!I:l;,ll>_++~~~++-}".I>!lli^'II::

Độ phân giải đã được giảm và bạn không thể thấy nhiều chi tiết như lúc trước nữa, nhưng điều này là cần thiết cho việc giảm số lượng ký tự ASCII.

Xử lý Aspect Ratio của ảnh

Những đôi mắt nhạy bén sẽ nhận ra rằng tỉ lệ ảnh đã không còn chính xác nữa. Mình đã xử lý các ký tự ASCII như thể nó là hình vuông giống pixel, nhưng thực sự các ký tự lại hình chữ nhật. Do đó, mình cần tính toán để resize lại bức ảnh để cho ra một ASCII art với tỉ lệ giống bức ảnh ban đầu.

Vì mình đã chọn một monospaced font, chiều rộng của mỗi ký tự sẽ bằng nhau. Bây giờ điều chúng ta cần làm là tính được aspect ratio của font. Vậy mình làm điều đó như thế nào? Mình không tìm ra giải pháp nào trong CSS thuần, vì thế mình đã thực hiện bằng một số thao tác DOM đơn giản dưới đây.

js

const getFontRatio = () => {

const pre = document.createElement('pre')

pre.style.display = 'inline'

pre.textContent = ' '

document.body.appendChild(pre)

const { width, height } = pre.getBoundingClientRect()

document.body.removeChild(pre)

return height / width

}

const fontRatio = getFontRatio()

Trick này sẽ add một thẻ <pre> (để giữ chính xác style) và tính toán kích thước bằng cách sử dụng function getBoundingClientRect().

Lưu ý là aspect ratio của font sẽ khác nhau khi bạn thay đổi font-size

Cho dù width của các ký tự bằng nhau đi chăng nữa thì một ký tự có width là 1px sẽ không làm cho 1000 ký tự xếp thành 1 hàng có width là 1000px. Đó là do tiến trình Kerning. Vậy nên cách của chúng ta nó chỉ giúp giảm thiểu sự chênh lệnh về aspect ratio chứ không chính xác 100%.

Cùng cập nhật clampDimensions function dựa trên font ratio.

diff

const clampDimensions = (width, height) => {

+ const rectifiedWidth = Math.floor(fontRatio * width);

+

if (height > MAXIMUM_HEIGHT) {

- const reducedWidth = Math.floor(width * MAXIMUM_HEIGHT / height);

+ const reducedWidth = Math.floor(rectifiedWidth * MAXIMUM_HEIGHT / height);

return [reducedWidth, MAXIMUM_HEIGHT];

}

if (width > MAXIMUM_WIDTH) {

- const reducedHeight = Math.floor(height * MAXIMUM_WIDTH / width);

+ const reducedHeight = Math.floor(height * MAXIMUM_WIDTH / rectifiedWidth);

return [MAXIMUM_WIDTH, reducedHeight];

}

- return [width, height];

+ return [rectifiedWidth, height];

};

Mình vừa tính toán size sản phẩm dựa trên font ratio và độ phân giải tối đa.

Và bây giờ, ASCII art của mình trông như một kiệt tác.

text

CXccxxcZbwJuvzCqkoohqLUJ0QJLpdZUvnXzXYLx(|ffz<}[<-__[~-!>l<>]-?])t)xcXXQOmOOqwqmpbppdddkhkqUxzOOYccJLCCQOmwpbkhakbbqm0CUCL0ZZOmwpdk*MMM&88

mQXx1?+1zqQr?~_tZbhoahhakOcvOpq0C0YLJnYvCXO1[(n)(}_1?~++~<]}{[1{)rvvJLCOQOqdphkkkpkbkbbbkkbqznJCCCZqwmLXXQmZZmwwwwmZOwpddpwmZ0Q0Owbaao*MWM

QOmwOX/)rZhL)<i~)zwbbbhoamYXULCQLmwOOOJmCpuuLcnuv{n/][++>]()||)/(/vJUXOCpZwdhakokhdkhhkhdkbkhmzUL0QOOQUcXOqqqqqwmwqdboabdpwOLCJQmpbbho**#*

CLLCQOLvxUpmf-!l!?fXOphohmYnuvUZwdpmZZbwbXUpqQJcfcn]11()11|(tfrjruvYCCJwZpqpdka**ookkhkkdaaabkwCUzcuUUYrjxULOqdbbka#M#kqmm0LJUJ0qdbkpmOZwm

XXQqbhbwmpamu1?_?}[?][|XZqwmqmmd#bb00ok#OQdbb0cuLC[jtr|{(rrxrfrnzzYcULOQqZqkkbho#M##abohkkkhdddOJLLufnYUUL0OZqbhkka#M*hbbhkdqmZmwqdmLULQLO

jfvLkMM*o*#dz/1{]-<><?)fz0qpdqmbbhpwkk&wwkohmZwmZrYnux/{1jzXunzzJUXzc0ZQmwqbhbho*WWWW#hkaha*a*dq0XUJzczczQwdbbppmmph#odZQOZmmZZZmwpka*#oo#

YJQ0ZZpkaoabZJJ00LzxrczUYJCQZda*bkbha&ppd##bwpqqLYcvXu(?/uYCcnvCQOQCYJ0pmqmqhbka#MM&&8&*akhh*aapdwQJQOmmwpbbdqOZOmpkhkqOQQZmmZZqdbkoMW&&88

QCL0O0OqdddqLx|tzZwOUuj//jrnqk#hakkoMhkh&WbdbdbdJQcuJu[+xzLQJrfCQ0Z0QQOqdwwpphaah*MM&&&W*hkka*ohapYfCwppkkho**apwqba**abqqpdpwqqqpbo##W&8%

ZZwOzrxUOmpqmCcXQqdbdpwZZqZQb#*aaaMaoka&WabkkbkqUZvzCn[<jzLwwnrYmCZZ0LZwbdwZbhhahhaMW&&8WMhahaobwhdpdhaaao**MWaqmph*#adqqpdpm00Omwdao*M8%%

YujftftfuUJ0wdwZmmZwqpwLznum*&M*p**MoaM&##akbdpQUmXxCX{~|C0wqv}f0qCmwmOpqbppdbhaooaoW&88%W*ahoo##ohahao######*dZOqbhhpZQLQQQQQQLCCOqkhoMW8

f)}}tzcxcQQQZOLCZpddbbdQr1|pM&*kkM*#oo&8M**ahkbLCqJuQLf_[UwdkQ(fvqqJqdqwkhdwkdkaooo**&&8%88#oh*ao**ohdbdbkkbbqOLOwppqOJzvucXUUC0mmZwpkao*W

ut|jzQwpqOCULQOwh#odOL0ZmqpoWa#**oM*hM8&*hoaohbOmdQYw0f?]vwbhd)1jrkpOpkdqkkdphhk**M#*#&8B%8&#Mh##M*#ddZO0OZZZLJCOmwqwZLUXXULOZZZZ0CJLmdka#

jt/())(/jfruU0ZphohdmZwdkkhWMWoaM*Wo#8&&M#oaoap0qdmCwOx[}(Lb*aU}|unbpmhhabhhhkaoa*WWM#M8%B%88##o**WodO00JYXYYXXCLXununncYC0ZwmOLUUQwdbbbho

Xzx(1[]})xCwdddppbddbhokpq*M&#**o&&*#8%&M####*mQpdZ0dZj1-}jwk*wt1)Ynqqp**adda*h*oba8W&##%%%%88M#o#aMadwQUcvvvvvuxnzCCJCQOZZZZO0QOmdkbdddba

qYf{[1fUmpqZJCOwpdbkhaakbd#W8M*##&*M&&&8&&MMMoZmdpZQqmr}-?}rdoav]]{/tCdmk#hkdak*a*aoMMMM#8%BB8&###M*opqLUXzvnrt|tuJQLUYUJCCLQ0OOZqkaakbdbk

0CznxYqhkqLntnCZpddwZmpdkh#W%W*#&%WM8%%88WWWWdOqbw0JwQf1)??/XpMqf[][1)(vwb**hbb****MMMMMM&8%BB8&WM***aadwOCUUJLCLLCUXvxuXCQO0QCL0mpbkkkkkh

ahhkh*Mo0n1})juYQmwOLLZmwpo&ho#*&M#&8BB%8&MW*mLmq0cumJt}][}{nOhh0/}[][{1tuLphhpwZOOQmbbdhko&%B%&&MM&#*hdOJutjYOmZLUUUYYYUUUUYXYCQ0Zwphoakk

MW&&&&#qc)][fzCOwqpddqOOwdaW%WM#&WM%B%B8&&MWkZOwpJXJbmLcunxrjvmaoCt?][{(ruvczXJ0LZCZOQQCOppaW%B%&&MWWohbwdkdmOLLQUzXYYXXXYJLQ0QLLLQQmbakdb

*MW&WMk0zcJOOYf)1/xYQYLwdda&&&MM&&M8%%B8&MoakdwwQLYULQUYJUXzunvC0v)]]})/frncJw*aokqvZkbOCUCma&BB%8&WWW#oaModm0Q0CzucXYYYYULZwmOQQQOqkaahkh

ko#ohkhbqZOZQv/1[?[jJQQQQZ##8W*#&WM%B%%%&om0XnjfffxvCqpbhdLUvnrf/1[?][1/jxuJYfLppw/|vXJzxfjcZo%@%88W&WMMh*ohqJntttxXUUXzccXUCL0OZmmqdkkkhk

kohq0Q0ZOCUvzn)+II<tQLvnYOdk88#*W*W%B%88WbQzuxrrnCZqCd*hapcYZvnrt([-+_[(fffxrr/)()1))(1[[[{/Xb%BB%8&W&#aakkkbdQvj||jcXXcvuuvcXXzcY0qk**abp

haaaahbpZz/{{]>:,i_)xXcrncUdh8&W8#&%B@88WdJxt/trvO0UjrLQCjfj/ft|(1]~ii~?}{1)(|()1{}[?+~<~~_}xw%B@%%&&&#abhkkbbbpLvrxuccvvvvczXXYJQwda*#*dZ

bkkhhkdZXf[~>>>><~~+]}[_++[tko&MWh&BB@B8&kUj(}]]}(tjff|111)))1}[]?_>I,;!<~__---_+~<i!ll!i<-1xm%B@@%8&&&ohdmOOmwJnttrucvuvuuucczzXUQwkahhhh

bbkbdpmc)?_-]}[[)/|}?++_??}tJh*WWoW%BB@%&*Qr1]_++__-?]]]]?--____++<!:^^"I!i>>>i!!llIIII!i~[|cdB@@@%8&W&#hbwLUC0Qzt)(rvcvunxxuuvczXJ0pbpqpk

bpZcrxLCc(]_?}}}1{}?_~<+--?}/Jp*W*#%%B@@%&kz)?+<<>>i>>><>>>>>><~<<>l:"^^:I!><<>i!lIIIII!>_}tU8@@@@B%%WWMahpmUnftttttfnvvuunnnnuczXUC0wqw0L

qOJf{}tzzj[~_{/({??]-+><~-[[rvmhMa#%BBBBB%8qx{_~<>i!!!l!!lll!i<~~<>l,^``";!i~-_+<i!!!!i>+?)nw%@@@@%%8&&Waddbq0UurxcuffxczcvuvczYCLCCCOmwqq

CXcvr|{{(t([--]1(|1[--]?[{f/))rO*#oM%%BBBB%%wx{?+~<>!!lllll!i<+?-~<I"`'''":I!?++++~<<<~+?{tUMBB@BB%888&&*hbbdqwwZOJXzXUYzcczYYJQZmmZZmZ0Op

ZO0OZQt{?[[}[?]|xcXcr([{[1((/xYZoM#W&8%%%%%%8pu([-+~>>i!!i>>~+_{?<<l,"^^,;i><>l!i>~~++-]}|vd%BB%B@%88%8&#ahpm0CUzczzcczccnucJLOmmO000Omqba

ULO00OCx)[??]?[/Y0OOOOQLJCL0QCLmoW**M&&%%88%8&dc/}]-++~<><~~~~~~][}(1]__-}j/]iIIl!i>~_?}(xm8%%B@B@%8&%8&WokqmZO0LL0LCUzuxfjxvYLmqqqwZ00wbd

(/f)[[}[?_+_?][)tt/jvznnYZwOQLOdhWM*#W#888&8&%8bzt)[?-++~~~<>i!i~}|fjt||(1]+!::I!<+_??}(rm8%BB@@B%8&8888WM#ooookpwmOZmmmOUnxuXC0mqqqwwmmZO

i>iI::::I>_}{1(/[+~?)/)fXwbbphhak*WM#MM&&888&&88*Uj(}]-_+~<>!!!ll!<-]{1{[??]}(/jvvxt||fvo8%BBB@BB%%&&888&*Mokbbkdm0OZZmqbkpmOOOZmmO0ZwwmZm

I!;::li<?1|/t/)}?}(rzJCQmddqdhkkZoWM*#M&8W88&8&&88hct1[?_++++____-(|nXLLXcvvXXvt}_>~tn0aM8BB@BBB%88&&88&&MW*kdbhhdmOOOZmqbbbbpmOZwqpw0LQOZ

[]]}1)11[_~+?}}??)zZpdbpqw0UXcvOLa#M##M&&&WW&888&&88aUj({[[}1|jXJYvnrxuuc/~;,^^```^";!tp*W8B@@%%88W&M888WMW&#khaahbdppppbbbdppqwwqkahdqpdb

+?1rv|]-<I:;l!li+{nU0mwZQUzvx(nLCk##WWM&WW&8M&88%&&88%8oUxfttfftffffnJx[<>>!!!iiii!I;:;l_k#8B@B8&&&W#&&&&WMM&#hhahkbdddppqppdbdpdbkhhhkbbb

_}jzr+l;!<_{|(1)/([?[|xttnXJzxzCwa**W#M#M&&&&&W&888888%%BWk0Xuxxxxxr{_+-]]~--+)rxt]+<i!I;l[q88%&&M8oW&&&W#**#&MhkkdppdbdqZ00Zwpkaahbwmmwqp

({{?+l"";<-{/vJJc{_+[/cJ0OCx)}frQb*oo#a*##W&W&&W&&&&88B8%%&*hdQzurjruvc<:,^^^^``^^",:I~~il;I!qaM&#*##WMMMa&M#ok*kpkha##M*bmOwka****ohqQC0w

{-<l;:l<_---?{(|){{fY0mZYt]~<-fXZb*#M#**o*#&8&&M&W&8&&88%88W*hbdwOUu[l,:;IllI;,,"^^`^^^^^^",::i)0aooM###*a*oWMhhbpmhaao*#ooohdbka*#obmOmbh

~iI;!_}()[+~~~__+~-1jj|]_++_?}xvZdo*M*#&**oM&8888W888888888&#ahbpm[l;I!~-[|)[?+~<<>!;,"^```^^^":!uqboaoohkkbdqZz|)f[xurnrv0QzuvJOwppbbpdka

+~><+_]{({[1t|)|jcYXr){[{((|()vJmd*o#WM**#M*#&888888&8&8888&Moabq(>i<_?)/~I,""^^^^",:,^````^^`^,;;irddkaqOYx1_][I,".. ..,!>_xCOqkkqZZmm

1)){}1|xxjuLbokOzjrxrt(juv/]1tjcpb*#WMM***WWMW&&&&88888W88&&M*okp(_+[rv<;,"^^```'''''........'^^^"":ixLpZX-]j[,`. '-rnJZq

1jj/jvJCQOmba*kCf{1///ft1?--~[jLbbo#WMWo#WWM*M&&&&8888&&W&WM#okdx{?{|?;:;li>>``'''''^",^''....'``^"",,lrLxr(!,^`'. 'Ir

1}][)rXQmqpqqmQzf||/(->lI^..^<zzpha*WWo*M#M&o#M&8&&8&&&&WM*o#dmY|?]1<:Ii+?l'`''... .'``````^^",,{}!:"`''.

[]??[{(/frrft|1}}[~;' '":[z0qMo*WM&##M#&#&8W8&&8&W&&#MhqkCJUXcu>l>+[,`^":Il;:,"^^`'.. ...'''``````^^^^","^`'..

_____+~+_?]]}[]>, . .^;~)YwwdMoMM#M#WWM8M8&W88W&&&MW*#b0kuvnnxj++?)I:l>+?]{|1]?-+~>!:"^```'.....'''''`'`^^''.. .

--_+_-?]}}?]?l. .;!+[]jQqdha*W*W*M###8&8MW8&W&&W#Ma#d0qf/t|/j}?[<_-]{ncccvvuuxf/1[?~>I:"^`''.. .'''''`^`.. .

???--??]?_]l '`^:i?_{rnmqbMo###*#M#MWWW8&W8&W&W#*o#*wOr(1{11j[?}[-[(nzXXXXzcunrf/|){]+<!;:,"^``'.....''''``' . . ......'

-_++~~~_?_" .^"^;-?{}1U0wqb*aM#W**&#&&&&88&8MWWMa#abbmX/1]}[[{1)//tfrxuvczzXXXzvnrjt|({]-~!;,""^^``'.'.'''''''.... . . .........'

__+++~_[}; ..^^`"i+?}/jXXOdbaa*M#o*&WMW&W8W&888MW*akaabQj{[-???[}1(|(|/tfjrnuczzXXzzvuxf/({[-+<!I;:,,"``'.......'`....................'

?]]]?[}}!. .``;",!+]{fjvYZhkkaa#oaa&M*WWW&88WWMMM#aaakaqc|]+<~~_-?[}())(||/tjxuvcczXXXXzzvnj/{[-+~>iII;:,^'.......'``''''.....''...'...'

())1){1]^. ``^,I:i-}(xrxUZqbhhhaMoo&M*8WW&8&&MMWMMokhpp0Jf{-<<i>><_?[}1))(||/fjrnuvzXYYYYXXzvnr/{[??-+~>il:"^`....'`^^^^````^^```''''''''

(((|(//<'.''`":::I?{(vxYmmphbkkh#oaWM*WWWW8&&&WM#**hdqmJUf1?_i!!!i>~_?]}11{{11)|/jxnvzYUJJJJYXcuxj/{[[]?+~>iI;,^`'.'```^",,::;;:,^``'''''`

[[}{1(]^'.`",,::l~(tjvXqwwkpkkaooa#W#MW&M&&&W&MM#adpOLzn([_<!!IIl!i>~_-]}][[][}1)|tfxvzYYJUUYYXzvnxj/{[]?-+<i!;,^`'....'^:Il><>iI,^```````

)1))|(]`''^^^";;>?}{z0qqpbdqhhhkoo###M#W&W&MW**appOLXx(-+>lI;IIIIll!><+_??]??][}{1(/trnuvczXXXzccvnrrj)}}[]_<ilI:,^^^^`":Il![{}?l,"^`'```'

{)111)i`''"`"::I~]+/CmOpbkqqqdkoa*o*#*WWWW#MoodZQXur)-<lI;::::::;IIl!i>~++___-??[}{)(//tfjjxnnuuvuunrx)+~[[[?~>>i!llIII!><il<)xiI,^`'''''`

((()}}^`''`^"":~if)vLmwkbOmpbkahoa*M*MWMW*akbQrnjf{?+i;,"""^""",,:;Ili>~~~++++__-?][}}{11)(|//fjjjrrjrn}~i-}?+<i!!!lIIl!i>ilI~i;"`''.....'

()1{[[``'`'`""i<+i{XYmkkhqdZkkkooaMMMM#*akdpU(~<-_i!;"^`'````^^"",:I!!>~~+++~~__-??]]]]]]][[}}1)((||/|]]}}[-?~>!III;;;IIllllI;I"`.... .

}}{)(_^`'^```":![}cOLwbbqqppbdkhkM****akpwJf1_<:`;I,^'''''''`^^^",:;Ili>~_--__-???--_++++++__-?]][{{-+<+?[[?_~>l;;;::::;IIIII;;;'... .

)))||:^````^^:;;+{fUCddq0Oddpkpk**#*obdZQcj1~;,. I^`...'..'`'''`^^,,;II!<+-???]]?-+~~<>iiiii>>~++_-Ill!i>~]--+>!l;;:::::;;IllIII:... .

)))()"^``''^";;<>[UUcZJwwdhqqmqa**akdqOOYr/<i". .`..........'''``^",:Ili~_???[?+<>i!!llIlllll!!,":;::;Il!~??+>!lI;:::;;;;;IIIIIl,.. ...

((1{}"^`````:,,!;}UvYwOZqdOdQmpahhbwOOJcu(>>' ...........''`^^",:;!>+-[{?+>!II;;::::::;;;,^^""^,",:;!_-~>ilI;;:,:;;;I;;;;ll!". .....

(1]-~"^^^```":,I>(znuZLQmOQZQJdhbwZQJXUx1_!' . .. ......''``^",:Ii<_}r_iI;:,""",""""""^`:,^'^^^^",;>-+<i!lI;:::;;I;;;;Ill!l^......

)))1>""^^```^,,l??t/rzCQmLCUCwdqZmCJzuf}_I^ ... ....'''``^",:li+}u<l:,""^^^^^``^^ ''.'.''``^"l~~>i!!I;;;:;;;;;;;;IIlll'.....

)(|)!,""""^`^^,;<~}1fXOQUzXXzqJOwvCjj/?[~: ... ....''`^^"":;!<[xi;,"`^^````''^^'.'' '.....''`,!<>i!lII;;;;IIII;;;IIll!!'....

)1{{I,,""""^^^""!>](rJYzvrXjCZCJcuuu}]";:` .'''.......'''``^^",:I>]|l:"^```'`''' .,:'' .. .'`:>ii!!ll;;;;IIIIIIIIllll!l.

{}[]I,,",,""^`^,>!+||xc/{ft1QCUJzXz[-!',l ':".......'''``^"",:I!_[;,^^``````^`. .'^liii!lIIII;IIIlIllllllll!;.

[]??I:,,,,,,"^^^:Ii1/f({)/||0Ufjfv}]>,`i, .`''.''''```^^"",:I!_-;,^^`^```. .'`. .. .'"i!i!lII;;IIIIIIIIIIll!!!iI'

???-I:,,,,,""^^^"Ii_]{[u|trvLx)rcf+I:` "^ ..'^`^`'````^^"",:;!__;,"^^^^` '",`. ' '`ii!lIII;;IIlllIIlIIIll!!i!:

{{{[l,:,,,""""^":;i]-[/|zvnXnjxXc(li:'.:. .^``'.^^`^^^""",:;Ii-?I:,"""''^"^. ... .':!!!lII;;IIIllllIIll!ll!!I:.

}[}]l::,,,,""""",:l_?(frUvntrtxz(_l"`' .. `"""",,::;Il>-|i;:,"'.. ' ..' ."ii!lIIII;IIllllllllll!ll:'

111}l:,,,,"""""^";!+?{xjXuntrYvt{+i^`. .. . . .,i;`',,:;;;Ili~]J~!;^ ..''' ''. .'^ii!!lIII;;IIllllll!!!!l;^

}{{[!:,,,""""""^:Ii<-/uxjnrjxx|1?>!;''. . .'. `"^`. `:;IIli<-(O]>" ^,,`. .`'. ..'"I>i!!llIIIIIlllll!!!!I:^ .

}}[?l:::,,,",""",:;!-{[jxrXCvc}?_<iI^`''.... . ''`. ';l!>~-{xQj^',` . '^^` ....'^:!_>i!!llIIIIll!!!!llI:. .'

[[]_l;::::,,,,,,,:;l<+[{|jxXcJn?~>!;""^```''''''''.....'..'^^` .. .',i<+?{fY_^^^.`:'."`" .........'''`^",>}~>ii!llllIl!l!!!l;, ..''

1))[l;:::::,,,,,::;Ii<-[1|trnzL(+>!I:,,"""^""^^^`'``````^^`':":`";lll:"+]1jz<:. ^ '",` ....''''`````^^",,I~{+<>ii!llll!l!llII^. .'``

)||[!;;;;:::::::::;I!>+?[1(truzC[~>!I;:::,,,,,,"""^^^^^``^```^;;:'^::"'._j/l.'^,:`.^::^.....'''``^^^""""",,,,;!_1_~<>ii!!!!!!!ll;' .`^"^

|/f1!;::;;;::,,,;:;Il>~_?}{(trnzO]_>!llllI;;;;;:,,,,""""""""^`^::l^`;lI;,`..,l". 'l,;'..''```^^^""",,,:::,::;li?1_+~<>i!!!!!!lIl ..`""""

)(({!;;;;;;:::::;;;Il!<~_?[1(trvUC|-!iii!!!!llIIIII;:::::::;;II;liil""^.....'.`':I!,''``^^"",,,,,:::;;;;;;;;I!>_]-+~<>iiii!!l!, .`","^^

[[]-l;:::;;;::,::;;Il!>~+-][1|fnXCx{><<>iiii!!!!l!lllllIII;;;;IIIli<il;I!I,^..`I!I",:;;III;;I;IIIIIIIlIIIIIIII:;<_+~<<iii!!!!^. ..`,;:""^

{[[-lI::;;:::::::;;Il!i<~+-]})/rcCc0n[_~<>>>iiiiiiiiiiiiiii!!!!!iii><<>I,^:lll!>i<<<<>>>>>>>iiiiiiiiiiii!i>!"^:I+~<>>iii!!ii;' .^:I;,"""

()}-l:;:;;:;:,::;;;Ill!><~_-]{(jvCwqwZQY1]+<<<>>>>><<<<<<<<~~~+~+~!;I:,,;!:,""":Ili<+_+++++++~~~~~~~++__-?]<^...';i>i!llll!iI,`.`,I!l:,,,:

Tốt hơn nhiều rồi đúng không? Giảm font-size cũng giúp bạn nhìn bức tranh dễ dàng hơn.

Để tăng độ phân giải thì chúng ta sẽ tăng MAXIMUM_HEIGHTMAXIMUM_WIDTH. Để tăng dải màu thì chúng ta tăng độ dài của chuỗi grayRamp. Không biết các bạn thấy thế nào chứ với mình nhìn thấy thì với độ phân giải thấp thì ta nên chọn dải màu ít để hình dễ nhìn hơn (mặc dầu có thể mất đi chi tiết).

Dưới đây là kết quả của dải màu với 10 cấp độ gray. Anh em so sánh nhé.

text

++====+##*+=++*####*+++*++*#*+==+++++=-=-=::::::::.:...:::::---=+++*********##*######*==+*+==++++*****######***++++*******##%%%%%%

*++=-::-**=:.:-*########+=+***+*+++=+++=+::+---:-::::.::-:---===+++***################*=++++*****++*************##*********####%%%

*****=--+#*=:..-+*######+++++++******+**=++-+=-=-::::::-------=+++*+***#################+++****++++**********#####***+++**########

++++**+==**=:...:=+*####+===+**#****##++*#**====:---------=====++++*****#################*++==+++===++**#####%%#****++++*####*****

++**###**##+-:::-:::-=+******####**###*+##*+=++:=-=---======+++=++****######%%%########*#*++*=-=++++***######%#######*****#**++*+*

===*#%%####+---::..::-=+**#**###**###**###****=+===---=++==+++++=******#####%%%%#########**++++=+=+**###****####*+*********###%##%

++****#####*++**+===++++++*########%**#%#*****++=+=-:-+++==++**+++******####%%%%%%#########**++*****###*****###**********###%%%%%%

*++****###**=--+***+=---==*#######%###%%*####+++=+=::=+*+=-+***********######%%%%%%#########+=+**########***#####***#*****###%%%%%

****===+****+++**###*******#%###%####%%######+*=++=::==**+==*+***+**##**######%%%%%%######*##*########%%#**#####***#*******###%%%%

+==----=++**#*********+==+#%%####%##%%#%####*+*+=+=::=+**+--+*+****#*#**#######%%%%%%#####%#######%%%%%#***####**++*++**+++*###%%%

=-:-=+==+****++**####*+--+#%###%####%%######*+*+=*+-:-**#*--=**+*#**###*########%%%%%%#####%###########********++===++++*****####%

=--=+****+++***#%#**+****#%#%###%##%%%######**#*+*+-:-+*##=:==##**##*########%###%%%%%%###%#%#*#*******++******++++++*****+++**##%

=------====+***###****####%%##%#%#%%%%%#####**#*+**=::-###*:-==#**###########%%%%%%%%%%%%###%##***+++++++++======++*****+++**#####

++=--::--+**##**#######*#%%%###%%#%%%%#%#%%***#**#*--:-=###=--+=**############%%%%%%%%%%%%###%##*++=========++++***********#######

*+-::-=****++***#########%%%%#%%#%%%%%%%%%%**#*****-:::-+##*::---+#*###########%%%%%%%%%%%%#%###*++++===--=++++++++++*****########

*+===+###*=-=+**##***####%%%##%%%%%%%%%%%%#**#**+*+--:::=*##=:::---+*#%#######%%%%%%%%%%%%%%######**++++++++++===++***++***#######

######%#+-:--=+****+****#%##%#%%%%%%%%%%%%#+***+=*+-:::--+##*=-:::--==*###******######%%%%%%%%%###*+=-=+***++++++++++++++****#####

%%%%%%#+=::-++***##*****##%%%#%%%%%%%%%%%#****+++#*+======+##*=:::--====++***+***++**##%%%%%%%%###*##***+*+++++++++++**+++++*#####

#%%%%#*+=+**+=---=+*+**##%%%%%%%%%%%%%%####***++++++++++===+*+-:::--===+*####*=*##*+++*#%%%%%%%%###%#*****+==+++++++*****+**######

########****+--:::=+**+*#%%%#%%%%%%%%%%#*+===-===+*###*++===--::::--===+=+*#*=-=++====+*%%%%%%%%%%####*+=--==++++==+++*******#####

###*****++++=-:..:=*+==+*#%%%#%#%%%%%%%*+=====+**+####*=++===-:::::-=-====--------:::-=+#%%%%%%%%######*+=---=+++====++++++*#####*

#######*+=--:.. .:-=+===+*#%%%%#%%%%%%#*+=--==**+==**+-==-=---::..:::--------:::::.:::-+#%%%%%%%%########*+==========+++++**##%##*

######*+=-:.....:::::::::-*#%%%#%%%%%%%*=--:::--==---------::::.. ...::::::::.......::-+#%%%%%%%%###*****+=-==========+++++*######

#####**=:::::::---:::::::-=##%%#%%%%%%%#+-::::::::::::::::::::.. ................:--+#%%%%%%%%###*+++*+=--===========++++*##**#

#**===*+=:::::---::::.::::-=##%#%%%%%%%%*=:::..............:..... ................:-=*%%%%%%%%%%##**+=------===========+++*****+

**+---=+=-:::---:::::..:::-=*#%#%%%%%%%%#+-::..............:::.. ...:::........::-+#%%%%%%%%%%####*++=====-==+====+++++++*****

++===------:::----:::::------+#%#%%%%%%%%#+-:::...........::::.. ...:::::...:::-=*%%%%%%%%%%%#####****+++++++==++++**********

*****+-:::::::-==+==-:::---=+*#%%%%%%%%%%%#+-:::.........:::::.. .........:::::--+%%%%%%%%%%%%###**++++++==++===++**********##

++****+--:::::-+*****++++**++*#%##%%%%%%%%%#+--::::....::::.:::--::::-=-:......::::-=#%%%%%%%%%%%%%##****++**+++======+*********##

----::::::::::----=++==+***+**#%%#%%%%%%%%%%#+=-::::::::.....:-==-----:.. ...:::::-=#%%%%%%%%%%%%%%%%#####********+===++**********

..... ..::----::::---=+##*#####%%%%%%%%%%%%%%+=-:::::.........::---::::---====---=#%%%%%%%%%%%%%%%#%#####*******##***************

... ...::------::-=+++**#*####*#%%#%%%%%%%%%%%%#=--:::::::::::--=++++==+++=-:..-=*#%%%%%%%%%%%%%%%%%%######*******####********++**

::::----:::::-::-+*###***+++=+*##%#%%%%%%%%%%%%%%#+=-:::---=++=======-. ..=##%%%%%%%%%%%%%%%%%%#######****####*****####**##

::-==-::.......:-=+****++==--++###%%%%%%%%%%%%%%%%%%#+==----=====+=:...............-#%%%%%%%%%#%%%%%%%%#########*****###*#########

:-=+-....::------:::-=-==++==+*###%#%#%%%%%%%%%%%%%%%%%*+=======-:::::.:::-==::......-%%%%%%%#%%%%%###%%####**###******#####******

---::. .::-=++=-:::=++**+-:-=+####%##%%%%%%%%%%%%%%%%%%##*+=======. .:.....:##%%###%%%%#%%####*####%%##***#######**+**

-:.....::::::-----=+***=-:.:-+*###%%###%%%%%%%%%%%%%%%%%%###**+=:. ..... .:=###%%%#####%####*#####%########%##***##

.....:---:::::::::-==-::::::-+*###%#%%###%%%%%%%%%%%%%%%%####*-....::---:::..... .:*#########**+=---======*+==++***##*###

:..::::--:-----==++=-::-----=+*###%%%##%%#%%%%%%%%%%%%%%%###*=...::--. . ...-*###**+=:::.. ..-+**##*****

---:--====+###*+====--===-:-==*###%%%##%%%%%%%%%%%%%%%%%%####=:::==.. .=+**=:-=: .==+**

-==-=+++**####+=-------:::::-+####%%%#%%%#%%%%%%%%%%%%%%%###+-:--: ..... .=+==:. .=

-:::-=+*******+=----:... =+*###%%##%#%%#%%%%%%%%%%%%###**-::-....:: -:.

:::::---===----::. .=+*%##%%%#%%%%%%%%%%%%%%%%%#**+++==...:: ....

::::::::::::::. .:=***%#%%%%%%%%%%%%%%%%%%%%##**+=====:::-....:::--:::::...

::::::::::::. .:::-+*####%#%#%#%%%%%%%%%%%%#%##**+-=--=-::.:::-+========--::...

::::::::::. .:::-=**#%#%%%#%%#%%%%%%%%%%%%##%#*+-----=:::::--=++++++====----::...

:::::.:::. .::-:=+**###%#%#%%%%%%%%%%%%%%#%##**=-::-:------======+++++====----::..

::::::::: .::--=++#####%###%%%%%%%%%%%%%######+-:::::::-------=====++++++===----:::....

:::::::- . .::-=-++#####%###%%%%%%%%%%%%%%#####*=-:.:.:::::-------======+++++++===-::::.....

-------. . .:--===**#####%##%%#%%%%%%%%%%%###***=-:::....:::--------=====+++++++++===-::::::...

------- .:--==+**########%%#%%%%%%%%%%####**+=-::......::::---------====+++++++++===--:::::.... ...

::----. .:-===+***#######%%#%%%%%%%%%%%##**+==-::........::::::::::----===+++++++++====--:::::... ........

------. ..::-+***##*########%%%%%%%%%###**++=-::............:::::::::-----=====+++++======---:::.... ...-:::.

-----: . .:::=***###***########%%%%####**+==-:.... .. .......::::::::::------=============-::::::..............:--..

----:: ..--++**##**#######%#%%%%###*====-::.. ......:::::::::::::---------========::.::::..............:.

----:. ..:.-++*###**######%%%%#####*-:.::... .....:::::::::::::::::::::--------::--:::.................

-----. .:-+*+*##***#*###%######**=-:.. .. .....::::::::::::::::::::::::--::.::::::.................

----- ..:-=++##*+*#*#########**+=-:.. . .....:::::::::::.........::::......:::::..... . .........

----: ...:++=****##***######***==-.. ....::::::::.............. .. ....::::..... ...........

----: ..:+=+***#*****####***+==:.. ....:::::....... . .... ..::....... ...........

--::. ..-===*+*****+*##**++++--.. ...::--.... . ..::....... ............

----. .::--=++**+++*#***++==--.. ...:-:.. .:.....................

----. ..::-=+**=++=**+*=+==-::. ..:-:. .......................

---- ..:-=+++===+*+++===:: . ..:-:. ........................

-::: ..:--==--=-+++++++::. . ...:. ........................

:::: ..--------+*-===::. . ...:. .........................

:::: ..::--=-==+=--==:. ...:. .........................

---:. ..::--=+===-==+-... ....:.. ........................

::::.. .::-==*===-==-:. ....:-:.. ........................

---:.. ..::-==+===++=-:. .. .......:=-.. .......................

---:. ..::========--:... .....::==:. .....................

-:::.. ...:--===+=+-::... ....::-++. .:...................

::::.. . ..:::--==+++::... ..::-==. .-:................

---:.. ...:::--===+=:... . ..... ::-==. .-::...............

---:....... .....::---===+-:...... . . . :=- . ..-::.............

---:....... ......:::---===*::............ .. .... .. . . ...:-:::...........

---:....... .......:::---==++-:.................. ........ .. ............::::..........

::::........ .......::::---=++=:.................................... ... ......................... .:::......... ..

-:::........ . ........::::--==+=*=:::.............................. ................................ ..:.......... ..

---:........ ........::::---=+****+=-::...............::::::.... ... ....::::::::::::::::::::::. .......... .... .

Tóm lại

Dưới đây là các link liên quan:

Vậy là trong bài viết này chúng ta đã tạo được một tool khá thú vị, thông qua đó chúng ta cũng học được khá nhiều liên quan đến việc xử lý ảnh. Chúc các bạn làm thành công nhé.

Tham khảo