#程序员也要秀恩爱
[TOC]
好几个月没更新博客了,眼看就快过年了,我这段时间都去干嘛了呢?
其实也没干嘛,就是趁着国庆期间结了个婚,换了个工作……
要说婚后体会的话,个人感觉还是挺棒的~
既然人生进入另一个阶段了,博客的第一篇还是要围绕这个主题的,那就是,程序员有时候也是不得不搞点讨好妹纸的事情啊……
然而,技术实现的本身更多注意力在业务逻辑上,而不是漂不漂亮……所以,程序员的很多秀恩爱,往往特别有硬感……而在这其中像素艺术和ASCII码艺术是其中两个脱颖而出的形式,可谓是层出不穷……比如说我个人就比较喜欢这两种形式……
## Excel是伪装成办公软件的画图工具
近几年来,关于用Excel画图的新闻轶事已经屡见不鲜了。为啥Excel画图这么受欢迎呢?
- 首先,对于大部分的电脑来说都是自带的,不用专门下载个工具。比较出名的用Excel画图的日本老人就是因为计算机自带的原因,当然,真正的原因是穷,买不起正版的专业画图软件……
- 其次,用它画图真的很好用。Excel自带的表格虚线为画图提供了天然的辅助线,还可以控制行高列宽来辅助对齐,这就是用Excel而不用word的原因。而其自带的矢量作图工具堪比PhotoShop的钢笔工具,对于简单的矢量图绘制来说,容易上手又好控制。好多人在面对画图的问题时,都会纠结要不要买个数位板。而用Excel画图完全不担心,矢量绘图是用鼠标拖动曲线的操纵柄来完成曲线的修改编辑的,真正为鼠标准备的作图方式,不关数位板的事;
- 再次,用Excel作图可以装B。不要觉得这条没什么用,这确实是很大一部分人非要把Excel搞成一个画图工具的真正原因。把一个非专业的作图工具搞成一个比专业作图工具更炫酷的东西,难道不吸引眼球么?
能说这么多是因为我也玩过Excel画图,然而我绘画水平尚浅,只能临摹,并不能独立构图,下面就是我在Excel上临摹的QQ图标(我真的没收腾讯一分钱)。
![用Excel绘制QQ图标](Excel_QQ.jpg "用Excel绘制QQ图标")
说起来并没有那么难,简单介绍一下,就是插入图片,放到最底下一层,然后用鼠标拖动曲线的操纵柄,顺着图画的边缘进行操作,精细处可以放大再操作,掌握好角点和平滑点的转换,然后选择填充和线条颜色粗度,注意好层次遮挡,巧妙的利用组合与解除组合,善于使用各种渐变来体现效果,就行了。听上去很玄乎,自己试试就会发现,没那么难……
然而说了这么多,这次要说的不是这个……因为矢量作图这些事,都是我四五年前玩的东西了,那个QQ图标是我13年底无聊时临摹的……这次我们来换个方式……
除了矢量图,自然是位图,用Excel绘制位图也是一种独特的体验,先把行高和列宽控制到想要的大小,然后,按照一格是一个像素来进行染色,染时间长了,就感觉自己在玩涂鸦游戏了~
不管是平涂还是厚涂的画师,一般都是先打辅助线,画草稿,然后才开始涂色。而excel也可以基本上做到这一切,可以先用曲线把轮廓勾勒一下,然后再选中相应的单元格设置背景色,最终完成一个位图图形。
使用这种位图作图手法所做的图的好处是颜色层次多,较为灵活,不好处就是单元格太大像素感会很重,单元格小了,操作又过于复杂,特别是对于边缘处理上,有点反复而机械,尽管可以通过双击格式刷的方式进行批量操作,也可以使用shift、ctrl进行方块区域的批量选择,但是并没有更好的方法。
或许有人能利用Excel的VBA扩展将Excel整成效率更高的位图作图工具,还是挺期待的(可以结合后面提到的图元生成尝试一下)……VBA使得Excel无所不能……
介绍完Excel的矢量图作图方法和位图作图方法之后,然而之后要说的事,跟这些都没有半毛钱关系……因为……我绘画水平拿不出门去啊……
既然矢量图能有一个跟着描的方式可以用来装B,位图一定也有一个方法吧……
这个其实就简单的多了……因为我们常见的图片都是位图,要搞成矢量图,一般情况下还是手工描最靠谱,当前的大部分转矢量图的方案可能并不太好使……但是位图就不一样了……
要转成表格也是分分钟的事情……我前段时间研究验证码的时候,搞luaGD库,想想稍微写写还是能够完成这种操作的……于是,基本思路就诞生了:
首先,通过luaGD库将位图转成html的表格,然后,复制,粘贴到Excel中稍微编辑一下,改一改行高列宽,然后,就可以冒充是在Excel里画的了……(画外音:我从未见过如此厚颜无耻之人)
这种方法可以提供一个很好的网格图样,对于十字绣之类的图样参考也是不错的……另外,我发现早期的Excel背景色只有256种颜色可选,就像是十字绣也是固定的那么几种颜色,并没有那么多的颜色层次……
于是,我还在里面提供了相应的参数来控制到相近的可选颜色上……然后下面就是代码和使用说明了……
```lua
-- File: img2table.lua
require("gd");
if #arg<1 or #arg>4 then
print("用法:"..arg[0].." <图片路径> [颜色数目,默认32] [是否抖动{1/true为是,其他默认false}] [单元格尺寸,单位:像素,默认:2]");
return false;
end
local colors = nil;
if #arg >=2 then
colors = tonumber(arg[2]);
end
if colors == nil then
colors = 32;
end
local dither = nil;
if #arg >=3 then
dither = (arg[3]:upper() == "TRUE" or arg[3]=="1");
end
if dither == nil then
dither = false;
end
local cellSize = nil;
if #arg >=4 then
cellSize = tonumber(arg[4]);
end
if cellSize == nil then
cellSize = 2;
end
--[[
function file_exists(path)
local file = io.open(path, "rb")
if file then file:close() end
return file ~= nil
end
if not file_exists(arg[1]) then
print("找不到文件【"..arg[1].."】");
return false;
end
--]]
local fp = io.open(arg[1], "rb");
if fp == nil then
print("读取文件失败【"..arg[1].."】");
return false;
end
local fstr = fp:read("*a");
fp:close();
fp = nil;
local tryFuncs={
png = gd.createFromPngStr,
jpg = gd.createFromJpegStr,
gif = gd.createFromGifStr,
xbm = gd.createFromXbmStr,
xpm = gd.createFromXpmStr,
gd = gd.createFromGdStr,
gd2 = gd.createFromGd2Str
};
local img = nil;
for ft,func in pairs(tryFuncs) do
img = func(fstr);
if img ~= nil then
print("文件类型【"..ft.."】");
break;
end
end
if img == nil then
print("无法识别的文件格式!");
return false;
end
local ox, oy = img:sizeXY();
print("原始图片尺寸:"..ox..","..oy);
local x, y = 256, math.floor(oy*256/ox);
print("输出表格尺寸:"..x..","..y);
local im = gd.createPalette(x, y);
img = img:createPaletteFromTrueColor(dither, colors);
im:paletteCopy(img);
--im:copyResized(img, 0, 0, 0, 0, x, y, ox, oy);
im:copyResampled(img, 0, 0, 0, 0, x, y, ox, oy);
img = nil;
local _,_,filePath,fileName = arg[1]:find('^(.*)([^\\/]+)[\\.][^.]+$');
fileName = filePath..fileName..".html";
print("输出文件名【"..fileName.."】");
fp = io.open(fileName, "w");
fp:write([[
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<meta http-equiv="Content-Type" content="text/html; charset=GBK" />
<meta name ="Keywords" content="生成图片表格" />
<meta name="Author" content="yimengqiannian/jingchangjiang1988@163.com">
<meta name="Date" content="]]..os.date()..[[" />
<meta name="Generator" content="lua program" />
<meta name="Description" content="生成图片表格" />
<meta name="Copyright" content="Copyleft" />
<meta name="Robots" content="all" />
<title>图片表格【]]..arg[1]..[[】</title>
<style>
td{
width:]]..cellSize..[[px;
height:]]..cellSize..[[px;
}
table{
border-collapse:collapse;
/*border: 1px solid #000;*/
}
</style>
<body>
<h1><center>图片表格【]]..arg[1]..[[】</center></h1>
<table align="center" border="0" cellspacing="0" cellpadding="0">
]]);
for j = 0,y-1 do
fp:write("<tr>");
for i = 0,x-1 do
local c = im:getPixel(i,j);
fp:write("<td bgcolor=\"");
fp:write(("#%02X%02X%02X"):format(im:red(c),im:green(c),im:blue(c)));
fp:write("\"></td>");
end
fp:write("</tr>\n");
end
fp:write([[
</table>
</body>
</html>
]]);
fp:close();
fp = nil;
print("输出完成~");
im = nil;
```
代码不多,很容易理解,就不过多解释了,然后就是使用说明:
```
将图片转换为带颜色的表格形式。
命令行方式调用:
用法:img2table.exe <图片路径> [颜色数目,默认32] [是否抖动{1/true为是,其他默认false}] [单元格尺寸,单位:像素,默认:2]
如:
img2table.exe yuxia.jpg 100 1 3
即将yuxia.jpg转换为颜色数目为100,含有抖动,每个单元格边长3像素的html表格
拖拽方式调用:
直接将图片文件拖拽到img2table.exe图标上,此时会在图片所在目录生成html文件,此时默认32种颜色,不使用抖动,单元格默认2像素
生成的html文件用浏览器打开后可以直接选中复制粘贴到excel中,全选,设置行高和列宽即可。
生成的表格统一默认宽度为256(程序内置,无法通过命令行控制)。
脚本文件源码以及绑定相关文件在src目录中,bind.bat为将lua脚本绑定到exe上时用的脚本。
```
生成的表格统一为256的原因是考虑到性能问题……大家感兴趣可以自己改……
然后就是生成效果的示意图了:
![生成html图片表格示意图](html.png "生成html图片表格示意图")
复制粘贴到Excel里是这样的:
![复制粘贴到Excel后的示意图](excel.png "复制粘贴到Excel后的示意图")
然后就可以发给妹纸说,哎呀,我花了一上午的功夫搞出来的,怎么样?(其实并没有说谎,写程序用了一上午呀~)
这样还可以录视频到B站,冒充技术宅~~
最后,给一个[下载链接](https://pan.baidu.com/s/1dGSQ2tj "下载链接")。
## 不要低估OpenWRT的杀伤力
我个人挺喜欢ASCII艺术和像素艺术的,这个不必多说,从很久之前的写过一个[字模获取](http://1157.huaying1988.com/getZM.html "字模获取")。
就说前两天吧,想起来,字模有了,当年不是说要搞的图模的么?图形比文字更有表现力和张力啊……
废了一番功夫,写了个图模的,可以[点击看效果](http://1157.huaying1988.com/pix2num.html "图模转换工具")。
其实本来是想把之前研究的[2D简单图元生成](http://1157.huaying1988.com/2DLearn/paint.html "2D简单图元生成")加进去的,但是想想,先用这个图模讨好一下妹纸吧……
既然图模有了,得选择一个语言来把它展示出来吧……
Windows脚本技术(WSH—— 一般使用VBScript和JScript,文件缩略名为*.wsf)是一个较好的可选技术,因为windows自带,双击就能运行。
然而我没有选……既然是这种比较有原始感的东西,自然要用看上去比较原始的表现形式……我选择批处理脚本……黑窗口一蹦出来,那种原始感就油然而生……
代码很简单,没几行:
```bat
@echo off & setlocal ENABLEDELAYEDEXPANSION & title I Love You! & color FC
set array=0,7196,15934,32639,32767,32767,32767,16382,8188,4088,2032,992,448,128,0,37735,38898,38898,37858,37314,24711,0
for %%i in (%array%) do (
set str=
for /L %%j in (0,1,15) do (
set /A test="%%i&(1<<%%j)"
if /i !test! GTR 0 (set str=!str!■) else (set str=!str! )
)
echo. !str!
)
pause
color 07 & title cmd & setlocal DISABLEDELAYEDEXPANSION & @echo on
```
展示效果是这样的:
![小心心效果图](iloveu.png "小心心效果图")
然而,这样就行了么?当然不可以……
这种花招玩的太多可就不新鲜了……得玩点更具有杀伤力的东西……
于是我把注意力关注到了我家的路由器上了……我家的路由器是被我刷了OpenWRT的……早就想在上面搞事情了,但是一直都没搞……为啥?因为懒……
我的想法很简单,用路由器劫持HTTP协议报文,然后插入我想要执行的脚本,这样就能实现我想要的HTTP轰炸!
只要打开网页,就会出现一个我想展示的图案,然后3秒钟后消失……这招当然是跟移动联通之类的运营商广告学的……
这样的轰炸应该对妹纸比较有效果~
首先得选择合适的OpenWRT插件来实现想要的效果……选来选去,最后用的是privoxy……
privoxy是个代理插件,它可以在代理过程中实现一些过滤,本来是用来过滤广告的……但是,我们要用它来插入广告……
首先是安装privoxy,这个不用多说,可以直接在LuCI界面操作,在线下载安装……
然后就是配置privoxy。我之前的时候,照着网上的配置来,结果总是不生效……经过我的仔细研究才明白过来……我安装的这个privoxy所使用的配置是通过config配置生成的,并不是一个静态配置,真正的配置文件是/etc/config/privoxy
我的/etc/config/privoxy是这么配的:
```conf
config privoxy 'privoxy'
option confdir '/etc/privoxy'
option logdir '/var/log'
option logfile 'privoxy.log'
list filterfile 'default.filter'
list filterfile 'user.filter'
list actionsfile 'match-all.action'
list actionsfile 'default.action'
list actionsfile 'user.action'
# list listen_address '127.0.0.1:8118'
# list listen_address '192.168.1.1:8118'
list listen_address '0.0.0.0:8118'
option toggle '1'
option enable_remote_toggle '1'
option enable_remote_http_toggle '0'
option enable_edit_actions '1'
option enforce_blocks '0'
option buffer_limit '4096'
option forwarded_connect_retries '0'
option accept_intercepted_requests '1'
option allow_cgi_request_crunching '0'
option split_large_forms '0'
option keep_alive_timeout '300'
option socket_timeout '300'
# list permit_access '192.168.1.0/24'
list permit_access '0.0.0.0/0'
option debug_1 '0'
option debug_512 '1'
option debug_1024 '0'
option debug_4096 '1'
option debug_8192 '1'
```
然后就是过滤器的配置,不过在这之前要干一件事……那就是,我得把想要植入的脚本写出来,需求已经很明确了,直接写就行了……
```JavaScript
(function(){
if(window.top == window){
var zm = [0x0000, 0x1C1C, 0x3E3E, 0x7F7F, 0x7FFF, 0x7FFF, 0x7FFF, 0x3FFE, 0x1FFC, 0x0FF8, 0x07F0, 0x03E0, 0x01C0, 0x0080, 0x0000, 0x9367, 0x97F2, 0x97F2, 0x93E2, 0x91C2, 0x6087, 0x0000];
var fontSize = 14;
var tp = parseInt(window.screen.availHeight/2)-fontSize*16;
var lft = parseInt(window.screen.availWidth/2)-fontSize*zm.length/2;
var strArray =[];
for(var i=0;i<zm.length;i++){
for(var j=0;j<16;j++){
if(zm[i]&1){
strArray[strArray.length]="▇";
}else{
strArray[strArray.length]=" ";
}
zm[i] >>= 1;
}
strArray[strArray.length]="<br />";
}
var div = document.createElement("div");
div.innerHTML = strArray.join("");
div.setAttribute("style","font-size:"+fontSize+"px;line-height:"+fontSize+"px;font-family:Consolas, Monaco, monospace;background-color:#FFF;color:red;position: absolute;left:"+lft+"px;top:"+tp+"px;");
document.body.appendChild(div);
setTimeout(function(){document.body.removeChild(div);},3000);
}
})();
```
这段代码没啥好说的,就是上边那个批处理脚本改成JS版了,然后再加上显示的一些样式,还有3秒钟后移除的操作。
然后把这段代码上传到自己的服务器上,到时候直接外部引入就行了,我把它[放到了这儿](http://1157.huaying1988.com/love.js "要执行的脚本")。
然后开始过滤器的配置。
首先是/etc/privoxy/user.filter文件的末尾添加这么几行,创建myLoveScript过滤器在body标签结束的前面添上自己要注入的脚本:
```
FILTER: myLoveScript
s|</body>|<script type="text/javascript" src="http://1157.huaying1988.com/love.js" charset="UTF-8"></script>$0|
```
然后是应用过滤器,在/etc/privoxy/user.action文件的末尾添加这么几行,对所有的请求都添加该过滤器:
```
{+filter{myLoveScript}}
.*
```
然后是设置privoxy自动启动,重启privoxy:
```shell
# /etc/init.d/privoxy enable
# /etc/init.d/privoxy restart
```
之后,还剩最后一步,通过iptables将所有的请求都转到privoxy代理上:
```shell
iptables -t nat -A PREROUTING -s 0.0.0.0/0.0.0.0 -p tcp --dport 80 -j REDIRECT --to-ports 8118
```
再随便打开个网页,不管是手机还是计算机,都会显示3秒钟的小心心图案了……
![OpenWRT注入示意图](boom.png "OpenWRT注入示意图")
然而有一个问题,就是……只能注入http协议的连接,不能注入https的连接……
要注入https还是有一定难度的,但也不是不可以……从这点小事上也可以看出https安全性确实比http高……
然而现在http的网站越来越少了,特别是较大的一些网站……所以,总有一天,这个方法会完全不奏效了……
当然,一开始在路由器上搞上这个东西的时候,妹纸是很开心的……然而确实能体会到,打开网页有些变慢了……
后来的结果是:显摆了一天之后,我就把它去了……
## 最终却败给了俄罗斯方块
我是很喜欢闲的没事实现一些小游戏的,比如说[贪吃蛇](http://1157.huaying1988.com/tcs.html "贪吃蛇")、[彩球滑梯](http://1157.huaying1988.com/cqht.html "彩球滑梯")、[打字游戏](http://1157.huaying1988.com/typeGame.html "打字游戏")、[记忆测试](http://1157.huaying1988.com/memoryTest.html "记忆测试")还有想当年跟同事一起合作的[扫雷](http://1157.huaying1988.com/hzsl.html "扫雷")。
还有很多,不再一一列举了……然而事实上我从初中开始就有一个想法,就是实现个俄罗斯方块游戏……之前的时候没去实现,是因为能力不够……确实实现不了……
而后来没去实现,那纯粹是因为懒……这不是前几天研究像素画么……就又想起来了……
想法是,整个俄罗斯方块所有的展示都用像素画的形式来完成,是不是感觉比较有时代感呢?
于是,说干就干……最终,实现了[一个比较粗糙的俄罗斯方块](http://1157.huaying1988.com/tetris.html "刚实现的一个比较粗糙的俄罗斯方块")……我就不在此处贴源码了……
操作很简单:A-左、D-右、S-加速落、W/J-顺时针转、K-逆时针转、空格-落到底、P-暂停(注:暂停期间仍然会计时)
试了试,感觉应该没太多的bug,就发给妹纸玩了……顺带还发给她了我打出来的最高分的效果图:
![我打出的最高分效果图](highScore.png "我打出的最高分效果图")
结果,妹纸不愿意了,非要超过我的分数不可……于是一有空就玩……几天过去了都没再理我……
一直到现在,还是一有功夫就玩它,想要超过我……:weary:
花楹 于
2018-1-29 19:51:50 说:
回复 @羽夏 :
可惜文章的结局好悲催……(T_T)
羽夏 于
2018-1-29 19:39:02 说:
这一波秀的有水平~hen专业呀~