UNPKG

@bokeh/bokehjs

Version:

Interactive, novel data visualization

110 lines 4.5 kB
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