Yahoo! Logo ASCII Animation in 462 bytes of C
Last week I put together another obfuscated C program and have been urged by my coworkers to post it publicly. I've made some refinements since posting it to our internal list, so here is the final version (to those who had seen it already: it's one line shorter now, and the angles are less screwy, and the animation is 2 seconds instead of 3). Go ahead, try it:
$ cat >yanim.c
c,p,i,j,n,F=40,k,m;float a,x,y,S=0,V=0;main(){for(;F--;usleep(50000),F?puts(
"\x1b[25A"):0)for(S+=V+=(1-S)/10-V/4,j=0;j<72;j+=3,putchar(10))for(i=0;x=S*(
i-27),i++<73;putchar(c[" ''\".$u$"]))for(c=0,n=3;n--;)for(y=S*(j+n-36),k=0,c
^=(136*x*x+84*y*y<92033)<<n,p=6,m=0;m<8;k++["<[\\]O=IKNAL;KNRbF8EbGEROQ@BSX"
"XtG!#t3!^"]/1.16-68>x*cos(a)+y*sin(a)?k=p,p="<AFJPTX"[m++]-50:k==p?c^=1<<n,
m=8:0)a=(k["O:85!fI,wfO8!yZfO8!f*hXK3&fO;:O;#hP;\"i[by asloane"]-79)/14.64;}
^D
$ gcc -o yanim yanim.c -lm
[warnings which real programmers ignore]
$ ./yanim
[you'll see - show the animation]
...uuuu$$$$$uuuu... $$$$$$$uuuuuu
..u$$$$$$$$$$$$$$$$$$$$$$$u.. $$$$$$$$$$$$'
.u$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$u. $$$$$$$$$$$$
.$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$. $$$$$$$$$$$"
.$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$. $$$$$$$$$$$
u$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$u $$$$$$$$$$$
.$$$$$$$ $$$$$$$$$$$$$$$$$$$$$$$. $$$$$$$$$$'
$$$$$$$$$$$$$u. uu$$$$$''''''''''''''$$$$$$$ $$$$$$$$$$
$$$$$$$$$$$$$$$$. $$$$$$.. ..$$$$$$$$$ $$$$$$$$$"
$$$$$$$$$$$$$$$$$u "$$$$$$"' u$$$$$$$$$$$$$ $$$$$$$$$
u$$$$$$$$$$$$$$$$$$$ '$$$"' u$$$$$$$$$$$$$$$u $$$$$$$$$
$$$$$$$$$$$$$$$$$$$$$. '"' u$$$$$$$$$$$$$$$$$$ $$$$$$$$
$$$$$$$$$$$$$$$$$$$$$u .u$$$$$$$$$$$$$$$$$$$ $$$$$$$$
$$$$$$$$$$$$$$$$$$$$$$u u$$$$$$$$$$$$$$$$$$$$$ $$$$$$$"
'$$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$$' $$$$$$$
"$$$$$$$$$$$$$$$$$$$$$ $$$$$$$$$$$$$$$$$$$$" ""$$$$$
'$$$$$$$$$$$$$$$""""" """""$$$$$$$$$$$$$$'
"$$$$$$$$$$$$$................$$$$$$$$$$$$" ..
'"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"' $$$$$$$u
'"$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$"' $$$$$$$'
'""$$$$$$$$$$$$$$$$$$$$$$$$$""' .$$$$$$$
''"""$$$$$$$$$$$$$"""'' '"""""$$
'''''
It's a 20fps, antialiased ASCII art animation of the Yahoo! logo. If you want to figure out how it works on your own, you're welcome to. Otherwise, read on.
I encourage you to play with the constants in the code: S+=V+=(1-S)/10-V/5 is the underdamped control system for the animation -- S is scale (=1/zoom), V is velocity, and 1/10 and 1/5 are the PD constants. S=0 corresponds to infinite zoom on the first frame. S<0 is funny. F is the frame counter. The 1.16 controls the scale of the polygon rendering (68 is an approximation of 79/1.16 so you have to adjust that too), and 136/84/92033 define the ellipse. The 14.64 is not a tunable parameter, though (it's 46/π, and for a good reason).
The antialiasing is simple: each character consists of three vertically-arranged samples and an 8-character lookup table for each arrangement of three on/off pixels. Each frame consists of 73x24 characters, or 73x72 pixels. The 73 horizontal choice was somewhat arbitrary; I suppose I could have gone up to 79.
The logo is rendered as an ellipse and eight convex polygons using a fairly neat method (I thought) with sub-pixel precision and no frame buffer. It required some design tradeoffs to fit into two printable-character arrays, but it's much less code than rendering triangles to a framebuffer, which is the typical way polygon rasterization is done.
To produce this, first I had to vectorize the "Y!" logo. I did this by taking some measurements of a reference image and writing coordinates down on graph paper. Then I wrote a utility program which takes the points and polygon definitions and turns them into angles and offsets as defined below. [I put the generator code on pastebin until I get can some code highlighting stuff set up for my blog].
The ellipse is fairly standard high-school math: x2/a2 + y2/b2 < 1. Each point is tested and if it's inside the ellipse, the pixel is plotted. (136x2 + 84y2 < 92033 was a trivial rearrangement of terms with a and b being the radii of the two axes of the ellipse measured from my source image, scaled to the pixel grid).
Each polygon is made up of a set of separating half-planes (a half-plane being all points on one side of an infinitely long line). If a given point is "inside" all of the half-planes, it's inside the polygon (which only works as long as the polygon is convex) and the pixel is toggled with the XOR operator ^ (thus it handles the "inverse" part inside the ellipse as well as the uninverted exclamation mark without any special cases). Each side of a polygon is defined by the equation ax + by > c. To represent both a and b I use an angle θ so that a = cos(θ) and b = sin(θ) and quantize the angle in π/46 increments — my angles are thus represented from -π to +π as ASCII 33 to 125 — '!' to '}' — with 'O' (ASCII 79) as zero. Then I solve for c, also quantized in scaled increments from -47 to +47, so that the midpoint of the side is considered inside the polygon.
Here's an extremely crude diagram: (I'm writing this on a plane and none of my drawing programs are working. Sorry.)

The shaded area is ax + by < c, implying it's outside the polygon, and the dashed line is ax + by = c.
(a,b) form a vector orthogonal to the line segment they represent pointing towards the inside of the polygon, so we can get them directly from the points defining the line segment by taking the vector defining the side — (x1 - x0, y1 - y0) — and rotating it 90 degrees, resulting in (a, b) = (y1 - y0, x0 - x1). Then we normalize (a, b) as the actual magnitude doesn't matter, but it will be 1 when we decode the angle and we can compensate with our choice of c later (if ax+by>c, then sax+sby>sc for some scale s>0). Then compute θ = atan2(a,b), quantize to one of our 94 angles, and get our new (a,b) = (cos(θ), sin(θ)).
c is easy to get by directly substituting any of the points making up the line on the side of the polygon into c = ax+by. I use the midpoint of the line segment on the side, (xt, yt) = ((x0 +x1)/2, (y0 + y1)/2), because the angle of the side can be slightly off after we quantize θ, and this evens the errors out across the length of the side.
You'll notice on the first couple frames (you can pause with ^S, resume with ^Q -- xon/xoff) that the bottom section of the 'Y' has little bites taken out of it due to the quantization error in the separating half-plane equations.
It could probably be made somewhat more efficient CPU-wise by careful reordering of the separating plane arrays so that most of the drawing area is rejected first. I didn't get to that in my generator code.
The animation is done by the <ESC>[25A sequence — it moves the cursor up 25 lines in just about any terminal emulation mode. I technically only need to move up 24 lines, but puts is shorter than printf and it implicitly adds a newline. If your terminal isn't at least 26 lines high, though, it does funky things to your scrollback. And usleep is there to limit it to 20fps, which is the only non-ANSI Cism about it.
And then I shrunk the code down by arranging it into clever for loops and taking unorthodox advantage of commas, conditionals, and globals being ints by default in C (which is all par for the course in obfuscated C code). And that pretty much reveals all the secrets as to how it was done.
It would be fairly easy to enhance this with a different movement sequence, or rotation (or any kind of 3D transform, as it's basically just ray-tracing the logo). I just animated the scale to prove the point that it was being rendered dynamically and not just a compressed logo, and kept the animation short and sweet.
I apologize in advance for the various sign errors I'm sure to have made when typing this up, but you get the idea.
Yahoo! Logo ASCII Animation in 462 bytes of C的更多相关文章
- booting logo & booting animation
開機第一張圖片: 圖片位置: linux_repo/vendor/mediatek/proprietary/bootable/bootloader/lk/dev/logo 因為 project 選用 ...
- 【C语言编程学习笔记】利用462字节代码实现雅虎logo ACSII 动画!
ACSII 动画演示: 不过本文介绍的是另一个作品:c 代码实现雅虎 logo ACSII 动图. 运行后,你将会看到: 它是一个 20fps.抗锯齿的 Yahoo! logo ASCII 动 ...
- 异常空格,ASCII (194,160)问题
今天运营的同学反映有一些店铺的名称后面带空格,我下意识的说不可能啊,我已经处理过了啊.然后就找出来看. 其中有个店铺的名称是“安踏 ”,第一眼看上去好像是带了个空格.然后我就仔细的看了下. pry(m ...
- 二进制;16进制; Byte , Python的bytes类; Base64数据编码; Bae64模块;
参考:中文维基 二进制 位操作(wiki) Byte字节 互联网数据处理:Base64数据编码 Python的模块Base64 16进制简介 python: bytes对象 字符集介绍:ascii 二 ...
- Python3中的bytes和str类型
Python 3最重要的新特性之一是对字符串和二进制数据流做了明确的区分.文本总是Unicode,由str类型表示,二进制数据则由bytes类型表示.Python 3不会以任意隐式的方式混用str和b ...
- Transact-SQL 数据类型转换
Syntax Syntax for CAST: CAST ( expression AS data_type [ ( length ) ] ) Syntax for CONVERT: CO ...
- python基础知识(四)
摘要:主要涉及lambda表达式.python内置函数(open文件重点).冒泡排序 一.lambda表达式 适用于创建简单函数,也叫匿名函数, 函数名 = lambda 参数 : 返回值 funct ...
- Python3使用urllib访问网页
介绍 改教程翻译自python官网的一篇文档. urllib.request是一个用于访问URL(统一资源定位符)的Python模块.它以urlopen函数的形式提供了一个非常简单的接口,可以访问使用 ...
- 初始python第三天(三)
全局变量与局部变量 1.什么是全局变量 在globals中的变量,都是全局变量,全局变量的作用域就是整个程序 NAME = 'alex' def global_test(): name = 'alex ...
随机推荐
- div居中的三种方法
方法1: #div1{ width:200px; height:200px; background:green; position:absolute; left:0; top:0; right:0; ...
- 关于Socket通讯原理
通常也称作"套接字",用于描述IP地址和端口,是一个通信链的句柄. 在Internet上的主机一般运行了多个服务软件,同时提供几种服务. 每种服务都打开一个Socket,并绑定到一 ...
- css扁平化博客学习总结(二)css样式重置
css样式重置 方法一:不推荐使用,这么写会让网页解析速度变慢. *{ margin: 0; padding: 0;} 方法二:大家常用的写法,比较流行. body, html, div, block ...
- 序列化和反序列化(C#)
有时候我们希望把类的实例保存下来,以便以后的时候用.一个直观的方法就是StreamWriter把类写成一行,用\t分隔开每个属性,然后用StreamReader读出来. 但是这样太麻烦,代码行数较多, ...
- DropdownListFor无法正确绑定值-同名问题
DropdownListFor无法正确绑定值 如果以下面的方式进行绑定: <%: Html.DropDownListFor(model => model.subType, ViewBag ...
- ### core文件使用
在Linux下程序崩溃,特别是在循环中产生Segment Fault错误时,根本不知道程序在哪出错,这时,利用core文件可以快速找到出错的问题所在. #@author: gr #@date: 201 ...
- IOS 高级开发 KVC(一)
熟练使用KVC 可以再开发过程中可以给我们带来巨大的好处,尤其是在json 转模型的时候,KVC让程序员摆脱了繁琐无营养的代码堆积.减少代码量就是减少出错的概率.KVC 用起来很灵活,这种灵活的基础是 ...
- C#基础总复习02
继续更新第二篇: 1:一元运算符:++ -- ++:不管是前加加还是后加加,变量的值最终都会自身加一. 前加加和后加加的区别体现在参与运算的时候,如果是后加加,则首先拿原值参与运算, 运算完成后再自身 ...
- [译]Autoprefixer:用最可行的方式处理浏览器前缀的CSS后处理器
Autoprefixer,通过Can I Use数据库来确定哪些浏览器前缀是需要的,然后解析CSS文件,将前缀添加到CSS规则里. 你所要做的就是添加你的资源构建工具(比如:Grunt),然后你就可以 ...
- 函数strtok
char* strtok(char *str, const char*delim) char *strtok_r(char *str, const char *delim, char **savept ...