x-graphics 0.2.1

Graphics framework for X
Documentation
use core::ffi::c_void;

use windows::Win32::Graphics::{
    Direct2D::{
        Common::{D2D1_ALPHA_MODE, D2D1_ALPHA_MODE_PREMULTIPLIED, D2D1_ALPHA_MODE_STRAIGHT, D2D1_PIXEL_FORMAT, D2D_SIZE_U},
        ID2D1Bitmap, D2D1_BITMAP_PROPERTIES,
    },
    Dxgi::Common::{DXGI_FORMAT, DXGI_FORMAT_A8_UNORM, DXGI_FORMAT_B8G8R8A8_UNORM, DXGI_FORMAT_R8G8B8A8_UNORM},
};

use super::{canvas::D2DCanvas, device::D2DDeviceContext};
use crate::{
    base::Dimension,
    bitmap::{BitmapBackend, BitmapPixelFormat},
    error::GraphicsError,
    geometry::{FSize, ISize},
};

const DEFAULT_DPI: f32 = 96.0;

#[derive(Clone, Debug)]
pub struct D2DBitmap {
    bitmap: ID2D1Bitmap,
}

impl Dimension for D2DBitmap {
    fn size(&self) -> FSize {
        let size = unsafe { self.bitmap.GetSize() };
        size.into()
    }

    fn pixel_size(&self) -> ISize {
        let size = unsafe { self.bitmap.GetPixelSize() };
        ISize::new(size.width as i32, size.height as i32)
    }
}

impl BitmapBackend for D2DBitmap {
    type DeviceContextType = D2DDeviceContext;
    type CanvasType = D2DCanvas;

    fn new(
        _context: Option<&Self::DeviceContextType>,
        width: usize,
        height: usize,
        pixel_format: BitmapPixelFormat,
        canvas: &Self::CanvasType,
    ) -> Result<Self, GraphicsError> {
        if width == 0 || height == 0 {
            return Err(invalid_param_error!((width, height)));
        }

        let size = D2D_SIZE_U {
            width: width as u32,
            height: height as u32,
        };

        let pixel_format = Self::get_d2d_pixel_format(pixel_format)?;
        let properties = D2D1_BITMAP_PROPERTIES {
            pixelFormat: pixel_format,
            dpiX: DEFAULT_DPI,
            dpiY: DEFAULT_DPI,
        };

        let render_target = canvas.render_target();
        let bitmap = unsafe { render_target.CreateBitmap(size, None, 0, &properties).map_err(|err| GraphicsError::CreationFailed(err.to_string()))? };

        Ok(Self {
            bitmap,
        })
    }

    fn from_buffer(
        _context: Option<&Self::DeviceContextType>,
        buffer: &[u8],
        pitch: usize,
        width: usize,
        height: usize,
        pixel_format: BitmapPixelFormat,
        canvas: &Self::CanvasType,
    ) -> Result<Self, GraphicsError> {
        if pitch == 0 || width == 0 || height == 0 {
            return Err(invalid_param_error!((pitch, width, height)));
        }

        if buffer.len() != pitch * height {
            return Err(invalid_param_error!(buffer));
        }

        let size = D2D_SIZE_U {
            width: width as u32,
            height: height as u32,
        };
        let pixel_format = Self::get_d2d_pixel_format(pixel_format)?;
        let properties = D2D1_BITMAP_PROPERTIES {
            pixelFormat: pixel_format,
            dpiX: DEFAULT_DPI,
            dpiY: DEFAULT_DPI,
        };

        let render_target = canvas.render_target();
        let buffer_ptr = buffer.as_ptr() as *const c_void;
        let bitmap = unsafe {
            render_target
                .CreateBitmap(size, Some(buffer_ptr), pitch as u32, &properties)
                .map_err(|err| GraphicsError::CreationFailed(err.to_string()))?
        };

        Ok(Self {
            bitmap,
        })
    }
}

impl D2DBitmap {
    pub(super) fn bitmap(&self) -> &ID2D1Bitmap {
        &self.bitmap
    }

    fn get_d2d_pixel_format(pixel_format: BitmapPixelFormat) -> Result<D2D1_PIXEL_FORMAT, GraphicsError> {
        let (dxgi_format, alpha_mode) = match pixel_format {
            BitmapPixelFormat::RGBA32 => (DXGI_FORMAT_R8G8B8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
            BitmapPixelFormat::BGRA32 => (DXGI_FORMAT_B8G8R8A8_UNORM, D2D1_ALPHA_MODE_PREMULTIPLIED),
            BitmapPixelFormat::A8 => (DXGI_FORMAT_A8_UNORM, D2D1_ALPHA_MODE_STRAIGHT),
            _ => return Err(GraphicsError::Unsupported(pixel_format.to_string())),
        };

        Ok(D2D1_PIXEL_FORMAT {
            format: dxgi_format,
            alphaMode: alpha_mode,
        })
    }
}