Skip to content

Commit fc57dbb

Browse files
committed
Implement image processing functions
1 parent 22e25f5 commit fc57dbb

File tree

6 files changed

+174
-80
lines changed

6 files changed

+174
-80
lines changed

src/functions/image.rs

Lines changed: 54 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -95,18 +95,6 @@ builtin_function!(blur => {
9595
}
9696
});
9797

98-
builtin_function!(crop => {
99-
[Value::Integer(x), Value::Integer(y), Value::Integer(width), Value::Integer(height), Value::Shape(image)] => {
100-
if *x < 0 || *y < 0 || *width < 0 || *height < 0 {
101-
return Err(Error::NegativeNumber);
102-
}
103-
104-
let image = dedup_shape(image);
105-
image.borrow_mut().add_image_op(ImageOp::Crop(*x as u32, *y as u32, *width as u32, *height as u32));
106-
Value::Shape(image)
107-
}
108-
});
109-
11098
builtin_function!(fast_blur => {
11199
[sigma, Value::Shape(image)] => {
112100
let sigma = match sigma {
@@ -121,6 +109,18 @@ builtin_function!(fast_blur => {
121109
}
122110
});
123111

112+
builtin_function!(crop => {
113+
[Value::Integer(x), Value::Integer(y), Value::Integer(width), Value::Integer(height), Value::Shape(image)] => {
114+
if *x < 0 || *y < 0 || *width < 0 || *height < 0 {
115+
return Err(Error::NegativeNumber);
116+
}
117+
118+
let image = dedup_shape(image);
119+
image.borrow_mut().add_image_op(ImageOp::Crop(*x as u32, *y as u32, *width as u32, *height as u32));
120+
Value::Shape(image)
121+
}
122+
});
123+
124124
builtin_function!(filter3x3 => {
125125
[Value::List(kernel), Value::Shape(image)] => {
126126
if kernel.len() != 9 {
@@ -138,64 +138,73 @@ builtin_function!(filter3x3 => {
138138
}
139139
});
140140

141-
builtin_function!(gradienth => {
142-
[Value::Hex(start_color), Value::Integer(start_alpha), Value::Hex(end_color), Value::Integer(end_alpha), Value::Shape(image)] => {
143-
let start = [start_color[0], start_color[1], start_color[2], (*start_alpha).clamp(0, 255) as u8];
144-
let end = [end_color[0], end_color[1], end_color[2], (*end_alpha).clamp(0, 255) as u8];
145-
141+
builtin_function!(fliph_image => {
142+
[Value::Shape(image)] => {
146143
let image = dedup_shape(image);
147-
image.borrow_mut().add_image_op(ImageOp::HorizontalGradient(start, end));
144+
image.borrow_mut().add_image_op(ImageOp::FlipHorizontal);
148145
Value::Shape(image)
149146
}
150147
});
151148

152-
builtin_function!(gradientv => {
153-
[Value::Hex(start_color), Value::Integer(start_alpha), Value::Hex(end_color), Value::Integer(end_alpha), Value::Shape(image)] => {
154-
let start = [start_color[0], start_color[1], start_color[2], (*start_alpha).clamp(0, 255) as u8];
155-
let end = [end_color[0], end_color[1], end_color[2], (*end_alpha).clamp(0, 255) as u8];
149+
builtin_function!(flipv_image => {
150+
[Value::Shape(image)] => {
151+
let image = dedup_shape(image);
152+
image.borrow_mut().add_image_op(ImageOp::FlipVertical);
153+
Value::Shape(image)
154+
}
155+
});
156156

157+
builtin_function!(flipd_image => {
158+
[Value::Shape(image)] => {
157159
let image = dedup_shape(image);
158-
image.borrow_mut().add_image_op(ImageOp::VerticalGradient(start, end));
160+
image.borrow_mut().add_image_op(ImageOp::FlipHorizontal);
161+
image.borrow_mut().add_image_op(ImageOp::FlipVertical);
159162
Value::Shape(image)
160163
}
161164
});
162165

163-
builtin_function!(interpolate_bilinear => {
164-
[x, y, Value::Shape(image)] => {
165-
let x = match x {
166-
Value::Integer(x) => *x as f32,
167-
Value::Float(x) => *x,
168-
_ => return Err(Error::InvalidArgument("interpolate_bilinear".into())),
166+
builtin_function!(gradienth => {
167+
[Value::Hex(start_color), start_alpha, Value::Hex(end_color), end_alpha, Value::Shape(image)] => {
168+
let start_alpha = match start_alpha {
169+
Value::Integer(start_alpha) => *start_alpha as f32,
170+
Value::Float(start_alpha) => *start_alpha,
171+
_ => return Err(Error::InvalidArgument("gradienth".into())),
169172
};
170173

171-
let y = match y {
172-
Value::Integer(y) => *y as f32,
173-
Value::Float(y) => *y,
174-
_ => return Err(Error::InvalidArgument("interpolate_bilinear".into())),
174+
let end_alpha = match end_alpha {
175+
Value::Integer(end_alpha) => *end_alpha as f32,
176+
Value::Float(end_alpha) => *end_alpha,
177+
_ => return Err(Error::InvalidArgument("gradienth".into())),
175178
};
176179

180+
let start = [start_color[0], start_color[1], start_color[2], (start_alpha * 255.0).clamp(0.0, 255.0) as u8];
181+
let end = [end_color[0], end_color[1], end_color[2], (end_alpha * 255.0).clamp(0.0, 255.0) as u8];
182+
177183
let image = dedup_shape(image);
178-
image.borrow_mut().add_image_op(ImageOp::InterpolateBilinear(x, y));
184+
image.borrow_mut().add_image_op(ImageOp::HorizontalGradient(start, end));
179185
Value::Shape(image)
180186
}
181187
});
182188

183-
builtin_function!(interpolate_nearest => {
184-
[x, y, Value::Shape(image)] => {
185-
let x = match x {
186-
Value::Integer(x) => *x as f32,
187-
Value::Float(x) => *x,
188-
_ => return Err(Error::InvalidArgument("interpolate_nearest".into())),
189+
builtin_function!(gradientv => {
190+
[Value::Hex(start_color), start_alpha, Value::Hex(end_color), end_alpha, Value::Shape(image)] => {
191+
let start_alpha = match start_alpha {
192+
Value::Integer(start_alpha) => *start_alpha as f32,
193+
Value::Float(start_alpha) => *start_alpha,
194+
_ => return Err(Error::InvalidArgument("gradientv".into())),
189195
};
190196

191-
let y = match y {
192-
Value::Integer(y) => *y as f32,
193-
Value::Float(y) => *y,
194-
_ => return Err(Error::InvalidArgument("interpolate_nearest".into())),
197+
let end_alpha = match end_alpha {
198+
Value::Integer(end_alpha) => *end_alpha as f32,
199+
Value::Float(end_alpha) => *end_alpha,
200+
_ => return Err(Error::InvalidArgument("gradientv".into())),
195201
};
196202

203+
let start = [start_color[0], start_color[1], start_color[2], (start_alpha * 255.0).clamp(0.0, 255.0) as u8];
204+
let end = [end_color[0], end_color[1], end_color[2], (end_alpha * 255.0).clamp(0.0, 255.0) as u8];
205+
197206
let image = dedup_shape(image);
198-
image.borrow_mut().add_image_op(ImageOp::InterpolateNearest(x, y));
207+
image.borrow_mut().add_image_op(ImageOp::VerticalGradient(start, end));
199208
Value::Shape(image)
200209
}
201210
});

src/functions/mod.rs

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -311,13 +311,14 @@ define_builtins! {
311311
"huerotate" => {image::huerotate, 2},
312312
"invert" => {image::invert, 1},
313313
"blur" => {image::blur, 2},
314-
"crop" => {image::crop, 5},
315314
"fast_blur" => {image::fast_blur, 2},
315+
"crop" => {image::crop, 5},
316316
"filter3x3" => {image::filter3x3, 2},
317+
"fliph_image" => {image::fliph_image, 1},
318+
"flipv_image" => {image::flipv_image, 1},
319+
"flipd_image" => {image::flipd_image, 1},
317320
"gradienth" => {image::gradienth, 5},
318321
"gradientv" => {image::gradientv, 5},
319-
"interpolate_bilinear" => {image::interpolate_bilinear, 3},
320-
"interpolate_nearest" => {image::interpolate_nearest, 3},
321322
"overlay" => {image::overlay, 4},
322323
"replace" => {image::replace, 4},
323324
"resize" => {image::resize, 4},

src/interpreter.rs

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -233,6 +233,8 @@ fn reduce_literal(literal: &Literal) -> Result<Value> {
233233
Literal::LineCap(lc) => Ok(Value::LineCap(*lc)),
234234
Literal::LineJoin(lj) => Ok(Value::LineJoin(*lj)),
235235
Literal::SpreadMode(sm) => Ok(Value::SpreadMode(*sm)),
236+
Literal::FilterQuality(fq) => Ok(Value::FilterQuality(*fq)),
237+
Literal::FilterType(ft) => Ok(Value::FilterType(*ft)),
236238
}
237239
}
238240

src/parser.rs

Lines changed: 47 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ use alloc::{
99
use crate::colors::color;
1010

1111
use core::str::FromStr;
12+
use image::imageops::FilterType;
1213
use nom::branch::alt;
1314
use nom::bytes::complete::{tag, take_while_m_n};
1415
use nom::character::complete::{
@@ -21,7 +22,7 @@ use nom::multi::{many0, many1, separated_list0, separated_list1};
2122
use nom::sequence::{delimited, preceded, terminated};
2223
use nom::{Err, IResult, Parser};
2324
use num::Complex;
24-
use tiny_skia::{BlendMode, LineCap, LineJoin, SpreadMode};
25+
use tiny_skia::{BlendMode, FilterQuality, LineCap, LineJoin, SpreadMode};
2526

2627
const KEYWORDS: &[&str] = &["let", "if", "else", "match", "for", "loop"];
2728

@@ -60,6 +61,8 @@ pub enum Literal {
6061
LineCap(LineCap),
6162
LineJoin(LineJoin),
6263
SpreadMode(SpreadMode),
64+
FilterQuality(FilterQuality),
65+
FilterType(FilterType),
6366
}
6467

6568
impl ToString for Literal {
@@ -120,6 +123,18 @@ impl ToString for Literal {
120123
SpreadMode::Reflect => "SPREAD_MODE_REFLECT".into(),
121124
SpreadMode::Repeat => "SPREAD_MODE_REPEAT".into(),
122125
},
126+
Literal::FilterQuality(fq) => match fq {
127+
FilterQuality::Nearest => "QUALITY_NEAREST".into(),
128+
FilterQuality::Bilinear => "QUALITY_BILINEAR".into(),
129+
FilterQuality::Bicubic => "QUALITY_BICUBIC".into(),
130+
},
131+
Literal::FilterType(ft) => match ft {
132+
FilterType::Nearest => "FILTER_NEAREST".into(),
133+
FilterType::Triangle => "FILTER_TRIANGLE".into(),
134+
FilterType::CatmullRom => "FILTER_CATMULL_ROM".into(),
135+
FilterType::Gaussian => "FILTER_GAUSSIAN".into(),
136+
FilterType::Lanczos3 => "FILTER_LANCZOS3".into(),
137+
},
123138
}
124139
}
125140
}
@@ -505,6 +520,32 @@ fn spread_mode(input: &str) -> IResult<&str, Literal> {
505520
.parse(input)
506521
}
507522

523+
fn filter_quality(input: &str) -> IResult<&str, Literal> {
524+
map(
525+
alt((
526+
value(FilterQuality::Nearest, tag("QUALITY_NEAREST")),
527+
value(FilterQuality::Bilinear, tag("QUALITY_BILINEAR")),
528+
value(FilterQuality::Bicubic, tag("QUALITY_BICUBIC")),
529+
)),
530+
Literal::FilterQuality,
531+
)
532+
.parse(input)
533+
}
534+
535+
fn filter_type(input: &str) -> IResult<&str, Literal> {
536+
map(
537+
alt((
538+
value(FilterType::Nearest, tag("FILTER_NEAREST")),
539+
value(FilterType::Triangle, tag("FILTER_TRIANGLE")),
540+
value(FilterType::CatmullRom, tag("FILTER_CATMULL_ROM")),
541+
value(FilterType::Gaussian, tag("FILTER_GAUSSIAN")),
542+
value(FilterType::Lanczos3, tag("FILTER_LANCZOS3")),
543+
)),
544+
Literal::FilterType,
545+
)
546+
.parse(input)
547+
}
548+
508549
fn literal(input: &str) -> IResult<&str, Literal> {
509550
alt((
510551
hex,
@@ -513,13 +554,15 @@ fn literal(input: &str) -> IResult<&str, Literal> {
513554
integer,
514555
boolean,
515556
shape,
557+
hex_color,
558+
character,
559+
string,
516560
blend_mode,
517561
line_cap,
518562
line_join,
519563
spread_mode,
520-
hex_color,
521-
character,
522-
string,
564+
filter_quality,
565+
filter_type,
523566
))
524567
.parse(input)
525568
}

src/renderer.rs

Lines changed: 67 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@ use crate::shape::{
1111
};
1212

1313
use core::{cell::RefCell, ops::Add};
14-
use image::{ImageFormat, ImageReader};
14+
use image::{imageops, DynamicImage, ImageFormat, ImageReader, Pixel};
1515
use palette::{rgb::Rgba, FromColor};
1616
use tiny_skia::{
1717
BlendMode, FillRule, GradientStop, IntSize, LinearGradient, Mask, MaskType, Paint, Path,
@@ -775,31 +775,72 @@ fn render_to_pixmap(shape_data: ShapeData, pixmap: &mut Pixmap, width: u32, heig
775775
for op in ops {
776776
match op {
777777
ImageOp::Brighten(value) => image = image.brighten(value),
778-
// Contrast(f32),
779-
// Grayscale,
780-
// GrayscaleAlpha,
781-
// Huerotate(i32),
782-
// Invert,
783-
// Blur(f32),
784-
// Crop(u32, u32, u32, u32),
785-
// FastBlur(f32),
786-
// Filter3x3(Vec<f32>),
787-
// FlipHorizontal,
788-
// FlipVertical,
789-
// HorizontalGradient([u8; 4], [u8; 4]),
790-
// VerticalGradient([u8; 4], [u8; 4]),
791-
// InterpolateBilinear(f32, f32),
792-
// InterpolateNearest(f32, f32),
793-
// Overlay(Rc<RefCell<Shape>>, i64, i64),
794-
// Replace(Rc<RefCell<Shape>>, i64, i64),
795-
// Resize(u32, u32, FilterType),
796-
// Rotate90,
797-
// Rotate180,
798-
// Rotate270,
799-
// Thumbnail(u32, u32),
800-
// Tile(Rc<RefCell<Shape>>),
801-
// Unsharpen(f32, i32),
802-
_ => todo!(),
778+
ImageOp::Contrast(c) => image = image.adjust_contrast(c),
779+
ImageOp::Grayscale => image = image.grayscale().into_rgba8().into(),
780+
ImageOp::GrayscaleAlpha => {
781+
image = imageops::colorops::grayscale_alpha(&image).into();
782+
image = image.into_rgba8().into();
783+
}
784+
ImageOp::Huerotate(value) => image = image.huerotate(value),
785+
ImageOp::Invert => image.invert(),
786+
ImageOp::Blur(sigma) => image = image.blur(sigma),
787+
ImageOp::FastBlur(sigma) => image = image.fast_blur(sigma),
788+
ImageOp::Crop(x, y, width, height) => {
789+
image = image.crop_imm(x, y, width, height);
790+
}
791+
ImageOp::Filter3x3(kernel) => image = image.filter3x3(&kernel),
792+
ImageOp::FlipHorizontal => image = image.fliph(),
793+
ImageOp::FlipVertical => image = image.flipv(),
794+
ImageOp::HorizontalGradient(start, end) => imageops::horizontal_gradient(
795+
&mut image,
796+
image::Rgba::from_slice(&start),
797+
image::Rgba::from_slice(&end),
798+
),
799+
ImageOp::VerticalGradient(start, end) => imageops::vertical_gradient(
800+
&mut image,
801+
image::Rgba::from_slice(&start),
802+
image::Rgba::from_slice(&end),
803+
),
804+
ImageOp::Overlay(top, x, y) => {
805+
let top = render(top, width, height).unwrap();
806+
let top = ImageReader::with_format(
807+
Cursor::new(top.encode_png().unwrap()),
808+
ImageFormat::Png,
809+
)
810+
.decode()
811+
.unwrap();
812+
imageops::overlay(&mut image, &top, x, y);
813+
}
814+
ImageOp::Replace(top, x, y) => {
815+
let top = render(top, width, height).unwrap();
816+
let top = ImageReader::with_format(
817+
Cursor::new(top.encode_png().unwrap()),
818+
ImageFormat::Png,
819+
)
820+
.decode()
821+
.unwrap();
822+
imageops::replace(&mut image, &top, x, y);
823+
}
824+
ImageOp::Resize(width, height, filter) => {
825+
image = image.resize(width, height, filter);
826+
}
827+
ImageOp::Rotate90 => image = image.rotate90(),
828+
ImageOp::Rotate180 => image = image.rotate180(),
829+
ImageOp::Rotate270 => image = image.rotate270(),
830+
ImageOp::Thumbnail(width, height) => image = image.thumbnail(width, height),
831+
ImageOp::Tile(top) => {
832+
let top = render(top, width, height).unwrap();
833+
let top = ImageReader::with_format(
834+
Cursor::new(top.encode_png().unwrap()),
835+
ImageFormat::Png,
836+
)
837+
.decode()
838+
.unwrap();
839+
imageops::tile(&mut image, &top);
840+
}
841+
ImageOp::Unsharpen(sigma, threshold) => {
842+
image = image.unsharpen(sigma, threshold)
843+
}
803844
}
804845
}
805846

src/shape.rs

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -232,8 +232,6 @@ pub enum ImageOp {
232232
FlipHorizontal,
233233
FlipVertical,
234234
HorizontalGradient([u8; 4], [u8; 4]),
235-
InterpolateBilinear(f32, f32),
236-
InterpolateNearest(f32, f32),
237235
Overlay(Rc<RefCell<Shape>>, i64, i64),
238236
Replace(Rc<RefCell<Shape>>, i64, i64),
239237
Resize(u32, u32, FilterType),

0 commit comments

Comments
 (0)