@bokeh/bokehjs
Version:
Interactive, novel data visualization
110 lines • 4.5 kB
JavaScript
import { BaseGLGlyph } from "./base";
import { Float32Buffer } from "./buffer";
import { assert } from "../../../core/util/assert";
export class ImageGL extends BaseGLGlyph {
glyph;
static __name__ = "ImageGL";
// data properties
_tex = [];
_bounds = [];
// image_changed is separate from data_changed as it can occur through changed colormapping.
_image_changed = false;
constructor(regl_wrapper, glyph) {
super(regl_wrapper, glyph);
this.glyph = glyph;
}
draw(indices, main_glyph, transform) {
const main_gl_glyph = main_glyph.glglyph;
// The only visual property that can change is global_alpha and that is read on every render,
// so ignore this.visuals_changed
const data_changed_or_mapped = main_gl_glyph.data_changed || main_gl_glyph.data_mapped;
if (data_changed_or_mapped) {
// Handle change of location or bounds.
main_gl_glyph._set_data();
}
if (main_gl_glyph._image_changed || main_gl_glyph.data_changed) {
// Handle change of image itself. If _image_changed then image has definitely changed such as
// from a change of colormapping. If data_changed then image may have changed so update just
// in case. If we could identify what in the CDS has changed (e.g. image or x) then we would
// know whether to call _set_image or not.
main_gl_glyph._set_image();
}
main_gl_glyph.data_changed = false;
main_gl_glyph.data_mapped = false;
main_gl_glyph._image_changed = false;
const { global_alpha } = this.glyph.visuals.image;
for (const i of indices) {
if (main_gl_glyph._tex[i] == null || main_gl_glyph._bounds[i] == null) {
continue;
}
const props = {
scissor: this.regl_wrapper.scissor,
viewport: this.regl_wrapper.viewport,
canvas_size: [transform.width, transform.height],
bounds: main_gl_glyph._bounds[i],
tex: main_gl_glyph._tex[i],
global_alpha: global_alpha.get(i),
};
this.regl_wrapper.image()(props);
}
}
set_image_changed() {
this._image_changed = true;
}
_set_data() {
const { image } = this.glyph;
const nimage = image.length;
if (this._bounds.length != nimage) {
this._bounds = Array(nimage).fill(null);
}
for (let i = 0; i < nimage; i++) {
const { sx, sy, sdw: sw, sdh: sh, xy_anchor, xy_scale, xy_sign } = this.glyph;
const sx_i = sx[i];
const sy_i = sy[i];
const sw_i = sw[i];
const sh_i = sh[i];
if (!isFinite(sx_i + sy_i + sw_i + sh_i)) {
this._bounds[i] = null;
continue;
}
if (this._bounds[i] == null) {
this._bounds[i] = new Float32Buffer(this.regl_wrapper);
}
const bounds_array = this._bounds[i].get_sized_array(4);
bounds_array[0] = sx[i] + sw[i] * (0.5 * (1 - xy_scale.x) - xy_anchor.x) * xy_sign.x;
bounds_array[1] = sy[i] + sh[i] * (0.5 * (1 - xy_scale.y) - xy_anchor.y) * xy_sign.y;
bounds_array[2] = bounds_array[0] + sw[i] * xy_scale.x * xy_sign.x;
bounds_array[3] = bounds_array[1] + sh[i] * xy_scale.y * xy_sign.y;
this._bounds[i].update();
}
}
_set_image() {
const { image, image_data } = this.glyph;
const nimage = image.length;
assert(image_data != null);
if (this._tex.length != nimage) {
this._tex = Array(nimage).fill(null);
}
for (let i = 0; i < nimage; i++) {
const image_data_i = image_data[i];
if (image_data_i == null) {
this._tex[i] = null;
continue;
}
const tex_options = {
width: image_data_i.width,
height: image_data_i.height,
data: image_data_i,
format: "rgba",
type: "uint8",
};
if (this._tex[i] == null) {
this._tex[i] = this.regl_wrapper.texture(tex_options);
}
else {
this._tex[i](tex_options); // Reuse existing WebGL texture
}
}
}
}
//# sourceMappingURL=image.js.map