1use super::*;
2
3impl WebGl2ChartBackend {
5 pub fn is_supported() -> bool {
15 is_supported()
16 }
17
18 pub fn create(canvas: &HtmlCanvasElement) -> Option<Self> {
33 let attributes: WebGlContextAttributes = WebGlContextAttributes::new();
34 attributes.set_alpha(true);
35 attributes.set_premultiplied_alpha(true);
36 attributes.set_antialias(true);
37 attributes.set_depth(false);
38 attributes.set_stencil(false);
39 attributes.set_preserve_drawing_buffer(false);
40 let options: &JsValue = attributes.as_ref();
41 let context: WebGl2RenderingContext = canvas
42 .get_context_with_context_options("webgl2", options)
43 .ok()
44 .flatten()?
45 .dyn_into::<WebGl2RenderingContext>()
46 .ok()?;
47 let mut backend: WebGl2ChartBackend = Self {
48 canvas: canvas.clone(),
49 context,
50 program: None,
51 vertex_array: None,
52 vertex_buffer: None,
53 index_buffer: None,
54 atlas_texture: None,
55 ramp_texture: None,
56 parameter_texture: None,
57 images: Vec::new(),
58 tessellator: Tessellator::new(),
59 vertices: Vec::new(),
60 indices: Vec::new(),
61 batches: Vec::new(),
62 gradients: Vec::new(),
63 fingerprint: 0,
64 built: false,
65 width: 0.0,
66 height: 0.0,
67 dpr: 1.0,
68 scissor: None,
69 scissor_dirty: true,
70 primary_texture: None,
71 };
72 if !backend.ensure_resources() {
73 return None;
74 }
75 Some(backend)
76 }
77
78 pub fn canvas(&self) -> &HtmlCanvasElement {
84 self.get_canvas()
85 }
86
87 pub fn draw_call_count(&self) -> usize {
97 self.get_batches().len()
98 }
99
100 fn ensure_resources(&mut self) -> bool {
110 if self.try_get_program().is_some()
111 && self.try_get_vertex_array().is_some()
112 && self.try_get_vertex_buffer().is_some()
113 && self.try_get_index_buffer().is_some()
114 {
115 return true;
116 }
117 let Some(program) = WebGl2Program::create(self.get_context()) else {
118 return false;
119 };
120 let Some(vertex_array) = self.get_context().create_vertex_array() else {
121 return false;
122 };
123 let Some(vertex_buffer) = self.get_context().create_buffer() else {
124 return false;
125 };
126 let Some(index_buffer) = self.get_context().create_buffer() else {
127 return false;
128 };
129 self.get_context().bind_vertex_array(Some(&vertex_array));
132 self.get_context()
133 .bind_buffer(WebGl2RenderingContext::ARRAY_BUFFER, Some(&vertex_buffer));
134 self.get_context().bind_buffer(
135 WebGl2RenderingContext::ELEMENT_ARRAY_BUFFER,
136 Some(&index_buffer),
137 );
138 for (location, size, offset) in [
139 (GL_ATTRIBUTE_POSITION, 2, 0),
140 (GL_ATTRIBUTE_UV, 2, 8),
141 (GL_ATTRIBUTE_COLOR, 4, 16),
142 (GL_ATTRIBUTE_META, 2, 24),
143 ] {
144 self.get_context().enable_vertex_attrib_array(location);
145 self.get_context().vertex_attrib_pointer_with_i32(
146 location,
147 size,
148 WebGl2RenderingContext::FLOAT,
149 false,
150 GL_VERTEX_STRIDE,
151 offset,
152 );
153 }
154 self.get_context().enable(WebGl2RenderingContext::BLEND);
159 self.get_context().blend_func_separate(
160 WebGl2RenderingContext::ONE,
161 WebGl2RenderingContext::ONE_MINUS_SRC_ALPHA,
162 WebGl2RenderingContext::ONE,
163 WebGl2RenderingContext::ONE_MINUS_SRC_ALPHA,
164 );
165 self.get_context()
166 .disable(WebGl2RenderingContext::DEPTH_TEST);
167 self.get_context()
168 .disable(WebGl2RenderingContext::CULL_FACE);
169 program.set_samplers(self.get_context());
170 self.set_program(Some(program));
171 self.set_vertex_array(Some(vertex_array));
172 self.set_vertex_buffer(Some(vertex_buffer));
173 self.set_index_buffer(Some(index_buffer));
174 true
175 }
176
177 fn rebuild(&mut self, list: &DisplayList, shaper: &mut TextShaper) {
189 self.get_mut_vertices().clear();
190 self.get_mut_indices().clear();
191 self.get_mut_batches().clear();
192 self.get_mut_gradients().clear();
193 let mut clip_stack: Vec<Option<WebGl2Scissor>> = Vec::new();
194 let mut clip: Option<WebGl2Scissor> = None;
195 let mut batch_start: usize = 0_usize;
196 let mut batch_kind: WebGl2BatchKind = WebGl2BatchKind::Atlas;
197 let mut batch_image: usize = 0_usize;
198 for item in list.items() {
199 match &item.cmd {
200 DrawCmd::Fill {
201 path,
202 paint,
203 opacity,
204 } => {
205 self.append_fill(path, paint, *opacity);
206 }
207 DrawCmd::Stroke {
208 path,
209 paint,
210 style,
211 opacity,
212 } => {
213 self.append_stroke(path, paint, style, *opacity);
214 }
215 DrawCmd::Strip {
216 points,
217 baseline,
218 paint,
219 opacity,
220 } => {
221 self.append_strip(points, *baseline, paint, *opacity);
222 }
223 DrawCmd::Text { text, x, y, style } => {
224 self.append_text(text, *x, *y, style, shaper);
225 }
226 DrawCmd::Image {
227 image,
228 x,
229 y,
230 width,
231 height,
232 } => {
233 self.flush_batch(&mut batch_start, batch_kind, batch_image, clip);
237 let image_index: usize = self.image_index(image);
238 self.append_image(*x, *y, *width, *height);
239 self.flush_batch(&mut batch_start, WebGl2BatchKind::Image, image_index, clip);
240 batch_kind = WebGl2BatchKind::Atlas;
241 batch_image = 0;
242 }
243 DrawCmd::Clip { path } => {
244 self.flush_batch(&mut batch_start, batch_kind, batch_image, clip);
245 clip_stack.push(clip);
246 clip = Some(intersect_scissor(clip, scissor_of(&path.bounds())));
247 }
248 DrawCmd::PopClip => {
249 self.flush_batch(&mut batch_start, batch_kind, batch_image, clip);
250 clip = clip_stack.pop().flatten();
251 }
252 }
253 }
254 self.flush_batch(&mut batch_start, batch_kind, batch_image, clip);
255 }
256
257 fn append_fill(&mut self, path: &Path, paint: &Fill, opacity: f64) {
265 let style: WebGl2Style = match paint {
266 Fill::None => return,
267 Fill::Solid(color) => WebGl2Style {
268 color: scaled_color(*color, opacity),
269 kind: WebGl2VertexKind::Solid.code(),
270 gradient: 0.0,
271 },
272 Fill::Linear(gradient) => {
273 let row: f32 = self.gradient_row(&WebGl2Gradient::Linear((*gradient).clone()));
274 WebGl2Style {
275 color: [1.0, 1.0, 1.0, opacity.clamp(0.0, 1.0) as f32],
276 kind: WebGl2VertexKind::Gradient.code(),
277 gradient: row,
278 }
279 }
280 Fill::Radial(gradient) => {
281 let row: f32 = self.gradient_row(&WebGl2Gradient::Radial((*gradient).clone()));
282 WebGl2Style {
283 color: [1.0, 1.0, 1.0, opacity.clamp(0.0, 1.0) as f32],
284 kind: WebGl2VertexKind::Gradient.code(),
285 gradient: row,
286 }
287 }
288 };
289 let mesh: SolidMesh = self.get_mut_tessellator().fill(path, 0);
290 self.push_mesh(&mesh, &style);
291 }
292
293 fn append_stroke(&mut self, path: &Path, paint: &Stroke, style: &LineStyle, opacity: f64) {
302 if style.width <= 0.0 {
303 return;
304 }
305 let alpha: f32 = (opacity * style.opacity).clamp(0.0, 1.0) as f32;
306 let shading: WebGl2Style = match paint {
307 Stroke::None => return,
308 Stroke::Solid(color) => WebGl2Style {
309 color: scaled_color(*color, opacity * style.opacity),
310 kind: WebGl2VertexKind::Solid.code(),
311 gradient: 0.0,
312 },
313 Stroke::Linear(gradient) => {
314 let row: f32 = self.gradient_row(&WebGl2Gradient::Linear((*gradient).clone()));
315 WebGl2Style {
316 color: [1.0, 1.0, 1.0, alpha],
317 kind: WebGl2VertexKind::Gradient.code(),
318 gradient: row,
319 }
320 }
321 Stroke::Radial(gradient) => {
322 let row: f32 = self.gradient_row(&WebGl2Gradient::Radial((*gradient).clone()));
323 WebGl2Style {
324 color: [1.0, 1.0, 1.0, alpha],
325 kind: WebGl2VertexKind::Gradient.code(),
326 gradient: row,
327 }
328 }
329 };
330 let geometry: StrokeStyle = style.geometry();
331 let mesh: SolidMesh = if style.dash.is_solid() {
332 self.get_mut_tessellator().stroke(path, &geometry, 0)
333 } else {
334 let contours: Vec<Contour> = self.get_mut_tessellator().flatten(path).contours.clone();
337 let dashed: Vec<Contour> =
338 dash_contours(&contours, &style.dash.pattern, style.dash.offset);
339 self.get_mut_tessellator()
340 .stroke_contours(&dashed, &geometry, 0)
341 };
342 self.push_mesh(&mesh, &shading);
343 }
344
345 fn append_strip(&mut self, points: &[Point], baseline: f32, paint: &Fill, opacity: f64) {
358 if points.len() < 2 {
359 return;
360 }
361 let style: WebGl2Style = match paint {
362 Fill::None => return,
363 Fill::Solid(color) => WebGl2Style {
364 color: scaled_color(*color, opacity),
365 kind: WebGl2VertexKind::Solid.code(),
366 gradient: 0.0,
367 },
368 Fill::Linear(gradient) => {
369 let row: f32 = self.gradient_row(&WebGl2Gradient::Linear((*gradient).clone()));
370 WebGl2Style {
371 color: [1.0, 1.0, 1.0, opacity.clamp(0.0, 1.0) as f32],
372 kind: WebGl2VertexKind::Gradient.code(),
373 gradient: row,
374 }
375 }
376 Fill::Radial(gradient) => {
377 let row: f32 = self.gradient_row(&WebGl2Gradient::Radial((*gradient).clone()));
378 WebGl2Style {
379 color: [1.0, 1.0, 1.0, opacity.clamp(0.0, 1.0) as f32],
380 kind: WebGl2VertexKind::Gradient.code(),
381 gradient: row,
382 }
383 }
384 };
385 let mesh: SolidMesh = self.get_mut_tessellator().fill_strip(points, baseline, 0);
386 self.push_mesh(&mesh, &style);
387 }
388
389 fn append_text(
403 &mut self,
404 text: &str,
405 x: f64,
406 y: f64,
407 style: &TextStyle,
408 shaper: &mut TextShaper,
409 ) {
410 let run: TextRun = shaper.shape(text, style, x, y);
411 if let Some(background) = style.background {
412 let shading: WebGl2Style = WebGl2Style {
413 color: scaled_color(background, 1.0),
414 kind: WebGl2VertexKind::Solid.code(),
415 gradient: 0.0,
416 };
417 let rect: WebGl2TextureRect = WebGl2TextureRect {
418 u: 0.0,
419 v: 0.0,
420 u_size: 0.0,
421 v_size: 0.0,
422 };
423 for line in &run.lines {
424 let left: f64 = line.left - style.padding.3;
425 let top: f64 = line.baseline - run.ascent - style.padding.0;
426 let width: f64 = line.width + style.padding.1 + style.padding.3;
427 let height: f64 = run.ascent + run.descent + style.padding.0 + style.padding.2;
428 self.push_quad(
429 left as f32,
430 top as f32,
431 width as f32,
432 height as f32,
433 rect,
434 &shading,
435 );
436 }
437 }
438 let shading: WebGl2Style = WebGl2Style {
439 color: unpack_color(style.color.to_u32()),
440 kind: WebGl2VertexKind::Text.code(),
441 gradient: 0.0,
442 };
443 for quad in &run.quads {
444 let rect: WebGl2TextureRect = WebGl2TextureRect {
445 u: quad.u,
446 v: quad.v,
447 u_size: quad.u_size,
448 v_size: quad.v_size,
449 };
450 self.push_quad(quad.x, quad.y, quad.width, quad.height, rect, &shading);
451 }
452 }
453
454 fn append_image(&mut self, x: f64, y: f64, width: f64, height: f64) {
460 let shading: WebGl2Style = WebGl2Style {
461 color: [1.0, 1.0, 1.0, 1.0],
462 kind: WebGl2VertexKind::Image.code(),
463 gradient: 0.0,
464 };
465 let rect: WebGl2TextureRect = WebGl2TextureRect {
466 u: 0.0,
467 v: 0.0,
468 u_size: 1.0,
469 v_size: 1.0,
470 };
471 self.push_quad(
472 x as f32,
473 y as f32,
474 width as f32,
475 height as f32,
476 rect,
477 &shading,
478 );
479 }
480
481 fn push_vertex(&mut self, x: f32, y: f32, u: f32, v: f32, style: &WebGl2Style) {
488 self.get_mut_vertices().push(x);
489 self.get_mut_vertices().push(y);
490 self.get_mut_vertices().push(u);
491 self.get_mut_vertices().push(v);
492 self.get_mut_vertices().extend_from_slice(&style.color);
493 self.get_mut_vertices().push(style.kind);
494 self.get_mut_vertices().push(style.gradient);
495 }
496
497 fn push_mesh(&mut self, mesh: &SolidMesh, style: &WebGl2Style) {
504 let base: u32 = (self.get_vertices().len() / GL_VERTEX_FLOATS) as u32;
505 for vertex in &mesh.vertices {
506 self.push_vertex(vertex.x, vertex.y, 0.0, 0.0, style);
507 }
508 for index in &mesh.indices {
509 self.get_mut_indices().push(base + index);
510 }
511 }
512
513 fn push_quad(
521 &mut self,
522 x: f32,
523 y: f32,
524 width: f32,
525 height: f32,
526 rect: WebGl2TextureRect,
527 style: &WebGl2Style,
528 ) {
529 let base: u32 = (self.get_vertices().len() / GL_VERTEX_FLOATS) as u32;
530 self.push_vertex(x, y, rect.u, rect.v, style);
531 self.push_vertex(x + width, y, rect.u + rect.u_size, rect.v, style);
532 self.push_vertex(
533 x + width,
534 y + height,
535 rect.u + rect.u_size,
536 rect.v + rect.v_size,
537 style,
538 );
539 self.push_vertex(x, y + height, rect.u, rect.v + rect.v_size, style);
540 self.get_mut_indices().push(base);
541 self.get_mut_indices().push(base + 1);
542 self.get_mut_indices().push(base + 2);
543 self.get_mut_indices().push(base);
544 self.get_mut_indices().push(base + 2);
545 self.get_mut_indices().push(base + 3);
546 }
547
548 fn flush_batch(
557 &mut self,
558 start: &mut usize,
559 kind: WebGl2BatchKind,
560 image: usize,
561 clip: Option<WebGl2Scissor>,
562 ) {
563 let count: usize = self.get_indices().len() - *start;
564 if count == 0 {
565 return;
566 }
567 self.get_mut_batches().push(WebGl2Batch {
568 kind,
569 first_index: *start as u32,
570 index_count: count as u32,
571 image,
572 clip,
573 });
574 *start = self.get_indices().len();
575 }
576
577 fn gradient_row(&mut self, gradient: &WebGl2Gradient) -> f32 {
591 for (index, existing) in self.get_gradients().iter().enumerate() {
592 let same: bool = match (existing, gradient) {
593 (WebGl2Gradient::Linear(left), WebGl2Gradient::Linear(right)) => left == right,
594 (WebGl2Gradient::Radial(left), WebGl2Gradient::Radial(right)) => left == right,
595 _ => false,
596 };
597 if same {
598 return index as f32;
599 }
600 }
601 self.get_mut_gradients().push(gradient.clone());
602 (self.get_gradients().len() - 1) as f32
603 }
604
605 fn image_index(&mut self, image: &HtmlImageElement) -> usize {
615 let incoming: &JsValue = image.as_ref();
616 for (index, existing) in self.get_images().iter().enumerate() {
617 let current: &JsValue = existing.element.as_ref();
618 if current == incoming {
619 return index;
620 }
621 }
622 self.get_mut_images().push(WebGl2Image {
623 element: image.clone(),
624 texture: None,
625 });
626 self.get_images().len() - 1
627 }
628
629 fn upload_geometry(&mut self) {
631 let (Some(vertex_array), Some(vertex_buffer), Some(index_buffer)) = (
632 self.try_get_vertex_array().as_ref(),
633 self.try_get_vertex_buffer().as_ref(),
634 self.try_get_index_buffer().as_ref(),
635 ) else {
636 return;
637 };
638 self.get_context().bind_vertex_array(Some(vertex_array));
639 let vertices: Float32Array = js_sys::Float32Array::from(self.get_vertices().as_slice());
640 self.get_context()
641 .bind_buffer(WebGl2RenderingContext::ARRAY_BUFFER, Some(vertex_buffer));
642 self.get_context().buffer_data_with_array_buffer_view(
643 WebGl2RenderingContext::ARRAY_BUFFER,
644 vertices.as_ref(),
645 WebGl2RenderingContext::STATIC_DRAW,
646 );
647 let indices: Uint32Array = js_sys::Uint32Array::from(self.get_indices().as_slice());
648 self.get_context().bind_buffer(
649 WebGl2RenderingContext::ELEMENT_ARRAY_BUFFER,
650 Some(index_buffer),
651 );
652 self.get_context().buffer_data_with_array_buffer_view(
653 WebGl2RenderingContext::ELEMENT_ARRAY_BUFFER,
654 indices.as_ref(),
655 WebGl2RenderingContext::STATIC_DRAW,
656 );
657 }
658
659 fn upload_atlas(&mut self, atlas: &GlyphAtlas) {
665 let existing: Option<WebGlTexture> = self.try_get_atlas_texture().clone();
666 let texture: WebGlTexture = match existing {
667 Some(texture) => texture,
668 None => {
669 let Some(texture) = self.get_context().create_texture() else {
670 return;
671 };
672 self.get_context()
673 .bind_texture(WebGl2RenderingContext::TEXTURE_2D, Some(&texture));
674 self.get_context().tex_parameteri(
675 WebGl2RenderingContext::TEXTURE_2D,
676 WebGl2RenderingContext::TEXTURE_MIN_FILTER,
677 WebGl2RenderingContext::LINEAR as i32,
678 );
679 self.get_context().tex_parameteri(
680 WebGl2RenderingContext::TEXTURE_2D,
681 WebGl2RenderingContext::TEXTURE_MAG_FILTER,
682 WebGl2RenderingContext::LINEAR as i32,
683 );
684 self.get_context().tex_parameteri(
685 WebGl2RenderingContext::TEXTURE_2D,
686 WebGl2RenderingContext::TEXTURE_WRAP_S,
687 WebGl2RenderingContext::CLAMP_TO_EDGE as i32,
688 );
689 self.get_context().tex_parameteri(
690 WebGl2RenderingContext::TEXTURE_2D,
691 WebGl2RenderingContext::TEXTURE_WRAP_T,
692 WebGl2RenderingContext::CLAMP_TO_EDGE as i32,
693 );
694 self.set_atlas_texture(Some(texture.clone()));
695 texture
696 }
697 };
698 self.get_context()
699 .active_texture(WebGl2RenderingContext::TEXTURE0 + GL_TEXTURE_UNIT_PRIMARY);
700 self.get_context()
701 .bind_texture(WebGl2RenderingContext::TEXTURE_2D, Some(&texture));
702 let _: Result<(), JsValue> = self
703 .get_context()
704 .tex_image_2d_with_u32_and_u32_and_html_canvas_element(
705 WebGl2RenderingContext::TEXTURE_2D,
706 0,
707 WebGl2RenderingContext::RGBA as i32,
708 WebGl2RenderingContext::RGBA,
709 WebGl2RenderingContext::UNSIGNED_BYTE,
710 atlas.canvas(),
711 );
712 self.set_primary_texture(None);
715 }
716
717 fn upload_gradients(&mut self) {
719 let rows: usize = self.get_gradients().len().max(1);
720 let mut ramp: Vec<u8> = vec![0_u8; GL_RAMP_WIDTH * rows * 4];
721 for (index, gradient) in self.get_gradients().iter().enumerate() {
722 let bytes: Vec<u8> = ramp_bytes(gradient);
723 let start: usize = index * GL_RAMP_WIDTH * 4;
724 ramp[start..start + bytes.len()].copy_from_slice(&bytes);
725 }
726 let mut parameters: Vec<f32> = vec![0.0_f32; GL_PARAMETER_WIDTH * rows * 4];
727 for (index, gradient) in self.get_gradients().iter().enumerate() {
728 let values: [f32; 8] = parameter_values(gradient);
729 let start: usize = index * GL_PARAMETER_WIDTH * 4;
730 parameters[start..start + values.len()].copy_from_slice(&values);
731 }
732 let existing: Option<WebGlTexture> = self.try_get_ramp_texture().clone();
734 let ramp_texture: WebGlTexture = match existing {
735 Some(texture) => texture,
736 None => {
737 let Some(texture) = self.get_context().create_texture() else {
738 return;
739 };
740 self.set_ramp_texture(Some(texture.clone()));
741 texture
742 }
743 };
744 self.get_context()
745 .active_texture(WebGl2RenderingContext::TEXTURE0 + GL_TEXTURE_UNIT_RAMP);
746 self.get_context()
747 .bind_texture(WebGl2RenderingContext::TEXTURE_2D, Some(&ramp_texture));
748 self.get_context().tex_parameteri(
749 WebGl2RenderingContext::TEXTURE_2D,
750 WebGl2RenderingContext::TEXTURE_MIN_FILTER,
751 WebGl2RenderingContext::LINEAR as i32,
752 );
753 self.get_context().tex_parameteri(
754 WebGl2RenderingContext::TEXTURE_2D,
755 WebGl2RenderingContext::TEXTURE_MAG_FILTER,
756 WebGl2RenderingContext::LINEAR as i32,
757 );
758 self.get_context().tex_parameteri(
759 WebGl2RenderingContext::TEXTURE_2D,
760 WebGl2RenderingContext::TEXTURE_WRAP_S,
761 WebGl2RenderingContext::CLAMP_TO_EDGE as i32,
762 );
763 self.get_context().tex_parameteri(
764 WebGl2RenderingContext::TEXTURE_2D,
765 WebGl2RenderingContext::TEXTURE_WRAP_T,
766 WebGl2RenderingContext::CLAMP_TO_EDGE as i32,
767 );
768 let _: Result<(), JsValue> = self
769 .get_context()
770 .tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_opt_u8_array(
771 WebGl2RenderingContext::TEXTURE_2D,
772 0,
773 WebGl2RenderingContext::RGBA as i32,
774 GL_RAMP_WIDTH as i32,
775 rows as i32,
776 0,
777 WebGl2RenderingContext::RGBA,
778 WebGl2RenderingContext::UNSIGNED_BYTE,
779 Some(&ramp),
780 );
781 let existing: Option<WebGlTexture> = self.try_get_parameter_texture().clone();
784 let parameter_texture: WebGlTexture = match existing {
785 Some(texture) => texture,
786 None => {
787 let Some(texture) = self.get_context().create_texture() else {
788 return;
789 };
790 self.set_parameter_texture(Some(texture.clone()));
791 texture
792 }
793 };
794 self.get_context()
795 .active_texture(WebGl2RenderingContext::TEXTURE0 + GL_TEXTURE_UNIT_PARAMETERS);
796 self.get_context()
797 .bind_texture(WebGl2RenderingContext::TEXTURE_2D, Some(¶meter_texture));
798 self.get_context().tex_parameteri(
799 WebGl2RenderingContext::TEXTURE_2D,
800 WebGl2RenderingContext::TEXTURE_MIN_FILTER,
801 WebGl2RenderingContext::NEAREST as i32,
802 );
803 self.get_context().tex_parameteri(
804 WebGl2RenderingContext::TEXTURE_2D,
805 WebGl2RenderingContext::TEXTURE_MAG_FILTER,
806 WebGl2RenderingContext::NEAREST as i32,
807 );
808 self.get_context().tex_parameteri(
809 WebGl2RenderingContext::TEXTURE_2D,
810 WebGl2RenderingContext::TEXTURE_WRAP_S,
811 WebGl2RenderingContext::CLAMP_TO_EDGE as i32,
812 );
813 self.get_context().tex_parameteri(
814 WebGl2RenderingContext::TEXTURE_2D,
815 WebGl2RenderingContext::TEXTURE_WRAP_T,
816 WebGl2RenderingContext::CLAMP_TO_EDGE as i32,
817 );
818 let view: Float32Array = js_sys::Float32Array::from(parameters.as_slice());
819 let _: Result<(), JsValue> = self
820 .get_context()
821 .tex_image_2d_with_i32_and_i32_and_i32_and_format_and_type_and_array_buffer_view_and_src_offset(
822 WebGl2RenderingContext::TEXTURE_2D,
823 0,
824 WebGl2RenderingContext::RGBA32F as i32,
825 GL_PARAMETER_WIDTH as i32,
826 rows as i32,
827 0,
828 WebGl2RenderingContext::RGBA,
829 WebGl2RenderingContext::FLOAT,
830 view.as_ref(),
831 0,
832 );
833 }
834
835 fn upload_images(&mut self) {
837 for index in 0..self.get_images().len() {
838 let element: HtmlImageElement = self.get_images()[index].element.clone();
839 let existing: Option<WebGlTexture> = self.get_images()[index].texture.clone();
840 let texture: WebGlTexture = match existing {
841 Some(texture) => texture,
842 None => {
843 let Some(texture) = self.get_context().create_texture() else {
844 continue;
845 };
846 self.get_context()
847 .bind_texture(WebGl2RenderingContext::TEXTURE_2D, Some(&texture));
848 self.get_context().tex_parameteri(
849 WebGl2RenderingContext::TEXTURE_2D,
850 WebGl2RenderingContext::TEXTURE_MIN_FILTER,
851 WebGl2RenderingContext::LINEAR as i32,
852 );
853 self.get_context().tex_parameteri(
854 WebGl2RenderingContext::TEXTURE_2D,
855 WebGl2RenderingContext::TEXTURE_MAG_FILTER,
856 WebGl2RenderingContext::LINEAR as i32,
857 );
858 self.get_context().tex_parameteri(
859 WebGl2RenderingContext::TEXTURE_2D,
860 WebGl2RenderingContext::TEXTURE_WRAP_S,
861 WebGl2RenderingContext::CLAMP_TO_EDGE as i32,
862 );
863 self.get_context().tex_parameteri(
864 WebGl2RenderingContext::TEXTURE_2D,
865 WebGl2RenderingContext::TEXTURE_WRAP_T,
866 WebGl2RenderingContext::CLAMP_TO_EDGE as i32,
867 );
868 self.get_mut_images()[index].texture = Some(texture.clone());
869 texture
870 }
871 };
872 self.get_context()
873 .active_texture(WebGl2RenderingContext::TEXTURE0 + GL_TEXTURE_UNIT_PRIMARY);
874 self.get_context()
875 .bind_texture(WebGl2RenderingContext::TEXTURE_2D, Some(&texture));
876 let _: Result<(), JsValue> = self
877 .get_context()
878 .tex_image_2d_with_u32_and_u32_and_html_image_element(
879 WebGl2RenderingContext::TEXTURE_2D,
880 0,
881 WebGl2RenderingContext::RGBA as i32,
882 WebGl2RenderingContext::RGBA,
883 WebGl2RenderingContext::UNSIGNED_BYTE,
884 &element,
885 );
886 }
887 self.set_primary_texture(None);
890 }
891
892 fn draw(&mut self) {
898 let Some(program) = self.try_get_program().as_ref() else {
899 return;
900 };
901 let drawing_width: u32 = self.get_canvas().width();
902 let drawing_height: u32 = self.get_canvas().height();
903 self.get_context()
904 .viewport(0, 0, drawing_width as i32, drawing_height as i32);
905 self.get_context().clear_color(0.0, 0.0, 0.0, 0.0);
906 self.get_context()
907 .clear(WebGl2RenderingContext::COLOR_BUFFER_BIT);
908 self.get_context().use_program(Some(&program.program));
909 program.set_resolution_uniform(
910 self.get_context(),
911 self.get_width() as f32,
912 self.get_height() as f32,
913 );
914 program.set_gradient_count_uniform(self.get_context(), self.get_gradients().len() as f32);
915 self.bind_ramp();
916 self.bind_parameters();
917 let batches: Vec<WebGl2Batch> = take(self.get_mut_batches());
921 for batch in &batches {
922 self.apply_scissor(batch.clip, drawing_height);
923 match batch.kind {
924 WebGl2BatchKind::Atlas => self.bind_atlas(),
925 WebGl2BatchKind::Image => self.bind_image(batch.image),
926 }
927 self.get_context().draw_elements_with_i32(
928 WebGl2RenderingContext::TRIANGLES,
929 batch.index_count as i32,
930 WebGl2RenderingContext::UNSIGNED_INT,
931 (batch.first_index as i32) * 4,
932 );
933 }
934 self.set_batches(batches);
935 }
936
937 fn apply_scissor(&mut self, clip: Option<WebGl2Scissor>, drawing_height: u32) {
949 if !self.get_scissor_dirty() && clip == *self.try_get_scissor() {
950 return;
951 }
952 self.set_scissor_dirty(false);
953 self.set_scissor(clip);
954 let Some(rect) = clip else {
955 self.get_context()
956 .disable(WebGl2RenderingContext::SCISSOR_TEST);
957 return;
958 };
959 let x: i32 = (rect.x * self.get_dpr()).round().max(0.0) as i32;
960 let y: i32 = (rect.y * self.get_dpr()).round().max(0.0) as i32;
961 let width: i32 = (rect.width * self.get_dpr()).round().max(0.0) as i32;
962 let height: i32 = (rect.height * self.get_dpr()).round().max(0.0) as i32;
963 let bottom: i32 = drawing_height as i32 - (y + height);
964 self.get_context()
965 .enable(WebGl2RenderingContext::SCISSOR_TEST);
966 self.get_context().scissor(x, bottom.max(0), width, height);
967 }
968
969 fn bind_atlas(&mut self) {
971 if *self.try_get_primary_texture() == Some((WebGl2BatchKind::Atlas, 0)) {
972 return;
973 }
974 let Some(texture) = self.try_get_atlas_texture().clone() else {
975 return;
976 };
977 self.get_context()
978 .active_texture(WebGl2RenderingContext::TEXTURE0 + GL_TEXTURE_UNIT_PRIMARY);
979 self.get_context()
980 .bind_texture(WebGl2RenderingContext::TEXTURE_2D, Some(&texture));
981 self.set_primary_texture(Some((WebGl2BatchKind::Atlas, 0)));
982 }
983
984 fn bind_image(&mut self, image: usize) {
990 if *self.try_get_primary_texture() == Some((WebGl2BatchKind::Image, image)) {
991 return;
992 }
993 let Some(texture) = self
994 .get_images()
995 .get(image)
996 .and_then(|entry: &WebGl2Image| entry.texture.clone())
997 else {
998 return;
999 };
1000 self.get_context()
1001 .active_texture(WebGl2RenderingContext::TEXTURE0 + GL_TEXTURE_UNIT_PRIMARY);
1002 self.get_context()
1003 .bind_texture(WebGl2RenderingContext::TEXTURE_2D, Some(&texture));
1004 self.set_primary_texture(Some((WebGl2BatchKind::Image, image)));
1005 }
1006
1007 fn bind_ramp(&mut self) {
1009 let Some(texture) = self.try_get_ramp_texture().clone() else {
1010 return;
1011 };
1012 self.get_context()
1013 .active_texture(WebGl2RenderingContext::TEXTURE0 + GL_TEXTURE_UNIT_RAMP);
1014 self.get_context()
1015 .bind_texture(WebGl2RenderingContext::TEXTURE_2D, Some(&texture));
1016 }
1017
1018 fn bind_parameters(&mut self) {
1020 let Some(texture) = self.try_get_parameter_texture().clone() else {
1021 return;
1022 };
1023 self.get_context()
1024 .active_texture(WebGl2RenderingContext::TEXTURE0 + GL_TEXTURE_UNIT_PARAMETERS);
1025 self.get_context()
1026 .bind_texture(WebGl2RenderingContext::TEXTURE_2D, Some(&texture));
1027 }
1028}
1029
1030impl ChartBackend for WebGl2ChartBackend {
1032 fn kind(&self) -> BackendKind {
1038 BackendKind::WebGl2
1039 }
1040
1041 fn resize(&mut self, width: f64, height: f64, dpr: f64) {
1047 self.set_dpr(dpr);
1048 self.set_width(width);
1049 self.set_height(height);
1050 let physical_width: u32 = (width * dpr).round().max(1.0) as u32;
1051 let physical_height: u32 = (height * dpr).round().max(1.0) as u32;
1052 if self.get_canvas().width() != physical_width {
1053 self.get_canvas().set_width(physical_width);
1054 }
1055 if self.get_canvas().height() != physical_height {
1056 self.get_canvas().set_height(physical_height);
1057 }
1058 self.get_context()
1061 .viewport(0, 0, physical_width as i32, physical_height as i32);
1062 self.set_scissor_dirty(true);
1065 }
1066
1067 fn render(&mut self, list: &DisplayList, shaper: &mut TextShaper) {
1074 if self.get_context().is_context_lost() {
1075 return;
1076 }
1077 if !self.ensure_resources() {
1078 return;
1079 }
1080 let fingerprint: u64 = fingerprint(list);
1084 if !self.get_built() || self.get_fingerprint() != fingerprint {
1085 self.rebuild(list, shaper);
1086 self.set_fingerprint(fingerprint);
1087 self.set_built(true);
1088 self.upload_geometry();
1089 self.upload_gradients();
1090 self.upload_images();
1091 }
1092 if self.try_get_atlas_texture().is_none() || shaper.atlas().is_dirty() {
1095 self.upload_atlas(shaper.atlas());
1096 shaper.atlas_mut().mark_clean();
1097 }
1098 self.draw();
1099 }
1100
1101 fn dispose(&mut self) {
1103 if let Some(program) = self.get_mut_program().take() {
1104 self.get_context().delete_program(Some(&program.program));
1105 }
1106 if let Some(vertex_array) = self.get_mut_vertex_array().take() {
1107 self.get_context().delete_vertex_array(Some(&vertex_array));
1108 }
1109 if let Some(buffer) = self.get_mut_vertex_buffer().take() {
1110 self.get_context().delete_buffer(Some(&buffer));
1111 }
1112 if let Some(buffer) = self.get_mut_index_buffer().take() {
1113 self.get_context().delete_buffer(Some(&buffer));
1114 }
1115 if let Some(texture) = self.get_mut_atlas_texture().take() {
1116 self.get_context().delete_texture(Some(&texture));
1117 }
1118 if let Some(texture) = self.get_mut_ramp_texture().take() {
1119 self.get_context().delete_texture(Some(&texture));
1120 }
1121 if let Some(texture) = self.get_mut_parameter_texture().take() {
1122 self.get_context().delete_texture(Some(&texture));
1123 }
1124 for index in 0..self.get_images().len() {
1125 let texture: Option<WebGlTexture> =
1126 self.get_mut_images()[index].get_mut_texture().take();
1127 if let Some(texture) = texture {
1128 self.get_context().delete_texture(Some(&texture));
1129 }
1130 }
1131 self.get_mut_images().clear();
1132 self.get_mut_vertices().clear();
1133 self.get_mut_indices().clear();
1134 self.get_mut_batches().clear();
1135 self.get_mut_gradients().clear();
1136 self.set_built(false);
1137 self.set_fingerprint(0);
1138 self.set_primary_texture(None);
1139 self.set_scissor(None);
1140 self.set_scissor_dirty(true);
1141 self.get_context().viewport(
1143 0,
1144 0,
1145 self.get_canvas().width() as i32,
1146 self.get_canvas().height() as i32,
1147 );
1148 self.get_context().clear_color(0.0, 0.0, 0.0, 0.0);
1149 self.get_context()
1150 .clear(WebGl2RenderingContext::COLOR_BUFFER_BIT);
1151 }
1152}
1153
1154impl Drop for WebGl2ChartBackend {
1156 fn drop(&mut self) {
1162 <Self as ChartBackend>::dispose(self);
1163 }
1164}
1165
1166impl WebGl2Program {
1168 fn create(context: &WebGl2RenderingContext) -> Option<Self> {
1182 let vertex: WebGlShader = compile_shader(
1183 context,
1184 WebGl2RenderingContext::VERTEX_SHADER,
1185 GL_VERTEX_SHADER,
1186 )?;
1187 let fragment: WebGlShader = compile_shader(
1188 context,
1189 WebGl2RenderingContext::FRAGMENT_SHADER,
1190 GL_FRAGMENT_SHADER,
1191 )?;
1192 let program: WebGlProgram = context.create_program()?;
1193 context.attach_shader(&program, &vertex);
1194 context.attach_shader(&program, &fragment);
1195 context.link_program(&program);
1196 context.delete_shader(Some(&vertex));
1197 context.delete_shader(Some(&fragment));
1198 let linked: bool = context
1199 .get_program_parameter(&program, WebGl2RenderingContext::LINK_STATUS)
1200 .as_bool()
1201 .unwrap_or(false);
1202 if !linked {
1203 let log: String = context
1204 .get_program_info_log(&program)
1205 .unwrap_or_else(|| "the program did not link".to_string());
1206 web_sys::console::error_1(&format!("euv-chart: {log}").into());
1207 context.delete_program(Some(&program));
1208 return None;
1209 }
1210 Some(Self {
1211 resolution: context.get_uniform_location(&program, "u_resolution"),
1212 atlas: context.get_uniform_location(&program, "u_atlas"),
1213 ramp: context.get_uniform_location(&program, "u_ramp"),
1214 parameters: context.get_uniform_location(&program, "u_parameters"),
1215 gradient_count: context.get_uniform_location(&program, "u_gradient_count"),
1216 program,
1217 })
1218 }
1219
1220 fn set_samplers(&self, context: &WebGl2RenderingContext) {
1226 if let Some(location) = self.try_get_atlas() {
1227 context.uniform1i(Some(location), GL_TEXTURE_UNIT_PRIMARY as i32);
1228 }
1229 if let Some(location) = self.try_get_ramp() {
1230 context.uniform1i(Some(location), GL_TEXTURE_UNIT_RAMP as i32);
1231 }
1232 if let Some(location) = self.try_get_parameters() {
1233 context.uniform1i(Some(location), GL_TEXTURE_UNIT_PARAMETERS as i32);
1234 }
1235 }
1236
1237 fn set_resolution_uniform(&self, context: &WebGl2RenderingContext, width: f32, height: f32) {
1244 if let Some(location) = self.try_get_resolution() {
1245 context.uniform2f(Some(location), width, height);
1246 }
1247 }
1248
1249 fn set_gradient_count_uniform(&self, context: &WebGl2RenderingContext, count: f32) {
1256 if let Some(location) = self.try_get_gradient_count() {
1257 context.uniform1f(Some(location), count);
1258 }
1259 }
1260}
1261
1262impl WebGl2VertexKind {
1264 pub fn code(self) -> f32 {
1274 match self {
1275 WebGl2VertexKind::Solid => 0.0,
1276 WebGl2VertexKind::Text => 1.0,
1277 WebGl2VertexKind::Gradient => 2.0,
1278 WebGl2VertexKind::Image => 3.0,
1279 }
1280 }
1281}
1282
1283impl DisplayHasher {
1285 const OFFSET: u64 = 0xcbf2_9ce4_8422_2325;
1287
1288 const PRIME: u64 = 0x0000_0100_0000_01b3;
1290
1291 pub(crate) fn new() -> Self {
1293 Self { hash: Self::OFFSET }
1294 }
1295
1296 pub(crate) fn write_u64(&mut self, value: u64) {
1302 self.set_hash(self.get_hash() ^ value);
1303 self.set_hash(self.get_hash().wrapping_mul(Self::PRIME));
1304 }
1305
1306 pub(crate) fn write_u32(&mut self, value: u32) {
1312 self.write_u64(value as u64);
1313 }
1314
1315 pub(crate) fn write_f32(&mut self, value: f32) {
1325 self.write_u32(value.to_bits());
1326 }
1327
1328 pub(crate) fn write_f64(&mut self, value: f64) {
1334 self.write_u64(value.to_bits());
1335 }
1336
1337 pub(crate) fn write_str(&mut self, value: &str) {
1343 self.write_u64(value.len() as u64);
1344 for chunk in value.as_bytes().chunks(8) {
1345 let mut word: [u8; 8] = [0_u8; 8];
1346 word[..chunk.len()].copy_from_slice(chunk);
1347 self.write_u64(u64::from_le_bytes(word));
1348 }
1349 }
1350
1351 pub(crate) fn finish(&self) -> u64 {
1357 self.get_hash()
1358 }
1359}