远程帧缓冲协议,即RFB(Remote Frame Buffer)协议VNC(Virtual Network Computing)软件所使用的通讯协议,用于客户端向服务器传送控制命令以及服务器向客户端发送其操作画面,借此,远程桌面共享软件(不涉及Windows自带的桌面共享)得以实现。

   本文仅对bill在开发过程中遇到的鼠标形状解析问题作简要记录。

   RFB协议规定,鼠标形状在伪编码范畴,协议原文如下:


       A client which requests the Cursor pseudo-encoding is declaring that it is capable of

   drawing a mouse cursor locally. This can signicantly improve perceived performance

   over slow links. The server sets the cursor shape by sending a pseudo-rectangle with

   the Cursor pseudo-encoding as part of an update. The pseudo-rectangle’s x-position

   and y-position indicate the hotspot of the cursor, and width and height indicate the

   width and height of the cursor in pixels. The data consists of width × height pixel

   values followed by a bitmask. The bitmask consists of left-to-right, top-to-bottom

   scanlines, where each scanline is padded to a whole number of bytes floor((width +

   7)/8). Within each byte the most signicant bit represents the leftmost pixel, with a

   1-bit meaning the corresponding pixel in the cursor is valid.


wKioL1MqaPORcO4TAACzYNERVrQ350.jpg


   协议规定鼠标指针伪编码格式为两个连续的数组,第一个数组用来保存指针形状的像素点,第二个数组则保存了一个位掩码,用来反映上述像素点的有效性。

   刚开始看这段描述,实在是没理解这个位掩码的功能,便直接忽略位掩码,得到了下图所示的指针原始RGB数据:

wKiom1MqcafC3RxUAAA7y4m6v9o068.jpg  wKioL1MqcYCwhQEEAAAznu_AZB0052.jpg

   乍看上去好像已经达到目的了,直到在客户端进行绘制才发现问题 —— 我们肉眼能够分辨出图中的指针,可是如何利用程序得到一个干净(没有黑色背景)的指针呢?貌似我们可以将该数组中所有黑色像素设为透明值来得到指针,但另一个问题又来了 —— 所有黑色像素透明后,白色指针便没有了边框,一旦VNC客户端显示背景为白色,鼠标就“消失”了。

   因此忽略位掩码的尝试是失败的。不得不再次认真理解 bitmask 的意义,才发现位掩码的真正用途:

   bitmask中每一个 bit 对应指针像素数组中的一个像素点(通常为 4bytes),以从左到右、自上而下的顺序反映对应像素点的有效性,若某 bit1,则表示它对应的是有效的鼠标指针像素点,应予以保留。否则便可以将该像素点设为透明值。


bitmask_len 位掩码数组长度
bitmask 位掩码数组
cursor 鼠标指针像素数组
bytes_per_pixel 每个像素点字节数,通常为4
int cursor_pixel_idx = 0;
for (int i = 0; i < bitmask_len; ++i) {
  uint8_t mask = bitmask[i];
  for (int j = 0; j < 8; ++j) {
    bool is_pixel_valid = mask & 128;
    mask <<= 1;
      
    if (!is_pixel_valid) {
      for (int k = 0; k != bytes_per_pixel; ++k) {
        cursor[cursor_pixel_idx + k] = 0xFF;
      }
    }
                   
    cursor_pixel_idx += bytes_per_pixel;
  }
} // for



   这样一来,便可以使程序“分辨“出上述图像中哪些像素才是真正的指针,而哪些像素是可以设为透明值加以忽略的。根据位掩码最终得到了如下的鼠标指针:

wKioL1MqhpvQalW5AAA0K7BaHD4323.jpg  wKiom1MqhsOTrwfvAAAudA9DG3k524.jpg



   现在便可放心的将指针绘制到客户端任何区域,而不用担心指针在白色区域无法识别的问题了。