JavaScript ArrayBuffer 二進制數組(二) 應用場景


ArrayBuffer 的應用場景

1.AJAX

傳統上,服務器通過 AJAX 操作只能返回文本數據,即responseType屬性默認為textXMLHttpRequest第二版XHR2允許服務器返回二進制數據,這時分成兩種情況。如果明確知道返回的二進制數據類型,可以把返回類型(responseType)設為arraybuffer;如果不知道,就設為blob

let xhr = new XMLHttpRequest();
xhr.open('GET', someUrl);
xhr.responseType = 'arraybuffer';

xhr.onload = function () {
  let arrayBuffer = xhr.response;
  // ···
};

xhr.send();

如果知道傳回來的是 32 位整數,可以像下面這樣處理。

xhr.onreadystatechange = function () {
  if (req.readyState === 4 ) {
    const arrayResponse = xhr.response;
    const dataView = new DataView(arrayResponse);
    const ints = new Uint32Array(dataView.byteLength / 4);

    xhrDiv.style.backgroundColor = "#00FF00";
    xhrDiv.innerText = "Array is " + ints.length + "uints long";
  }
}

2.Canvas

網頁Canvas元素輸出的二進制像素數據,就是 TypedArray 數組。

const canvas = document.getElementById('myCanvas');
const ctx = canvas.getContext('2d');

const imageData = ctx.getImageData(0, 0, canvas.width, canvas.height);
const uint8ClampedArray = imageData.data;

需要注意的是,上面代碼的uint8ClampedArray雖然是一個 TypedArray 數組,但是它的視圖類型是一種針對Canvas元素的專有類型Uint8ClampedArray。這個視圖類型的特點,就是專門針對顏色,把每個字節解讀為無符號的 8 位整數,即只能取值 0 ~ 255,而且發生運算的時候自動過濾高位溢出。這為圖像處理帶來了巨大的方便。

舉例來說,如果把像素的顏色值設為Uint8Array類型,那么乘以一個 gamma 值的時候,就必須這樣計算:

u8[i] = Math.min(255, Math.max(0, u8[i] * gamma));

因為Uint8Array類型對於大於 255 的運算結果(比如0xFF+1),會自動變為0x00,所以圖像處理必須要像上面這樣算。這樣做很麻煩,而且影響性能。如果將顏色值設為Uint8ClampedArray類型,計算就簡化許多。

pixels[i] *= gamma;

Uint8ClampedArray類型確保將小於 0 的值設為 0,將大於 255 的值設為 255。注意,IE 10 不支持該類型。

 

3.WebSocket

WebSocket可以通過ArrayBuffer,發送或接收二進制數據。

let socket = new WebSocket('ws://127.0.0.1:8081');
socket.binaryType = 'arraybuffer';

// Wait until socket is open
socket.addEventListener('open', function (event) {
  // Send binary data
  const typedArray = new Uint8Array(4);
  socket.send(typedArray.buffer);
});

// Receive binary data
socket.addEventListener('message', function (event) {
  const arrayBuffer = event.data;
  // ···
});

4.Fetch API

Fetch API 取回的數據,就是ArrayBuffer對象。

fetch(url)
.then(function(response){
  return response.arrayBuffer()
})
.then(function(arrayBuffer){
  // ...
});

5.File API

如果知道一個文件的二進制數據類型,也可以將這個文件讀取為ArrayBuffer對象。

const fileInput = document.getElementById('fileInput');
const file = fileInput.files[0];
const reader = new FileReader();
reader.readAsArrayBuffer(file);
reader.onload = function () {
  const arrayBuffer = reader.result;
  // ···
};

下面以處理 bmp 文件為例。假定file變量是一個指向 bmp 文件的文件對象,首先讀取文件。

const reader = new FileReader();
reader.addEventListener("load", processimage, false);
reader.readAsArrayBuffer(file);

然后,定義處理圖像的回調函數:先在二進制數據之上建立一個DataView視圖,再建立一個bitmap對象,用於存放處理后的數據,最后將圖像展示在Canvas元素之中。

function processimage(e) {
  const buffer = e.target.result;
  const datav = new DataView(buffer);
  const bitmap = {};
  // 具體的處理步驟
}

具體處理圖像數據時,先處理 bmp 的文件頭。具體每個文件頭的格式和定義,請參閱有關資料。

bitmap.fileheader = {};
bitmap.fileheader.bfType = datav.getUint16(0, true);
bitmap.fileheader.bfSize = datav.getUint32(2, true);
bitmap.fileheader.bfReserved1 = datav.getUint16(6, true);
bitmap.fileheader.bfReserved2 = datav.getUint16(8, true);
bitmap.fileheader.bfOffBits = datav.getUint32(10, true);

接着處理圖像元信息部分。

bitmap.infoheader = {};
bitmap.infoheader.biSize = datav.getUint32(14, true);
bitmap.infoheader.biWidth = datav.getUint32(18, true);
bitmap.infoheader.biHeight = datav.getUint32(22, true);
bitmap.infoheader.biPlanes = datav.getUint16(26, true);
bitmap.infoheader.biBitCount = datav.getUint16(28, true);
bitmap.infoheader.biCompression = datav.getUint32(30, true);
bitmap.infoheader.biSizeImage = datav.getUint32(34, true);
bitmap.infoheader.biXPelsPerMeter = datav.getUint32(38, true);
bitmap.infoheader.biYPelsPerMeter = datav.getUint32(42, true);
bitmap.infoheader.biClrUsed = datav.getUint32(46, true);
bitmap.infoheader.biClrImportant = datav.getUint32(50, true);

最后處理圖像本身的像素信息。

const start = bitmap.fileheader.bfOffBits;
bitmap.pixels = new Uint8Array(buffer, start);

至此,圖像文件的數據全部處理完成。下一步,可以根據需要,進行圖像變形,或者轉換格式,或者展示在Canvas網頁元素之中。

6.SharedArrayBuffer

JavaScript 是單線程的,Web worker 引入了多線程:主線程用來與用戶互動,Worker 線程用來承擔計算任務。每個線程的數據都是隔離的,通過postMessage()通信。下面是一個例子。

// 主線程 const w = new Worker('myworker.js'); 

上面代碼中,主線程新建了一個 Worker 線程。該線程與主線程之間會有一個通信渠道,主線程通過w.postMessage向 Worker 線程發消息,同時通過message事件監聽 Worker 線程的回應。

// 主線程 w.postMessage('hi'); w.onmessage = function (ev) { console.log(ev.data); } 

上面代碼中,主線程先發一個消息hi,然后在監聽到 Worker 線程的回應后,就將其打印出來。

Worker 線程也是通過監聽message事件,來獲取主線程發來的消息,並作出反應。

// Worker 線程 onmessage = function (ev) { console.log(ev.data); postMessage('ho'); } 

線程之間的數據交換可以是各種格式,不僅僅是字符串,也可以是二進制數據。這種交換采用的是復制機制,即一個進程將需要分享的數據復制一份,通過postMessage方法交給另一個進程。如果數據量比較大,這種通信的效率顯然比較低。很容易想到,這時可以留出一塊內存區域,由主線程與 Worker 線程共享,兩方都可以讀寫,那么就會大大提高效率,協作起來也會比較簡單(不像postMessage那么麻煩)。

ES2017 引入SharedArrayBuffer,允許 Worker 線程與主線程共享同一塊內存。SharedArrayBuffer的 API 與ArrayBuffer一模一樣,唯一的區別是后者無法共享數據。

// 主線程 // 新建 1KB 共享內存 const sharedBuffer = new SharedArrayBuffer(1024); // 主線程將共享內存的地址發送出去 w.postMessage(sharedBuffer); // 在共享內存上建立視圖,供寫入數據 const sharedArray = new Int32Array(sharedBuffer); 

上面代碼中,postMessage方法的參數是SharedArrayBuffer對象。

Worker 線程從事件的data屬性上面取到數據。

// Worker 線程 onmessage = function (ev) { // 主線程共享的數據,就是 1KB 的共享內存 const sharedBuffer = ev.data; // 在共享內存上建立視圖,方便讀寫 const sharedArray = new Int32Array(sharedBuffer); // ... }; 

共享內存也可以在 Worker 線程創建,發給主線程。

SharedArrayBufferArrayBuffer一樣,本身是無法讀寫的,必須在上面建立視圖,然后通過視圖讀寫。

// 分配 10 萬個 32 位整數占據的內存空間 const sab = new SharedArrayBuffer(Int32Array.BYTES_PER_ELEMENT * 100000); // 建立 32 位整數視圖 const ia = new Int32Array(sab); // ia.length == 100000 // 新建一個質數生成器 const primes = new PrimeGenerator(); // 將 10 萬個質數,寫入這段內存空間 for ( let i=0 ; i < ia.length ; i++ ) ia[i] = primes.next(); // 向 Worker 線程發送這段共享內存 w.postMessage(ia); 

Worker 線程收到數據后的處理如下。

// Worker 線程 let ia; onmessage = function (ev) { ia = ev.data; console.log(ia.length); // 100000 console.log(ia[37]); // 輸出 163,因為這是第38個質數 }; 

7.Atomics 對象

多線程共享內存,最大的問題就是如何防止兩個線程同時修改某個地址,或者說,當一個線程修改共享內存以后,必須有一個機制讓其他線程同步。SharedArrayBuffer API 提供Atomics對象,保證所有共享內存的操作都是“原子性”的,並且可以在所有線程內同步。

此處不錯更多探討.........

 

 

更多:

JavaScript ArrayBuffer 二進制數組(一) 

JS DataURL 整理(二) DataURL 和圖片 

JS DataURL 整理(一)


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM