@@ -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
315404impl 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