Skip to main content

nvg_gl/
lib.rs

1#[macro_use]
2extern crate anyhow;
3
4use nvg::renderer::*;
5use slab::Slab;
6use std::ffi::c_void;
7
8struct Shader {
9    prog: gl::types::GLuint,
10    frag: gl::types::GLuint,
11    vert: gl::types::GLuint,
12    loc_viewsize: i32,
13    loc_tex: i32,
14    loc_frag: u32,
15}
16
17impl Drop for Shader {
18    fn drop(&mut self) {
19        unsafe {
20            gl::DeleteProgram(self.prog);
21            gl::DeleteShader(self.vert);
22            gl::DeleteShader(self.frag);
23        }
24    }
25}
26
27impl Shader {
28    unsafe fn load() -> anyhow::Result<Shader> {
29        let mut status: gl::types::GLint = std::mem::zeroed();
30        let prog = gl::CreateProgram();
31        let vert = gl::CreateShader(gl::VERTEX_SHADER);
32        let frag = gl::CreateShader(gl::FRAGMENT_SHADER);
33        let vert_source =
34            std::ffi::CString::from_vec_unchecked(include_bytes!("shader.vert").to_vec());
35        let frag_source =
36            std::ffi::CString::from_vec_unchecked(include_bytes!("shader.frag").to_vec());
37
38        gl::ShaderSource(
39            vert,
40            1,
41            [vert_source.as_ptr()].as_ptr() as *const *const i8,
42            std::ptr::null(),
43        );
44        gl::ShaderSource(
45            frag,
46            1,
47            [frag_source.as_ptr()].as_ptr() as *const *const i8,
48            std::ptr::null(),
49        );
50
51        gl::CompileShader(vert);
52        gl::GetShaderiv(vert, gl::COMPILE_STATUS, &mut status);
53        if status != gl::TRUE as i32 {
54            return Err(shader_error(vert, "shader.vert"));
55        }
56
57        gl::CompileShader(frag);
58        gl::GetShaderiv(frag, gl::COMPILE_STATUS, &mut status);
59        if status != gl::TRUE as i32 {
60            return Err(shader_error(vert, "shader.frag"));
61        }
62
63        gl::AttachShader(prog, vert);
64        gl::AttachShader(prog, frag);
65
66        let name_vertex = std::ffi::CString::new("vertex").unwrap();
67        let name_tcoord = std::ffi::CString::new("tcoord").unwrap();
68        gl::BindAttribLocation(prog, 0, name_vertex.as_ptr() as *const i8);
69        gl::BindAttribLocation(prog, 1, name_tcoord.as_ptr() as *const i8);
70
71        gl::LinkProgram(prog);
72        gl::GetProgramiv(prog, gl::LINK_STATUS, &mut status);
73        if status != gl::TRUE as i32 {
74            return Err(program_error(prog));
75        }
76
77        let name_viewsize = std::ffi::CString::new("viewSize").unwrap();
78        let name_tex = std::ffi::CString::new("tex").unwrap();
79        let name_frag = std::ffi::CString::new("frag").unwrap();
80
81        Ok(Shader {
82            prog,
83            frag,
84            vert,
85            loc_viewsize: gl::GetUniformLocation(prog, name_viewsize.as_ptr() as *const i8),
86            loc_tex: gl::GetUniformLocation(prog, name_tex.as_ptr() as *const i8),
87            loc_frag: gl::GetUniformBlockIndex(prog, name_frag.as_ptr() as *const i8),
88        })
89    }
90}
91
92enum ShaderType {
93    FillGradient,
94    FillImage,
95    Simple,
96    Image,
97}
98
99#[derive(PartialEq, Eq)]
100enum CallType {
101    Fill,
102    ConvexFill,
103    Stroke,
104    Triangles,
105}
106
107struct Blend {
108    src_rgb: gl::types::GLenum,
109    dst_rgb: gl::types::GLenum,
110    src_alpha: gl::types::GLenum,
111    dst_alpha: gl::types::GLenum,
112}
113
114impl From<CompositeOperationState> for Blend {
115    fn from(state: CompositeOperationState) -> Self {
116        Blend {
117            src_rgb: convert_blend_factor(state.src_rgb),
118            dst_rgb: convert_blend_factor(state.dst_rgb),
119            src_alpha: convert_blend_factor(state.src_alpha),
120            dst_alpha: convert_blend_factor(state.dst_alpha),
121        }
122    }
123}
124
125struct Call {
126    call_type: CallType,
127    image: Option<usize>,
128    path_offset: usize,
129    path_count: usize,
130    triangle_offset: usize,
131    triangle_count: usize,
132    uniform_offset: usize,
133    blend_func: Blend,
134}
135
136struct Texture {
137    tex: gl::types::GLuint,
138    width: usize,
139    height: usize,
140    texture_type: TextureType,
141    flags: ImageFlags,
142}
143
144impl Drop for Texture {
145    fn drop(&mut self) {
146        unsafe { gl::DeleteTextures(1, &self.tex) }
147    }
148}
149
150struct GLPath {
151    fill_offset: usize,
152    fill_count: usize,
153    stroke_offset: usize,
154    stroke_count: usize,
155}
156
157#[derive(Default)]
158#[allow(dead_code)]
159struct FragUniforms {
160    scissor_mat: [f32; 12],
161    paint_mat: [f32; 12],
162    inner_color: Color,
163    outer_color: Color,
164    scissor_ext: [f32; 2],
165    scissor_scale: [f32; 2],
166    extent: [f32; 2],
167    radius: f32,
168    feather: f32,
169    stroke_mult: f32,
170    stroke_thr: f32,
171    tex_type: i32,
172    type_: i32,
173}
174
175pub struct Renderer {
176    shader: Shader,
177    textures: Slab<Texture>,
178    view: Extent,
179    vert_buf: gl::types::GLuint,
180    vert_arr: gl::types::GLuint,
181    frag_buf: gl::types::GLuint,
182    frag_size: usize,
183    calls: Vec<Call>,
184    paths: Vec<GLPath>,
185    vertexes: Vec<Vertex>,
186    uniforms: Vec<u8>,
187}
188
189impl Drop for Renderer {
190    fn drop(&mut self) {
191        unsafe {
192            gl::DeleteBuffers(1, &self.frag_buf);
193            gl::DeleteBuffers(1, &self.vert_buf);
194            gl::DeleteVertexArrays(1, &self.vert_arr);
195        }
196    }
197}
198
199impl Renderer {
200    pub fn create() -> anyhow::Result<Renderer> {
201        unsafe {
202            let shader = Shader::load()?;
203
204            let mut vert_arr: gl::types::GLuint = std::mem::zeroed();
205            gl::GenVertexArrays(1, &mut vert_arr);
206
207            let mut vert_buf: gl::types::GLuint = std::mem::zeroed();
208            gl::GenBuffers(1, &mut vert_buf);
209
210            gl::UniformBlockBinding(shader.prog, shader.loc_frag, 0);
211            let mut frag_buf: gl::types::GLuint = std::mem::zeroed();
212            gl::GenBuffers(1, &mut frag_buf);
213
214            let mut align = std::mem::zeroed();
215            gl::GetIntegerv(gl::UNIFORM_BUFFER_OFFSET_ALIGNMENT, &mut align);
216
217            let frag_size = std::mem::size_of::<FragUniforms>() + (align as usize)
218                - std::mem::size_of::<FragUniforms>() % (align as usize);
219
220            gl::Finish();
221
222            Ok(Renderer {
223                shader,
224                textures: Default::default(),
225                view: Default::default(),
226                vert_buf,
227                vert_arr,
228                frag_buf,
229                frag_size,
230                calls: Default::default(),
231                paths: Default::default(),
232                vertexes: Default::default(),
233                uniforms: Default::default(),
234            })
235        }
236    }
237
238    unsafe fn set_uniforms(&self, offset: usize, img: Option<usize>) {
239        gl::BindBufferRange(
240            gl::UNIFORM_BUFFER,
241            0,
242            self.frag_buf,
243            (offset * self.frag_size) as isize,
244            std::mem::size_of::<FragUniforms>() as isize,
245        );
246
247        if let Some(img) = img {
248            if let Some(texture) = self.textures.get(img) {
249                gl::BindTexture(gl::TEXTURE_2D, texture.tex);
250            }
251        } else {
252            gl::BindTexture(gl::TEXTURE_2D, 0);
253        }
254    }
255
256    unsafe fn do_fill(&self, call: &Call) {
257        let paths = &self.paths[call.path_offset..call.path_offset + call.path_count];
258
259        gl::Enable(gl::STENCIL_TEST);
260        gl::StencilMask(0xff);
261        gl::StencilFunc(gl::ALWAYS, 0, 0xff);
262        gl::ColorMask(gl::FALSE, gl::FALSE, gl::FALSE, gl::FALSE);
263
264        self.set_uniforms(call.uniform_offset, call.image);
265
266        gl::StencilOpSeparate(gl::FRONT, gl::KEEP, gl::KEEP, gl::INCR_WRAP);
267        gl::StencilOpSeparate(gl::BACK, gl::KEEP, gl::KEEP, gl::DECR_WRAP);
268        gl::Disable(gl::CULL_FACE);
269        for path in paths {
270            gl::DrawArrays(
271                gl::TRIANGLE_FAN,
272                path.fill_offset as i32,
273                path.fill_count as i32,
274            );
275        }
276        gl::Enable(gl::CULL_FACE);
277
278        gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
279
280        self.set_uniforms(call.uniform_offset + 1, call.image);
281
282        gl::StencilFunc(gl::EQUAL, 0x00, 0xff);
283        gl::StencilOp(gl::KEEP, gl::KEEP, gl::KEEP);
284        for path in paths {
285            gl::DrawArrays(
286                gl::TRIANGLE_STRIP,
287                path.stroke_offset as i32,
288                path.stroke_count as i32,
289            );
290        }
291
292        gl::StencilFunc(gl::NOTEQUAL, 0x00, 0xff);
293        gl::StencilOp(gl::ZERO, gl::ZERO, gl::ZERO);
294        gl::DrawArrays(
295            gl::TRIANGLE_STRIP,
296            call.triangle_offset as i32,
297            call.triangle_count as i32,
298        );
299
300        gl::Disable(gl::STENCIL_TEST);
301    }
302
303    unsafe fn do_contex_fill(&self, call: &Call) {
304        let paths = &self.paths[call.path_offset..call.path_offset + call.path_count];
305        self.set_uniforms(call.uniform_offset, call.image);
306        for path in paths {
307            gl::DrawArrays(
308                gl::TRIANGLE_FAN,
309                path.fill_offset as i32,
310                path.fill_count as i32,
311            );
312            if path.stroke_count > 0 {
313                gl::DrawArrays(
314                    gl::TRIANGLE_STRIP,
315                    path.stroke_offset as i32,
316                    path.stroke_count as i32,
317                );
318            }
319        }
320    }
321
322    unsafe fn do_stroke(&self, call: &Call) {
323        let paths = &self.paths[call.path_offset..call.path_offset + call.path_count];
324
325        gl::Enable(gl::STENCIL_TEST);
326        gl::StencilMask(0xff);
327        gl::StencilFunc(gl::EQUAL, 0x0, 0xff);
328        gl::StencilOp(gl::KEEP, gl::KEEP, gl::INCR);
329        self.set_uniforms(call.uniform_offset + 1, call.image);
330        for path in paths {
331            gl::DrawArrays(
332                gl::TRIANGLE_STRIP,
333                path.stroke_offset as i32,
334                path.stroke_count as i32,
335            );
336        }
337
338        self.set_uniforms(call.uniform_offset, call.image);
339        gl::StencilFunc(gl::EQUAL, 0x0, 0xff);
340        gl::StencilOp(gl::KEEP, gl::KEEP, gl::KEEP);
341        for path in paths {
342            gl::DrawArrays(
343                gl::TRIANGLE_STRIP,
344                path.stroke_offset as i32,
345                path.stroke_count as i32,
346            );
347        }
348
349        gl::ColorMask(gl::FALSE, gl::FALSE, gl::FALSE, gl::FALSE);
350        gl::StencilFunc(gl::ALWAYS, 0x0, 0xff);
351        gl::StencilOp(gl::ZERO, gl::ZERO, gl::ZERO);
352        for path in paths {
353            gl::DrawArrays(
354                gl::TRIANGLE_STRIP,
355                path.stroke_offset as i32,
356                path.stroke_count as i32,
357            );
358        }
359        gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
360
361        gl::Disable(gl::STENCIL_TEST);
362    }
363
364    unsafe fn do_triangles(&self, call: &Call) {
365        self.set_uniforms(call.uniform_offset, call.image);
366        gl::DrawArrays(
367            gl::TRIANGLES,
368            call.triangle_offset as i32,
369            call.triangle_count as i32,
370        );
371    }
372
373    fn convert_paint(
374        &self,
375        paint: &Paint,
376        scissor: &Scissor,
377        width: f32,
378        fringe: f32,
379        stroke_thr: f32,
380    ) -> FragUniforms {
381        let mut frag = FragUniforms {
382            scissor_mat: Default::default(),
383            paint_mat: Default::default(),
384            inner_color: premul_color(paint.inner_color),
385            outer_color: premul_color(paint.outer_color),
386            scissor_ext: Default::default(),
387            scissor_scale: Default::default(),
388            extent: Default::default(),
389            radius: 0.0,
390            feather: 0.0,
391            stroke_mult: 0.0,
392            stroke_thr,
393            tex_type: 0,
394            type_: 0,
395        };
396
397        if scissor.extent.width < -0.5 || scissor.extent.height < -0.5 {
398            frag.scissor_ext[0] = 1.0;
399            frag.scissor_ext[1] = 1.0;
400            frag.scissor_scale[0] = 1.0;
401            frag.scissor_scale[1] = 1.0;
402        } else {
403            frag.scissor_mat = xform_to_3x4(scissor.xform.inverse());
404            frag.scissor_ext[0] = scissor.extent.width;
405            frag.scissor_ext[1] = scissor.extent.height;
406            frag.scissor_scale[0] = (scissor.xform.0[0] * scissor.xform.0[0]
407                + scissor.xform.0[2] * scissor.xform.0[2])
408                .sqrt()
409                / fringe;
410            frag.scissor_scale[1] = (scissor.xform.0[1] * scissor.xform.0[1]
411                + scissor.xform.0[3] * scissor.xform.0[3])
412                .sqrt()
413                / fringe;
414        }
415
416        frag.extent = [paint.extent.width, paint.extent.height];
417        frag.stroke_mult = (width * 0.5 + fringe * 0.5) / fringe;
418
419        let mut invxform = Transform::default();
420
421        if let Some(img) = paint.image {
422            if let Some(texture) = self.textures.get(img) {
423                if texture.flags.contains(ImageFlags::FLIPY) {
424                    let m1 = Transform::translate(0.0, frag.extent[1] * 0.5) * paint.xform;
425                    let m2 = Transform::scale(1.0, -1.0) * m1;
426                    let m1 = Transform::translate(0.0, -frag.extent[1] * 0.5) * m2;
427                    invxform = m1.inverse();
428                } else {
429                    invxform = paint.xform.inverse();
430                };
431
432                frag.type_ = ShaderType::FillImage as i32;
433                match texture.texture_type {
434                    TextureType::RGBA => {
435                        frag.tex_type = if texture.flags.contains(ImageFlags::PREMULTIPLIED) {
436                            0
437                        } else {
438                            1
439                        }
440                    }
441                    TextureType::Alpha => frag.tex_type = 2,
442                }
443            }
444        } else {
445            frag.type_ = ShaderType::FillGradient as i32;
446            frag.radius = paint.radius;
447            frag.feather = paint.feather;
448            invxform = paint.xform.inverse();
449        }
450
451        frag.paint_mat = xform_to_3x4(invxform);
452
453        frag
454    }
455
456    fn append_uniforms(&mut self, uniforms: FragUniforms) {
457        self.uniforms
458            .resize(self.uniforms.len() + self.frag_size, 0);
459        unsafe {
460            let idx = self.uniforms.len() - self.frag_size;
461            let p = self.uniforms.as_mut_ptr().add(idx) as *mut FragUniforms;
462            *p = uniforms;
463        }
464    }
465}
466
467impl renderer::Renderer for Renderer {
468    fn edge_antialias(&self) -> bool {
469        true
470    }
471
472    fn create_texture(
473        &mut self,
474        texture_type: TextureType,
475        width: usize,
476        height: usize,
477        flags: ImageFlags,
478        data: Option<&[u8]>,
479    ) -> anyhow::Result<ImageId> {
480        let tex = unsafe {
481            let mut tex: gl::types::GLuint = std::mem::zeroed();
482            gl::GenTextures(1, &mut tex);
483            gl::BindTexture(gl::TEXTURE_2D, tex);
484            gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1);
485
486            match texture_type {
487                TextureType::RGBA => {
488                    gl::TexImage2D(
489                        gl::TEXTURE_2D,
490                        0,
491                        gl::RGBA as i32,
492                        width as i32,
493                        height as i32,
494                        0,
495                        gl::RGBA,
496                        gl::UNSIGNED_BYTE,
497                        match data {
498                            Some(data) => data.as_ptr() as *const c_void,
499                            None => std::ptr::null(),
500                        },
501                    );
502                }
503                TextureType::Alpha => {
504                    gl::TexImage2D(
505                        gl::TEXTURE_2D,
506                        0,
507                        gl::R8 as i32,
508                        width as i32,
509                        height as i32,
510                        0,
511                        gl::RED,
512                        gl::UNSIGNED_BYTE,
513                        match data {
514                            Some(data) => data.as_ptr() as *const c_void,
515                            None => std::ptr::null(),
516                        },
517                    );
518                }
519            }
520
521            if flags.contains(ImageFlags::GENERATE_MIPMAPS) {
522                if flags.contains(ImageFlags::NEAREST) {
523                    gl::TexParameteri(
524                        gl::TEXTURE_2D,
525                        gl::TEXTURE_MIN_FILTER,
526                        gl::NEAREST_MIPMAP_NEAREST as i32,
527                    );
528                } else {
529                    gl::TexParameteri(
530                        gl::TEXTURE_2D,
531                        gl::TEXTURE_MIN_FILTER,
532                        gl::LINEAR_MIPMAP_LINEAR as i32,
533                    );
534                }
535            } else {
536                if flags.contains(ImageFlags::NEAREST) {
537                    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::NEAREST as i32);
538                } else {
539                    gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MIN_FILTER, gl::LINEAR as i32);
540                }
541            }
542
543            if flags.contains(ImageFlags::NEAREST) {
544                gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::NEAREST as i32);
545            } else {
546                gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_MAG_FILTER, gl::LINEAR as i32);
547            }
548
549            if flags.contains(ImageFlags::REPEATX) {
550                gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::REPEAT as i32);
551            } else {
552                gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_S, gl::CLAMP_TO_EDGE as i32);
553            }
554
555            if flags.contains(ImageFlags::REPEATY) {
556                gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::REPEAT as i32);
557            } else {
558                gl::TexParameteri(gl::TEXTURE_2D, gl::TEXTURE_WRAP_T, gl::CLAMP_TO_EDGE as i32);
559            }
560
561            gl::PixelStorei(gl::UNPACK_ALIGNMENT, 4);
562
563            if flags.contains(ImageFlags::GENERATE_MIPMAPS) {
564                gl::GenerateMipmap(gl::TEXTURE_2D);
565            }
566
567            gl::BindTexture(gl::TEXTURE_2D, 0);
568            tex
569        };
570
571        let id = self.textures.insert(Texture {
572            tex,
573            width,
574            height,
575            texture_type,
576            flags,
577        });
578        Ok(id)
579    }
580
581    fn delete_texture(&mut self, img: ImageId) -> anyhow::Result<()> {
582        if let Some(texture) = self.textures.get(img) {
583            unsafe { gl::DeleteTextures(1, &texture.tex) }
584            self.textures.remove(img);
585            Ok(())
586        } else {
587            bail!("texture '{}' not found", img);
588        }
589    }
590
591    fn update_texture(
592        &mut self,
593        img: ImageId,
594        x: usize,
595        y: usize,
596        width: usize,
597        height: usize,
598        data: &[u8],
599    ) -> anyhow::Result<()> {
600        if let Some(texture) = self.textures.get(img) {
601            unsafe {
602                gl::BindTexture(gl::TEXTURE_2D, texture.tex);
603                gl::PixelStorei(gl::UNPACK_ALIGNMENT, 1);
604
605                match texture.texture_type {
606                    TextureType::RGBA => gl::TexSubImage2D(
607                        gl::TEXTURE_2D,
608                        0,
609                        x as i32,
610                        y as i32,
611                        width as i32,
612                        height as i32,
613                        gl::RGBA,
614                        gl::UNSIGNED_BYTE,
615                        data.as_ptr() as *const c_void,
616                    ),
617                    TextureType::Alpha => gl::TexSubImage2D(
618                        gl::TEXTURE_2D,
619                        0,
620                        x as i32,
621                        y as i32,
622                        width as i32,
623                        height as i32,
624                        gl::RED,
625                        gl::UNSIGNED_BYTE,
626                        data.as_ptr() as *const c_void,
627                    ),
628                }
629
630                gl::PixelStorei(gl::UNPACK_ALIGNMENT, 4);
631                gl::BindTexture(gl::TEXTURE_2D, 0);
632            }
633            Ok(())
634        } else {
635            bail!("texture '{}' not found", img);
636        }
637    }
638
639    fn texture_size(&self, img: ImageId) -> anyhow::Result<(usize, usize)> {
640        if let Some(texture) = self.textures.get(img) {
641            Ok((texture.width, texture.height))
642        } else {
643            bail!("texture '{}' not found", img);
644        }
645    }
646
647    fn viewport(&mut self, extent: Extent, _device_pixel_ratio: f32) -> anyhow::Result<()> {
648        self.view = extent;
649        Ok(())
650    }
651
652    fn cancel(&mut self) -> anyhow::Result<()> {
653        self.vertexes.clear();
654        self.paths.clear();
655        self.calls.clear();
656        self.uniforms.clear();
657        Ok(())
658    }
659
660    fn flush(&mut self) -> anyhow::Result<()> {
661        if !self.calls.is_empty() {
662            unsafe {
663                gl::UseProgram(self.shader.prog);
664
665                gl::Enable(gl::CULL_FACE);
666                gl::CullFace(gl::BACK);
667                gl::FrontFace(gl::CCW);
668                gl::Enable(gl::BLEND);
669                gl::Disable(gl::DEPTH_TEST);
670                gl::Disable(gl::SCISSOR_TEST);
671                gl::ColorMask(gl::TRUE, gl::TRUE, gl::TRUE, gl::TRUE);
672                gl::StencilMask(0xffffffff);
673                gl::StencilOp(gl::KEEP, gl::KEEP, gl::KEEP);
674                gl::StencilFunc(gl::ALWAYS, 0, 0xffffffff);
675                gl::ActiveTexture(gl::TEXTURE0);
676                gl::BindTexture(gl::TEXTURE_2D, 0);
677
678                gl::BindBuffer(gl::UNIFORM_BUFFER, self.frag_buf);
679                gl::BufferData(
680                    gl::UNIFORM_BUFFER,
681                    self.uniforms.len() as isize,
682                    self.uniforms.as_ptr() as *const c_void,
683                    gl::STREAM_DRAW,
684                );
685
686                gl::BindVertexArray(self.vert_arr);
687                gl::BindBuffer(gl::ARRAY_BUFFER, self.vert_buf);
688                gl::BufferData(
689                    gl::ARRAY_BUFFER,
690                    (self.vertexes.len() * std::mem::size_of::<Vertex>()) as isize,
691                    self.vertexes.as_ptr() as *const c_void,
692                    gl::STREAM_DRAW,
693                );
694                gl::EnableVertexAttribArray(0);
695                gl::EnableVertexAttribArray(1);
696                gl::VertexAttribPointer(
697                    0,
698                    2,
699                    gl::FLOAT,
700                    gl::FALSE,
701                    std::mem::size_of::<Vertex>() as i32,
702                    std::ptr::null(),
703                );
704                gl::VertexAttribPointer(
705                    1,
706                    2,
707                    gl::FLOAT,
708                    gl::FALSE,
709                    std::mem::size_of::<Vertex>() as i32,
710                    (2 * std::mem::size_of::<f32>()) as *const c_void,
711                );
712
713                gl::Uniform1i(self.shader.loc_tex, 0);
714                gl::Uniform2fv(
715                    self.shader.loc_viewsize,
716                    1,
717                    &self.view as *const Extent as *const f32,
718                );
719
720                gl::BindBuffer(gl::UNIFORM_BUFFER, self.frag_buf);
721
722                for call in &self.calls {
723                    let blend = &call.blend_func;
724
725                    gl::BlendFuncSeparate(
726                        blend.src_rgb,
727                        blend.dst_rgb,
728                        blend.src_alpha,
729                        blend.dst_alpha,
730                    );
731
732                    match call.call_type {
733                        CallType::Fill => self.do_fill(&call),
734                        CallType::ConvexFill => self.do_contex_fill(&call),
735                        CallType::Stroke => self.do_stroke(&call),
736                        CallType::Triangles => self.do_triangles(&call),
737                    }
738                }
739
740                gl::DisableVertexAttribArray(0);
741                gl::DisableVertexAttribArray(1);
742                gl::BindVertexArray(0);
743                gl::Disable(gl::CULL_FACE);
744                gl::BindBuffer(gl::ARRAY_BUFFER, 0);
745                gl::UseProgram(0);
746                gl::BindTexture(gl::TEXTURE_2D, 0);
747            }
748        }
749
750        self.vertexes.clear();
751        self.paths.clear();
752        self.calls.clear();
753        self.uniforms.clear();
754        Ok(())
755    }
756
757    fn fill(
758        &mut self,
759        paint: &Paint,
760        composite_operation: CompositeOperationState,
761        scissor: &Scissor,
762        fringe: f32,
763        bounds: Bounds,
764        paths: &[Path],
765    ) -> anyhow::Result<()> {
766        let mut call = Call {
767            call_type: CallType::Fill,
768            image: paint.image,
769            path_offset: self.paths.len(),
770            path_count: paths.len(),
771            triangle_offset: 0,
772            triangle_count: 4,
773            uniform_offset: 0,
774            blend_func: composite_operation.into(),
775        };
776
777        if paths.len() == 1 && paths[0].convex {
778            call.call_type = CallType::ConvexFill;
779        }
780
781        let mut offset = self.vertexes.len();
782        for path in paths {
783            let fill = path.get_fill();
784            let mut gl_path = GLPath {
785                fill_offset: 0,
786                fill_count: 0,
787                stroke_offset: 0,
788                stroke_count: 0,
789            };
790
791            if !fill.is_empty() {
792                gl_path.fill_offset = offset;
793                gl_path.fill_count = fill.len();
794                self.vertexes.extend(fill);
795                offset += fill.len();
796            }
797
798            let stroke = path.get_stroke();
799            if !stroke.is_empty() {
800                gl_path.stroke_offset = offset;
801                gl_path.stroke_count = stroke.len();
802                self.vertexes.extend(stroke);
803                offset += stroke.len();
804            }
805
806            self.paths.push(gl_path);
807        }
808
809        if call.call_type == CallType::Fill {
810            call.triangle_offset = offset;
811            self.vertexes
812                .push(Vertex::new(bounds.max.x, bounds.max.y, 0.5, 1.0));
813            self.vertexes
814                .push(Vertex::new(bounds.max.x, bounds.min.y, 0.5, 1.0));
815            self.vertexes
816                .push(Vertex::new(bounds.min.x, bounds.max.y, 0.5, 1.0));
817            self.vertexes
818                .push(Vertex::new(bounds.min.x, bounds.min.y, 0.5, 1.0));
819
820            call.uniform_offset = self.uniforms.len() / self.frag_size;
821            self.append_uniforms(FragUniforms {
822                stroke_thr: -1.0,
823                type_: ShaderType::Simple as i32,
824                ..FragUniforms::default()
825            });
826            self.append_uniforms(self.convert_paint(paint, scissor, fringe, fringe, -1.0));
827        } else {
828            call.uniform_offset = self.uniforms.len() / self.frag_size;
829            self.append_uniforms(self.convert_paint(paint, scissor, fringe, fringe, -1.0));
830        }
831
832        self.calls.push(call);
833        Ok(())
834    }
835
836    fn stroke(
837        &mut self,
838        paint: &Paint,
839        composite_operation: CompositeOperationState,
840        scissor: &Scissor,
841        fringe: f32,
842        stroke_width: f32,
843        paths: &[Path],
844    ) -> anyhow::Result<()> {
845        let mut call = Call {
846            call_type: CallType::Stroke,
847            image: paint.image,
848            path_offset: self.paths.len(),
849            path_count: paths.len(),
850            triangle_offset: 0,
851            triangle_count: 0,
852            uniform_offset: 0,
853            blend_func: composite_operation.into(),
854        };
855
856        let mut offset = self.vertexes.len();
857        for path in paths {
858            let mut gl_path = GLPath {
859                fill_offset: 0,
860                fill_count: 0,
861                stroke_offset: 0,
862                stroke_count: 0,
863            };
864
865            let stroke = path.get_stroke();
866            if !stroke.is_empty() {
867                gl_path.stroke_offset = offset;
868                gl_path.stroke_count = stroke.len();
869                self.vertexes.extend(stroke);
870                offset += stroke.len();
871                self.paths.push(gl_path);
872            }
873        }
874
875        call.uniform_offset = self.uniforms.len() / self.frag_size;
876        self.append_uniforms(self.convert_paint(paint, scissor, stroke_width, fringe, -1.0));
877        self.append_uniforms(self.convert_paint(
878            paint,
879            scissor,
880            stroke_width,
881            fringe,
882            1.0 - 0.5 / 255.0,
883        ));
884
885        self.calls.push(call);
886        Ok(())
887    }
888
889    fn triangles(
890        &mut self,
891        paint: &Paint,
892        composite_operation: CompositeOperationState,
893        scissor: &Scissor,
894        vertexes: &[Vertex],
895    ) -> anyhow::Result<()> {
896        let call = Call {
897            call_type: CallType::Triangles,
898            image: paint.image,
899            path_offset: 0,
900            path_count: 0,
901            triangle_offset: self.vertexes.len(),
902            triangle_count: vertexes.len(),
903            uniform_offset: self.uniforms.len() / self.frag_size,
904            blend_func: composite_operation.into(),
905        };
906
907        self.calls.push(call);
908        self.vertexes.extend(vertexes);
909
910        let mut uniforms = self.convert_paint(paint, scissor, 1.0, 1.0, -1.0);
911        uniforms.type_ = ShaderType::Image as i32;
912        self.append_uniforms(uniforms);
913        Ok(())
914    }
915}
916
917fn shader_error(shader: gl::types::GLuint, filename: &str) -> anyhow::Error {
918    unsafe {
919        let mut data: [gl::types::GLchar; 512 + 1] = std::mem::zeroed();
920        let mut len: gl::types::GLsizei = std::mem::zeroed();
921        gl::GetShaderInfoLog(shader, 512, &mut len, data.as_mut_ptr());
922        if len > 512 {
923            len = 512;
924        }
925        data[len as usize] = 0;
926        let err_msg = std::ffi::CStr::from_ptr(data.as_ptr());
927        anyhow!(
928            "failed to compile shader: {}: {}",
929            filename,
930            err_msg.to_string_lossy()
931        )
932    }
933}
934
935fn program_error(prog: gl::types::GLuint) -> anyhow::Error {
936    unsafe {
937        let mut data: [gl::types::GLchar; 512 + 1] = std::mem::zeroed();
938        let mut len: gl::types::GLsizei = std::mem::zeroed();
939        gl::GetProgramInfoLog(prog, 512, &mut len, data.as_mut_ptr());
940        if len > 512 {
941            len = 512;
942        }
943        data[len as usize] = 0;
944        let err_msg = std::ffi::CStr::from_ptr(data.as_ptr());
945        anyhow!("failed to link program: {}", err_msg.to_string_lossy())
946    }
947}
948
949fn convert_blend_factor(factor: BlendFactor) -> gl::types::GLenum {
950    match factor {
951        BlendFactor::Zero => gl::ZERO,
952        BlendFactor::One => gl::ONE,
953        BlendFactor::SrcColor => gl::SRC_COLOR,
954        BlendFactor::OneMinusSrcColor => gl::ONE_MINUS_SRC_COLOR,
955        BlendFactor::DstColor => gl::DST_COLOR,
956        BlendFactor::OneMinusDstColor => gl::ONE_MINUS_DST_COLOR,
957        BlendFactor::SrcAlpha => gl::SRC_ALPHA,
958        BlendFactor::OneMinusSrcAlpha => gl::ONE_MINUS_SRC_ALPHA,
959        BlendFactor::DstAlpha => gl::DST_ALPHA,
960        BlendFactor::OneMinusDstAlpha => gl::ONE_MINUS_DST_ALPHA,
961        BlendFactor::SrcAlphaSaturate => gl::SRC_ALPHA_SATURATE,
962    }
963}
964
965#[inline]
966fn premul_color(color: Color) -> Color {
967    Color {
968        r: color.r * color.a,
969        g: color.g * color.a,
970        b: color.b * color.a,
971        a: color.a,
972    }
973}
974
975#[inline]
976fn xform_to_3x4(xform: Transform) -> [f32; 12] {
977    let mut m = [0f32; 12];
978    let t = &xform.0;
979    m[0] = t[0];
980    m[1] = t[1];
981    m[2] = 0.0;
982    m[3] = 0.0;
983    m[4] = t[2];
984    m[5] = t[3];
985    m[6] = 0.0;
986    m[7] = 0.0;
987    m[8] = t[4];
988    m[9] = t[5];
989    m[10] = 1.0;
990    m[11] = 0.0;
991    m
992}