重构ASCII艺术:字符画

2019-06-21 12:00:00   技术   css javascript

很久以前做过一个ASCII艺术代码,是python版本的。当时在django的架构下跑。自从换了Github Page就不能用了。这几天终于抽空用javascript重构了一下。

                                             
                                             
               芒芒芒                           
         芒连满相暑相夏满连                 芒满满       
    满暑相夏满夏夏                 芒连满夏相暑相夏满连       
         连连  芒               芒芒芒相连           
       芒连夏满满满夏暑连               芒相芒           
       夏芒  芒 芒相芒               夏芒  连         
       满夏夏满连 芒夏               满连   连暑连       
       连连 连满芒芒满              夏满芒芒连满夏夏暑芒      
       芒连芒   芒满            芒暑暑暑相满芒   连芒      
       芒连连满夏夏相芒            芒满芒  夏满           
       芒相连                      连满           
        满满 连满夏芒                 连满芒满满        
       芒满芒芒  相满             连满夏相暑相满满芒        
      芒夏连连  连夏               芒芒 连连           
     芒夏芒  满满夏芒                  连连           
    芒满    芒相相连                  满夏满满相相暑暑夏芒   
    芒    满夏芒芒相夏芒        芒相相相相相夏满连芒芒芒   芒连芒   
      芒满夏连    夏暑相连       芒芒                  
               连相暑相夏连                        
                                             

文字转换

图片转换

原理

本身的原理和python版本的没有大的区别。

图片版本就是首先把彩色图片给灰度了,然后根据像素点的灰色深度替换成字符。

文字版本就是先把文字写成图片,接着重复上面图片版本的做法。

对于代码实在感兴趣的同学可以参照这里

重构的时候发现的新的玩意儿

灰度的标准

之前玩python版本的时候直接用了PIL的convert方法,这里转成javascript版本的时候,因为用了Canvas中转,结果是要自己用算法把RGBA切成灰度。

一开始当然是粗鲁地直接各取三分之一的红绿蓝加起来,结果总觉得好像有点黑黑的。

后来去调查了一下,才发现灰度是有标准和算法的,具体不是这个专业不是太明白,直接搬过来加上了选项。

参考文档: Grayscale: Luma coding in video systems

RGBA格式

另一个小坑是这样的,Canvas中使用getImageData返回的颜色是RGBA,一开始没有在意这个区别,结果碰到了一些透明的PNG图片,背景就变成了黑色(因为透明值为0)。为了解决这个小问题,改了很多代码,后来发现,为什么不事先把Canvas的背景涂白呢,涂白之后就可以直接无视A了。:joy:

计算字符浓度

这个是很早以前就有的想法,根据自定义的字符显示字符画,但是原来的算法并不会计算哪个字更深一点,所以加了一点小小的料在里面。

目前出问题的之后一次,就是魔瓶同学发现空心方块□和实心方块■在计算上好像不太正确。其实也懒得检查了,回头再说。

相关代码:

function getCharDensity(char) {
  var canvas = document.createElement("canvas");
  canvas.width = 12;
  canvas.height = 12;
  var ctx = canvas.getContext("2d");
  ctx.fillStyle = "#ffffff";
  ctx.fillRect(0, 0, 12, 12);
  ctx.fillStyle = "#000000";
  ctx.textBaseline = "middle";
  ctx.textAlign = "center";
  ctx.font = "12pt Courier, monospace";
  ctx.fillText(char, 6, 6);
  var sourceImg = ctx.getImageData(0, 0, canvas.width, canvas.height);
  var sourceData = sourceImg.data;
  var point = 0;
  var s = sourceData.length / 4;
  for (i = 0; i < sourceData.length; i += 4) {
    luma = Math.floor((sourceData[i] + sourceData[i + 1] + sourceData[i + 2]) / 3);
    if (luma != 255) {
      point += 1;
    }
  }
  return point / s;
}
评论共