作业

原因

最近逛b站时发现一个宝藏up主:鉴无虚发QQ涂鸦(uid402778937),他利用QQ不久前更新的涂鸦功能进行创作,绘制了许多有趣的涂鸦,还让粉丝群的成员发送一张图 片,他照着画出来。这让我很羡慕,可是我美术不行,加之用手机画画实在不是一键容易的事,手指太粗,屏幕太小,据up自述,完成一件创造大约需要30min,这让我这个急性子更难受。不过我会一点编程,于是想尝试写一个简单的程序来实现此功能。
Up
Chat Screenshot 1
Chat Screenshot 2

过程

准备

自己使用的手机用的安卓系统,安卓在安卓7.0(Nougat)为辅助功能(AccessibilityService)添加了

1
Boolean dispatchGesture(gesture: GestureDescription, callback: AccessibilityService.GestureResultCallback?, handler: Handler?)

来在触摸屏上执行一个手势。
我们可以利用这个方法来实现涂鸦的功能。不过原生安卓开发很麻烦,受时间限制,采取跨平台的Auto.js来编写js脚本实现。
我把步骤分为两步
1读取图片并出来成类似简笔画的效果
2在屏幕上画出来

图片处理

处理过程见代码了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
var img = images.read("/storage/emulated/0/n.jpg")//读取图片
var img_w = img.getWidth()
var img_h = img.getHeight()
var img_l = images.fromBase64("iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAABHNCSVQICAgIfAhkiAAAAAtJREFU\nCJlj+A8EAAn7A/3jVfKcAAAAAElFTkSuQmCC\n")//这是一个1*1px的白色图片的base64编码
var img_l_o = img_l//做一个备份
var length = 160//定义处理后图片大小
img = images.resize(img, [length - 1, length], "LANCZOS4")//缩放图片尺寸
//img = images.resize(img, (width > height) ? [size, size * height / width] : [size / height * width, size]);
//img = images.grayscale(img)
var th=colors.red(images.resize(img, [64, 64]).pixel(0,0))
img = images.threshold(img, 150,255) //越大越暗
for (var i = 0; i < length - 1; i++) {
img_l = images.concat(img_l_o, img_l, "TOP")
}
img = images.concat(img, img_l)
images.save(img, "/storage/emulated/0/QQBrowser/图片收藏/09.png")//把处理好的图片保存到本地

模拟绘制

  • 1.让我们先运行Auto.js脚本!
    以下是上面脚本剩余的部分:
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    21
    22
    23
    24
    25
    26
    27
    28
    29
    30
    31
    32
    33
    34
    35
    36
    37
    38
    39
    40
    41
    42
    43
    44
    45
    46
    47
    board = id("bo7").findOne()//寻找涂鸦控件
    //记录控件的位置和大小
    rect = board.bounds()
    rect_w = rect.width()//绘制区域宽度
    rect_h = rect.height()//绘制区域高度
    rect_l = rect.left//绘制区域左上角的屏幕x坐标
    rect_t = rect.top//绘制区域左上角的屏幕y坐标
    var isPaint = false
    //穷举像素点
    //ox是笔画起点在图片的x坐标,oy是笔画起点在图片的y坐标
    for (var y = 0; y < length; y++) {
    for (var x = 0; x < length; x++) {
    color = img.pixel(x, y)//获取像素点颜色
    if (colors.red(color) < 128) { //如果当前点的颜色中Red<128
    //如果未记录笔画起始点,则记录笔画起点
    if (!isPaint) {
    ox = x
    oy = y
    isPaint = true
    }
    } else {
    //如果记录了笔画起点,就绘制这个笔画,并且清除状态
    if (isPaint) {
    line((ox + 0.5) / length, oy / length, (x - 0.5) / length, y / length)
    isPaint = false
    }
    }
    }
    }

    //根据图片上的两点,相应地在屏幕上模拟滑动,以绘制笔画
    function line(ox, oy, x, y) {
    var k = img_h / img_w
    var sizeX = 0.9
    var sizeY = 1
    if (k > 1) {
    sizeX = sizeX / k
    } else {
    sizeY = sizeY * k
    }
    ox = (ox - 0.5) * sizeX + 0.5
    oy = (oy - 0.5) * sizeY + 0.5
    x = (x - 0.5) * sizeX + 0.5
    y = (y - 0.5) * sizeY + 0.5
    swipe(ox * rect_w + rect_l, oy * rect_h + rect_t, x * rect_w + rect_l, y * rect_h + rect_t, 10)
    }
    toast("finish")
  • 2.稍微等待一会儿,图片处理完毕,打开QQ的涂鸦界面,即会自动绘制
  • 3.稍作等待,涂鸦就画好了~

效果

我一开始处理出来是这种黑白图:
Old Image
实际上效果不太好,现在处理出来的图片绘制效果就很棒:
New Image
最终绘制出的涂鸦如下:
Effect

总结

我们写完上面的代码,测试了效果,发现想要实现这个功能一点儿也不难。只要我们愿意做,没有什么是做不到的。
同时我们也能发现脚本实际上还是有提升的空间的。
QQ的涂鸦是支持彩色的(如下图颜色选择器),而我们的脚本现在只有单一的颜色,画面是不是展现得不如彩色的涂鸦生动呢?
Color Picker Screenshot
实际上,脚本理论也可以模拟进行颜色的选择,我们将在将来对脚本进行改进,使得涂鸦更具有表现力。