1#![cfg(target_os = "linux")]
5#![allow(non_upper_case_globals)]
6#![allow(non_camel_case_types)]
7#![allow(non_snake_case)]
8#![allow(clippy::missing_safety_doc)]
9
10include!("./ffi.rs");
11
12use four_char_code::{four_char_code, FourCharCode};
13use nix::ioctl_write_ptr;
14use std::{
15 ffi::{c_char, CStr},
16 fmt::Display,
17 os::{
18 fd::RawFd,
19 raw::{c_ulong, c_void},
20 },
21 ptr::null_mut,
22 rc::Rc,
23};
24
25pub const YUYV: FourCharCode = four_char_code!("YUYV");
28pub const VYUY: FourCharCode = four_char_code!("VYUY");
30pub const RGBA: FourCharCode = four_char_code!("RGBA");
31pub const RGB: FourCharCode = four_char_code!("RGB ");
32pub const NV12: FourCharCode = four_char_code!("NV12");
33
34const G2D_2_3_0: Version = Version::new(6, 4, 11, 1049711);
35
36pub type Result<T, E = Error> = std::result::Result<T, E>;
37
38#[derive(Debug)]
39pub enum Error {
40 IoError(std::io::Error),
41 LibraryError(libloading::Error),
42 InvalidFormat(String),
43}
44
45impl std::fmt::Display for Error {
46 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
47 match self {
48 Error::IoError(e) => write!(f, "I/O error: {e}"),
49 Error::LibraryError(e) => write!(f, "Library error: {e}"),
50 Error::InvalidFormat(s) => write!(f, "Invalid format: {s}"),
51 }
52 }
53}
54
55impl std::error::Error for Error {
56 fn source(&self) -> Option<&(dyn std::error::Error + 'static)> {
57 match self {
58 Error::IoError(e) => Some(e),
59 Error::LibraryError(e) => Some(e),
60 Error::InvalidFormat(_) => None,
61 }
62 }
63}
64
65impl From<std::io::Error> for Error {
66 fn from(err: std::io::Error) -> Self {
67 Error::IoError(err)
68 }
69}
70
71impl From<libloading::Error> for Error {
72 fn from(err: libloading::Error) -> Self {
73 Error::LibraryError(err)
74 }
75}
76
77#[derive(Debug, Copy, Clone)]
78pub struct G2DFormat(g2d_format);
79
80impl G2DFormat {
81 pub fn try_from(fourcc: FourCharCode) -> Result<Self> {
84 fourcc.try_into()
85 }
86
87 pub fn format(&self) -> g2d_format {
89 self.0
90 }
91}
92
93impl TryFrom<FourCharCode> for G2DFormat {
94 type Error = Error;
95
96 fn try_from(format: FourCharCode) -> Result<Self, Self::Error> {
97 match format {
98 RGB => Ok(G2DFormat(g2d_format_G2D_RGB888)),
99 RGBA => Ok(G2DFormat(g2d_format_G2D_RGBA8888)),
100 YUYV => Ok(G2DFormat(g2d_format_G2D_YUYV)),
101 VYUY => Ok(G2DFormat(g2d_format_G2D_VYUY)),
102 NV12 => Ok(G2DFormat(g2d_format_G2D_NV12)),
103 _ => Err(Error::InvalidFormat(format.to_string())),
105 }
106 }
107}
108
109impl TryFrom<G2DFormat> for FourCharCode {
110 type Error = Error;
111
112 fn try_from(format: G2DFormat) -> Result<Self, Self::Error> {
115 match format.0 {
116 g2d_format_G2D_RGB888 => Ok(RGB),
117 g2d_format_G2D_RGBA8888 => Ok(RGBA),
118 g2d_format_G2D_YUYV => Ok(YUYV),
119 g2d_format_G2D_VYUY => Ok(VYUY),
120 g2d_format_G2D_NV12 => Ok(NV12),
121 _ => Err(Error::InvalidFormat(format!(
122 "Unsupported G2D format: {format:?}"
123 ))),
124 }
125 }
126}
127
128#[derive(Debug, Copy, Clone, PartialEq, Eq)]
129pub struct G2DPhysical(c_ulong);
130
131impl G2DPhysical {
132 pub fn new(fd: RawFd) -> Result<Self> {
133 let phys = dma_buf_phys(0);
134 let err = unsafe { ioctl_dma_buf_phys(fd, &phys.0).unwrap_or(1) };
135 if err != 0 {
136 return Err(std::io::Error::last_os_error().into());
137 }
138
139 Ok(G2DPhysical(phys.0))
140 }
141
142 pub fn address(&self) -> c_ulong {
143 self.0
144 }
145}
146
147#[repr(C)]
148#[derive(Debug, Copy, Clone)]
149struct dma_buf_phys(std::ffi::c_ulong);
150
151const DMA_BUF_BASE: u8 = b'b';
152const DMA_BUF_IOCTL_PHYS: u8 = 10;
153ioctl_write_ptr!(
154 ioctl_dma_buf_phys,
155 DMA_BUF_BASE,
156 DMA_BUF_IOCTL_PHYS,
157 std::ffi::c_ulong
158);
159
160impl TryFrom<RawFd> for G2DPhysical {
161 type Error = Error;
162
163 fn try_from(fd: RawFd) -> Result<Self, Self::Error> {
164 G2DPhysical::new(fd)
165 }
166}
167
168impl From<u64> for G2DPhysical {
169 fn from(buf: u64) -> Self {
170 G2DPhysical(buf)
171 }
172}
173
174#[repr(C)]
175#[derive(Debug, Clone, PartialEq, PartialOrd, Eq, Ord, Default, Copy)]
176pub struct Version {
178 pub major: i64,
179 pub minor: i64,
180 pub patch: i64,
181 pub num: i64,
182}
183
184impl Display for Version {
185 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
186 write!(
187 f,
188 "{}.{}.{}:{}",
189 self.major, self.minor, self.patch, self.num
190 )
191 }
192}
193
194impl Version {
195 const fn new(major: i64, minor: i64, patch: i64, num: i64) -> Self {
196 Version {
197 major,
198 minor,
199 patch,
200 num,
201 }
202 }
203}
204
205fn guess_version(g2d: &g2d) -> Option<Version> {
206 unsafe {
207 let version = g2d
208 .__library
209 .get::<*const *const c_char>(b"_G2D_VERSION")
210 .map_or(None, |v| Some(*v));
211
212 if let Some(v) = version {
213 let ptr = (*v).byte_offset(2);
216 let s = CStr::from_ptr(ptr).to_string_lossy().to_string();
217 log::debug!("G2D Version string is {s}");
218 let mut version = G2D_2_3_0;
220 if let Some(s) = s.strip_prefix("$VERSION$") {
221 let parts: Vec<_> = s.split(':').collect();
222 let v: Vec<_> = parts[0].split('.').collect();
223 version.major = v
224 .first()
225 .and_then(|s| s.parse().ok())
226 .unwrap_or(version.major);
227 version.minor = v
228 .get(1)
229 .and_then(|s| s.parse().ok())
230 .unwrap_or(version.minor);
231 version.patch = v
232 .get(2)
233 .and_then(|s| s.parse().ok())
234 .unwrap_or(version.patch);
235 version.num = parts
236 .get(1)
237 .and_then(|s| s.parse().ok())
238 .unwrap_or(version.num);
239 }
240
241 Some(version)
242 } else {
243 None
244 }
245 }
246}
247
248#[repr(C)]
249#[derive(Debug, Clone, Copy, PartialEq)]
250pub struct G2DSurface {
251 pub format: g2d_format,
252 pub planes: [::std::os::raw::c_ulong; 3usize],
253 pub left: ::std::os::raw::c_int,
254 pub top: ::std::os::raw::c_int,
255 pub right: ::std::os::raw::c_int,
256 pub bottom: ::std::os::raw::c_int,
257 #[doc = "< buffer stride, in Pixels"]
258 pub stride: ::std::os::raw::c_int,
259 #[doc = "< surface width, in Pixels"]
260 pub width: ::std::os::raw::c_int,
261 #[doc = "< surface height, in Pixels"]
262 pub height: ::std::os::raw::c_int,
263 #[doc = "< alpha blending parameters"]
264 pub blendfunc: g2d_blend_func,
265 #[doc = "< value is 0 ~ 255"]
266 pub global_alpha: ::std::os::raw::c_int,
267 pub clrcolor: ::std::os::raw::c_int,
268 pub rot: g2d_rotation,
269}
270
271impl Default for G2DSurface {
272 fn default() -> Self {
273 G2DSurface {
274 format: g2d_format_G2D_RGB888,
275 planes: [0, 0, 0],
276 left: 0,
277 top: 0,
278 right: 0,
279 bottom: 0,
280 stride: 0,
281 width: 0,
282 height: 0,
283 blendfunc: g2d_blend_func_G2D_ZERO,
284 global_alpha: 255,
285 clrcolor: 0,
286 rot: g2d_rotation_G2D_ROTATION_0,
287 }
288 }
289}
290
291#[repr(C)]
292#[derive(Debug, Clone, Copy, PartialEq)]
293pub struct G2DSurfaceLegacy {
294 pub format: g2d_format,
295 pub planes: [::std::os::raw::c_int; 3usize],
296 pub left: ::std::os::raw::c_int,
297 pub top: ::std::os::raw::c_int,
298 pub right: ::std::os::raw::c_int,
299 pub bottom: ::std::os::raw::c_int,
300 #[doc = "< buffer stride, in Pixels"]
301 pub stride: ::std::os::raw::c_int,
302 #[doc = "< surface width, in Pixels"]
303 pub width: ::std::os::raw::c_int,
304 #[doc = "< surface height, in Pixels"]
305 pub height: ::std::os::raw::c_int,
306 #[doc = "< alpha blending parameters"]
307 pub blendfunc: g2d_blend_func,
308 #[doc = "< value is 0 ~ 255"]
309 pub global_alpha: ::std::os::raw::c_int,
310 pub clrcolor: ::std::os::raw::c_int,
311 pub rot: g2d_rotation,
312}
313
314impl Default for G2DSurfaceLegacy {
315 fn default() -> Self {
316 G2DSurfaceLegacy {
317 format: g2d_format_G2D_RGB888,
318 planes: [0, 0, 0],
319 left: 0,
320 top: 0,
321 right: 0,
322 bottom: 0,
323 stride: 0,
324 width: 0,
325 height: 0,
326 blendfunc: g2d_blend_func_G2D_ZERO,
327 global_alpha: 255,
328 clrcolor: 0,
329 rot: g2d_rotation_G2D_ROTATION_0,
330 }
331 }
332}
333
334impl From<&G2DSurface> for G2DSurfaceLegacy {
335 fn from(surface: &G2DSurface) -> Self {
336 G2DSurfaceLegacy {
337 format: surface.format,
338 planes: [
339 surface.planes[0] as ::std::os::raw::c_int,
340 surface.planes[1] as ::std::os::raw::c_int,
341 surface.planes[2] as ::std::os::raw::c_int,
342 ],
343 left: surface.left,
344 top: surface.top,
345 right: surface.right,
346 bottom: surface.bottom,
347 stride: surface.stride,
348 width: surface.width,
349 height: surface.height,
350 blendfunc: surface.blendfunc,
351 global_alpha: surface.global_alpha,
352 clrcolor: surface.clrcolor,
353 rot: surface.rot,
354 }
355 }
356}
357
358#[derive(Debug)]
359pub struct G2D {
360 pub lib: Rc<g2d>,
361 pub handle: *mut c_void,
362 pub version: Version,
363}
364
365impl G2D {
366 pub fn new<P>(path: P) -> Result<Self>
367 where
368 P: AsRef<::std::ffi::OsStr>,
369 {
370 let lib = unsafe { g2d::new(path)? };
371 let mut handle: *mut c_void = null_mut();
372
373 if unsafe { lib.g2d_open(&mut handle) } != 0 {
374 return Err(std::io::Error::last_os_error().into());
375 }
376
377 let version = guess_version(&lib).unwrap_or(G2D_2_3_0);
378
379 Ok(Self {
380 lib: Rc::new(lib),
381 version,
382 handle,
383 })
384 }
385
386 pub fn version(&self) -> Version {
387 self.version
388 }
389
390 pub fn clear(&self, dst: &mut G2DSurface, color: [u8; 4]) -> Result<()> {
395 dst.clrcolor = i32::from_le_bytes(color);
396 let ret = if self.version >= G2D_2_3_0 {
397 unsafe {
398 self.lib
399 .g2d_clear(self.handle, dst as *const _ as *mut g2d_surface)
400 }
401 } else {
402 let dst: G2DSurfaceLegacy = (dst as &G2DSurface).into();
403 unsafe {
404 self.lib
405 .g2d_clear(self.handle, &dst as *const _ as *mut g2d_surface)
406 }
407 };
408
409 if ret != 0 {
410 return Err(std::io::Error::last_os_error().into());
411 }
412 dst.clrcolor = 0;
413
414 Ok(())
415 }
416
417 pub fn blit(&self, src: &G2DSurface, dst: &G2DSurface) -> Result<()> {
422 let ret = if self.version >= G2D_2_3_0 {
423 unsafe {
424 self.lib.g2d_blit(
425 self.handle,
426 src as *const _ as *mut g2d_surface,
427 dst as *const _ as *mut g2d_surface,
428 )
429 }
430 } else {
431 let src: G2DSurfaceLegacy = src.into();
432 let dst: G2DSurfaceLegacy = dst.into();
433
434 unsafe {
435 self.lib.g2d_blit(
436 self.handle,
437 &src as *const _ as *mut g2d_surface,
438 &dst as *const _ as *mut g2d_surface,
439 )
440 }
441 };
442
443 if ret != 0 {
444 return Err(std::io::Error::last_os_error().into());
445 }
446
447 Ok(())
448 }
449
450 pub fn finish(&self) -> Result<()> {
455 if unsafe { self.lib.g2d_finish(self.handle) } != 0 {
456 return Err(std::io::Error::last_os_error().into());
457 }
458 Ok(())
459 }
460
461 pub fn flush(&self) -> Result<()> {
471 if unsafe { self.lib.g2d_flush(self.handle) } != 0 {
472 return Err(std::io::Error::last_os_error().into());
473 }
474 Ok(())
475 }
476
477 pub fn set_bt601_colorspace(&mut self) -> Result<()> {
478 if unsafe {
479 self.lib
480 .g2d_enable(self.handle, g2d_cap_mode_G2D_YUV_BT_601)
481 } != 0
482 {
483 return Err(std::io::Error::last_os_error().into());
484 }
485 if unsafe {
486 self.lib
487 .g2d_disable(self.handle, g2d_cap_mode_G2D_YUV_BT_709)
488 } != 0
489 {
490 return Err(std::io::Error::last_os_error().into());
491 }
492 Ok(())
493 }
494
495 pub fn set_bt709_colorspace(&mut self) -> Result<()> {
496 if unsafe {
497 self.lib
498 .g2d_disable(self.handle, g2d_cap_mode_G2D_YUV_BT_601)
499 } != 0
500 {
501 return Err(std::io::Error::last_os_error().into());
502 }
503
504 if unsafe {
505 self.lib
506 .g2d_disable(self.handle, g2d_cap_mode_G2D_YUV_BT_601FR)
507 } != 0
508 {
509 return Err(std::io::Error::last_os_error().into());
510 }
511
512 if unsafe {
513 self.lib
514 .g2d_disable(self.handle, g2d_cap_mode_G2D_YUV_BT_709FR)
515 } != 0
516 {
517 return Err(std::io::Error::last_os_error().into());
518 }
519
520 if unsafe {
521 self.lib
522 .g2d_enable(self.handle, g2d_cap_mode_G2D_YUV_BT_709)
523 } != 0
524 {
525 return Err(std::io::Error::last_os_error().into());
526 }
527 Ok(())
528 }
529}
530
531impl Drop for G2D {
532 fn drop(&mut self) {
533 if !self.handle.is_null() {
534 unsafe {
535 self.lib.g2d_close(self.handle);
536 }
537 }
538 }
539}