1use crate::context::GpuContext;
8use crate::core::{Color, Position, Rect};
9use crate::paint::{Gradient, GradientStop, ImageHandle, Shadow};
10use crate::types::TextureFormat;
11use crate::vertex::Vertex;
12
13pub struct ShapeRenderer {
15 vertices: Vec<Vertex>,
16 indices: Vec<u32>,
17 pipeline: wgpu::RenderPipeline,
18 uniform_buffer: wgpu::Buffer,
19 uniform_bind_group: wgpu::BindGroup,
20 vertex_buffer: wgpu::Buffer,
21 index_buffer: wgpu::Buffer,
22 vertex_capacity: usize,
23 index_capacity: usize,
24 viewport: [f32; 2],
25 sample_count: u32,
26}
27
28const CIRCLE_SEGMENTS: u32 = 32;
30
31const INITIAL_VERTEX_CAPACITY: usize = 4096;
33const INITIAL_INDEX_CAPACITY: usize = 8192;
34
35impl ShapeRenderer {
36 pub fn new(
38 gpu: &GpuContext,
39 format: TextureFormat,
40 width: f32,
41 height: f32,
42 sample_count: u32,
43 ) -> Self {
44 let device = gpu.device();
45 let viewport = [width, height];
47 let uniform_buffer = device.create_buffer(&wgpu::BufferDescriptor {
48 label: Some("agpu_uniform"),
49 size: 8, usage: wgpu::BufferUsages::UNIFORM | wgpu::BufferUsages::COPY_DST,
51 mapped_at_creation: false,
52 });
53
54 let bind_group_layout = device.create_bind_group_layout(&wgpu::BindGroupLayoutDescriptor {
55 label: Some("agpu_bind_group_layout"),
56 entries: &[wgpu::BindGroupLayoutEntry {
57 binding: 0,
58 visibility: wgpu::ShaderStages::VERTEX,
59 ty: wgpu::BindingType::Buffer {
60 ty: wgpu::BufferBindingType::Uniform,
61 has_dynamic_offset: false,
62 min_binding_size: None,
63 },
64 count: None,
65 }],
66 });
67
68 let uniform_bind_group = device.create_bind_group(&wgpu::BindGroupDescriptor {
69 label: Some("agpu_bind_group"),
70 layout: &bind_group_layout,
71 entries: &[wgpu::BindGroupEntry {
72 binding: 0,
73 resource: uniform_buffer.as_entire_binding(),
74 }],
75 });
76
77 let pipeline_layout = device.create_pipeline_layout(&wgpu::PipelineLayoutDescriptor {
78 label: Some("agpu_pipeline_layout"),
79 bind_group_layouts: &[&bind_group_layout],
80 push_constant_ranges: &[],
81 });
82
83 let shader = device.create_shader_module(wgpu::ShaderModuleDescriptor {
84 label: Some("agpu_shader"),
85 source: wgpu::ShaderSource::Wgsl(include_str!("shader.wgsl").into()),
86 });
87
88 let pipeline = device.create_render_pipeline(&wgpu::RenderPipelineDescriptor {
89 label: Some("agpu_shape_pipeline"),
90 layout: Some(&pipeline_layout),
91 vertex: wgpu::VertexState {
92 module: &shader,
93 entry_point: Some("vs_main"),
94 buffers: &[Vertex::LAYOUT],
95 compilation_options: wgpu::PipelineCompilationOptions::default(),
96 },
97 fragment: Some(wgpu::FragmentState {
98 module: &shader,
99 entry_point: Some("fs_main"),
100 targets: &[Some(wgpu::ColorTargetState {
101 format,
102 blend: Some(wgpu::BlendState::ALPHA_BLENDING),
103 write_mask: wgpu::ColorWrites::ALL,
104 })],
105 compilation_options: wgpu::PipelineCompilationOptions::default(),
106 }),
107 primitive: wgpu::PrimitiveState {
108 topology: wgpu::PrimitiveTopology::TriangleList,
109 strip_index_format: None,
110 front_face: wgpu::FrontFace::Ccw,
111 cull_mode: None,
112 polygon_mode: wgpu::PolygonMode::Fill,
113 unclipped_depth: false,
114 conservative: false,
115 },
116 depth_stencil: None,
117 multisample: wgpu::MultisampleState {
118 count: sample_count,
119 mask: !0,
120 alpha_to_coverage_enabled: false,
121 },
122 multiview: None,
123 cache: None,
124 });
125
126 let vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
127 label: Some("agpu_vertex"),
128 size: (INITIAL_VERTEX_CAPACITY * std::mem::size_of::<Vertex>()) as u64,
129 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
130 mapped_at_creation: false,
131 });
132
133 let index_buffer = device.create_buffer(&wgpu::BufferDescriptor {
134 label: Some("agpu_index"),
135 size: (INITIAL_INDEX_CAPACITY * std::mem::size_of::<u32>()) as u64,
136 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
137 mapped_at_creation: false,
138 });
139
140 Self {
141 vertices: Vec::with_capacity(INITIAL_VERTEX_CAPACITY),
142 indices: Vec::with_capacity(INITIAL_INDEX_CAPACITY),
143 pipeline,
144 uniform_buffer,
145 uniform_bind_group,
146 vertex_buffer,
147 index_buffer,
148 vertex_capacity: INITIAL_VERTEX_CAPACITY,
149 index_capacity: INITIAL_INDEX_CAPACITY,
150 viewport,
151 sample_count,
152 }
153 }
154
155 pub fn begin_frame(&mut self) {
157 self.vertices.clear();
158 self.indices.clear();
159 }
160
161 pub fn set_viewport(&mut self, width: f32, height: f32) {
163 self.viewport = [width, height];
164 }
165
166 pub fn fill_rect(&mut self, rect: Rect, color: Color, corner_radius: f32) {
168 if corner_radius <= 0.5 {
169 self.push_quad(rect, color);
170 } else {
171 self.push_rounded_rect(rect, color, corner_radius);
172 }
173 }
174
175 pub fn stroke_rect(&mut self, rect: Rect, color: Color, width: f32, corner_radius: f32) {
177 if corner_radius <= 0.5 {
178 self.push_quad(Rect::new(rect.x, rect.y, rect.width, width), color);
180 self.push_quad(
182 Rect::new(rect.x, rect.y + rect.height - width, rect.width, width),
183 color,
184 );
185 self.push_quad(
187 Rect::new(rect.x, rect.y + width, width, rect.height - 2.0 * width),
188 color,
189 );
190 self.push_quad(
192 Rect::new(
193 rect.x + rect.width - width,
194 rect.y + width,
195 width,
196 rect.height - 2.0 * width,
197 ),
198 color,
199 );
200 } else {
201 self.stroke_rounded_rect(rect, color, width, corner_radius, Color::TRANSPARENT);
202 }
203 }
204
205 pub fn stroke_rounded_rect(
210 &mut self,
211 rect: Rect,
212 color: Color,
213 width: f32,
214 corner_radius: f32,
215 bg_color: Color,
216 ) {
217 self.push_rounded_rect(rect, color, corner_radius);
219 let inner_radius = (corner_radius - width).max(0.0);
221 let inner = Rect::new(
222 rect.x + width,
223 rect.y + width,
224 rect.width - 2.0 * width,
225 rect.height - 2.0 * width,
226 );
227 if inner.width > 0.0 && inner.height > 0.0 {
228 let fill = if bg_color.a > 0.0 {
229 bg_color
230 } else {
231 Color::TRANSPARENT
233 };
234 self.push_rounded_rect(inner, fill, inner_radius);
235 }
236 }
237
238 pub fn fill_circle(&mut self, center: Position, radius: f32, color: Color) {
240 let base = self.vertices.len() as u32;
241 let c = [color.r, color.g, color.b, color.a];
242
243 self.vertices
245 .push(Vertex::new(center.x, center.y, c[0], c[1], c[2], c[3]));
246
247 for i in 0..CIRCLE_SEGMENTS {
248 let angle = (i as f32 / CIRCLE_SEGMENTS as f32) * std::f32::consts::TAU;
249 let x = center.x + radius * angle.cos();
250 let y = center.y + radius * angle.sin();
251 self.vertices
252 .push(Vertex::new(x, y, c[0], c[1], c[2], c[3]));
253 }
254
255 for i in 0..CIRCLE_SEGMENTS {
256 self.indices.push(base); self.indices.push(base + 1 + i);
258 self.indices.push(base + 1 + (i + 1) % CIRCLE_SEGMENTS);
259 }
260 }
261
262 pub fn stroke_circle(&mut self, center: Position, radius: f32, color: Color, width: f32) {
264 let outer = radius;
265 let inner = (radius - width).max(0.0);
266 let base = self.vertices.len() as u32;
267 let c = [color.r, color.g, color.b, color.a];
268
269 for i in 0..CIRCLE_SEGMENTS {
270 let angle = (i as f32 / CIRCLE_SEGMENTS as f32) * std::f32::consts::TAU;
271 let cos_a = angle.cos();
272 let sin_a = angle.sin();
273 self.vertices.push(Vertex::new(
275 center.x + outer * cos_a,
276 center.y + outer * sin_a,
277 c[0],
278 c[1],
279 c[2],
280 c[3],
281 ));
282 self.vertices.push(Vertex::new(
284 center.x + inner * cos_a,
285 center.y + inner * sin_a,
286 c[0],
287 c[1],
288 c[2],
289 c[3],
290 ));
291 }
292
293 for i in 0..CIRCLE_SEGMENTS {
294 let i0 = base + i * 2;
295 let i1 = base + i * 2 + 1;
296 let i2 = base + ((i + 1) % CIRCLE_SEGMENTS) * 2;
297 let i3 = base + ((i + 1) % CIRCLE_SEGMENTS) * 2 + 1;
298 self.indices.extend_from_slice(&[i0, i2, i1, i1, i2, i3]);
299 }
300 }
301
302 pub fn fill_rect_gradient(&mut self, rect: Rect, gradient: &Gradient, corner_radius: f32) {
306 match gradient {
307 Gradient::Linear { start, end, stops } => {
308 if stops.is_empty() {
309 return;
310 }
311 if stops.len() == 1 {
312 self.fill_rect(rect, stops[0].color, corner_radius);
313 return;
314 }
315 let dx = end.x - start.x;
317 let dy = end.y - start.y;
318 let len_sq = dx * dx + dy * dy;
319 if len_sq < 0.0001 {
320 self.fill_rect(rect, stops[0].color, corner_radius);
321 return;
322 }
323 let strip_count = 16u32;
326 for i in 0..strip_count {
327 let t0 = i as f32 / strip_count as f32;
328 let t1 = (i + 1) as f32 / strip_count as f32;
329 let c0 = sample_gradient(stops, t0);
330 let c1 = sample_gradient(stops, t1);
331 let avg = Color::rgba(
332 (c0.r + c1.r) * 0.5,
333 (c0.g + c1.g) * 0.5,
334 (c0.b + c1.b) * 0.5,
335 (c0.a + c1.a) * 0.5,
336 );
337 let strip_rect = if dx.abs() >= dy.abs() {
339 let x0 = rect.x + rect.width * t0;
341 let x1 = rect.x + rect.width * t1;
342 Rect::new(x0, rect.y, x1 - x0, rect.height)
343 } else {
344 let y0 = rect.y + rect.height * t0;
346 let y1 = rect.y + rect.height * t1;
347 Rect::new(rect.x, y0, rect.width, y1 - y0)
348 };
349 self.push_quad(strip_rect, avg);
350 }
351 }
352 Gradient::Radial {
353 center,
354 radius,
355 stops,
356 } => {
357 if stops.is_empty() {
358 return;
359 }
360 if stops.len() == 1 {
361 self.fill_rect(rect, stops[0].color, corner_radius);
362 return;
363 }
364 let ring_count = 12u32;
366 for i in (0..ring_count).rev() {
367 let t = (i + 1) as f32 / ring_count as f32;
368 let r = radius * t;
369 let color = sample_gradient(stops, t);
370 self.fill_circle(*center, r, color);
371 }
372 }
373 }
374 }
375
376 pub fn shadow_rect(&mut self, rect: Rect, shadow: &Shadow, corner_radius: f32) {
378 let expand = shadow.blur_radius * 0.5;
379 let shadow_rect = Rect::new(
380 rect.x + shadow.offset_x - expand,
381 rect.y + shadow.offset_y - expand,
382 rect.width + expand * 2.0,
383 rect.height + expand * 2.0,
384 );
385 self.fill_rect(shadow_rect, shadow.color, corner_radius + expand);
386 }
387
388 pub fn draw_image(&mut self, _handle: &ImageHandle, _rect: Rect) {
390 }
394
395 pub fn line(&mut self, from: Position, to: Position, color: Color, width: f32) {
397 let dx = to.x - from.x;
398 let dy = to.y - from.y;
399 let len = (dx * dx + dy * dy).sqrt().max(0.001);
400 let half = width * 0.5;
401
402 let nx = -dy / len * half;
404 let ny = dx / len * half;
405
406 let base = self.vertices.len() as u32;
407 let c = [color.r, color.g, color.b, color.a];
408
409 self.vertices.push(Vertex::new(
410 from.x + nx,
411 from.y + ny,
412 c[0],
413 c[1],
414 c[2],
415 c[3],
416 ));
417 self.vertices.push(Vertex::new(
418 from.x - nx,
419 from.y - ny,
420 c[0],
421 c[1],
422 c[2],
423 c[3],
424 ));
425 self.vertices
426 .push(Vertex::new(to.x - nx, to.y - ny, c[0], c[1], c[2], c[3]));
427 self.vertices
428 .push(Vertex::new(to.x + nx, to.y + ny, c[0], c[1], c[2], c[3]));
429
430 self.indices
431 .extend_from_slice(&[base, base + 1, base + 2, base, base + 2, base + 3]);
432 }
433
434 pub fn flush(&mut self, gpu: &GpuContext, pass: &mut wgpu::RenderPass<'_>) {
436 if self.indices.is_empty() {
437 return;
438 }
439
440 let device = gpu.device();
441 let queue = gpu.queue();
442
443 queue.write_buffer(&self.uniform_buffer, 0, bytemuck::bytes_of(&self.viewport));
445
446 if self.vertices.len() > self.vertex_capacity {
448 self.vertex_capacity = self.vertices.len().next_power_of_two();
449 self.vertex_buffer = device.create_buffer(&wgpu::BufferDescriptor {
450 label: Some("agpu_vertex"),
451 size: (self.vertex_capacity * std::mem::size_of::<Vertex>()) as u64,
452 usage: wgpu::BufferUsages::VERTEX | wgpu::BufferUsages::COPY_DST,
453 mapped_at_creation: false,
454 });
455 }
456 if self.indices.len() > self.index_capacity {
457 self.index_capacity = self.indices.len().next_power_of_two();
458 self.index_buffer = device.create_buffer(&wgpu::BufferDescriptor {
459 label: Some("agpu_index"),
460 size: (self.index_capacity * std::mem::size_of::<u32>()) as u64,
461 usage: wgpu::BufferUsages::INDEX | wgpu::BufferUsages::COPY_DST,
462 mapped_at_creation: false,
463 });
464 }
465
466 queue.write_buffer(&self.vertex_buffer, 0, bytemuck::cast_slice(&self.vertices));
468 queue.write_buffer(&self.index_buffer, 0, bytemuck::cast_slice(&self.indices));
469
470 pass.set_pipeline(&self.pipeline);
471 pass.set_bind_group(0, &self.uniform_bind_group, &[]);
472 pass.set_vertex_buffer(0, self.vertex_buffer.slice(..));
473 pass.set_index_buffer(self.index_buffer.slice(..), wgpu::IndexFormat::Uint32);
474 pass.draw_indexed(0..self.indices.len() as u32, 0, 0..1);
475 }
476
477 pub fn vertex_count(&self) -> usize {
479 self.vertices.len()
480 }
481
482 pub fn index_count(&self) -> usize {
484 self.indices.len()
485 }
486
487 pub fn sample_count(&self) -> u32 {
489 self.sample_count
490 }
491
492 fn push_quad(&mut self, rect: Rect, color: Color) {
495 let base = self.vertices.len() as u32;
496 let c = [color.r, color.g, color.b, color.a];
497 let x0 = rect.x;
498 let y0 = rect.y;
499 let x1 = rect.x + rect.width;
500 let y1 = rect.y + rect.height;
501
502 self.vertices
503 .push(Vertex::new(x0, y0, c[0], c[1], c[2], c[3]));
504 self.vertices
505 .push(Vertex::new(x1, y0, c[0], c[1], c[2], c[3]));
506 self.vertices
507 .push(Vertex::new(x1, y1, c[0], c[1], c[2], c[3]));
508 self.vertices
509 .push(Vertex::new(x0, y1, c[0], c[1], c[2], c[3]));
510
511 self.indices
512 .extend_from_slice(&[base, base + 1, base + 2, base, base + 2, base + 3]);
513 }
514
515 fn push_rounded_rect(&mut self, rect: Rect, color: Color, radius: f32) {
516 let r = radius.min(rect.width * 0.5).min(rect.height * 0.5);
517 let c = [color.r, color.g, color.b, color.a];
518 let base = self.vertices.len() as u32;
519
520 let cx = rect.x + rect.width * 0.5;
522 let cy = rect.y + rect.height * 0.5;
523 self.vertices
524 .push(Vertex::new(cx, cy, c[0], c[1], c[2], c[3]));
525
526 let mut outline: Vec<[f32; 2]> = Vec::with_capacity(4 * 8 + 4);
528
529 let corners = [
531 (
532 rect.x + r,
533 rect.y + r,
534 std::f32::consts::PI,
535 std::f32::consts::FRAC_PI_2 * 3.0,
536 ), (
538 rect.x + rect.width - r,
539 rect.y + r,
540 std::f32::consts::FRAC_PI_2 * 3.0,
541 std::f32::consts::TAU,
542 ), (
544 rect.x + rect.width - r,
545 rect.y + rect.height - r,
546 0.0,
547 std::f32::consts::FRAC_PI_2,
548 ), (
550 rect.x + r,
551 rect.y + rect.height - r,
552 std::f32::consts::FRAC_PI_2,
553 std::f32::consts::PI,
554 ), ];
556
557 let arc_segments = 8u32;
558 for (corner_x, corner_y, start_angle, end_angle) in &corners {
559 for j in 0..=arc_segments {
560 let t =
561 *start_angle + (*end_angle - *start_angle) * (j as f32 / arc_segments as f32);
562 outline.push([corner_x + r * t.cos(), corner_y + r * t.sin()]);
563 }
564 }
565
566 for pt in &outline {
568 self.vertices
569 .push(Vertex::new(pt[0], pt[1], c[0], c[1], c[2], c[3]));
570 }
571
572 let n = outline.len() as u32;
574 for i in 0..n {
575 self.indices.push(base); self.indices.push(base + 1 + i);
577 self.indices.push(base + 1 + (i + 1) % n);
578 }
579 }
580}
581
582fn sample_gradient(stops: &[GradientStop], t: f32) -> Color {
584 let t = t.clamp(0.0, 1.0);
585 if stops.is_empty() {
586 return Color::WHITE;
587 }
588 if stops.len() == 1 || t <= stops[0].offset {
589 return stops[0].color;
590 }
591 if t >= stops[stops.len() - 1].offset {
592 return stops[stops.len() - 1].color;
593 }
594 for i in 1..stops.len() {
595 if t <= stops[i].offset {
596 let prev = &stops[i - 1];
597 let next = &stops[i];
598 let range = next.offset - prev.offset;
599 if range < 0.0001 {
600 return next.color;
601 }
602 let f = (t - prev.offset) / range;
603 return Color::rgba(
604 prev.color.r + (next.color.r - prev.color.r) * f,
605 prev.color.g + (next.color.g - prev.color.g) * f,
606 prev.color.b + (next.color.b - prev.color.b) * f,
607 prev.color.a + (next.color.a - prev.color.a) * f,
608 );
609 }
610 }
611 stops[stops.len() - 1].color
612}