JavaScriptArrayBuffer⼆进制数组(⼆)应⽤场景ArrayBuffer 的应⽤场景
1.AJAX
传统上,服务器通过 AJAX 操作只能返回⽂本数据,即responseType属性默认为text。XMLHttpRequest第⼆版XHR2允许服务器返回⼆进制数据,这时分成两种情况。如果明确知道返回的⼆进制数据类型,可以把返回类型(responseType)设为arraybuffer;如果不知道,就设为blob。
let xhr = new XMLHttpRequest();
xhr.open('GET', someUrl);
let arrayBuffer = sponse;
// ···
};
xhr.send();
如果知道传回来的是 32 位整数,可以像下⾯这样处理。
if (adyState === 4 ) {
const arrayResponse = sponse;
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 = ElementById('myCanvas');
const ctx = Context('2d');
const imageData = ImageData(0, 0, canvas.width, canvas.height);
const uint8ClampedArray = imageData.data;
需要注意的是,上⾯代码的uint8ClampedArray虽然是⼀个 TypedArray 数组,但是它的视图类型是⼀种针对Canvas元素的专有类
型Uint8ClampedArray。这个视图类型的特点,就是专门针对颜⾊,把每个字节解读为⽆符号的 8 位整数,即只能取值 0 ~ 255,⽽且发⽣运算的时候⾃动过滤⾼位溢出。这为图像处理带来了巨⼤的⽅便。
举例来说,如果把像素的颜⾊值设为Uint8Array类型,那么乘以⼀个 gamma 值的时候,就必须这样计算:
javascript数组对象
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 = ElementById('fileInput');
const file = fileInput.files[0];
const reader = new FileReader();
const arrayBuffer = sult;
// ···
};
下⾯以处理 bmp ⽂件为例。假定file变量是⼀个指向 bmp ⽂件的⽂件对象,⾸先读取⽂件。
const reader = new FileReader();
reader.addEventListener("load", processimage, false);
然后,定义处理图像的回调函数:先在⼆进制数据之上建⽴⼀个DataView视图,再建⽴⼀个bitmap对象,⽤于存放处理后的数据,最后将图像展⽰在Canvas元素之中。
function processimage(e) {
const buffer = sult;
const datav = new DataView(buffer);
const bitmap = {};
// 具体的处理步骤
}
具体处理图像数据时,先处理 bmp 的⽂件头。具体每个⽂件头的格式和定义,请参阅有关资料。
bitmap.fileheader = {};
bitmap.fileheader.bfType = Uint16(0, true);
bitmap.fileheader.bfSize = Uint32(2, true);
bitmap.fileheader.bfReserved1 = Uint16(6, true);
bitmap.fileheader.bfReserved2 = Uint16(8, true);
bitmap.fileheader.bfOffBits = Uint32(10, true);
接着处理图像元信息部分。
bitmap.infoheader = {};
bitmap.infoheader.biSize = Uint32(14, true);
bitmap.infoheader.biWidth = Uint32(18, true);
bitmap.infoheader.biHeight = Uint32(22, true);
bitmap.infoheader.biPlanes = Uint16(26, true);
bitmap.infoheader.biBitCount = Uint16(28, true);
bitmap.infoheader.biCompression = Uint32(30, true);
bitmap.infoheader.biSizeImage = Uint32(34, true);
bitmap.infoheader.biXPelsPerMeter = Uint32(38, true);
bitmap.infoheader.biYPelsPerMeter = Uint32(42, true);
bitmap.infoheader.biClrUsed = Uint32(46, true);
bitmap.infoheader.biClrImportant = Uint32(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');
console.log(ev.data);
}
上⾯代码中,主线程先发⼀个消息hi,然后在监听到 Worker 线程的回应后,就将其打印出来。
Worker 线程也是通过监听message事件,来获取主线程发来的消息,并作出反应。
// Worker 线程
onmessage = function (ev) {
console.log(ev.data);
postMessage('ho');
}
线程之间的数据交换可以是各种格式,不仅仅是字符串,也可以是⼆进制数据。这种交换采⽤的是复制机制,即⼀个进程将需要分享的数据复制⼀份,通过postMessage⽅法交给另⼀个进程。如果数据量⽐较⼤,这种通信的效率显然⽐较低。很容易想到,这时可以留出⼀块内存区域,由主线程与 Worker 线程共享,两⽅都可以读写,那么就会⼤⼤提⾼效率,协作起来也会⽐较简单(不像postMessage那么⿇烦)。
ES2017 引⼊,允许 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 线程创建,发给主线程。
SharedArrayBuffer与ArrayBuffer⼀样,本⾝是⽆法读写的,必须在上⾯建⽴视图,然后通过视图读写。
// 分配 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] = ();
// 向 Worker 线程发送这段共享内存
w.postMessage(ia);
Worker 线程收到数据后的处理如下。
// Worker 线程
let ia;
onmessage = function (ev) {
ia = ev.data;
console.log(ia[37]); // 输出 163,因为这是第38个质数
};
7.Atomics 对象
多线程共享内存,最⼤的问题就是如何防⽌两个线程同时修改某个地址,或者说,当⼀个线程修改共享内存以后,必须有⼀个机制让其他线程同步。SharedArrayBuffer API 提供Atomics对象,保证所有共享内存的操作都是“原⼦性”的,并且可以在所有线程内同步。
此处不错更多探讨.........
更多:
版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。
发表评论