#[cfg(feature = "png")]
use std::fs::File;
#[cfg(feature = "png")]
use std::io::BufWriter;
use std::path::Path;
#[cfg(feature = "png")]
use png::{ColorType, Encoder};
use piet::{ErrorKind, ImageFormat};
use piet_direct2d::d2d::{Bitmap, Brush as D2DBrush};
use piet_direct2d::d3d::{
D3D11Device, D3D11DeviceContext, D3D11Texture2D, TextureMode, DXGI_MAP_READ,
};
#[doc(hidden)]
pub use piet_direct2d::*;
pub type Piet<'a> = D2DRenderContext<'a>;
pub type Brush = D2DBrush;
pub type PietText<'a> = D2DText<'a>;
pub type PietFont = D2DFont;
pub type PietFontBuilder<'a> = D2DFontBuilder<'a>;
pub type PietTextLayout = D2DTextLayout;
pub type PietTextLayoutBuilder<'a> = D2DTextLayoutBuilder<'a>;
pub type Image = Bitmap;
pub struct Device {
d2d: D2DFactory,
dwrite: DwriteFactory,
d3d: D3D11Device,
d3d_ctx: D3D11DeviceContext,
device: D2DDevice,
}
pub struct BitmapTarget<'a> {
width: usize,
height: usize,
d2d: &'a D2DFactory,
dwrite: &'a DwriteFactory,
d3d: &'a D3D11Device,
d3d_ctx: &'a D3D11DeviceContext,
tex: D3D11Texture2D,
context: D2DDeviceContext,
}
trait WrapError<T> {
fn wrap(self) -> Result<T, piet::Error>;
}
impl Device {
pub fn new() -> Result<Device, piet::Error> {
let d2d = D2DFactory::new().unwrap();
let dwrite = DwriteFactory::new().unwrap();
let (d3d, d3d_ctx) = D3D11Device::create().unwrap();
let device = unsafe { d2d.create_device(d3d.as_dxgi().unwrap().as_raw()).unwrap() };
Ok(Device {
d2d,
dwrite,
d3d,
d3d_ctx,
device,
})
}
pub fn bitmap_target(
&mut self,
width: usize,
height: usize,
pix_scale: f64,
) -> Result<BitmapTarget, piet::Error> {
let mut context = self.device.create_device_context().unwrap();
let tex = self
.d3d
.create_texture(width as u32, height as u32, TextureMode::Target)
.unwrap();
let target = unsafe {
context
.create_bitmap_from_dxgi(&tex.as_dxgi(), pix_scale as f32)
.unwrap()
};
context.set_target(&target);
context.set_dpi_scale(pix_scale as f32);
context.begin_draw();
Ok(BitmapTarget {
width,
height,
d2d: &self.d2d,
dwrite: &self.dwrite,
d3d: &self.d3d,
d3d_ctx: &self.d3d_ctx,
tex,
context,
})
}
}
impl<'a> BitmapTarget<'a> {
pub fn render_context(&mut self) -> D2DRenderContext {
D2DRenderContext::new(self.d2d, self.dwrite, &mut self.context)
}
pub fn into_raw_pixels(mut self, fmt: ImageFormat) -> Result<Vec<u8>, piet::Error> {
self.context.end_draw()?;
if fmt != ImageFormat::RgbaPremul {
return Err(piet::new_error(ErrorKind::NotSupported));
}
let temp_texture = self
.d3d
.create_texture(self.width as u32, self.height as u32, TextureMode::Read)
.unwrap();
let mut raw_pixels: Vec<u8> = Vec::with_capacity(self.width * self.height * 4);
unsafe {
self.d3d_ctx
.inner()
.CopyResource(temp_texture.as_raw() as *mut _, self.tex.as_raw() as *mut _);
self.d3d_ctx.inner().Flush();
let surface = temp_texture.as_dxgi();
let mut mapped_rect = std::mem::zeroed();
let _hr = surface.Map(&mut mapped_rect, DXGI_MAP_READ);
for y in 0..self.height {
let src = mapped_rect
.pBits
.offset(mapped_rect.Pitch as isize * y as isize);
let dst = raw_pixels
.as_mut_ptr()
.offset(self.width as isize * 4 * y as isize);
std::ptr::copy_nonoverlapping(src, dst, self.width * 4);
}
raw_pixels.set_len(self.width * self.height * 4);
}
Ok(raw_pixels)
}
#[cfg(feature = "png")]
pub fn save_to_file<P: AsRef<Path>>(self, path: P) -> Result<(), piet::Error> {
let height = self.height;
let width = self.width;
let image = self.into_raw_pixels(ImageFormat::RgbaPremul)?;
let file = BufWriter::new(File::create(path).map_err(|e| Into::<Box<_>>::into(e))?);
let mut encoder = Encoder::new(file, width as u32, height as u32);
encoder.set_color(ColorType::RGBA);
encoder
.write_header()
.map_err(|e| Into::<Box<_>>::into(e))?
.write_image_data(&image)
.map_err(|e| Into::<Box<_>>::into(e))?;
Ok(())
}
#[cfg(not(feature = "png"))]
pub fn save_to_file<P: AsRef<Path>>(self, _path: P) -> Result<(), piet::Error> {
Err(piet::new_error(ErrorKind::MissingFeature))
}
}
impl<'a> Drop for BitmapTarget<'a> {
fn drop(&mut self) {
let _ = self.context.end_draw();
}
}
mod tests {
#[test]
fn bitmap_target_drop() {
let mut device = crate::Device::new().unwrap();
let bitmap_target = device.bitmap_target(640, 480, 1.0);
std::mem::drop(bitmap_target);
}
}