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