屏幕不够,算法来凑(一):Ditherpunk 抖动算法原理与 JS 实时演示

屏幕不够,算法来凑(一):Ditherpunk 抖动算法原理与 JS 实时演示

背景

在嵌入式开发领域,我们经常会遇到色彩位数极低的显示设备:

  • 经典的 SSD1306 (0.96寸 OLED),仅支持黑白两色。
  • 电子墨水屏 (E-Ink),通常只有黑白,且刷新率极低。

如果直接将 24 位真彩图片进行量化处理,其结果往往如同烧焦的木炭,细节丢失殆尽。但若引入 抖动算法(Dithering),这些 1-Bit 屏幕便能模拟出细腻的灰度感。

此前我曾尝试用 Rust 实现过一个版本,但作为博客演示,使用 JavaScript 与 Canvas 在浏览器中直接进行仿真最为直观。本文将介绍几种主流抖动算法的原理及其 JS 实现。


阅读更多

使用Bindgen为ELK生成Rust绑定

介绍

bindgen 是一个能自动为 C(或 C++)库生成 Rust 绑定的辅助库和命令行工具。

elk 是一个迷你的JS引擎.
能够实现类似于这样的效果

main.rs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
##include <stdio.h>
##include "elk.h"

// C function that adds two numbers. Will be called from JS
jsval_t sum(struct js *js, jsval_t *args, int nargs) {
if (nargs != 2) return js_err(js, "2 args expected");
double a = js_getnum(args[0]); // Fetch 1st arg
double b = js_getnum(args[1]); // Fetch 2nd arg
return js_mknum(a + b);
}

int main(void) {
char mem[200];
struct js *js = js_create(mem, sizeof(mem)); // Create JS instance
js_set(js, js_glob(js), "sum", js_mkfun(sum))); // Import sum()
jsval_t result = js_eval(js, "sum(3, 4);", ~0); // Call sum
printf("result: %s\n", js_str(js, result)); // result: 7
return 0;
}

如果这个执行内容来自于服务器下发,那就可以很方便地动态下发程序然后执行特定的任务.

阅读更多

为 AdSense 铺路:我如何修复 'GSC 重复网页' 与 Canonical 致命错误(Hexo, 301, robots 详解)

背景

之前发布的MCompass项目文章站从Cloudflare后台分析来看, 流量可观.
但是考虑到因为版权问题无法售卖装置变现,所以决定采取流量变现的方式. 国内的广告联盟需要备案, 而本站使用Cloudflare解析到境外的Pages服务器, 这对于备案来说不是很合规, 最后决定接入AdSense, 结果没想到申请多次都收到以”需要采取行动:您的 AdSense 申请“为标题,内容是”感谢您关注 Google AdSense。经审核,您的申请需要稍作调整,才能获得批准。您完成调整后,我们的专家会审核您的网站是否符合我们的计划政策。“的邮件.

最终决定对本站进行一次彻底的SEO优化.

阅读更多

Claude Code初体验

背景

之前买的Jetpack Compose教程的群群主最近一直在称赞Codex,Claude Code之类的有多么强大, 激发了我对这种CLI式的AI兴趣.是时候研究一下了.
claude

初步了解

在哔哩哔哩上简单搜索到几个介绍视频, 尤其是看到了Codex的那个演示, 一个人下发多个任务,然后每个任务后面都有一个AI在不同分支为他自动修改代码,调试代码直到完成任务,更是迫不及待的体验一下这种一个人就是一个团队的力量.

阅读更多

踩坑与收获:2025独立开发者Google Play上架实战复盘

背景与初衷

入行安卓开发已有一段时间。学生时期曾注册并使用过
Google Play 开发者账号,在当时的审核环境下完成过
多款应用的发布与实践。

随着平台政策和合规要求的持续调整,
该历史账号已主动终止使用,相关应用亦已停止维护。
该阶段的经历主要作为个人技术成长过程的一部分予以记录。
play_store_old_account_screenshot

阅读更多

声明式 UI 架构下的生命周期演进:从内建属性到显式订阅

消失的“实体”: 从View到Composable/Widget

在原生Android开发的世界, 创建模版项目后就会得到一个MainActivity和activity_main.xml.
我们习惯了在MainActivity里去绑定ButtonText再设置各种属性. 他们都是内存中看得见,摸得着的View.
但是到了Flutter中, 得到的了却是main()函数和一堆StatelessWidgetStatefulWidget.
不仅仅是Flutter, 连Jetpack Compose也在向这种’声明式UI’靠拢.
Compose中同样找不到onResume(入口Activity除外),取而代之的而是层层嵌套的Composable函数

这种转变本质上是:我们不再持有 UI 的“句柄”,我们只持有“数据”。

正因为我们持有的只是数据,而数据本身是没有‘前后台’概念的(一个 String 字符串哪里知道自己是否在前台?)。所以,在声明式 UI 中寻找 onResume 本身就是一个伪命题。
只有当数据需要根据系统状态(如用户回来了)进行刷新时,我们才去主动询问系统。

生命周期演进图

阅读更多

在LVGL中实现可变字体(Variable Font)-第一章

前言

(2025年11月 重制版说明):
这篇文章的初版我曾发布于第三方平台(简书+Bilibili),并累计获得了50,000+ 次阅读 和大量开发者的反馈。
为了提供更好的阅读体验,我对文章排版和部分内容进行了优化,并将其独家发布在此个人博客

不知道多久前看到了MIUI更新了”动态字体系统“功能,不过当时没太在意(毕竟我用的也不是MIUI,哈哈哈,不过确实挺方便的),演示视频里面展示了随意调节字体粗细的功能,后来知道这个参数叫做字重(zhong第四声).

阅读更多

Fate Grand/Order 官方抽卡数据统计

没错,就是CBA池子沉了结论:SR和SSR的出货概率符合官方公示的数据,只是我运气太差了

19年8月30日晚7点,坐标上海唐镇,马路上一弱智掏出手机领完3周年签到补贴300石头后开始抽卡,10S后抽卡结束,没有看到CBA。

我抽了,1秒十连有什么好说的,一定是阿B在后台动了出货概率!

我一定要调查清楚,不出货肯定不是我的原因。
这时候想起了Fgo的B站官网有个实时滚动的出货信息,打开Chrome,F12查看调试信息。
得到如下两个链接,

阅读更多

Other 训练一个自己的YOLO-tiny模型

训练一个自己的YOLO-tiny模型0x01 YOLO是什么

YOLO是一个实时目标检测系统,通俗点说就是在输入数据(图片或者视频)中查找特定的目标。举个例子,如果让一个专门识别龙的YOLO模型观看《权力的游戏》,在理想情况下,一旦画面中出现了龙,YOLO系统就会激动地用框框标记出画面中的龙。

为什么是YOLO-tiny

大概是贫穷限制了我的运算速度吧,YOLO-tiny用精确度换来了速度快和性能要求低的优点,适合练手和学习或者像我一样玩一玩的用户。

阅读更多

Code FFmpeg on Android

移植FFmpeg到Android上

Ffmpegonandroidp1

因为Mix Music解码的需求,所以得选择合适的解码工具.尝试了4种解码方式,最后还是FFmpeg的效果最好

  • MediaCodec配合MediaExtractor进行解码操作
  • MediaCodec不用MediaExtractor进行解码操作
    • 在给Bytebuffer填充数据后,MediaCodec处理数据的时候总出错,大概是因为没有跳过非帧数据的部分
  • 使用LAME进行解码操作
    • 不幸的是LAME中的hip_decode()也是只能处理帧数据,需要手动跳过非帧数据.当手动跳过非帧数据后,最终发现速度并没有提升多少(尽管已经设置了不同大小的buffer)
  • 使用FFmpeg进行解码操作
    • 编译的时候真的是各种错误,头文件明明就在那里呆的好好的,编译器还是报找不到函数的错误.好在最终效果令人非常满意,3秒钟解码一个音频文件.
阅读更多