Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

39 changes: 35 additions & 4 deletions canvas/src/color.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,8 @@ use hsluv::*;
#[derive(Clone, Copy, PartialEq, Debug, Serialize, Deserialize)]
pub enum ColorFormat {
Rgba,
Hsluv
Hsluv,
Cmyka
}

///
Expand All @@ -15,7 +16,8 @@ pub enum ColorFormat {
#[derive(Clone, Copy, Debug, Serialize, Deserialize)]
pub enum Color {
Rgba(f32, f32, f32, f32),
Hsluv(f32, f32, f32, f32)
Hsluv(f32, f32, f32, f32),
Cmyka(f32, f32, f32, f32, f32)
}

impl PartialEq for Color {
Expand All @@ -40,11 +42,15 @@ impl Color {
pub fn to_rgba_components(&self) -> (f32, f32, f32, f32) {
match self {
&Color::Rgba(r, g, b, a) => (r, g, b, a),

&Color::Hsluv(h, s, l, a) => {
let (r, g, b) = hsluv_to_rgb((h as f64, s as f64, l as f64));
(r as f32, g as f32, b as f32, a)
}
&Color::Cmyka(c, m, y, k, a) => {
todo!("to_rgba_components");
(c, m, y, a)
}
}
}

Expand All @@ -60,6 +66,24 @@ impl Color {
let s = if l <= 0.0 { 100.0 } else { s };
(h as f32, s as f32, l as f32, a)
}
&Color::Cmyka(c, m, y, k, a) => {
todo!("to_hsluv_components");
(c, m, y, a)
}
}
}


///
/// Returns this colour as CMYKA components
///
pub fn to_cmyka_components(&self) -> (f32, f32, f32, f32, f32) {
match self {
&Color::Rgba(r, g, b, a) => { todo!("to_cmyka_components") }
&Color::Hsluv(h, s, l, a) => { todo!("to_cmyka_components") }
&Color::Cmyka(c, m, y, k, a) => {
(c, m, y, k, a)
}
}
}

Expand All @@ -70,6 +94,7 @@ impl Color {
match self {
Color::Hsluv(_, _, _, a) => *a,
Color::Rgba(_, _, _, a) => *a,
Color::Cmyka(_, _, _, _, a) => *a,
}
}

Expand All @@ -87,6 +112,11 @@ impl Color {
let s = if l <= 0.0 { 100.0 } else { s };
Color::Hsluv(h as f32, s as f32, l as f32, a)
}
ColorFormat::Cmyka => match self {
&Color::Rgba(x, y, z, a) => Color::Cmyka(x, y, z, 0.0, a),
&Color::Hsluv(x, y, z, a) => Color::Cmyka(x, y, z, 0.0, a),
&Color::Cmyka(_, _, _, _, _) => self.clone()
}
}
}

Expand All @@ -96,7 +126,8 @@ impl Color {
pub fn with_alpha(&self, new_alpha: f32) -> Color {
match self {
&Color::Rgba(r, g, b, _) => Color::Rgba(r, g, b, new_alpha),
&Color::Hsluv(h, s, l, _) => Color::Hsluv(h, s, l, new_alpha)
&Color::Hsluv(h, s, l, _) => Color::Hsluv(h, s, l, new_alpha),
&Color::Cmyka(c, m, y, k, _) => Color::Cmyka(c, m, y, k, new_alpha)
}
}
}
Expand Down
1 change: 1 addition & 0 deletions render_software/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,4 @@ rayon = { version = "1.7", optional = true }
[dev-dependencies]
flo_canvas = { version = "0.4", features = ["image-loading", "outline-fonts"] }
futures = "0.3"
tiff = "0.10"
150 changes: 150 additions & 0 deletions render_software/examples/cmyk_software_filters.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,150 @@
use flo_render_software::canvas::*;
use flo_render_software::render::*;

use flo_render_software::draw::{CanvasDrawing, CanvasDrawingRegionRenderer};
use flo_render_software::pixel::{F32CmykaPixel, F32LinearPixel, Pixel, U8RgbaPremultipliedPixel};
use flo_render_software::scanplan::ShardScanPlanner;
use std::f32;
use std::f32::consts::PI;
use std::fs::File;
use std::ops::{Add, Div, Mul};
use tiff::encoder::TiffEncoder;

fn draw_circles<P: Pixel<N>, const N: usize>(drawing: &mut CanvasDrawing<P, N>, circles: Vec<(f32, Color)>) {

let mut sprite = vec![];
sprite.sprite(SpriteId(1));
for (rotate, color) in circles.iter() {
let x = rotate.mul(PI).cos().mul(200.0);
let y = rotate.mul(PI).sin().mul(200.0);
sprite.new_path();
sprite.circle(x, y, 250.0);
sprite.fill_color(color.clone());
sprite.fill();
}
sprite.layer(LayerId(0));

// Create drawing instructions for the png
let mut canvas = vec![];

// Clear the canvas and set up the coordinates
canvas.canvas_height(1000.0);
canvas.center_region(0.0, 0.0, 2000.0, 1000.0);

canvas.layer(LayerId(0));
canvas.clear_layer();

drawing.draw(canvas);

drawing.draw(sprite.iter().cloned());

for part in 0..2 {
let max_scale = 0.5;
let min_scale = 0.1;
let iters = 100;
let div = iters as f32;
let cycles = 1.66;
let radius = 200.0;
for i in 0..iters {
let mut draw = vec![];
draw.layer(LayerId(i + 1));
let i = i as f32;
let scale = max_scale - (max_scale - min_scale).mul(i.div(div).powi(2));
let r = 200.0 + (radius / div) * i;
let rot = PI.div(div).mul(cycles).mul(i).mul(2.0);
let x = 500.0 + 1000.0 * part as f32 + rot.cos().mul(r);
let y = 500.0 + rot.sin().mul(r);
// println!("{scale} | {rot} | {r} | {x}, {y} ");
draw.sprite_transform(SpriteTransform::Identity);
draw.sprite_transform(SpriteTransform::Scale(scale, scale));
draw.sprite_transform(SpriteTransform::Rotate(rot.mul(1.66).to_degrees()));
draw.sprite_transform(SpriteTransform::Translate(x, y));
match part {
0 => draw.draw_sprite(SpriteId(1)),
_ => draw.draw_sprite_with_filters(SpriteId(1), vec![TextureFilter::GaussianBlur(20.0 + 30.0 * (1.0 - i / div).powi(2))]),
};
drawing.draw(draw);
}

}

}

///
/// Draws a bunch of circles in CMYK colorspace
///
pub fn main() {

let alpha = 0.7;
let width = 2000;
let height = 1000;

{

// Draw circles on a CMYK tiff

let mut canvas_drawing = CanvasDrawing::<F32CmykaPixel, 5>::empty();
canvas_drawing.draw(vec![Draw::ClearCanvas(Color::Cmyka(0.0, 0.0, 0.0, 0.0, 0.0))]);

let circles = vec![
(0.0, Color::Cmyka(1.0, 0.0, 0.0, 0.0, alpha)),
(0.5, Color::Cmyka(0.0, 1.0, 0.0, 0.0, alpha)),
(1.0, Color::Cmyka(0.0, 0.0, 1.0, 0.0, alpha)),
(1.5, Color::Cmyka(0.0, 0.0, 0.0, 1.0, alpha)),
];
draw_circles(&mut canvas_drawing, circles);

let renderer = CanvasDrawingRegionRenderer::new(
ShardScanPlanner::default(), ScanlineRenderer::new(canvas_drawing.program_runner(height as _)),
height
);

let renderer = F32CmykaFrameRenderer::new(renderer);
let frame_size = GammaFrameSize { width, height, gamma: 2.2 };
let mut pixel_data = vec![F32CmykaPixel::default(); width * height];

renderer.render(&frame_size, &canvas_drawing, pixel_data.as_mut_slice());

let file = File::create("circles_cmyk.tiff").unwrap();
let mut tiff_enc = TiffEncoder::new_big(file).unwrap();
let mut img_enc = tiff_enc.new_image::<tiff::encoder::colortype::CMYKA8>(width as u32, height as u32).unwrap();
let es: &[u8] = &[2u8];
img_enc.encoder().write_tag(tiff::tags::Tag::ExtraSamples, es).unwrap();

let pixel_data = pixel_data.iter().map(|p| p.to_u8()).flatten().collect::<Vec<_>>();
img_enc.write_data(&pixel_data).unwrap();

}

#[cfg(feature="render_png")]
{

// Draw same? circles in RGB for comparison

let mut canvas_drawing = CanvasDrawing::<F32LinearPixel, 4>::empty();
canvas_drawing.draw(vec![Draw::ClearCanvas(Color::Rgba(0.0, 0.0, 0.0, 0.0))]);

let circles = vec![
(0.0, Color::Rgba(0.0, 1.0, 1.0, alpha)),
(0.5, Color::Rgba(1.0, 0.0, 1.0, alpha)),
(1.0, Color::Rgba(1.0, 1.0, 0.0, alpha)),
(1.5, Color::Rgba(0.0, 0.0, 0.0, alpha)),
];
draw_circles(&mut canvas_drawing, circles);

let renderer = CanvasDrawingRegionRenderer::new(
ShardScanPlanner::default(), ScanlineRenderer::new(canvas_drawing.program_runner(height as _)),
height
);

let mut png_data: Vec<u8> = vec![];
{
let mut png_render = PngRenderTarget::from_stream(&mut png_data, width, height, 2.2);
png_render.render(renderer, &canvas_drawing);
}

std::fs::write("circles_rgb.png", png_data).unwrap();

}

}
96 changes: 96 additions & 0 deletions render_software/examples/cmyk_software_gradient.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
use flo_render_software::render::*;
use flo_render_software::canvas::*;

use std::f32;
use std::fs::File;
use std::path::PathBuf;
use flo_render_software::draw::{CanvasDrawing, CanvasDrawingRegionRenderer};
use flo_render_software::pixel::{F32CmykaPixel, F32LinearPixel, Pixel, ToRgbaPremultipliedPixels};
use flo_render_software::scanplan::ShardScanPlanner;
use tiff::encoder::TiffEncoder;

///
/// Draws a simple linear gradient
///
pub fn main() {
// Create drawing instructions for the png
let mut canvas = vec![];

let angle = (30.0 / 360.0) * (2.0 * f32::consts::PI);

// Clear the canvas and set up the coordinates
canvas.clear_canvas(Color::Cmyka(0.0, 0.0, 0.0, 0.0, 1.0));
canvas.canvas_height(1000.0);
canvas.center_region(0.0, 0.0, 1000.0, 1000.0);

canvas.layer(LayerId(0));
canvas.clear_layer();

// Set up the canvas
canvas.canvas_height(1000.0);
canvas.center_region(0.0, 0.0, 1000.0, 1000.0);

// Set up a gradient
canvas.create_gradient(GradientId(1), Color::Cmyka(0.8, 0.0, 0.0, 0.0, 1.0));
canvas.gradient_stop(GradientId(1), 0.33, Color::Cmyka(0.3, 0.8, 0.0, 0.0, 1.0));
canvas.gradient_stop(GradientId(1), 0.66, Color::Cmyka(0.0, 0.3, 0.8, 0.9, 1.0));
canvas.gradient_stop(GradientId(1), 1.0, Color::Cmyka(0.6, 0.3, 0.9, 0.0, 1.0));

let x1 = 500.0 - 300.0 * f32::cos(angle);
let y1 = 500.0 - 300.0 * f32::sin(angle);
let x2 = 500.0 + 300.0 * f32::cos(angle);
let y2 = 500.0 + 300.0 * f32::sin(angle);

// Draw a circle using the gradient
canvas.new_path();
canvas.circle(500.0, 500.0, 250.0);
canvas.fill_gradient(GradientId(1), x1, y1, x2, y2);
canvas.fill();

canvas.line_width(4.0);
canvas.stroke_color(Color::Cmyka(0.0, 0.0, 0.0, 1.0, 1.0));
canvas.stroke();

// Draw indicators where the gradient is moving between
canvas.line_width(1.0);

canvas.new_path();
canvas.circle(x1, y1, 8.0);
canvas.stroke();

canvas.new_path();
canvas.circle(x2, y2, 8.0);
canvas.stroke();

// Render to the terminal window
// render_drawing(&mut TerminalRenderTarget::new(1920, 1080), canvas.iter().cloned());

let mut canvas_drawing = CanvasDrawing::<F32CmykaPixel, 5>::empty();
canvas_drawing.draw(canvas);

let width = 1920;
let height = 1080;

let renderer = CanvasDrawingRegionRenderer::new(ShardScanPlanner::default(), ScanlineRenderer::new(canvas_drawing.program_runner(height as _)), height);

let renderer = F32CmykaFrameRenderer::new(renderer);
let frame_size = GammaFrameSize { width, height, gamma: 2.2 };
let mut pixel_data = vec![F32CmykaPixel::default(); width * height];

renderer.render(&frame_size, &canvas_drawing, pixel_data.as_mut_slice());
println!("wat {}", pixel_data.iter().flat_map(|c| c.to_cmyk()).reduce(f32::max).unwrap());

let file = File::create("cmyk_software_gradient.tiff").unwrap();
let mut tiff_enc = TiffEncoder::new_big(file).unwrap();
let mut img_enc = tiff_enc.new_image::<tiff::encoder::colortype::CMYKA8>(width as u32, height as u32).unwrap();
let es: &[u8] = &[2u8];
img_enc.encoder().write_tag(tiff::tags::Tag::ExtraSamples, es).unwrap();

let pixel_data = pixel_data.iter().map(|p| p.to_u8()).flatten().collect::<Vec<_>>();
img_enc.write_data(&pixel_data).unwrap();

// render_drawing(&mut renderer, canvas.iter().clone())

// Send the buffer to the png file
// self.writer.write_image_data(&pixel_data).unwrap();
}
Loading