图像的加载和处理
本章主要围绕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();
};