Skip to content

Latest commit

 

History

History
181 lines (139 loc) · 6.13 KB

porting_notes_3.md

File metadata and controls

181 lines (139 loc) · 6.13 KB

AWTK WEB 版移植笔记-图片

前面我们介绍了,为减小代码的体积,提高解码的速度,我们选择了使用浏览器的图片解码,而不是 stbimage 去解码。实际操作并非那么简单,其中遇到不少问题。

  • 浏览器的图片加载是异步的,而 AWTK 的图片加载是同步的。

  • 浏览器的图片 Image 在 AWTK 中没法直接表示。

  • AWTK 中的图片名称是抽象的。比如名为 earth 图片,可能是 png、也可能是 jpg 或其它图片格式,这个由资源管理器自动检查。这种方式在浏览器中,会造成大量网络请求,就不太实用了。

  • AWTK 不能直接操作浏览器的 Image 对象中的数据,要实现 mutable image 就得另外想办法。

一、资源列表

为了解决异步的问题和图片名到 URL 的自动映射的问题,我们用工具生成一个资源列表。

资源列表在 build.py 中自动生成。

const g_awtk_assets = {
  image: [
    {name:"arrow_up_o", uri:"assets/raw/images/x1/arrow_up_o.png", w:24, h:24},
    {name:"arrow_up_p", uri:"assets/raw/images/x1/arrow_up_p.png", w:24, h:24},
    {name:"backspace", uri:"assets/raw/images/x1/backspace.png", w:16, h:16},
    {name:"battery_0", uri:"assets/raw/images/x1/battery_0.png", w:57, h:32},
    {name:"battery_1", uri:"assets/raw/images/x1/battery_1.png", w:57, h:32},
    {name:"battery_2", uri:"assets/raw/images/x1/battery_2.png", w:57, h:32},
    {name:"battery_3", uri:"assets/raw/images/x1/battery_3.png", w:57, h:32},
    {name:"battery_4", uri:"assets/raw/images/x1/battery_4.png", w:57, h:32},
    {name:"battery_5", uri:"assets/raw/images/x1/battery_5.png", w:57, h:32},
...
}

有了资源列表,我们就可以:

  • 同步获取图片的信息,先构建图片对象,再异步加载。

  • 同时也很容易通过图片名字,去找到对应的 URL。

优先找满足当前 DPI 的图片,如果找不到再使用 x1 的图片。

AssetsManager.getImageByDPI = function (name, dpi, theme = 'default') {
  let anydpi = '/xx/';
  let _theme = '/' + theme + '/';
  const assets = g_awtk_assets['image'];

  if (assets) {
    let asset = assets.find(iter => {
      return name == iter.name && iter.uri.indexOf(_theme) >= 0 && iter.uri.indexOf(dpi) >= 0;
    });
    if (asset == null) {
      asset = assets.find(iter => {
        return name == iter.name && iter.uri.indexOf(_theme) >= 0 && iter.uri.indexOf(anydpi) >= 0;
      });
    }

    return asset;
  }

  return null;
}

AssetsManager.getImage = function (name, theme = 'default', log = true) {
  let dpi = '/x' + Math.round(TBrowser.getDevicePixelRatio()) + '/';
  let asset = AssetsManager.getImageByDPI(name, dpi, theme);

  if (!asset && log) {
    console.log('Not found ' + name);
  }

  return asset;
}

二、image loader 的实现

AWTK 已经对 image loader 做了抽象,在移植到 WEB 平台时只要实现 image loader 接口就行了,在 C 语言这一层,只是对 JS 的包装。C 语言这边 id 是 image 的唯一标识,对应 JS 那边的 Image 对象。

static ret_t image_loader_web_load(image_loader_t *l, const asset_info_t *asset,
                                   bitmap_t *image) {
  int32_t w = 0;
  int32_t h = 0;
  int32_t id = 0;
  const char* name = NULL;
  const char* theme = NULL;
  assets_manager_t* am = assets_manager();
  return_value_if_fail(l != NULL && asset != NULL && image != NULL && am != NULL,
                       RET_BAD_PARAMS);

  name = asset->name;
  theme = am->theme;

  id = EM_ASM_INT({ return ImageLoader.load(pointerToString($0), pointerToString($1)); }, name, theme);
  w = EM_ASM_INT({ return ImageLoader.getWidth(pointerToString($0)); }, name);
  h = EM_ASM_INT({ return ImageLoader.getHeight(pointerToString($0)); }, name);

  memset(image, 0x00, sizeof(bitmap_t));
  image->w = w;
  image->h = h;
  image->specific = tk_pointer_from_int(id);

  return (w > 0 && h > 0) ? RET_OK : RET_NOT_FOUND;
}

在 JS 一侧,通过 ImageCache 建立 ID 与 Image 对象之间的关系。

ImageLoader.load = function (name, theme = 'default') {
  let id = ImageCache.getIdOfNameAndTheme(name, theme);
  if (id == ImageCache.invalidImageId) {
    let is_default_theme = (theme == 'default');
    let uri = AssetsManager.getImageURI(name, theme, is_default_theme);
    if (uri) {
      let image = new Image();
      let name_with_theme = theme + ':' + name;
      image.src = uri;
      image.name = name_with_theme;
      image.onload = function () {
        console.log('image loaded: ' + name_with_theme + ' ' + uri);
        Awtk.requestRepaint(1);
      }
      id = ImageCache.add(image);
    } else if (!is_default_theme) {
      id = ImageLoader.load(name);
    }
  }
  return id;
}

在图片加载完成后,调用 Awtk.requestRepaint 请求重绘。

三、mutable 图片

浏览器中的图片是只读的,但在 AWTK 中,有时需要直接修改图片的数据,比如颜色选择器。可以动态修改图片内容的图片,我们称为 mutable 图片

我们用 canvas 来模拟 mutable 图片:

TBrowser.createMutableImage = function (name, addr, w, h, line_length, format) {
  const image = TBrowser.createCanvas(name, w, h);

  image.name = name;
  image.addr = addr;
  image.format = format;
  image.line_length = line_length;

  return image;
}

当数据有变化时,调用 updateMutableImage 将 C 语言这边的内存数据同步到 Canvas 中去:

VGCanvas.updateMutableImage = function (id) {
  let mutableImage = ImageCache.get(id);

  let w = mutableImage.width;
  let h = mutableImage.height;
  let size = mutableImage.width * mutableImage.height;
  let start = mutableImage.addr >> 2;
  let end = start + size;
  let array = Module.HEAP32.subarray(start, end);
  let ctx = mutableImage.getContext('2d');
  let imageData = ctx.getImageData(0, 0, w, h);
  let data = new Int32Array(imageData.data.buffer);

  for (let i = 0; i < size; i++) {
    data[i] = array[i];
  }
  ctx.putImageData(imageData, 0, 0, 0, 0, w, h);

  return true;
}

官方文档并没有介绍如何在 C 和 JS 之间传递二进制数据,从 malloc 的实现代码可以看到,malloc 分配的数据在 Module.HEAP32 中,我们把它取出来即可。