1#![forbid(unsafe_code, rust_2018_idioms)]
40
41pub use piet_hardware::piet;
42pub use wgpu;
43
44use piet_hardware::piet::kurbo::Affine;
45use piet_hardware::piet::{Color, Error as Pierror, ImageFormat, InterpolationMode};
46
47mod buffer;
48mod context;
49mod texture;
50
51use context::GpuContext;
52
53#[derive(Debug)]
55pub struct WgpuContext {
56 source: piet_hardware::Source<GpuContext>,
58
59 text: Text,
61}
62
63impl WgpuContext {
64 pub fn new(
66 device: &wgpu::Device,
67 queue: &wgpu::Queue,
68 format: wgpu::TextureFormat,
69 depth_format: Option<wgpu::TextureFormat>,
70 samples: u32,
71 ) -> Self {
72 let source = piet_hardware::Source::new(
73 GpuContext::new(device, queue, format, depth_format, samples),
74 device,
75 queue,
76 )
77 .expect("failed to create GPU context");
78 let text = Text(source.text().clone());
79 Self { source, text }
80 }
81
82 pub fn prepare<'this, 'dev, 'que>(
86 &'this mut self,
87 device: &'dev wgpu::Device,
88 queue: &'que wgpu::Queue,
89 width: u32,
90 height: u32,
91 ) -> RenderContext<'this, 'dev, 'que> {
92 RenderContext {
93 context: self.source.render_context(device, queue, width, height),
94 text: &mut self.text,
95 }
96 }
97
98 pub fn render<'this>(&'this self, pass: &mut wgpu::RenderPass<'this>) {
100 self.source.context().render(pass);
101 }
102
103 pub fn after_submit(&mut self, device: &wgpu::Device) {
105 self.source.gpu_flushed();
106 self.source.context_mut().gpu_flushed(device);
107 }
108}
109
110#[derive(Debug)]
112pub struct RenderContext<'context, 'device, 'queue> {
113 context: piet_hardware::RenderContext<'context, 'device, 'queue, GpuContext>,
114 text: &'context mut Text,
115}
116
117impl RenderContext<'_, '_, '_> {
118 #[inline]
120 pub fn tolerance(&self) -> f64 {
121 self.context.tolerance()
122 }
123
124 #[inline]
126 pub fn set_tolerance(&mut self, tolerance: f64) {
127 self.context.set_tolerance(tolerance)
128 }
129
130 #[inline]
132 pub fn bitmap_scale(&self) -> f64 {
133 self.context.bitmap_scale()
134 }
135
136 #[inline]
138 pub fn set_bitmap_scale(&mut self, scale: f64) {
139 self.context.set_bitmap_scale(scale)
140 }
141}
142
143impl piet::RenderContext for RenderContext<'_, '_, '_> {
144 type Brush = Brush;
145 type Image = Image;
146 type Text = Text;
147 type TextLayout = TextLayout;
148
149 fn blurred_rect(
150 &mut self,
151 rect: piet::kurbo::Rect,
152 blur_radius: f64,
153 brush: &impl piet::IntoBrush<Self>,
154 ) {
155 let brush = brush.make_brush(self, || rect);
156 self.context
157 .blurred_rect(rect, blur_radius, &brush.as_ref().0)
158 }
159
160 fn capture_image_area(
161 &mut self,
162 src_rect: impl Into<piet::kurbo::Rect>,
163 ) -> Result<Self::Image, Pierror> {
164 self.context.capture_image_area(src_rect).map(Image)
165 }
166
167 fn clear(&mut self, region: impl Into<Option<piet::kurbo::Rect>>, color: Color) {
168 self.context.clear(region, color)
169 }
170
171 fn clip(&mut self, shape: impl piet::kurbo::Shape) {
172 self.context.clip(shape)
173 }
174
175 fn current_transform(&self) -> Affine {
176 self.context.current_transform()
177 }
178
179 fn draw_image(
180 &mut self,
181 image: &Self::Image,
182 dst_rect: impl Into<piet::kurbo::Rect>,
183 interp: InterpolationMode,
184 ) {
185 self.context.draw_image(&image.0, dst_rect, interp)
186 }
187
188 fn draw_image_area(
189 &mut self,
190 image: &Self::Image,
191 src_rect: impl Into<piet::kurbo::Rect>,
192 dst_rect: impl Into<piet::kurbo::Rect>,
193 interp: InterpolationMode,
194 ) {
195 self.context
196 .draw_image_area(&image.0, src_rect, dst_rect, interp)
197 }
198
199 fn draw_text(&mut self, layout: &Self::TextLayout, pos: impl Into<piet::kurbo::Point>) {
200 self.context.draw_text(&layout.0, pos)
201 }
202
203 fn fill(&mut self, shape: impl piet::kurbo::Shape, brush: &impl piet::IntoBrush<Self>) {
204 let brush = brush.make_brush(self, || shape.bounding_box());
205 self.context.fill(shape, &brush.as_ref().0)
206 }
207
208 fn fill_even_odd(
209 &mut self,
210 shape: impl piet::kurbo::Shape,
211 brush: &impl piet::IntoBrush<Self>,
212 ) {
213 let brush = brush.make_brush(self, || shape.bounding_box());
214 self.context.fill_even_odd(shape, &brush.as_ref().0)
215 }
216
217 fn finish(&mut self) -> Result<(), Pierror> {
218 self.context.finish()
219 }
220
221 fn gradient(
222 &mut self,
223 gradient: impl Into<piet::FixedGradient>,
224 ) -> Result<Self::Brush, Pierror> {
225 self.context.gradient(gradient).map(Brush)
226 }
227
228 fn make_image(
229 &mut self,
230 width: usize,
231 height: usize,
232 buf: &[u8],
233 format: ImageFormat,
234 ) -> Result<Self::Image, Pierror> {
235 self.context
236 .make_image(width, height, buf, format)
237 .map(Image)
238 }
239
240 fn restore(&mut self) -> Result<(), Pierror> {
241 self.context.restore()
242 }
243
244 fn save(&mut self) -> Result<(), Pierror> {
245 self.context.save()
246 }
247
248 fn solid_brush(&mut self, color: Color) -> Self::Brush {
249 Brush(self.context.solid_brush(color))
250 }
251
252 fn status(&mut self) -> Result<(), Pierror> {
253 self.context.status()
254 }
255
256 fn stroke(
257 &mut self,
258 shape: impl piet::kurbo::Shape,
259 brush: &impl piet::IntoBrush<Self>,
260 width: f64,
261 ) {
262 let brush = brush.make_brush(self, || shape.bounding_box());
263 self.context.stroke(shape, &brush.as_ref().0, width)
264 }
265
266 fn stroke_styled(
267 &mut self,
268 shape: impl piet::kurbo::Shape,
269 brush: &impl piet::IntoBrush<Self>,
270 width: f64,
271 style: &piet::StrokeStyle,
272 ) {
273 let brush = brush.make_brush(self, || shape.bounding_box());
274 self.context
275 .stroke_styled(shape, &brush.as_ref().0, width, style)
276 }
277
278 fn text(&mut self) -> &mut Self::Text {
279 self.text
280 }
281
282 fn transform(&mut self, transform: Affine) {
283 self.context.transform(transform)
284 }
285}
286
287#[derive(Debug)]
289pub struct Brush(piet_hardware::Brush<GpuContext>);
290
291impl Clone for Brush {
292 fn clone(&self) -> Self {
293 Self(self.0.clone())
294 }
295}
296
297impl piet::IntoBrush<RenderContext<'_, '_, '_>> for Brush {
298 fn make_brush<'a>(
299 &'a self,
300 _piet: &mut RenderContext<'_, '_, '_>,
301 _bbox: impl FnOnce() -> piet::kurbo::Rect,
302 ) -> std::borrow::Cow<'a, Brush> {
303 std::borrow::Cow::Borrowed(self)
304 }
305}
306
307#[derive(Debug)]
309pub struct Image(piet_hardware::Image<GpuContext>);
310
311impl Clone for Image {
312 fn clone(&self) -> Self {
313 Self(self.0.clone())
314 }
315}
316
317impl piet::Image for Image {
318 fn size(&self) -> piet::kurbo::Size {
319 self.0.size()
320 }
321}
322
323#[derive(Debug, Clone)]
325pub struct TextLayout(piet_hardware::TextLayout);
326
327impl piet::TextLayout for TextLayout {
328 fn size(&self) -> piet::kurbo::Size {
329 self.0.size()
330 }
331
332 fn line_text(&self, line_number: usize) -> Option<&str> {
333 self.0.line_text(line_number)
334 }
335
336 fn line_metric(&self, line_number: usize) -> Option<piet::LineMetric> {
337 self.0.line_metric(line_number)
338 }
339
340 fn line_count(&self) -> usize {
341 self.0.line_count()
342 }
343
344 fn hit_test_point(&self, point: piet::kurbo::Point) -> piet::HitTestPoint {
345 self.0.hit_test_point(point)
346 }
347
348 fn trailing_whitespace_width(&self) -> f64 {
349 self.0.trailing_whitespace_width()
350 }
351
352 fn image_bounds(&self) -> piet::kurbo::Rect {
353 self.0.image_bounds()
354 }
355
356 fn text(&self) -> &str {
357 self.0.text()
358 }
359
360 fn hit_test_text_position(&self, idx: usize) -> piet::HitTestPosition {
361 self.0.hit_test_text_position(idx)
362 }
363}
364
365#[derive(Debug)]
367pub struct TextLayoutBuilder(piet_hardware::TextLayoutBuilder);
368
369impl piet::TextLayoutBuilder for TextLayoutBuilder {
370 type Out = TextLayout;
371
372 fn max_width(self, width: f64) -> Self {
373 Self(self.0.max_width(width))
374 }
375
376 fn alignment(self, alignment: piet::TextAlignment) -> Self {
377 Self(self.0.alignment(alignment))
378 }
379
380 fn default_attribute(self, attribute: impl Into<piet::TextAttribute>) -> Self {
381 Self(self.0.default_attribute(attribute))
382 }
383
384 fn range_attribute(
385 self,
386 range: impl std::ops::RangeBounds<usize>,
387 attribute: impl Into<piet::TextAttribute>,
388 ) -> Self {
389 Self(self.0.range_attribute(range, attribute))
390 }
391
392 fn build(self) -> Result<Self::Out, Pierror> {
393 Ok(TextLayout(self.0.build()?))
394 }
395}
396
397#[derive(Debug, Clone)]
399pub struct Text(piet_hardware::Text);
400
401impl Text {
402 pub fn dpi(&self) -> f64 {
404 self.0.dpi()
405 }
406
407 pub fn set_dpi(&mut self, dpi: f64) {
409 self.0.set_dpi(dpi)
410 }
411}
412
413impl piet::Text for Text {
414 type TextLayoutBuilder = TextLayoutBuilder;
415 type TextLayout = TextLayout;
416
417 fn font_family(&mut self, family_name: &str) -> Option<piet::FontFamily> {
418 self.0.font_family(family_name)
419 }
420
421 fn load_font(&mut self, data: &[u8]) -> Result<piet::FontFamily, Pierror> {
422 self.0.load_font(data)
423 }
424
425 fn new_text_layout(&mut self, text: impl piet::TextStorage) -> Self::TextLayoutBuilder {
426 TextLayoutBuilder(self.0.new_text_layout(text))
427 }
428}