Skip to content

Commit 1ba04f5

Browse files
committed
WIP macOS impl
1 parent 1519b6a commit 1ba04f5

2 files changed

Lines changed: 290 additions & 36 deletions

File tree

src/backends/cg.rs

Lines changed: 288 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -103,7 +103,8 @@ pub struct CGImpl<D, W> {
103103
/// Can also be retrieved from `layer.superlayer()`.
104104
root_layer: SendCALayer,
105105
observer: Retained<Observer>,
106-
color_space: CFRetained<CGColorSpace>,
106+
rgb_color_space: CFRetained<CGColorSpace>,
107+
gray_color_space: CFRetained<CGColorSpace>,
107108
/// The width of the underlying buffer.
108109
width: usize,
109110
/// The height of the underlying buffer.
@@ -229,7 +230,8 @@ impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for CGImpl<
229230
layer.setOpaque(true);
230231

231232
// Initialize color space here, to reduce work later on.
232-
let color_space = CGColorSpace::new_device_rgb().unwrap();
233+
let rgb_color_space = CGColorSpace::new_device_rgb().unwrap();
234+
let gray_color_space = CGColorSpace::new_device_gray().unwrap();
233235

234236
// Grab initial width and height from the layer (whose properties have just been initialized
235237
// by the observer using `NSKeyValueObservingOptionInitial`).
@@ -242,7 +244,8 @@ impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for CGImpl<
242244
layer: SendCALayer(layer),
243245
root_layer: SendCALayer(root_layer),
244246
observer,
245-
color_space,
247+
rgb_color_space,
248+
gray_color_space,
246249
width,
247250
height,
248251
_display: PhantomData,
@@ -256,9 +259,73 @@ impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for CGImpl<
256259
}
257260

258261
#[inline]
259-
fn supported_pixel_formats(&self, _alpha_mode: AlphaMode) -> &[PixelFormat] {
262+
fn supported_pixel_formats(&self, alpha_mode: AlphaMode) -> &[PixelFormat] {
260263
// All alpha modes supported (ish).
261-
&[PixelFormat::Bgra8]
264+
//
265+
// <https://developer.apple.com/library/archive/documentation/GraphicsImaging/Conceptual/drawingwithquartz2d/dq_context/dq_context.html#//apple_ref/doc/uid/TP30001066-CH203-BCIBHHBB>
266+
// <https://developer.apple.com/library/archive/qa/qa1501/_index.html#//apple_ref/doc/uid/DTS10004198>
267+
match alpha_mode {
268+
AlphaMode::Ignored | AlphaMode::Opaque | AlphaMode::Postmultiplied => {
269+
&[
270+
// Some of these probably depend on host endianess?
271+
PixelFormat::Rgb8,
272+
PixelFormat::Bgra8,
273+
PixelFormat::Rgba8,
274+
PixelFormat::Abgr8,
275+
PixelFormat::Argb8,
276+
PixelFormat::Rgb16,
277+
PixelFormat::Rgba16,
278+
PixelFormat::Argb16,
279+
// Grayscale formats are supported.
280+
PixelFormat::R1,
281+
PixelFormat::R2,
282+
PixelFormat::R4,
283+
PixelFormat::R8,
284+
PixelFormat::R16,
285+
// Packed formats only support RGB, and not `R3g3b2`. `Bgra4` etc. also seems to instead
286+
// support the ordering `[G, B, A, R]`, `[B, A, R, G]` etc., which we can't use.
287+
PixelFormat::R5g6b5,
288+
PixelFormat::Rgb5a1, // TODO: Doesn't support premul alpha?
289+
PixelFormat::A1rgb5, // TODO: Doesn't support premul alpha?
290+
PixelFormat::Rgb10a2,
291+
PixelFormat::A2rgb10,
292+
// *BGR* versions of floats just produce black?
293+
PixelFormat::Rgb16f,
294+
PixelFormat::Rgba16f,
295+
PixelFormat::Argb16f,
296+
PixelFormat::Rgb32f,
297+
PixelFormat::Rgba32f,
298+
PixelFormat::Argb32f,
299+
]
300+
}
301+
AlphaMode::Premultiplied => {
302+
// Same table as above, except for `PixelFormat::Rgb5a1` and `PixelFormat::A1rgb5`.
303+
&[
304+
PixelFormat::Rgb8,
305+
PixelFormat::Bgra8,
306+
PixelFormat::Rgba8,
307+
PixelFormat::Abgr8,
308+
PixelFormat::Argb8,
309+
PixelFormat::Rgb16,
310+
PixelFormat::Rgba16,
311+
PixelFormat::Argb16,
312+
PixelFormat::R1,
313+
PixelFormat::R2,
314+
PixelFormat::R4,
315+
PixelFormat::R8,
316+
PixelFormat::R16,
317+
PixelFormat::R5g6b5,
318+
PixelFormat::Rgb10a2,
319+
PixelFormat::A2rgb10,
320+
PixelFormat::Rgb16f,
321+
PixelFormat::Rgba16f,
322+
PixelFormat::Argb16f,
323+
PixelFormat::Rgb32f,
324+
PixelFormat::Rgba32f,
325+
PixelFormat::Argb32f,
326+
]
327+
}
328+
}
262329
}
263330

264331
fn configure(
@@ -281,22 +348,42 @@ impl<D: HasDisplayHandle, W: HasWindowHandle> SurfaceInterface<D, W> for CGImpl<
281348
fn next_buffer(
282349
&mut self,
283350
alpha_mode: AlphaMode,
284-
_pixel_format: PixelFormat,
351+
pixel_format: PixelFormat,
285352
) -> Result<BufferImpl<'_>, SoftBufferError> {
286-
let buffer_size = util::byte_stride(self.width as u32) as usize * self.height / 4;
353+
let num_bytes = util::byte_stride(self.width as u32, pixel_format.bits_per_pixel())
354+
as usize
355+
* self.height;
356+
// `CGBitmapInfo` consists of a combination of `CGImageAlphaInfo`, `CGImageComponentInfo`
357+
// `CGImageByteOrderInfo` and `CGImagePixelFormatInfo`, see `CGBitmapInfoMake`.
358+
//
359+
// TODO: Use `CGBitmapInfo::new` once the next version of objc2-core-graphics is released.
360+
let bitmap_info = CGBitmapInfo(
361+
alpha_info(alpha_mode, pixel_format).0
362+
| component_info(pixel_format).0
363+
| byte_order_info(pixel_format).0
364+
| pixel_format_info(pixel_format).0,
365+
);
366+
// Required that we use a different color space when using grayscale colors.
367+
let color_space = if matches!(
368+
pixel_format,
369+
PixelFormat::R1
370+
| PixelFormat::R2
371+
| PixelFormat::R4
372+
| PixelFormat::R8
373+
| PixelFormat::R16
374+
) {
375+
&self.gray_color_space
376+
} else {
377+
&self.rgb_color_space
378+
};
287379
Ok(BufferImpl {
288-
buffer: util::PixelBuffer(vec![Pixel::default(); buffer_size]),
380+
buffer: util::PixelBuffer(vec![Pixel::default(); num_bytes / size_of::<Pixel>()]),
289381
width: self.width,
290382
height: self.height,
291-
color_space: &self.color_space,
292-
alpha_info: match (alpha_mode, cfg!(target_endian = "little")) {
293-
(AlphaMode::Opaque | AlphaMode::Ignored, true) => CGImageAlphaInfo::NoneSkipFirst,
294-
(AlphaMode::Opaque | AlphaMode::Ignored, false) => CGImageAlphaInfo::NoneSkipLast,
295-
(AlphaMode::Premultiplied, true) => CGImageAlphaInfo::PremultipliedFirst,
296-
(AlphaMode::Premultiplied, false) => CGImageAlphaInfo::PremultipliedLast,
297-
(AlphaMode::Postmultiplied, true) => CGImageAlphaInfo::First,
298-
(AlphaMode::Postmultiplied, false) => CGImageAlphaInfo::Last,
299-
},
383+
color_space,
384+
bitmap_info,
385+
bits_per_component: bits_per_component(pixel_format),
386+
bits_per_pixel: pixel_format.bits_per_pixel(),
300387
layer: &mut self.layer,
301388
})
302389
}
@@ -308,13 +395,15 @@ pub struct BufferImpl<'surface> {
308395
height: usize,
309396
color_space: &'surface CGColorSpace,
310397
buffer: util::PixelBuffer,
311-
alpha_info: CGImageAlphaInfo,
398+
bitmap_info: CGBitmapInfo,
399+
bits_per_component: u8,
400+
bits_per_pixel: u8,
312401
layer: &'surface mut SendCALayer,
313402
}
314403

315404
impl BufferInterface for BufferImpl<'_> {
316405
fn byte_stride(&self) -> NonZeroU32 {
317-
NonZeroU32::new(util::byte_stride(self.width as u32)).unwrap()
406+
NonZeroU32::new(util::byte_stride(self.width as u32, self.bits_per_pixel)).unwrap()
318407
}
319408

320409
fn width(&self) -> NonZeroU32 {
@@ -359,26 +448,15 @@ impl BufferInterface for BufferImpl<'_> {
359448
}
360449
};
361450

362-
// `CGBitmapInfo` consists of a combination of `CGImageAlphaInfo`, `CGImageComponentInfo`
363-
// `CGImageByteOrderInfo` and `CGImagePixelFormatInfo` (see e.g. `CGBitmapInfoMake`).
364-
//
365-
// TODO: Use `CGBitmapInfo::new` once the next version of objc2-core-graphics is released.
366-
let bitmap_info = CGBitmapInfo(
367-
self.alpha_info.0
368-
| CGImageComponentInfo::Integer.0
369-
| CGImageByteOrderInfo::Order32Host.0
370-
| CGImagePixelFormatInfo::Packed.0,
371-
);
372-
373451
let image = unsafe {
374452
CGImage::new(
375453
self.width,
376454
self.height,
377-
8,
378-
32,
379-
util::byte_stride(self.width as u32) as usize,
455+
self.bits_per_component as usize,
456+
self.bits_per_pixel as usize,
457+
util::byte_stride(self.width as u32, self.bits_per_pixel) as usize,
380458
Some(self.color_space),
381-
bitmap_info,
459+
self.bitmap_info,
382460
Some(&data_provider),
383461
ptr::null(),
384462
false,
@@ -421,3 +499,179 @@ impl Deref for SendCALayer {
421499
&self.0
422500
}
423501
}
502+
503+
fn alpha_info(alpha_mode: AlphaMode, pixel_format: PixelFormat) -> CGImageAlphaInfo {
504+
let first = match alpha_mode {
505+
AlphaMode::Opaque | AlphaMode::Ignored => CGImageAlphaInfo::NoneSkipFirst,
506+
AlphaMode::Premultiplied => CGImageAlphaInfo::PremultipliedFirst,
507+
AlphaMode::Postmultiplied => CGImageAlphaInfo::First,
508+
};
509+
let last = match alpha_mode {
510+
AlphaMode::Opaque | AlphaMode::Ignored => CGImageAlphaInfo::NoneSkipLast,
511+
AlphaMode::Premultiplied => CGImageAlphaInfo::PremultipliedLast,
512+
AlphaMode::Postmultiplied => CGImageAlphaInfo::Last,
513+
};
514+
515+
match pixel_format {
516+
// Byte-aligned RGB formats.
517+
PixelFormat::Bgr8 | PixelFormat::Bgr16 => CGImageAlphaInfo::None,
518+
PixelFormat::Rgb8 | PixelFormat::Rgb16 => CGImageAlphaInfo::None,
519+
PixelFormat::Bgra8 | PixelFormat::Bgra16 => first,
520+
PixelFormat::Rgba8 | PixelFormat::Rgba16 => last,
521+
PixelFormat::Abgr8 | PixelFormat::Abgr16 => last,
522+
PixelFormat::Argb8 | PixelFormat::Argb16 => first,
523+
// Grayscale formats.
524+
PixelFormat::R1
525+
| PixelFormat::R2
526+
| PixelFormat::R4
527+
| PixelFormat::R8
528+
| PixelFormat::R16 => CGImageAlphaInfo::None,
529+
// Packed formats.
530+
PixelFormat::B2g3r3 | PixelFormat::R3g3b2 => CGImageAlphaInfo::None,
531+
PixelFormat::B5g6r5 | PixelFormat::R5g6b5 => CGImageAlphaInfo::None,
532+
PixelFormat::Bgra4 | PixelFormat::Bgr5a1 | PixelFormat::Bgr10a2 => first,
533+
PixelFormat::Rgba4 | PixelFormat::Rgb5a1 | PixelFormat::Rgb10a2 => last,
534+
PixelFormat::Abgr4 | PixelFormat::A1bgr5 | PixelFormat::A2bgr10 => last,
535+
PixelFormat::Argb4 | PixelFormat::A1rgb5 | PixelFormat::A2rgb10 => first,
536+
// Floating point formats.
537+
PixelFormat::Bgr16f | PixelFormat::Bgr32f => CGImageAlphaInfo::None,
538+
PixelFormat::Rgb16f | PixelFormat::Rgb32f => CGImageAlphaInfo::None,
539+
PixelFormat::Bgra16f | PixelFormat::Bgra32f => first,
540+
PixelFormat::Rgba16f | PixelFormat::Rgba32f => last,
541+
PixelFormat::Abgr16f | PixelFormat::Abgr32f => last,
542+
PixelFormat::Argb16f | PixelFormat::Argb32f => first,
543+
}
544+
}
545+
546+
fn component_info(pixel_format: PixelFormat) -> CGImageComponentInfo {
547+
if matches!(
548+
pixel_format,
549+
PixelFormat::Bgr16f
550+
| PixelFormat::Rgb16f
551+
| PixelFormat::Bgra16f
552+
| PixelFormat::Rgba16f
553+
| PixelFormat::Abgr16f
554+
| PixelFormat::Argb16f
555+
| PixelFormat::Bgr32f
556+
| PixelFormat::Rgb32f
557+
| PixelFormat::Bgra32f
558+
| PixelFormat::Rgba32f
559+
| PixelFormat::Abgr32f
560+
| PixelFormat::Argb32f
561+
) {
562+
CGImageComponentInfo::Float
563+
} else {
564+
CGImageComponentInfo::Integer
565+
}
566+
}
567+
568+
fn byte_order_info(pixel_format: PixelFormat) -> CGImageByteOrderInfo {
569+
match pixel_format {
570+
// Byte-aligned RGB formats.
571+
PixelFormat::Bgr8 => unimplemented!(),
572+
PixelFormat::Rgb8 => CGImageByteOrderInfo::OrderDefault,
573+
PixelFormat::Bgra8 | PixelFormat::Abgr8 => CGImageByteOrderInfo::Order32Little,
574+
PixelFormat::Rgba8 | PixelFormat::Argb8 => CGImageByteOrderInfo::Order32Big,
575+
PixelFormat::Bgr16 | PixelFormat::Bgra16 | PixelFormat::Abgr16 => {
576+
CGImageByteOrderInfo::Order16Big
577+
}
578+
PixelFormat::Rgb16 | PixelFormat::Rgba16 | PixelFormat::Argb16 => {
579+
CGImageByteOrderInfo::Order16Little
580+
}
581+
582+
// Grayscale formats.
583+
PixelFormat::R1 | PixelFormat::R2 | PixelFormat::R4 | PixelFormat::R8 => {
584+
CGImageByteOrderInfo::OrderDefault
585+
}
586+
PixelFormat::R16 => CGImageByteOrderInfo::Order16Little,
587+
588+
// Packed formats.
589+
PixelFormat::B2g3r3 | PixelFormat::R3g3b2 => CGImageByteOrderInfo::OrderDefault,
590+
PixelFormat::B5g6r5 => CGImageByteOrderInfo::Order16Big,
591+
PixelFormat::R5g6b5 => CGImageByteOrderInfo::Order16Little,
592+
PixelFormat::Bgra4 | PixelFormat::Abgr4 | PixelFormat::Bgr5a1 | PixelFormat::A1bgr5 => {
593+
CGImageByteOrderInfo::Order16Big
594+
}
595+
PixelFormat::Rgba4 | PixelFormat::Argb4 | PixelFormat::Rgb5a1 | PixelFormat::A1rgb5 => {
596+
CGImageByteOrderInfo::Order16Little
597+
}
598+
PixelFormat::Bgr10a2 | PixelFormat::A2bgr10 => CGImageByteOrderInfo::Order32Big,
599+
PixelFormat::Rgb10a2 | PixelFormat::A2rgb10 => CGImageByteOrderInfo::Order32Little,
600+
601+
// Floating point formats.
602+
PixelFormat::Bgr16f | PixelFormat::Bgra16f | PixelFormat::Abgr16f => {
603+
CGImageByteOrderInfo::Order16Big
604+
}
605+
PixelFormat::Rgb16f | PixelFormat::Rgba16f | PixelFormat::Argb16f => {
606+
CGImageByteOrderInfo::Order16Little
607+
}
608+
PixelFormat::Bgr32f | PixelFormat::Bgra32f | PixelFormat::Abgr32f => {
609+
CGImageByteOrderInfo::Order32Big
610+
}
611+
PixelFormat::Rgb32f | PixelFormat::Rgba32f | PixelFormat::Argb32f => {
612+
CGImageByteOrderInfo::Order32Little
613+
}
614+
}
615+
}
616+
617+
fn pixel_format_info(pixel_format: PixelFormat) -> CGImagePixelFormatInfo {
618+
match pixel_format {
619+
PixelFormat::R5g6b5 => CGImagePixelFormatInfo::RGB565,
620+
PixelFormat::Rgb5a1 | PixelFormat::A1rgb5 => CGImagePixelFormatInfo::RGB555,
621+
PixelFormat::Rgb10a2 | PixelFormat::A2rgb10 => CGImagePixelFormatInfo::RGB101010,
622+
// Probably not correct for some formats, but it's the best we can do.
623+
_ => CGImagePixelFormatInfo::Packed,
624+
}
625+
}
626+
627+
fn bits_per_component(pixel_format: PixelFormat) -> u8 {
628+
match pixel_format {
629+
// Byte-aligned RGB formats.
630+
PixelFormat::Bgr8
631+
| PixelFormat::Rgb8
632+
| PixelFormat::Bgra8
633+
| PixelFormat::Rgba8
634+
| PixelFormat::Abgr8
635+
| PixelFormat::Argb8 => 8,
636+
PixelFormat::Bgr16
637+
| PixelFormat::Rgb16
638+
| PixelFormat::Bgra16
639+
| PixelFormat::Rgba16
640+
| PixelFormat::Abgr16
641+
| PixelFormat::Argb16 => 16,
642+
643+
// Grayscale formats.
644+
PixelFormat::R1 => 1,
645+
PixelFormat::R2 => 2,
646+
PixelFormat::R4 => 4,
647+
PixelFormat::R8 => 8,
648+
PixelFormat::R16 => 16,
649+
650+
// Packed formats.
651+
PixelFormat::B2g3r3 | PixelFormat::R3g3b2 => 3,
652+
PixelFormat::B5g6r5 | PixelFormat::R5g6b5 => 5,
653+
PixelFormat::Bgra4 | PixelFormat::Rgba4 | PixelFormat::Abgr4 | PixelFormat::Argb4 => 4,
654+
PixelFormat::Bgr5a1 | PixelFormat::Rgb5a1 | PixelFormat::A1bgr5 | PixelFormat::A1rgb5 => 5,
655+
PixelFormat::Bgr10a2
656+
| PixelFormat::Rgb10a2
657+
| PixelFormat::A2bgr10
658+
| PixelFormat::A2rgb10 => 10,
659+
660+
// Floating point formats.
661+
PixelFormat::Bgr16f
662+
| PixelFormat::Rgb16f
663+
| PixelFormat::Bgra16f
664+
| PixelFormat::Rgba16f
665+
| PixelFormat::Abgr16f
666+
| PixelFormat::Argb16f => 16,
667+
PixelFormat::Bgr32f
668+
| PixelFormat::Rgb32f
669+
| PixelFormat::Bgra32f
670+
| PixelFormat::Rgba32f
671+
| PixelFormat::Abgr32f
672+
| PixelFormat::Argb32f => 32,
673+
}
674+
}
675+
676+
#[cfg(target_endian = "big")]
677+
compile_error!("softbuffer's Apple implementation has not been tested on big endian");

0 commit comments

Comments
 (0)