duangsuse::Echo
583 subscribers
4.12K photos
118 videos
579 files
6.13K links
import this:
美而不丑、明而不暗、短而不凡、长而不乱,扁平不宽,读而后码,行之天下,勿托地上天国。
异常勿吞,难过勿过,叹一真理。效率是很重要,盲目最是低效。
简明是可靠的先验,不是可靠的祭品。
知其变,守其恒,为天下式;穷其变,知不穷,得地上势。知变守恒却穷变知新,我认真理,我不认真。

技术相干订阅~
另外有 throws 闲杂频道 @dsuset
转载频道 @dsusep
极小可能会有批评zf的消息 如有不适可退出
suse小站(面向运气编程): https://WOJS.org/#/
Download Telegram
#fix #cs 首先说两件错误:

1. #Python #cg #font 关于 freetype.py 的矩阵循环命名不当

出事的消息 👉

首先,不得不说 duangsuse 不是完全的了解 Python(比如说,我不理解 Python 元编程比如运算符重载、没有完整使用 Python 函数式编程、decorator,map 类数据结构,和 assert 等特性...),不过这其实也没啥

然后在吐槽丸自己后,我们来看看不对在哪里

def draw_bitmap(image, bitmap, x_pos=0, y_pos=0):
x_max = x_pos + bitmap.width
y_max = y_pos + bitmap.rows
for row, i in enumerate(range(x_pos, x_max)):
for col, j in enumerate(range(y_pos, y_max)):
if i < 0 or j < 0 or i >= WIDTH or j >= HEIGHT:
continue
pixel = image.getpixel((i, j))
font_pixel = bitmap.buffer[col * bitmap.width + row]
pixel |= int(font_pixel) * ALPHA
image.putpixel((i, j), pixel)

为了阅读方便,我删除了注释、内联文档并且修改了一些语义无关的缩进。

这个函数呢,其实很简单,我们这么描述它:

draw_bitmap 一个『原图』(像素积通常比『叠加图』要大)和一个『叠加图』,再给一个『叠加图 x 轴偏移量』一个『叠加图 y 轴偏移量』
draw_bitmap 会按照偏移量将叠加层混合到原图上(超出的部分忽略),并且利用 putpixel 设置原图像素,最后返回空

它是怎么做的呢?(算法)

我们假设有一个平直角座标系(并且,假设你的图片按照『最符合常识』的方法被放在第一象限),这就是我们对『Python Pillow Image』的高层抽象,我们忽视颜色系统等细节。

首先,假设我们确保原图一定比叠加大,那么,简单的算法可以这么描述:

foreach bx by. ox = bx | ix, oy = by | iy

或者说,就是简单的叠加混成

那么,其实这个混成算法就是:

1. 算出添加『绘制』偏移量(像素)后最大的 x 轴和 y 轴索引
2. 循环,枚举『原图』偏移绘制化后的索引 ix iy,并且还要枚举『叠加图』的索引 bx by:
假若索引越了原图的索引范围(不可能越叠加图的索引,因为我们是要枚举它的全部索引的,而对它是不加偏移量的),跳过
否则,用索引去拿 i 图和 b 图的像素,混合(按位与运算,RGB 颜色系统 int32 格式里可以这么做,不能用加法,因为这 32 位是分成 3 部分 0x0-0xff 256 色深而不是真正的 integer)
然后设置原图像素

== 那么错在哪里?

其实算法没有错(我抄的,我承认,而且这也不是很厉害的算法,甚至有点幼稚,也没用三角函数等 CG 库用户级别基础...)

    for row, i in enumerate(range(x_pos, x_max)):
for col, j in enumerate(range(y_pos, y_max)):

下面循环算法结构相关的代码:

if i < 0  or j < 0 or i >= WIDTH or j >= HEIGHT: continue
pixel = image.getpixel((i, j))
font_pixel = bitmap.buffer[col * bitmap.width + row]

为啥不对呢?命名名错了。

所谓的 row,列,所谓的 col,行,离散数学里会提到的,二维矩阵基础,不熟悉则可以说无法搞任何的信号处理、线性代数、计算机图形学、(尤其是)关系代数,数据分析怕不是都有点困难户
所谓学计算机科学需要数学功底,其实很多都是这种的,离散数学不知道别想毕业(虽然现在很多大学 CS 比较水... 真的,就是搞搞运维,还不如软件工程的呢... 然鹅,我觉得不知 NS 图、UML 是啥的大有人在

我恰巧不熟悉,其实它是这么说:

for ix, bx in enum_with_index(ixs, ixe):
for iy, by in enum_with_index(iys, iye):
... 这段代码会遍历两个图片指定像素偏移后 b 与 i 重叠的部分

bitmap.buffer[col * bitmap.width + row]

后面这一行代码,稍微有点常识的人🐸 不难看出, 根本不对!访问计算表达式应该是 row * width + col(数学公式里 row 可能叫 n、col 可能叫 m)(这也体现出了“反直觉”『从 0 数』的一个好处,另外一个不识二进制人不知道的事情是,C 语言里数组指针的第一个元素的确是从 0 这个偏移量开始的,往后 n 个元素就是 *(ary + n * sizeof(t)) 这个数据,n = 0 时恰巧是第一个元素,当然即便是 CLR 也支持非零基的和多维度的数组,不过和那个无关,等到你们开始用 JDK 的高层抽象后,就不识得 C++ 的 std::arraystd::vector 了... 所见不过是『一组有序数据』... 那一层的直觉的确是『从 1 数』... 说起来其实不知道的人现在还蛮多的...)

那么错就在这里:

for row, i in enumerate(range(x_pos, x_max)):
for col, j in enumerate(range(y_pos, y_max)):

实际是(而且我代码缩进也有点莫名其妙了,我随了大流,其实这两个不应该显示出其嵌套关系的,因为是『平级』的枚举):

for row, n in enumerate(range(x_start, x_end)):
for col, m in enumerate(range(y_start, y_end)):

完美了!(跑)

2. 关于『第一个 x86 汇编程序』对机器栈的理解和模拟错误

出事的消息 👉

首先,会写 x86 / arm 汇编(或者 C inline assembly、或者 LLVM IL)本来就不应该被视为很厉害的技能,因为上个世纪的人打孔带、机器代码(可能也是利用打孔带,或者... 什么?)都手写过了(当然不是这个世纪的计算机可能)

(当时我写的是 intel 风格 386-compatible 的汇编)(其实是 AT&T 风格的... 我更喜欢 intel 风格的其实)

而且,按照老冯(冯·诺依曼)的说法,我们怎么能用 assembler 呢!怎么能用邪恶的 assembler 浪费处理器(即便是 CPU 而不是更没算力的 MCU、PLC...) cycle 呢?
当然都过去了哈哈,现代的计算机一秒内不知能进行多少整数运算... 还有并行处理呢!如果有显卡浮点也不成问题

duangsuse 该被批判的(

所以下次再敢膜我有时候会直接写汇编什么的,小心被 😫 哦(跑

sysprintf:    
mov %rsp, %rbp # stdcall # 不应该是 stdcall,其实这里我不关心到底是 stdcall、fastcall 还是 cdef 什么的,因为这个函数是被仅仅内部使用(hidden)的,题外话,其实这句没有用,这层栈帧不需要任何空间

mov $1, %rax # sys_write
mov $1, %rdi # use stdout

pop %rsi # string ptr # 错!先 pop 出来的明明是长度而不是数据指针,后 push 的($20)先 pop,实际上我被 rsi 这个名字(我以为是 source indicator)糊住了
pop %rdx # length # 弄反了!

syscall

ret

以上,不过顺便说一句,虽然现在因为某些原因(想做一些好玩的工程... 不过,话说其实我并不懂字面意义的软件工程学科)duangsuse 『停止』了信息科学/计算机科学的学习,但是,还是能发现自己的『智商』在提高!因为模拟分析能力得到了大幅增强!
#Font #CG 这项功能被称为 Ligature, 连字, HarfBuzz 文字造型系统里有支持, FreeType 里已经没有这种 OpenType "Layout Font" 扩展了...