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