(3) 圖像的加載和處理

圖像的加載和處理

本章主要圍繞Bitmap類展開
Bitmap本質上是PIXI.BaseTexture的一層 wrapper
它提供了對圖片資源的異步加載, 回調等方法
同時暴露出Canvas.context對象來為外部使用來繪制圖形

主線 1: 加載一張 Image

加載圖片是從靜態(tài)方法Bitmap.load入手的

Bitmap.load = function (url) {
  const bitmap = Object.create(Bitmap.prototype);
  bitmap.initialize();
  bitmap._url = url; // ※ 設置圖片路徑
  bitmap._startLoading(); // ※ 觸發(fā)加載
  return bitmap;
};

Bitmap.prototype._startLoading = function () {
  // 實質上相當于創(chuàng)建了一個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來瀏覽器就會發(fā)送異步請求
    this._image.src = this._url;
  }
};

// 回調事件
Bitmap.prototype._onLoad = function () {
  // 和加密相關
  if (Utils.hasEncryptedImages()) {
    URL.revokeObjectURL(this._image.src);
  }
  this._loadingState = 'loaded';
  // 將Image作為source創(chuàng)建BaseTexture
  this._createBaseTexture(this._image);
  // 激活所有監(jiān)聽中的回調函數(shù), 見下文
  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 的監(jiān)聽事件

Bitmap 提供了注冊監(jiān)聽事件的方法, 讓外部及時知道圖片已經(jīng)加載完畢

// 順便吐槽一下他們listener居然都給拼錯了
// 看起來是沒用SpellingChecker啊
Bitmap.prototype.addLoadListener = function (listner) {
  // 如果尚未就緒則加入監(jiān)聽者隊列, 否則就直接觸發(fā)
  if (!this.isReady()) {
    this._loadListeners.push(listner);
  } else {
    listner(this);
  }
};

主線 2: 加載 Canvas 對象

Canvas對象一樣可以作為PIXI.BaseTexture的來源

// 想要繪制Canvas對象, 首先要創(chuàng)建Bitmap實例
const bitmap = new Bitmap(width, height);
// bitmap的canvas會在第一次引用的時候自動創(chuàng)建
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);
    // 否則就直接創(chuàng)建一個空窗口
    } else {
      this._createCanvas(0, 0);
    }
  }
};

// 創(chuàng)建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創(chuàng)建的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(); // 保存當前畫筆狀態(tài)
    context.fillStyle = grad;
    context.fillRect(x, y, width, height);
    context.restore(); // 恢復畫筆狀態(tài)
    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
?著作權歸作者所有,轉載或內容合作請聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖温算,帶你破解...
    沈念sama閱讀 218,386評論 6 506
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件誊涯,死亡現(xiàn)場離奇詭異,居然都是意外死亡觅捆,警方通過查閱死者的電腦和手機叮雳,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,142評論 3 394
  • 文/潘曉璐 我一進店門想暗,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人帘不,你說我怎么就攤上這事说莫。” “怎么了厌均?”我有些...
    開封第一講書人閱讀 164,704評論 0 353
  • 文/不壞的土叔 我叫張陵唬滑,是天一觀的道長告唆。 經(jīng)常有香客問我棺弊,道長,這世上最難降的妖魔是什么擒悬? 我笑而不...
    開封第一講書人閱讀 58,702評論 1 294
  • 正文 為了忘掉前任模她,我火速辦了婚禮,結果婚禮上懂牧,老公的妹妹穿的比我還像新娘侈净。我一直安慰自己,他們只是感情好僧凤,可當我...
    茶點故事閱讀 67,716評論 6 392
  • 文/花漫 我一把揭開白布畜侦。 她就那樣靜靜地躺著,像睡著了一般躯保。 火紅的嫁衣襯著肌膚如雪旋膳。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 51,573評論 1 305
  • 那天途事,我揣著相機與錄音验懊,去河邊找鬼擅羞。 笑死,一個胖子當著我的面吹牛义图,可吹牛的內容都是我干的减俏。 我是一名探鬼主播,決...
    沈念sama閱讀 40,314評論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼碱工,長吁一口氣:“原來是場噩夢啊……” “哼娃承!你這毒婦竟也來了?” 一聲冷哼從身側響起怕篷,我...
    開封第一講書人閱讀 39,230評論 0 276
  • 序言:老撾萬榮一對情侶失蹤草慧,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后匙头,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體漫谷,經(jīng)...
    沈念sama閱讀 45,680評論 1 314
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 37,873評論 3 336
  • 正文 我和宋清朗相戀三年蹂析,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片电抚。...
    茶點故事閱讀 39,991評論 1 348
  • 序言:一個原本活蹦亂跳的男人離奇死亡惕稻,死狀恐怖蝙叛,靈堂內的尸體忽然破棺而出俺祠,到底是詐尸還是另有隱情借帘,我是刑警寧澤,帶...
    沈念sama閱讀 35,706評論 5 346
  • 正文 年R本政府宣布肺然,位于F島的核電站蔫缸,受9級特大地震影響际起,放射性物質發(fā)生泄漏。R本人自食惡果不足惜街望,卻給世界環(huán)境...
    茶點故事閱讀 41,329評論 3 330
  • 文/蒙蒙 一校翔、第九天 我趴在偏房一處隱蔽的房頂上張望灾前。 院中可真熱鬧,春花似錦、人聲如沸告希。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,910評論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽喝噪。三九已至,卻和暖如春酝惧,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背伯诬。 一陣腳步聲響...
    開封第一講書人閱讀 33,038評論 1 270
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留盗似,地道東北人哩陕。 一個月前我還...
    沈念sama閱讀 48,158評論 3 370
  • 正文 我出身青樓赫舒,卻偏偏與公主長得像,于是被迫代替她去往敵國和親接癌。 傳聞我的和親對象是個殘疾皇子心赶,可洞房花燭夜當晚...
    茶點故事閱讀 44,941評論 2 355