(3) 图像的加载和处理

图像的加载和处理

本章主要围绕Bitmap类展开
Bitmap本质上是PIXI.BaseTexture的一层 wrapper
它提供了对图片资源的异步加载, 回调等方法
同时暴露出Canvas.context对象来为外部使用来绘制图形

主线 1: 加载一张 Image

加载图片是从静态方法Bitmap.load入手的

Bitmap.load = function (url) {
  const bitmap = Object.create(Bitmap.prototype);
  bitmap.initialize();
  bitmap._url = url; // ※ 设置图片路径
  bitmap._startLoading(); // ※ 触发加载
  return bitmap;
};

Bitmap.prototype._startLoading = function () {
  // 实质上相当于创建了一个Image标签
  this._image = new Image();
  // 绑定回调事件
  this._image.onload = this._onLoad.bind(this);
  this._image.onerror = this._onError.bind(this);
  this._destroyCanvas();
  this._loadingState = 'loading';
  // 和加密相关的内容
  if (Utils.hasEncryptedImages()) {
    this._startDecrypting();
  } else {
    // 通过设置url来浏览器就会发送异步请求
    this._image.src = this._url;
  }
};

// 回调事件
Bitmap.prototype._onLoad = function () {
  // 和加密相关
  if (Utils.hasEncryptedImages()) {
    URL.revokeObjectURL(this._image.src);
  }
  this._loadingState = 'loaded';
  // 将Image作为source创建BaseTexture
  this._createBaseTexture(this._image);
  // 激活所有监听中的回调函数, 见下文
  this._callLoadListeners();
};

Bitmap.prototype._createBaseTexture = function (source) {
  // ※ 通过bitmap.baseTexture暴露给外部
  this._baseTexture = new PIXI.BaseTexture(source);
  this._baseTexture.mipmap = false;
  this._baseTexture.width = source.width;
  this._baseTexture.height = source.height;
  // 更新缩放模式, 关于PIXI的具体配置就不多在本系列里阐述了
  this._updateScaleMode();
};

支线 onLoad 的监听事件

Bitmap 提供了注册监听事件的方法, 让外部及时知道图片已经加载完毕

// 顺便吐槽一下他们listener居然都给拼错了
// 看起来是没用SpellingChecker啊
Bitmap.prototype.addLoadListener = function (listner) {
  // 如果尚未就绪则加入监听者队列, 否则就直接触发
  if (!this.isReady()) {
    this._loadListeners.push(listner);
  } else {
    listner(this);
  }
};

主线 2: 加载 Canvas 对象

Canvas对象一样可以作为PIXI.BaseTexture的来源

// 想要绘制Canvas对象, 首先要创建Bitmap实例
const bitmap = new Bitmap(width, height);
// bitmap的canvas会在第一次引用的时候自动创建
Object.defineProperty(Bitmap.prototype, 'canvas', {
  get: function () {
    this._ensureCanvas();
    return this._canvas;
  },
  configurable: true,
});

Bitmap.prototype._ensureCanvas = function () {
  if (!this._canvas) {
    // 这里的_image是从Image.load方法加载后得到的
    // 当对图片有处理的时候, 比如对windowskin.png取色的时候
    // 需要把图片渲染到canvas上来操作
    if (this._image) {
      this._createCanvas(this._image.width, this._image.height);
      this._context.drawImage(this._image, 0, 0);
    // 否则就直接创建一个空窗口
    } else {
      this._createCanvas(0, 0);
    }
  }
};

// 创建Canvas和BaseTexture
Bitmap.prototype._createCanvas = function (width, height) {
  this._canvas = document.createElement('canvas');
  this._context = this._canvas.getContext('2d');
  this._canvas.width = width;
  this._canvas.height = height;
  // 这里会覆盖Image.load创建的BaseTexture
  this._createBaseTexture(this._canvas);
};

主线: 操作 Canvas

Bitmap提供了一系列canvas绘制方法,
这里简单得列举一些

// 渐变填涂矩形
Bitmap.prototype.gradientFillRect = function(
    x, y, width, height, color1, color2, vertical
) {
    const context = this.context;
    const x1 = vertical ? x : x + width;
    const y1 = vertical ? y + height : y;
    const grad = context.createLinearGradient(x, y, x1, y1);
    grad.addColorStop(0, color1);
    grad.addColorStop(1, color2);
    context.save(); // 保存当前画笔状态
    context.fillStyle = grad;
    context.fillRect(x, y, width, height);
    context.restore(); // 恢复画笔状态
    this._baseTexture.update(); // ※ 绘制完成后要更新BaseTexture
};

// 圆
Bitmap.prototype.drawCircle = function (x, y, radius, color) {
  const context = this.context;
  context.save();
  context.fillStyle = color;
  context.beginPath();
  context.arc(x, y, radius, 0, Math.PI * 2, false);
  context.fill();
  context.restore();
  this._baseTexture.update();
};

// 文字
Bitmap.prototype.drawText = function (text, x, y, maxWidth, lineHeight, align) {
  // [Note] Different browser makes different rendering with
  //   textBaseline == 'top'. So we use 'alphabetic' here.
  const context = this.context;
  const alpha = context.globalAlpha;
  maxWidth = maxWidth || 0xffffffff;
  let tx = x;
  let ty = Math.round(y + lineHeight / 2 + this.fontSize * 0.35);
  if (align === 'center') {
    tx += maxWidth / 2;
  }
  if (align === 'right') {
    tx += maxWidth;
  }
  context.save();
  context.font = this._makeFontNameText();
  context.textAlign = align;
  context.textBaseline = 'alphabetic';
  context.globalAlpha = 1;
  this._drawTextOutline(text, tx, ty, maxWidth);
  context.globalAlpha = alpha;
  this._drawTextBody(text, tx, ty, maxWidth);
  context.restore();
  this._baseTexture.update();
};

如果熟悉canvas的接口, 我们在这里可以比较自由得绘制任何内容.

支线: 图片缓存

Bitmap对象的管理是由ImageManager来处理的

// 尽管有很多诸如:
// ImageManager.loadTitle1
// ImageManager.loadSystem
// ImageManager.loadCharacter
// 等等加载方法, 实际上本质都是调用Bitmap.load
ImageManager.loadBitmapFromUrl = function(url) {
    // 注意, 这里通过判断是否有system路径来给图片分了2个不同的缓存池
    const cache = url.includes("/system/") ? this._system : this._cache;
    if (!cache[url]) {
        cache[url] = Bitmap.load(url);
    }
    return cache[url];
};

// 而它的清空方法仅清空._cache而不会清空._system
ImageManager.clear = function() {
    const cache = this._cache;
    for (const url in cache) {
        cache[url].destroy();
    }
    this._cache = {};
};

// 而清空缓存的场合仅有一处
// 也就是在切换地图的时候
// 说明这个缓存管理主要是用来处理地图图块缓存的问题的
Scene_Map.prototype.onTransfer = function() {
    ImageManager.clear();
    EffectManager.clear();
};

总结

image.png
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 159,015评论 4 362
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 67,262评论 1 292
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 108,727评论 0 243
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 43,986评论 0 205
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 52,363评论 3 287
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 40,610评论 1 219
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 31,871评论 2 312
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 30,582评论 0 198
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 34,297评论 1 242
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 30,551评论 2 246
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 32,053评论 1 260
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 28,385评论 2 253
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 33,035评论 3 236
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 26,079评论 0 8
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 26,841评论 0 195
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 35,648评论 2 274
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 35,550评论 2 270