惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

L
LangChain Blog
B
Blog RSS Feed
阮一峰的网络日志
阮一峰的网络日志
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
H
Help Net Security
MyScale Blog
MyScale Blog
WordPress大学
WordPress大学
Microsoft Azure Blog
Microsoft Azure Blog
GbyAI
GbyAI
小众软件
小众软件
大猫的无限游戏
大猫的无限游戏
Martin Fowler
Martin Fowler
Vercel News
Vercel News
S
SegmentFault 最新的问题
M
MIT News - Artificial intelligence
Microsoft Security Blog
Microsoft Security Blog
G
Google Developers Blog
Last Week in AI
Last Week in AI
Hugging Face - Blog
Hugging Face - Blog
酷 壳 – CoolShell
酷 壳 – CoolShell
博客园 - 【当耐特】
Google DeepMind News
Google DeepMind News
Engineering at Meta
Engineering at Meta
云风的 BLOG
云风的 BLOG

博客园 - 秋·风

修复lazarus datetimepicker控件在linux下拉不能选择日期的bug 用fpc trunk编译lazarus控件找不到TTimeZone 在银河麒麟v10操作系统应用程序执行遇到“权限不够”的提示 修复pl_cindy的Panel系列控件在gtk3出错的bug 龙芯 3A4000工具链的编译(mingw64) lazarus 4.8及之前的版本QT5中文输入多字词组时只输入前2个中文 修正lazarus在GTK3下的Synedit只能输入2个中文字符的Bug lazarus trunk linux从2026.07.02开始,LCLWidgetType默认使用gtk3,怎样改回gtk2 【aarch64-win64】DonAlfredo终于修复fpc将TSmallPoint赋值为一个负数,并将TSmallPoint赋值给TPoint时得到一个655xx的数值的bug lazarus编译时提示链接出错的处理方法 mORMot2两个BUG 【aarch64-win64】发现fpc aarch64-win64 将TSmallPoint赋值为一个负数,并赋值给TPoint时得到一个655xx的数值的bug 修复aarch64 win64下lazarus不弹出SourceTabPopUpMenu fpc aarch64-win64版编译应用时遇到一个奇怪的bug 在windows 11 on arm64系统编译lazarus出错的处理方法 修复TDBMemo只读不能使用Ctrl+C复制文字的Bug freepascal打补丁后已支持windows 11 on arm64(lazarus aarch64 windows版也可以运行了) 查看libc.so.6的版本号 Lazarus移动版启动程序 fpc 3.2.2交叉编译i386-linux链接出错 lazarus鸿蒙开发12:使用鸿蒙原生API画图--鸿蒙侧关键代码 lazarus鸿蒙开发11:使用鸿蒙原生接口画图 lazarus鸿蒙开发10:lazarus一键编译、打包助手 lazarus鸿蒙开发9:使用命令行编译DevEco Studio应用 lazarus鸿蒙开发8:在DevEco Studio的日志窗口显示日志信息 lazarus鸿蒙开发7:在 Windows 上为 OpenHarmony (LoongArch64) 编译 Qt 5.15.12 SDK 指南 lazarus鸿蒙开发6:鸿蒙project配置 lazarus鸿蒙开发5:编译ohos_hap_project lazarus鸿蒙开发4:编译OHOS_QT_Lazarus lazarus鸿蒙开发3:编译libLazarusOHOS_Wrapper.so
lazarus鸿蒙开发13:使用鸿蒙原生API画图--lazarus侧关键代码
秋·风 · 2026-06-10 · via 博客园 - 秋·风

OHOS_Lazarus.lpr:

library OHOS_Lazarus;

{$mode objfpc}{$H+}
{$longstrings on}
{$packenum 4}   //确保 Pascal 中的枚举类型和记录体在内存中的对齐方式与 C 语言完全一致,避免内存对齐导致的读取错误
{$packrecords C}//确保 Pascal 中的枚举类型和记录体在内存中的对齐方式与 C 语言完全一致,避免内存对齐导致的读取错误

uses
  Classes, SysUtils, CTypes, Hilog, drawing_canvas;

const
  LIBNAME = 'libnative_drawing.so';

type
  TOH_Drawing_ColorFormat = (
    COLOR_FORMAT_UNKNOWN, COLOR_FORMAT_ALPHA_8, COLOR_FORMAT_RGB_565,
    COLOR_FORMAT_ARGB_4444, COLOR_FORMAT_RGBA_8888, COLOR_FORMAT_BGRA_8888
  );

  TOH_Drawing_AlphaFormat = (
    ALPHA_FORMAT_UNKNOWN, ALPHA_FORMAT_OPAQUE, ALPHA_FORMAT_PREMUL, ALPHA_FORMAT_UNPREMUL
  );

  TOH_Drawing_BitmapFormat = record
    colorFormat: TOH_Drawing_ColorFormat;
    alphaFormat: TOH_Drawing_AlphaFormat;
  end;
  POH_Drawing_BitmapFormat = ^TOH_Drawing_BitmapFormat;

// --- 外部函数声明 ---
function OH_Drawing_BitmapCreate: POH_Drawing_Bitmap; cdecl; external LIBNAME;
procedure OH_Drawing_BitmapDestroy(bitmap: POH_Drawing_Bitmap); cdecl; external LIBNAME;
procedure OH_Drawing_BitmapBuild(bitmap: POH_Drawing_Bitmap; width: UInt32; height: UInt32; format: POH_Drawing_BitmapFormat); cdecl; external LIBNAME;
function OH_Drawing_BitmapGetPixels(bitmap: POH_Drawing_Bitmap): Pointer; cdecl; external LIBNAME; // ★ 获取像素数据指针

function OH_Drawing_CanvasCreate: POH_Drawing_Canvas; cdecl; external LIBNAME;
procedure OH_Drawing_CanvasDestroy(canvas: POH_Drawing_Canvas); cdecl; external LIBNAME;
procedure OH_Drawing_CanvasBind(canvas: POH_Drawing_Canvas; bitmap: POH_Drawing_Bitmap); cdecl; external LIBNAME;
procedure OH_Drawing_CanvasClear(canvas: POH_Drawing_Canvas; color: UInt32); cdecl; external LIBNAME;
procedure OH_Drawing_CanvasAttachPen(canvas: POH_Drawing_Canvas; pen: POH_Drawing_Pen); cdecl; external LIBNAME;
procedure OH_Drawing_CanvasAttachBrush(canvas: POH_Drawing_Canvas; brush: POH_Drawing_Brush); cdecl; external LIBNAME;
procedure OH_Drawing_CanvasDetachPen(canvas: POH_Drawing_Canvas); cdecl; external LIBNAME;
procedure OH_Drawing_CanvasDetachBrush(canvas: POH_Drawing_Canvas); cdecl; external LIBNAME;
procedure OH_Drawing_CanvasDrawLine(canvas: POH_Drawing_Canvas; x0: Single; y0: Single; x1: Single; y1: Single); cdecl; external LIBNAME;
procedure OH_Drawing_CanvasDrawRect(canvas: POH_Drawing_Canvas; rect: POH_Drawing_Rect); cdecl; external LIBNAME;

function OH_Drawing_PenCreate: POH_Drawing_Pen; cdecl; external LIBNAME;
procedure OH_Drawing_PenDestroy(pen: POH_Drawing_Pen); cdecl; external LIBNAME;
procedure OH_Drawing_PenSetColor(pen: POH_Drawing_Pen; color: UInt32); cdecl; external LIBNAME;
procedure OH_Drawing_PenSetWidth(pen: POH_Drawing_Pen; width: Single); cdecl; external LIBNAME;

function OH_Drawing_BrushCreate: POH_Drawing_Brush; cdecl; external LIBNAME;
procedure OH_Drawing_BrushDestroy(brush: POH_Drawing_Brush); cdecl; external LIBNAME;
procedure OH_Drawing_BrushSetColor(brush: POH_Drawing_Brush; color: UInt32); cdecl; external LIBNAME;

function OH_Drawing_RectCreate(left: Single; top: Single; right: Single; bottom: Single): POH_Drawing_Rect; cdecl; external LIBNAME;
procedure OH_Drawing_RectDestroy(rect: POH_Drawing_Rect); cdecl; external LIBNAME;

// ★ 全局变量,保存位图以便外部获取像素
var
  g_Bitmap: POH_Drawing_Bitmap = nil;

// ★ 供外部调用的绘制函数
procedure DoDraw; cdecl; export;
var
  canvas: POH_Drawing_Canvas;
  pen: POH_Drawing_Pen;
  brush: POH_Drawing_Brush;
  rect: POH_Drawing_Rect;
  bitmapFormat: TOH_Drawing_BitmapFormat;
begin
  HILogInfo('=== 开始执行绘制 ===');

  // 1. 创建位图
  g_Bitmap := OH_Drawing_BitmapCreate();
  bitmapFormat.colorFormat := COLOR_FORMAT_RGBA_8888;
  bitmapFormat.alphaFormat := ALPHA_FORMAT_OPAQUE;
  OH_Drawing_BitmapBuild(g_Bitmap, 400, 400, @bitmapFormat);

  // 2. 创建画布并绑定
  canvas := OH_Drawing_CanvasCreate();
  OH_Drawing_CanvasBind(canvas, g_Bitmap);

  // 3. 白色背景
  OH_Drawing_CanvasClear(canvas, $FFFFFFFF);

  // 4. 画笔 (黑色描边)
  pen := OH_Drawing_PenCreate();
  OH_Drawing_PenSetColor(pen, $FF000000);
  OH_Drawing_PenSetWidth(pen, 5.0);
  OH_Drawing_CanvasAttachPen(canvas, pen);

  // 5. 画刷 (绿色填充)
  brush := OH_Drawing_BrushCreate();
  OH_Drawing_BrushSetColor(brush, $FF00FF00);
  OH_Drawing_CanvasAttachBrush(canvas, brush);

  // 6. 画线和矩形
  OH_Drawing_CanvasDrawLine(canvas, 10.0, 10.0, 390.0, 10.0);
  rect := OH_Drawing_RectCreate(50.0, 50.0, 350.0, 350.0);
  OH_Drawing_CanvasDrawRect(canvas, rect);

  HILogInfo('=== 绘制完成,像素保留在内存中 ===');

  // 注意:这里不要销毁 g_Bitmap,否则像素就没了!
  // 在实际应用中,渲染到屏幕后再销毁
end;

// ★ 供外部获取像素数据的指针
function GetBitmapPixels: Pointer; cdecl; export;
begin
  if Assigned(g_Bitmap) then
    Result := OH_Drawing_BitmapGetPixels(g_Bitmap)
  else
    Result := nil;
end;

exports
DoDraw name 'DoDraw',
GetBitmapPixels name 'GetBitmapPixels';

begin
  // 这里什么都不要写!
end.


这程序的核心目的是在 OpenHarmony (OHOS) 系统上,利用其原生绘图 API (libnative_drawing.so) 进行离屏渲染,并将像素数据提供给外部程序使用。

以下是该代码的技术要点分析:

1. 跨语言与跨框架互操作 (FFI)

这是代码最核心的基础,实现了 Pascal 与 C 语言底层库的无缝对接:

  • ABI 兼容性:通过编译指令 {$packenum 4} 和 {$packrecords C},确保 Pascal 中的枚举类型和记录体在内存中的对齐方式与 C 语言完全一致,避免内存对齐导致的读取错误。
  • 调用约定:所有外部函数声明均使用 cdecl,这是 C/C++ 标准的调用约定,确保了参数入栈顺序和堆栈清理方式与 libnative_drawing.so 匹配。
  • 数据类型映射:精确使用 UInt32SinglePointer 等类型映射 C 层的 uint32_tfloatvoid*,保证数据类型的字节长度一致。

2. 离屏渲染机制

代码没有直接将图形绘制到屏幕上,而是采用了典型的离屏渲染 模式:

  • 创建画布与位图:先创建 Bitmap (像素缓冲区),再创建 Canvas (画布)。
  • 绑定目标:通过 OH_Drawing_CanvasBind(canvas, g_Bitmap) 将画布与位图绑定,后续所有绘制操作(画线、画矩形)的实际像素数据都写入这块内存中。
  • 用途:这种模式常用于生成纹理、图像处理后台计算,或作为中间件将绘制结果传递给其他 UI 框架(如代码文件名暗示的 Qt)。

3. 零拷贝的像素数据传递

代码通过全局变量和导出函数实现了一种高效的跨模块数据共享:

  • 生命周期控制g_Bitmap 被设计为全局变量,在 DoDraw 执行完毕后故意不销毁(代码中有明确注释),以保持像素数据在内存中有效。
  • 直接暴露指针GetBitmapPixels 函数直接返回底层像素数据的内存指针 (Pointer)。
  • 零拷贝:外部程序(如 Qt)获取到这个指针后,可以直接读取或映射到自身的图像结构(如 QImage)中进行显示,无需进行内存拷贝,极大地提升了性能。

4. OpenHarmony Drawing API 的标准使用范式

代码展示了 OHOS 原生绘图的标准流程和面向对象的使用方式:

  • 画笔与画刷分离
    • Pen (画笔) 负责描边,设置为黑色、5像素宽度,用于画线 (DrawLine) 和矩形的边框。
    • Brush (画刷) 负责填充,设置为绿色,用于矩形的内部填充 (DrawRect)。
  • 状态挂载:通过 AttachPen 和 AttachBrush 将画笔/画刷应用到画布上,绘制完毕后可 Detach,这是一种典型的状态机绘图模式(类似 Skia,OHOS 底层正是基于 Skia)。

5. 动态链接库 (Shared Library) 设计

该文件被编译为动态库 (library),而非独立可执行文件:

  • 明确的导出接口:使用 exports 关键字只向外暴露 DoDraw 和 GetBitmapPixels 两个干净的功能接口,隐藏了内部的复杂绘图逻辑和 OHOS API 细节。
  • 无副作用初始化begin ... end. 之间为空,确保动态库在被 dlopen 加载时不会执行任何阻塞或可能导致崩溃的代码,保证了库加载的安全性。

6. 像素格式与色彩空间

  • 指定了 COLOR_FORMAT_RGBA_8888,这是跨平台 UI 开发中最通用、兼容性最好的 32 位像素格式。
  • 指定了 ALPHA_FORMAT_OPAQUE,表明图像不含透明通道,这允许底层图形库进行渲染优化。
  • 颜色值使用 32 位十六进制(如 $FFFFFFFF 为白色,$FF000000 为黑色),符合 ARGB 的标准内存布局。