use blinc_core::{
BillboardFacing, BlendMode, Brush, Camera, ClipShape, CornerRadius, DrawCommand, DrawContext,
Environment, ImageId, ImageOptions, LayerConfig, LayerId, Light, Mat4, MaterialId, MeshId,
MeshInstance, Path, Point, RecordingContext, Rect, SdfBuilder, Shadow, Size, Stroke, TextStyle,
Transform,
};
pub use blinc_core::{LineCap, LineJoin};
pub struct PaintContext {
recording: RecordingContext,
}
impl PaintContext {
pub fn new(width: f32, height: f32) -> Self {
Self {
recording: RecordingContext::new(Size::new(width, height)),
}
}
pub fn from_size(size: Size) -> Self {
Self {
recording: RecordingContext::new(size),
}
}
pub fn commands(&self) -> &[DrawCommand] {
self.recording.commands()
}
pub fn take_commands(&mut self) -> Vec<DrawCommand> {
self.recording.take_commands()
}
pub fn fill_rect_xywh(
&mut self,
x: f32,
y: f32,
width: f32,
height: f32,
brush: impl Into<Brush>,
) {
self.fill_rect(
Rect::new(x, y, width, height),
CornerRadius::default(),
brush.into(),
);
}
pub fn stroke_rect_xywh(
&mut self,
x: f32,
y: f32,
width: f32,
height: f32,
stroke: &Stroke,
brush: impl Into<Brush>,
) {
self.stroke_rect(
Rect::new(x, y, width, height),
CornerRadius::default(),
stroke,
brush.into(),
);
}
pub fn fill_rounded_rect_xywh(
&mut self,
x: f32,
y: f32,
width: f32,
height: f32,
radius: f32,
brush: impl Into<Brush>,
) {
self.fill_rect(Rect::new(x, y, width, height), radius.into(), brush.into());
}
pub fn fill_circle_xyr(&mut self, cx: f32, cy: f32, radius: f32, brush: impl Into<Brush>) {
self.fill_circle(Point::new(cx, cy), radius, brush.into());
}
pub fn stroke_circle_xyr(
&mut self,
cx: f32,
cy: f32,
radius: f32,
stroke: &Stroke,
brush: impl Into<Brush>,
) {
self.stroke_circle(Point::new(cx, cy), radius, stroke, brush.into());
}
pub fn draw_text_simple(
&mut self,
text: impl Into<String>,
x: f32,
y: f32,
size: f32,
color: blinc_core::Color,
) {
self.draw_text(
&text.into(),
Point::new(x, y),
&TextStyle::new(size).with_color(color),
);
}
pub fn translate(&mut self, x: f32, y: f32) {
self.push_transform(Transform::translate(x, y));
}
pub fn scale(&mut self, sx: f32, sy: f32) {
self.push_transform(Transform::scale(sx, sy));
}
pub fn rotate(&mut self, angle: f32) {
self.push_transform(Transform::rotate(angle));
}
}
impl Default for PaintContext {
fn default() -> Self {
Self::new(0.0, 0.0)
}
}
impl DrawContext for PaintContext {
fn push_transform(&mut self, transform: Transform) {
self.recording.push_transform(transform);
}
fn pop_transform(&mut self) {
self.recording.pop_transform();
}
fn current_transform(&self) -> Transform {
self.recording.current_transform()
}
fn push_clip(&mut self, shape: ClipShape) {
self.recording.push_clip(shape);
}
fn pop_clip(&mut self) {
self.recording.pop_clip();
}
fn push_opacity(&mut self, opacity: f32) {
self.recording.push_opacity(opacity);
}
fn pop_opacity(&mut self) {
self.recording.pop_opacity();
}
fn push_blend_mode(&mut self, mode: BlendMode) {
self.recording.push_blend_mode(mode);
}
fn pop_blend_mode(&mut self) {
self.recording.pop_blend_mode();
}
fn fill_path(&mut self, path: &Path, brush: Brush) {
self.recording.fill_path(path, brush);
}
fn stroke_path(&mut self, path: &Path, stroke: &Stroke, brush: Brush) {
self.recording.stroke_path(path, stroke, brush);
}
fn fill_rect(&mut self, rect: Rect, corner_radius: CornerRadius, brush: Brush) {
self.recording.fill_rect(rect, corner_radius, brush);
}
fn stroke_rect(
&mut self,
rect: Rect,
corner_radius: CornerRadius,
stroke: &Stroke,
brush: Brush,
) {
self.recording
.stroke_rect(rect, corner_radius, stroke, brush);
}
fn fill_circle(&mut self, center: Point, radius: f32, brush: Brush) {
self.recording.fill_circle(center, radius, brush);
}
fn stroke_circle(&mut self, center: Point, radius: f32, stroke: &Stroke, brush: Brush) {
self.recording.stroke_circle(center, radius, stroke, brush);
}
fn draw_text(&mut self, text: &str, origin: Point, style: &TextStyle) {
self.recording.draw_text(text, origin, style);
}
fn draw_image(&mut self, image: ImageId, rect: Rect, options: &ImageOptions) {
self.recording.draw_image(image, rect, options);
}
fn draw_shadow(&mut self, rect: Rect, corner_radius: CornerRadius, shadow: Shadow) {
self.recording.draw_shadow(rect, corner_radius, shadow);
}
fn draw_inner_shadow(&mut self, rect: Rect, corner_radius: CornerRadius, shadow: Shadow) {
self.recording
.draw_inner_shadow(rect, corner_radius, shadow);
}
fn draw_circle_shadow(&mut self, center: Point, radius: f32, shadow: Shadow) {
self.recording.draw_circle_shadow(center, radius, shadow);
}
fn draw_circle_inner_shadow(&mut self, center: Point, radius: f32, shadow: Shadow) {
self.recording
.draw_circle_inner_shadow(center, radius, shadow);
}
fn sdf_build(&mut self, f: &mut dyn FnMut(&mut dyn SdfBuilder)) {
self.recording.sdf_build(f);
}
fn set_camera(&mut self, camera: &Camera) {
self.recording.set_camera(camera);
}
fn draw_mesh(&mut self, mesh: MeshId, material: MaterialId, transform: Mat4) {
self.recording.draw_mesh(mesh, material, transform);
}
fn draw_mesh_instanced(&mut self, mesh: MeshId, instances: &[MeshInstance]) {
self.recording.draw_mesh_instanced(mesh, instances);
}
fn add_light(&mut self, light: Light) {
self.recording.add_light(light);
}
fn set_environment(&mut self, env: &Environment) {
self.recording.set_environment(env);
}
fn billboard_draw(
&mut self,
size: Size,
transform: Mat4,
facing: BillboardFacing,
f: &mut dyn FnMut(&mut dyn DrawContext),
) {
self.recording.billboard_draw(size, transform, facing, f);
}
fn viewport_3d_draw(
&mut self,
rect: Rect,
camera: &Camera,
f: &mut dyn FnMut(&mut dyn DrawContext),
) {
self.recording.viewport_3d_draw(rect, camera, f);
}
fn push_layer(&mut self, config: LayerConfig) {
self.recording.push_layer(config);
}
fn pop_layer(&mut self) {
self.recording.pop_layer();
}
fn sample_layer(&mut self, id: LayerId, source_rect: Rect, dest_rect: Rect) {
self.recording.sample_layer(id, source_rect, dest_rect);
}
fn viewport_size(&self) -> Size {
self.recording.viewport_size()
}
fn is_3d_context(&self) -> bool {
self.recording.is_3d_context()
}
fn current_opacity(&self) -> f32 {
self.recording.current_opacity()
}
fn current_blend_mode(&self) -> BlendMode {
self.recording.current_blend_mode()
}
}
#[cfg(test)]
mod tests {
use super::*;
use blinc_core::Color;
#[test]
fn test_paint_context_creation() {
let ctx = PaintContext::new(800.0, 600.0);
assert_eq!(ctx.viewport_size(), Size::new(800.0, 600.0));
}
#[test]
fn test_fill_rect() {
let mut ctx = PaintContext::new(800.0, 600.0);
ctx.fill_rect_xywh(10.0, 20.0, 100.0, 50.0, Color::BLUE);
assert_eq!(ctx.commands().len(), 1);
}
#[test]
fn test_transform_convenience() {
let mut ctx = PaintContext::new(800.0, 600.0);
ctx.translate(10.0, 20.0);
ctx.fill_rect_xywh(0.0, 0.0, 100.0, 50.0, Color::RED);
ctx.pop_transform();
assert_eq!(ctx.commands().len(), 3);
}
#[test]
fn test_implements_draw_context() {
fn use_draw_context(ctx: &mut dyn DrawContext) {
ctx.fill_rect(
Rect::new(0.0, 0.0, 100.0, 50.0),
CornerRadius::default(),
Color::GREEN.into(),
);
}
let mut ctx = PaintContext::new(800.0, 600.0);
use_draw_context(&mut ctx);
assert_eq!(ctx.commands().len(), 1);
}
}