-
Notifications
You must be signed in to change notification settings - Fork 16
/
Copy pathRGBADataSet.js
87 lines (78 loc) · 3.39 KB
/
RGBADataSet.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
import * as util from '../src/utils.js'
import DataSet from './DataSet.js'
// Parse an RGBA image to a DataSet of the given type.
// We use all 4 bytes of the pixels, thus map exactly onto
// multiples all [TypedArray](https://goo.gl/3OOQzy) sizes.
class RGBADataSet extends DataSet {
constructor(img, Type = Float32Array, options = {}) {
const bytes = imageToBytes(img)
const data = new Type(bytes.buffer) // Parse via a Type view on the buffer
const dataPerPixel = (4 * data.length) / bytes.length
const width = dataPerPixel * img.width
const height = img.height
super(width, height, data)
Object.assign(this, options)
this.src = img.src
}
}
// Use webgl texture to convert img to Uint8Array w/o alpha premultiply
// or color profile modification.
// Img can be Image, ImageData, Canvas: [See MDN](https://goo.gl/a3oyRA).
// `flipY` is used to invert image to upright.
// REMIND: use webgl12 if it works .. allows all imagables, not just Image.
// REMIND: use imagebitmap when available in safari!
// https://dev.to/nektro/createimagebitmap-polyfill-for-safari-and-edge-228
let imageToBytesCtx = null
export function imageToBytes(img, flipY = false, imgFormat = 'RGBA') {
// Create the gl context using the image width and height
if (!imageToBytesCtx) {
const can = util.createCanvas(0, 0)
imageToBytesCtx = can.getContext('webgl', {
premultipliedAlpha: false,
})
}
const { width, height } = img
const gl = imageToBytesCtx
Object.assign(gl.canvas, { width, height })
const fmt = gl[imgFormat]
// Create and initialize the texture.
const texture = gl.createTexture()
gl.bindTexture(gl.TEXTURE_2D, texture)
if (flipY) {
// Mainly used for pictures rather than data
gl.pixelStorei(gl.UNPACK_FLIP_Y_WEBGL, true)
}
// Insure [no color profile applied](https://goo.gl/BzBVJ9):
gl.pixelStorei(gl.UNPACK_COLORSPACE_CONVERSION_WEBGL, gl.NONE)
// Insure no [alpha premultiply](http://goo.gl/mejNCK).
// False is the default, but lets make sure!
gl.pixelStorei(gl.UNPACK_PREMULTIPLY_ALPHA_WEBGL, false)
// gl.texImage2D(gl.TEXTURE_2D, 0, gl.RGBA, gl.RGBA, gl.UNSIGNED_BYTE, img)
gl.texImage2D(gl.TEXTURE_2D, 0, fmt, fmt, gl.UNSIGNED_BYTE, img)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MAG_FILTER, gl.NEAREST)
gl.texParameteri(gl.TEXTURE_2D, gl.TEXTURE_MIN_FILTER, gl.NEAREST)
// Create the framebuffer used for the texture
const framebuffer = gl.createFramebuffer()
gl.bindFramebuffer(gl.FRAMEBUFFER, framebuffer)
gl.framebufferTexture2D(
gl.FRAMEBUFFER,
gl.COLOR_ATTACHMENT0,
gl.TEXTURE_2D,
texture,
0
)
// See if it all worked. Apparently not async.
const status = gl.checkFramebufferStatus(gl.FRAMEBUFFER)
if (status !== gl.FRAMEBUFFER_COMPLETE) {
throw Error(`imageToBytes: status not FRAMEBUFFER_COMPLETE: ${status}`)
}
// If all OK, create the pixels buffer and read data.
const pixSize = imgFormat === 'RGB' ? 3 : 4
const pixels = new Uint8Array(pixSize * width * height)
// gl.readPixels(0, 0, width, height, gl.RGBA, gl.UNSIGNED_BYTE, pixels)
gl.readPixels(0, 0, width, height, fmt, gl.UNSIGNED_BYTE, pixels)
// Unbind the framebuffer and return pixels
gl.bindFramebuffer(gl.FRAMEBUFFER, null)
return pixels
}
export default RGBADataSet