1use crate::combined_geometry_impl::CombinedGeometryImpl;
2use crate::ellipse_geometry_impl::EllipseGeometryImpl;
3use crate::geometry_group_impl::GeometryGroupImpl;
4use crate::geometry_impl::{FillPath, VelloPath};
5use crate::glyph_run_impl::GlyphRunImpl;
6use crate::helpers::pixel_format_helper;
7use crate::immutable_bitmap::ImmutableBitmap;
8use crate::line_geometry_impl::LineGeometryImpl;
9use crate::rectangle_geometry_impl::RectangleGeometryImpl;
10use crate::render_target_bitmap_impl::RenderTargetBitmapImpl;
11use crate::simple_geometry_impl::SimpleGeometryImpl;
12use crate::stream_geometry_builder::StreamGeometryBuilder;
13use crate::vello_backend_context::VelloContext;
14use crate::vello_options::{VelloOptions, VelloRenderingMode};
15use crate::vello_region_impl::VelloRegionImpl;
16use crate::vello_typeface::VelloTypeface;
17use crate::writeable_bitmap_impl::WriteableBitmapImpl;
18use ferroui_base::media::imaging::BitmapInterpolationMode;
19use ferroui_base::media::text_formatting::GlyphInfo;
20use ferroui_base::media::{FillRule, GeometryCombineMode, GlyphRun, GlyphTypeface};
21use ferroui_base::platform::{
22 AlphaFormat, IBitmapImpl, IGeometryImpl, IGlyphRunImpl, IPlatformGraphicsContext, IPlatformRenderInterface,
23 IPlatformRenderInterfaceContext, IPlatformRenderInterfaceRegion, IRenderTargetBitmapImpl, IStreamGeometryBuilder,
24 IWriteableBitmapImpl, PixelFormat,
25};
26use ferroui_base::{PixelSize, Point, Rect, Vector};
27use kurbo::BezPath;
28use std::fs::File;
29use std::io::{self, Read};
30use std::rc::Rc;
31use std::sync::Arc;
32
33pub struct PlatformRenderInterface {
35 rendering_modes: Vec<VelloRenderingMode>,
37}
38
39impl PlatformRenderInterface {
40 pub fn new(options: VelloOptions) -> Self {
42 Self { rendering_modes: options.rendering_mode_order() }
43 }
44
45 pub fn rendering_modes(&self) -> &[VelloRenderingMode] {
47 &self.rendering_modes
48 }
49}
50
51impl Default for PlatformRenderInterface {
52 fn default() -> Self {
53 Self::new(VelloOptions::default())
54 }
55}
56
57impl IPlatformRenderInterface for PlatformRenderInterface {
58 fn create_ellipse_geometry(&self, rect: Rect) -> Arc<dyn IGeometryImpl> {
59 EllipseGeometryImpl::new(rect)
60 }
61
62 fn create_line_geometry(&self, p1: Point, p2: Point) -> Arc<dyn IGeometryImpl> {
63 LineGeometryImpl::new(p1, p2)
64 }
65
66 fn create_rectangle_geometry(&self, rect: Rect) -> Arc<dyn IGeometryImpl> {
67 RectangleGeometryImpl::new(rect)
68 }
69
70 fn create_stream_geometry_builder(&self, source: Option<&dyn IGeometryImpl>) -> Box<dyn IStreamGeometryBuilder> {
71 Box::new(StreamGeometryBuilder::new(source))
72 }
73
74 fn create_geometry_group(&self, fill_rule: FillRule, children: &[Arc<dyn IGeometryImpl>]) -> Arc<dyn IGeometryImpl> {
75 GeometryGroupImpl::new(fill_rule, children)
76 }
77
78 fn create_combined_geometry(
79 &self,
80 combine_mode: GeometryCombineMode,
81 g1: Arc<dyn IGeometryImpl>,
82 g2: Arc<dyn IGeometryImpl>,
83 ) -> Arc<dyn IGeometryImpl> {
84 CombinedGeometryImpl::force_create(combine_mode, &*g1, &*g2)
85 }
86
87 fn build_glyph_run_geometry(&self, glyph_run: &GlyphRun) -> Arc<dyn IGeometryImpl> {
88 let glyph_typeface = VelloTypeface::try_get(&**glyph_run.glyph_typeface().platform_typeface())
89 .unwrap_or_else(|| panic!("PlatformImpl can't be null."));
90
91 let font_rendering_em_size = glyph_run.font_rendering_em_size();
92
93 let mut path = BezPath::new();
94
95 let baseline_origin = glyph_run.baseline_origin();
96 let (mut current_x, current_y) = (baseline_origin.x, baseline_origin.y);
97
98 let glyph_infos = glyph_run.glyph_infos();
99
100 for glyph_info in glyph_infos.borrow().iter() {
103 if let Some(glyph_path) = glyph_typeface.face().glyph_path(glyph_info.glyph_index, font_rendering_em_size) {
104 if !glyph_path.elements().is_empty() {
105 path.extend(kurbo::Affine::translate((current_x, current_y)) * glyph_path);
106 }
107 }
108
109 current_x += glyph_info.glyph_advance;
110 }
111
112 SimpleGeometryImpl::new(VelloPath::new(path, FillRule::NonZero), FillPath::SameAsStroke, None)
113 }
114
115 fn create_glyph_run(
116 &self,
117 glyph_typeface: &Rc<GlyphTypeface>,
118 font_rendering_em_size: f64,
119 glyph_infos: &[GlyphInfo],
120 baseline_origin: Point,
121 ) -> Arc<dyn IGlyphRunImpl> {
122 Arc::new(GlyphRunImpl::new(glyph_typeface, font_rendering_em_size, glyph_infos, baseline_origin))
123 }
124
125 fn create_render_target_bitmap(&self, size: PixelSize, dpi: Vector) -> Arc<dyn IRenderTargetBitmapImpl> {
126 if size.width < 1 {
127 panic!("Width can't be less than 1");
128 }
129
130 if size.height < 1 {
131 panic!("Height can't be less than 1");
132 }
133
134 Arc::new(RenderTargetBitmapImpl::new(size, dpi, self.rendering_modes.clone()))
135 }
136
137 fn create_writeable_bitmap(
138 &self,
139 size: PixelSize,
140 dpi: Vector,
141 format: PixelFormat,
142 alpha_format: AlphaFormat,
143 ) -> Arc<dyn IWriteableBitmapImpl> {
144 WriteableBitmapImpl::new(size, dpi, format, alpha_format)
145 }
146
147 fn load_bitmap_from_file(&self, file_name: &str) -> io::Result<Arc<ferroui_base::platform::SharedBitmapImpl>> {
148 let mut stream = File::open(file_name)?;
149 self.load_bitmap(&mut stream)
150 }
151
152 fn load_bitmap(&self, stream: &mut dyn Read) -> io::Result<Arc<ferroui_base::platform::SharedBitmapImpl>> {
153 Ok(Arc::new(ImmutableBitmap::from_stream(stream)?))
154 }
155
156 fn load_writeable_bitmap_to_width(
157 &self,
158 stream: &mut dyn Read,
159 width: i32,
160 interpolation_mode: BitmapInterpolationMode,
161 ) -> io::Result<Arc<dyn IWriteableBitmapImpl>> {
162 Ok(WriteableBitmapImpl::from_stream_to_size(stream, width, true, interpolation_mode)?)
163 }
164
165 fn load_writeable_bitmap_to_height(
166 &self,
167 stream: &mut dyn Read,
168 height: i32,
169 interpolation_mode: BitmapInterpolationMode,
170 ) -> io::Result<Arc<dyn IWriteableBitmapImpl>> {
171 Ok(WriteableBitmapImpl::from_stream_to_size(stream, height, false, interpolation_mode)?)
172 }
173
174 fn load_writeable_bitmap_from_file(&self, file_name: &str) -> io::Result<Arc<dyn IWriteableBitmapImpl>> {
175 let mut stream = File::open(file_name)?;
176 self.load_writeable_bitmap(&mut stream)
177 }
178
179 fn load_writeable_bitmap(&self, stream: &mut dyn Read) -> io::Result<Arc<dyn IWriteableBitmapImpl>> {
180 Ok(WriteableBitmapImpl::from_stream(stream)?)
181 }
182
183 fn load_bitmap_to_width(
184 &self,
185 stream: &mut dyn Read,
186 width: i32,
187 interpolation_mode: BitmapInterpolationMode,
188 ) -> io::Result<Arc<ferroui_base::platform::SharedBitmapImpl>> {
189 Ok(Arc::new(ImmutableBitmap::from_stream_to_size(stream, width, true, interpolation_mode)?))
190 }
191
192 fn load_bitmap_to_height(
193 &self,
194 stream: &mut dyn Read,
195 height: i32,
196 interpolation_mode: BitmapInterpolationMode,
197 ) -> io::Result<Arc<ferroui_base::platform::SharedBitmapImpl>> {
198 Ok(Arc::new(ImmutableBitmap::from_stream_to_size(stream, height, false, interpolation_mode)?))
199 }
200
201 fn resize_bitmap(
202 &self,
203 bitmap_impl: &dyn IBitmapImpl,
204 destination_size: PixelSize,
205 interpolation_mode: BitmapInterpolationMode,
206 ) -> Arc<ferroui_base::platform::SharedBitmapImpl> {
207 match bitmap_impl.as_any().downcast_ref::<ImmutableBitmap>() {
208 Some(ibmp) => Arc::new(
209 ImmutableBitmap::resized(ibmp, destination_size, interpolation_mode)
210 .unwrap_or_else(|error| panic!("Unable to resize the bitmap: {error}")),
211 ),
212 None => panic!("Invalid source bitmap type."),
213 }
214 }
215
216 fn load_bitmap_from_pixels(
217 &self,
218 format: PixelFormat,
219 alpha_format: AlphaFormat,
220 data: &[u8],
221 size: PixelSize,
222 dpi: Vector,
223 stride: i32,
224 ) -> Arc<ferroui_base::platform::SharedBitmapImpl> {
225 Arc::new(
226 ImmutableBitmap::from_pixels(size, dpi, stride, format, alpha_format, data)
227 .unwrap_or_else(|error| panic!("{error}")),
228 )
229 }
230
231 fn create_backend_context(
232 &self,
233 graphics_api_context: Option<Rc<dyn IPlatformGraphicsContext>>,
234 ) -> Rc<dyn IPlatformRenderInterfaceContext> {
235 #[cfg(feature = "hybrid-webgl")]
238 if let Some(graphics_context) = &graphics_api_context {
239 match crate::web_gl::VelloWebGlGpu::try_new(graphics_context) {
240 Some(Ok(web_gl)) => return Rc::new(VelloContext::with_web_gl(web_gl, self.rendering_modes.clone())),
241 Some(Err(error)) => panic!("The Vello backend cannot draw with the WebGL2 context of the canvas: {error}."),
242 None => {}
243 }
244 }
245
246 match graphics_api_context {
247 None => Rc::new(VelloContext::new(self.rendering_modes.clone())),
248 #[cfg(any(feature = "hybrid", feature = "gpu"))]
251 Some(graphics_context) => {
252 Rc::new(VelloContext::with_gpu(crate::gpu::create_gpu(&graphics_context), self.rendering_modes.clone()))
253 }
254 #[cfg(not(any(feature = "hybrid", feature = "gpu")))]
255 Some(_) => panic!(
256 "The Vello backend was built without a GPU mode for this graphics context (the features `hybrid` and \
257 `gpu` of ferroui-vello draw on the device of a desktop platform, `hybrid-webgl` with the WebGL2 \
258 context of a canvas): it draws into the framebuffer of a platform that renders in software"
259 ),
260 }
261 }
262
263 fn supports_individual_round_rects(&self) -> bool {
264 true
265 }
266
267 fn default_alpha_format(&self) -> AlphaFormat {
268 AlphaFormat::Premul
269 }
270
271 fn default_pixel_format(&self) -> PixelFormat {
272 PixelFormat::RGBA8888
274 }
275
276 fn is_supported_bitmap_pixel_format(&self, format: PixelFormat) -> bool {
277 pixel_format_helper::is_supported(format)
278 }
279
280 fn supports_regions(&self) -> bool {
281 true
282 }
283
284 fn create_region(&self) -> Rc<dyn IPlatformRenderInterfaceRegion> {
285 Rc::new(VelloRegionImpl::new())
286 }
287}