1 use crate::rasterizer::Rasterizer;
2
3 use crate::blitter::*;
4 use sw_composite::*;
5
6 use crate::dash::*;
7 use crate::geom::*;
8 use crate::path_builder::*;
9
10 pub use crate::path_builder::Winding;
11 use lyon_geom::cubic_to_quadratic::cubic_to_quadratics;
12 use lyon_geom::CubicBezierSegment;
13
14 #[cfg(feature = "text")]
15 mod fk {
16 pub use font_kit::canvas::{Canvas, Format, RasterizationOptions};
17 pub use font_kit::font::Font;
18 pub use font_kit::hinting::HintingOptions;
19 pub use font_kit::loader::FontTransform;
20 }
21
22 use std::fs::*;
23 use std::io::BufWriter;
24
25 use crate::stroke::*;
26 use crate::{IntRect, IntPoint, Point, Transform, Vector};
27
28 use euclid::vec2;
29
30 #[derive(Clone)]
31 pub struct Mask {
32 pub width: i32,
33 pub height: i32,
34 pub data: Vec<u8>,
35 }
36
37 /// A premultiplied color. i.e. r,b,g <= a
38 #[derive(Clone, Copy, PartialEq, Debug)]
39 pub struct SolidSource {
40 pub r: u8,
41 pub g: u8,
42 pub b: u8,
43 pub a: u8,
44 }
45
46 impl SolidSource {
to_u32(&self) -> u3247 pub fn to_u32(&self) -> u32 {
48 let color = ((self.a as u32) << 24)
49 | ((self.r as u32) << 16)
50 | ((self.g as u32) << 8)
51 | ((self.b as u32) << 0);
52 color
53 }
54
from_unpremultiplied_argb(a: u8, r: u8, g: u8, b: u8) -> Self55 pub fn from_unpremultiplied_argb(a: u8, r: u8, g: u8, b: u8) -> Self {
56 SolidSource {
57 a: a,
58 r: muldiv255(a as u32, r as u32) as u8,
59 g: muldiv255(a as u32, g as u32) as u8,
60 b: muldiv255(a as u32, b as u32) as u8
61 }
62 }
63 }
64
65 #[derive(PartialEq, Clone, Copy, Debug)]
66 pub enum BlendMode {
67 Dst,
68 Src,
69 Clear,
70 SrcOver,
71 DstOver,
72 SrcIn,
73 DstIn,
74 SrcOut,
75 DstOut,
76 SrcAtop,
77 DstAtop,
78 Xor,
79 Add,
80
81 Screen,
82 Overlay,
83 Darken,
84 Lighten,
85 ColorDodge,
86 ColorBurn,
87 HardLight,
88 SoftLight,
89 Difference,
90 Exclusion,
91 Multiply,
92 Hue,
93 Saturation,
94 Color,
95 Luminosity
96 }
97
98 trait Blender {
99 type Output;
build<T: blend::Blend>() -> Self::Output100 fn build<T: blend::Blend>() -> Self::Output;
101 }
102
103 struct BlendRow;
104
blend_row<T: blend::Blend>(src: &[u32], dst: &mut [u32])105 fn blend_row<T: blend::Blend>(src: &[u32], dst: &mut [u32]) {
106 for (dst, src) in dst.iter_mut().zip(src) {
107 *dst = T::blend(*src, *dst);
108 }
109 }
110
111 impl Blender for BlendRow {
112 type Output = fn(&[u32], &mut [u32]);
build<T: blend::Blend>() -> Self::Output113 fn build<T: blend::Blend>() -> Self::Output {
114 blend_row::<T>
115 }
116 }
117
118 struct BlendRowMask;
119
blend_row_mask<T: blend::Blend>(src: &[u32], mask: &[u8], dst: &mut [u32])120 fn blend_row_mask<T: blend::Blend>(src: &[u32], mask: &[u8], dst: &mut [u32]) {
121 for ((dst, src), mask) in dst.iter_mut().zip(src).zip(mask) {
122 *dst = lerp(
123 *dst,
124 T::blend(*src, *dst),
125 alpha_to_alpha256(*mask as u32),
126 );
127 }
128 }
129
130 impl Blender for BlendRowMask {
131 type Output = fn(&[u32], &[u8], &mut [u32]);
build<T: blend::Blend>() -> Self::Output132 fn build<T: blend::Blend>() -> Self::Output {
133 blend_row_mask::<T>
134 }
135 }
136
137 struct BlendRowMaskClip;
138
blend_row_mask_clip<T: blend::Blend>(src: &[u32], mask: &[u8], clip: &[u8], dst: &mut [u32])139 fn blend_row_mask_clip<T: blend::Blend>(src: &[u32], mask: &[u8], clip: &[u8], dst: &mut [u32]) {
140 for (((dst, src), mask), clip) in dst.iter_mut().zip(src).zip(mask).zip(clip) {
141 *dst = alpha_lerp(
142 *dst,
143 T::blend(*src, *dst),
144 *mask as u32,
145 *clip as u32
146 );
147 }
148 }
149
150 impl Blender for BlendRowMaskClip {
151 type Output = fn(&[u32], &[u8], &[u8], &mut [u32]);
build<T: blend::Blend>() -> Self::Output152 fn build<T: blend::Blend>() -> Self::Output {
153 blend_row_mask_clip::<T>
154 }
155 }
156
build_blend_proc<T: Blender>(mode: BlendMode) -> T::Output157 fn build_blend_proc<T: Blender>(mode: BlendMode) -> T::Output {
158 use sw_composite::blend::*;
159 match mode {
160 BlendMode::Dst => T::build::<Dst>(),
161 BlendMode::Src => T::build::<Src>(),
162 BlendMode::Clear => T::build::<Clear>(),
163 BlendMode::SrcOver => T::build::<SrcOver>(),
164 BlendMode::DstOver => T::build::<DstOver>(),
165 BlendMode::SrcIn => T::build::<SrcIn>(),
166 BlendMode::DstIn => T::build::<DstIn>(),
167 BlendMode::SrcOut => T::build::<SrcOut>(),
168 BlendMode::DstOut => T::build::<DstOut>(),
169 BlendMode::SrcAtop => T::build::<SrcAtop>(),
170 BlendMode::DstAtop => T::build::<DstAtop>(),
171 BlendMode::Xor => T::build::<Xor>(),
172 BlendMode::Add => T::build::<Add>(),
173 BlendMode::Screen => T::build::<Screen>(),
174 BlendMode::Overlay => T::build::<Overlay>(),
175 BlendMode::Darken => T::build::<Darken>(),
176 BlendMode::Lighten => T::build::<Lighten>(),
177 BlendMode::ColorDodge => T::build::<ColorDodge>(),
178 BlendMode::ColorBurn => T::build::<ColorBurn>(),
179 BlendMode::HardLight => T::build::<HardLight>(),
180 BlendMode::SoftLight => T::build::<SoftLight>(),
181 BlendMode::Difference => T::build::<Difference>(),
182 BlendMode::Exclusion => T::build::<Exclusion>(),
183 BlendMode::Multiply => T::build::<Multiply>(),
184 BlendMode::Hue => T::build::<Hue>(),
185 BlendMode::Saturation => T::build::<Saturation>(),
186 BlendMode::Color => T::build::<Color>(),
187 BlendMode::Luminosity => T::build::<Luminosity>(),
188 }
189 }
190
191 #[derive(Copy, Clone)]
192 pub enum ExtendMode {
193 Pad,
194 Repeat
195 }
196
197 #[derive(Copy, Clone, PartialEq)]
198 pub enum FilterMode {
199 Bilinear,
200 Nearest
201 }
202
203 /// LinearGradients have an implicit start point at 0,0 and an end point at 256,0. The transform
204 /// parameter can be used to adjust them to the desired location.
205 /// RadialGradients have an implict center at 0,0 and a radius of 128.
206 /// The helper functions: `new_linear_gradient`, `new_radial_gradient` and `new_two_circle_radial_gradient`
207 /// allow the gradients to be constructed with easier to understand inputs.
208 /// The `transform` parameter maps user space to source space. This means that setting the same transform
209 /// on the draw target as the source will have the effect of canceling out.
210 ///
211 /// These locations are an artifact of the blitter implementation and will probably change in the
212 /// future to become more ergonomic.
213 #[derive(Clone)]
214 pub enum Source<'a> {
215 Solid(SolidSource),
216 Image(Image<'a>, ExtendMode, FilterMode, Transform),
217 RadialGradient(Gradient, Spread, Transform),
218 TwoCircleRadialGradient(Gradient, Spread, Point, f32, Point, f32, Transform),
219 LinearGradient(Gradient, Spread, Transform),
220 }
221
222 impl<'a> Source<'a> {
223 /// Creates a new linear gradient source where the start point corresponds to the gradient
224 /// stop at position = 0 and the end point corresponds to the gradient stop at position = 1.
new_linear_gradient(gradient: Gradient, start: Point, end: Point, spread: Spread) -> Source<'a>225 pub fn new_linear_gradient(gradient: Gradient, start: Point, end: Point, spread: Spread) -> Source<'a> {
226 let gradient_vector = Vector::new(end.x - start.x, end.y - start.y);
227 // Get length of desired gradient vector
228 let length = gradient_vector.length();
229 if length != 0. {
230 let gradient_vector = gradient_vector.normalize();
231
232 let sin = gradient_vector.y;
233 let cos = gradient_vector.x;
234 // Build up a rotation matrix from our vector
235 let mat = Transform::row_major(cos, -sin, sin, cos, 0., 0.);
236
237 // Adjust for the start point
238 let mat = mat.pre_translate(vec2(-start.x, -start.y));
239
240 // Scale gradient to desired length
241 let mat = mat.post_scale(1. / length, 1. / length);
242 Source::LinearGradient(gradient, spread, mat)
243 } else {
244 // use some degenerate matrix
245 Source::LinearGradient(gradient, spread, Transform::create_scale(0., 0.))
246 }
247 }
248
249 /// Creates a new radial gradient that is centered at the given point and has the given radius.
new_radial_gradient(gradient: Gradient, center: Point, radius: f32, spread: Spread) -> Source<'a>250 pub fn new_radial_gradient(gradient: Gradient, center: Point, radius: f32, spread: Spread) -> Source<'a> {
251 // Scale gradient to desired radius
252 let scale = Transform::create_scale(radius, radius);
253 // Transform gradient to center of gradient
254 let translate = Transform::create_translation(center.x, center.y);
255 // Compute final transform
256 let transform = translate.pre_transform(&scale).inverse().unwrap();
257
258 Source::RadialGradient(gradient, spread, transform)
259 }
260
261 /// Creates a new radial gradient that is centered at the given point and has the given radius.
new_two_circle_radial_gradient(gradient: Gradient, center1: Point, radius1: f32, center2: Point, radius2: f32, spread: Spread) -> Source<'a>262 pub fn new_two_circle_radial_gradient(gradient: Gradient, center1: Point, radius1: f32, center2: Point, radius2: f32, spread: Spread) -> Source<'a> {
263 let transform = Transform::identity();
264 Source::TwoCircleRadialGradient(gradient, spread, center1, radius1, center2, radius2, transform)
265 }
266 }
267
268 #[derive(PartialEq, Clone, Copy, Debug)]
269 pub enum AntialiasMode {
270 None,
271 Gray,
272 }
273
274 #[derive(PartialEq, Clone, Copy, Debug)]
275 pub struct DrawOptions {
276 pub blend_mode: BlendMode,
277 pub alpha: f32,
278 pub antialias: AntialiasMode,
279 }
280
281 impl DrawOptions {
new() -> Self282 pub fn new() -> Self {
283 Default::default()
284 }
285 }
286
287 impl Default for DrawOptions {
default() -> Self288 fn default() -> Self {
289 DrawOptions {
290 blend_mode: BlendMode::SrcOver,
291 alpha: 1.,
292 antialias: AntialiasMode::Gray,
293 }
294 }
295 }
296
297 #[derive(Clone)]
298 struct Clip {
299 rect: IntRect,
300 mask: Option<Vec<u8>>,
301 }
302
303 #[derive(Clone)]
304 struct Layer {
305 buf: Vec<u32>,
306 opacity: f32,
307 rect: IntRect,
308 blend: BlendMode,
309 }
310
scaled_tolerance(x: f32, trans: &Transform) -> f32311 fn scaled_tolerance(x: f32, trans: &Transform) -> f32 {
312 // The absolute value of the determinant is the area parallelogram
313 // Take the sqrt of the area to losily convert to one dimension
314 x / trans.determinant().abs().sqrt()
315 }
316
317
318
319 /// The main type used for drawing
320 pub struct DrawTarget {
321 width: i32,
322 height: i32,
323 rasterizer: Rasterizer,
324 current_point: Option<Point>,
325 first_point: Option<Point>,
326 buf: Vec<u32>,
327 clip_stack: Vec<Clip>,
328 layer_stack: Vec<Layer>,
329 transform: Transform,
330 }
331
332 impl DrawTarget {
new(width: i32, height: i32) -> DrawTarget333 pub fn new(width: i32, height: i32) -> DrawTarget {
334 DrawTarget {
335 width,
336 height,
337 current_point: None,
338 first_point: None,
339 rasterizer: Rasterizer::new(width, height),
340 buf: vec![0; (width * height) as usize],
341 clip_stack: Vec::new(),
342 layer_stack: Vec::new(),
343 transform: Transform::identity(),
344 }
345 }
346
width(&self) -> i32347 pub fn width(&self) -> i32 {
348 self.width
349 }
350
height(&self) -> i32351 pub fn height(&self) -> i32 {
352 self.height
353 }
354
355 /// Use a previously used vector for the bitmap and extend it to the given size(if needed)
from_vec(width: i32, height: i32, mut vec: Vec<u32>) -> DrawTarget356 pub fn from_vec(width: i32, height: i32, mut vec: Vec<u32>) -> DrawTarget{
357 vec.resize((width*height) as usize, 0);
358 DrawTarget {
359 width,
360 height,
361 current_point: None,
362 first_point: None,
363 rasterizer: Rasterizer::new(width, height),
364 buf: vec,
365 clip_stack: Vec::new(),
366 layer_stack: Vec::new(),
367 transform: Transform::identity()
368 }
369 }
370
371 /// sets a transform that will be applied to all drawing operations
set_transform(&mut self, transform: &Transform)372 pub fn set_transform(&mut self, transform: &Transform) {
373 self.transform = *transform;
374 }
375
376 /// gets the current transform
get_transform(&self) -> &Transform377 pub fn get_transform(&self) -> &Transform {
378 &self.transform
379 }
380
move_to(&mut self, pt: Point)381 fn move_to(&mut self, pt: Point) {
382 self.current_point = Some(pt);
383 self.first_point = Some(pt);
384 }
385
line_to(&mut self, pt: Point)386 fn line_to(&mut self, pt: Point) {
387 if self.current_point.is_none() {
388 self.current_point = Some(pt);
389 self.first_point = Some(pt);
390 }
391 if let Some(current_point) = self.current_point {
392 self.rasterizer
393 .add_edge(current_point, pt, false, Point::new(0., 0.));
394 self.current_point = Some(pt);
395 }
396 }
397
quad_to(&mut self, cpt: Point, pt: Point)398 fn quad_to(&mut self, cpt: Point, pt: Point) {
399 if self.current_point.is_none() {
400 self.current_point = Some(cpt);
401 self.first_point = Some(cpt);
402 }
403 if let Some(current_point) = self.current_point {
404 let curve = [current_point, cpt, pt];
405 self.current_point = Some(curve[2]);
406 self.add_quad(curve);
407 }
408 }
409
add_quad(&mut self, mut curve: [Point; 3])410 fn add_quad(&mut self, mut curve: [Point; 3]) {
411 let a = curve[0].y;
412 let b = curve[1].y;
413 let c = curve[2].y;
414 if is_not_monotonic(a, b, c) {
415 let mut t_value = 0.;
416 if valid_unit_divide(a - b, a - b - b + c, &mut t_value) {
417 let mut dst = [Point::new(0., 0.); 5];
418 chop_quad_at(&curve, &mut dst, t_value);
419 flatten_double_quad_extrema(&mut dst);
420 self.rasterizer.add_edge(dst[0], dst[2], true, dst[1]);
421 self.rasterizer.add_edge(dst[2], dst[4], true, dst[3]);
422 return;
423 }
424 // if we get here, we need to force dst to be monotonic, even though
425 // we couldn't compute a unit_divide value (probably underflow).
426 let b = if abs(a - b) < abs(b - c) { a } else { c };
427 curve[1].y = b;
428 }
429 self.rasterizer.add_edge(curve[0], curve[2], true, curve[1]);
430 }
431
cubic_to(&mut self, cpt1: Point, cpt2: Point, pt: Point)432 fn cubic_to(&mut self, cpt1: Point, cpt2: Point, pt: Point) {
433 if self.current_point.is_none() {
434 self.current_point = Some(cpt1);
435 self.first_point = Some(cpt1);
436 }
437 if let Some(current_point) = self.current_point {
438 let c = CubicBezierSegment {
439 from: current_point,
440 ctrl1: cpt1,
441 ctrl2: cpt2,
442 to: pt,
443 };
444 cubic_to_quadratics(&c, 0.01, &mut |q| {
445 let curve = [q.from, q.ctrl, q.to];
446 self.add_quad(curve);
447 });
448 self.current_point = Some(pt);
449 }
450 }
451
close(&mut self)452 fn close(&mut self) {
453 if let (Some(first_point), Some(current_point)) = (self.first_point, self.current_point) {
454 self.rasterizer.add_edge(
455 current_point,
456 first_point,
457 false,
458 Point::new(0., 0.),
459 );
460 }
461 self.current_point = self.first_point;
462 }
463
apply_path(&mut self, path: &Path)464 fn apply_path(&mut self, path: &Path) {
465 for op in &path.ops {
466 match *op {
467 PathOp::MoveTo(pt) => {
468 self.close();
469 self.move_to(self.transform.transform_point(pt));
470 },
471 PathOp::LineTo(pt) => self.line_to(self.transform.transform_point(pt)),
472 PathOp::QuadTo(cpt, pt) => self.quad_to(
473 self.transform.transform_point(cpt),
474 self.transform.transform_point(pt),
475 ),
476 PathOp::CubicTo(cpt1, cpt2, pt) => self.cubic_to(
477 self.transform.transform_point(cpt1),
478 self.transform.transform_point(cpt2),
479 self.transform.transform_point(pt),
480 ),
481 PathOp::Close => self.close(),
482 }
483 }
484 // make sure the path is closed
485 self.close();
486 // XXX: we'd like for this function to return the bounds of the path
487 }
488
push_clip_rect(&mut self, rect: IntRect)489 pub fn push_clip_rect(&mut self, rect: IntRect) {
490 // intersect with current clip
491 let clip = match self.clip_stack.last() {
492 Some(Clip {
493 rect: current_clip,
494 mask: _,
495 }) => Clip {
496 rect: current_clip.intersection(&rect),
497 mask: None,
498 },
499 _ => Clip {
500 rect: rect,
501 mask: None,
502 },
503 };
504 self.clip_stack.push(clip);
505 }
506
pop_clip(&mut self)507 pub fn pop_clip(&mut self) {
508 self.clip_stack.pop();
509 }
510
push_clip(&mut self, path: &Path)511 pub fn push_clip(&mut self, path: &Path) {
512 self.apply_path(path);
513
514 // XXX: restrict to clipped area
515 let mut blitter = MaskSuperBlitter::new(0, 0, self.width, self.height);
516 self.rasterizer.rasterize(&mut blitter, path.winding);
517
518 if let Some(last) = self.clip_stack.last() {
519 // combine with previous mask
520 if let Some(last_mask) = &last.mask {
521 for i in 0..((self.width * self.height) as usize) {
522 blitter.buf[i] = muldiv255(blitter.buf[i] as u32, last_mask[i] as u32) as u8
523 }
524 }
525 }
526
527 let current_bounds = self.clip_bounds();
528 //XXX: handle interleaving of clip rect/masks better
529 self.clip_stack.push(Clip {
530 rect: current_bounds,
531 mask: Some(blitter.buf),
532 });
533 self.rasterizer.reset();
534 }
535
clip_bounds(&self) -> IntRect536 fn clip_bounds(&self) -> IntRect {
537 self.clip_stack.last().map(|c| c.rect).unwrap_or(IntRect::new(
538 euclid::Point2D::new(0, 0),
539 euclid::Point2D::new(self.width, self.height),
540 ))
541 }
542
543 /// Pushes a new layer as the drawing target. This is used for implementing
544 /// group opacity effects.
push_layer(&mut self, opacity: f32)545 pub fn push_layer(&mut self, opacity: f32) {
546 self.push_layer_with_blend(opacity, BlendMode::SrcOver)
547 }
548
549 /// Pushes a new layer as the drawing target. This is used for implementing
550 /// group opacity or blend effects.
push_layer_with_blend(&mut self, opacity: f32, blend: BlendMode)551 pub fn push_layer_with_blend(&mut self, opacity: f32, blend: BlendMode) {
552 let rect = self.clip_bounds();
553 self.layer_stack.push(Layer {
554 rect,
555 buf: vec![0; (rect.size().width * rect.size().height) as usize],
556 opacity,
557 blend
558 });
559 }
560
561 /// Draws the most recently pushed layer to the drawing target with
562 /// the pushed opacity applied.
pop_layer(&mut self)563 pub fn pop_layer(&mut self) {
564 let layer = self.layer_stack.pop().unwrap();
565 let opacity = (layer.opacity * 255. + 0.5) as u8;
566 // Allocating an entire mask just for the opacity is needlessly bad.
567 // We should be able to fix it once the blitters work better.
568 let mask = vec![opacity; (self.width * self.height) as usize];
569 let size = layer.rect.size();
570 let ctm = self.transform;
571 self.transform = Transform::identity();
572 let image = Source::Image(Image {
573 width: size.width,
574 height: size.height,
575 data: &layer.buf
576 },
577 ExtendMode::Pad,
578 FilterMode::Nearest,
579 Transform::create_translation(-layer.rect.min.x as f32,
580 -layer.rect.min.y as f32));
581 self.composite(&image, Some(&mask), intrect(0, 0, self.width, self.height), layer.rect, layer.blend, 1.);
582 self.transform = ctm;
583 }
584
585 /// Draws an image at (x, y) with the size (width, height). This will rescale the image to the
586 /// destination size.
draw_image_with_size_at(&mut self, width: f32, height: f32, x: f32, y: f32, image: &Image, options: &DrawOptions)587 pub fn draw_image_with_size_at(&mut self, width: f32, height: f32, x: f32, y: f32, image: &Image, options: &DrawOptions) {
588 let source = Source::Image(*image,
589 ExtendMode::Pad,
590 FilterMode::Bilinear,
591 Transform::create_translation(-x, -y).post_scale(image.width as f32 / width, image.height as f32 / height));
592
593 self.fill_rect(x, y, width, height, &source, options);
594 }
595
596 /// Draws an image at x, y
draw_image_at(&mut self, x: f32, y: f32, image: &Image, options: &DrawOptions)597 pub fn draw_image_at(&mut self, x: f32, y: f32, image: &Image, options: &DrawOptions) {
598 self.draw_image_with_size_at(image.width as f32, image.height as f32, x, y, image, options);
599 }
600
601 /// Draws `src` through an untransformed `mask` positioned at `x`, `y` in device space
mask(&mut self, src: &Source, x: i32, y: i32, mask: &Mask)602 pub fn mask(&mut self, src: &Source, x: i32, y: i32, mask: &Mask) {
603 self.composite(src, Some(&mask.data), intrect(x, y, mask.width, mask.height), intrect(x, y, mask.width, mask.height), BlendMode::SrcOver, 1.);
604 }
605
606 /// Strokes `path` with `style` and fills the result with `src`
stroke(&mut self, path: &Path, src: &Source, style: &StrokeStyle, options: &DrawOptions)607 pub fn stroke(&mut self, path: &Path, src: &Source, style: &StrokeStyle, options: &DrawOptions) {
608 let tolerance = 0.1;
609
610 // Since we're flattening in userspace, we need to compensate for the transform otherwise
611 // we'll flatten too much or not enough depending on the scale. We approximate doing this
612 // correctly by scaling the tolerance value using the same mechanism as Fitz. This
613 // approximation will fail if the scale between axes is drastically different. An
614 // alternative would be to use transform specific flattening but I haven't seen that done
615 // anywhere.
616 let tolerance = scaled_tolerance(tolerance, &self.transform);
617 let mut path = path.flatten(tolerance);
618
619 if !style.dash_array.is_empty() {
620 path = dash_path(&path, &style.dash_array, style.dash_offset);
621 }
622 let stroked = stroke_to_path(&path, style);
623 self.fill(&stroked, src, options);
624 }
625
626 /// Fills the rect `x`, `y,`, `width`, `height` with `src`. If the result is an
627 /// integer aligned rectangle performance will be faster than filling a rectangular path.
fill_rect(&mut self, x: f32, y: f32, width: f32, height: f32, src: &Source, options: &DrawOptions)628 pub fn fill_rect(&mut self, x: f32, y: f32, width: f32, height: f32, src: &Source, options: &DrawOptions) {
629 let ix = x as i32;
630 let iy = y as i32;
631 let iwidth = width as i32;
632 let iheight = height as i32;
633 let integer_rect = ix as f32 == x && iy as f32 == y &&
634 iwidth as f32 == width && iheight as f32 == height;
635
636 if self.transform == Transform::identity() && integer_rect && self.clip_stack.is_empty() {
637 let bounds = intrect(0, 0, self.width, self.height);
638 let mut irect = intrect(ix, iy, ix + iwidth, iy + iheight);
639 irect = irect.intersection(&bounds);
640 if irect.is_negative() {
641 return;
642 }
643 self.composite(src, None, irect, irect, options.blend_mode, options.alpha);
644 } else {
645 let mut pb = PathBuilder::new();
646 pb.rect(x, y, width, height);
647 self.fill(&pb.finish(), src, options);
648 }
649 }
650
651 /// Fills `path` with `src`
fill(&mut self, path: &Path, src: &Source, options: &DrawOptions)652 pub fn fill(&mut self, path: &Path, src: &Source, options: &DrawOptions) {
653 self.apply_path(path);
654 let bounds = self.rasterizer.get_bounds();
655 match options.antialias {
656 AntialiasMode::None => {
657 let mut blitter = MaskBlitter::new(bounds.min.x, bounds.min.y, bounds.size().width, bounds.size().height);
658 self.rasterizer.rasterize(&mut blitter, path.winding);
659 self.composite(
660 src,
661 Some(&blitter.buf),
662 bounds,
663 bounds,
664 options.blend_mode,
665 options.alpha,
666 );
667 }
668 AntialiasMode::Gray => {
669 let mut blitter = MaskSuperBlitter::new(bounds.min.x, bounds.min.y, bounds.size().width, bounds.size().height);
670 self.rasterizer.rasterize(&mut blitter, path.winding);
671 self.composite(
672 src,
673 Some(&blitter.buf),
674 bounds,
675 bounds,
676 options.blend_mode,
677 options.alpha,
678 );
679 }
680 }
681 self.rasterizer.reset();
682 }
683
684 /// Fills the current clip with the solid color `solid`
clear(&mut self, solid: SolidSource)685 pub fn clear(&mut self, solid: SolidSource) {
686 let mut pb = PathBuilder::new();
687 if self.clip_stack.is_empty() {
688 let color = solid.to_u32();
689 for pixel in &mut self.buf[..] {
690 *pixel = color;
691 }
692 } else {
693 let ctm = self.transform;
694 self.transform = Transform::identity();
695 pb.rect(0., 0., self.width as f32, self.height as f32);
696 self.fill(
697 &pb.finish(),
698 &Source::Solid(solid),
699 &DrawOptions {
700 blend_mode: BlendMode::Src,
701 alpha: 1.,
702 antialias: AntialiasMode::Gray,
703 },
704 );
705 self.transform = ctm;
706 }
707 }
708
709 #[cfg(feature = "text")]
draw_text( &mut self, font: &fk::Font, point_size: f32, text: &str, mut start: Point, src: &Source, options: &DrawOptions, )710 pub fn draw_text(
711 &mut self,
712 font: &fk::Font,
713 point_size: f32,
714 text: &str,
715 mut start: Point,
716 src: &Source,
717 options: &DrawOptions,
718 ) {
719 let mut ids = Vec::new();
720 let mut positions = Vec::new();
721 for c in text.chars() {
722 let id = font.glyph_for_char(c).unwrap();
723 ids.push(id);
724 positions.push(start);
725 start += font.advance(id).unwrap() * point_size / 24. / 96.;
726 }
727 self.draw_glyphs(font, point_size, &ids, &positions, src, options);
728 }
729
730 #[cfg(feature = "text")]
draw_glyphs( &mut self, font: &fk::Font, point_size: f32, ids: &[u32], positions: &[Point], src: &Source, options: &DrawOptions, )731 pub fn draw_glyphs(
732 &mut self,
733 font: &fk::Font,
734 point_size: f32,
735 ids: &[u32],
736 positions: &[Point],
737 src: &Source,
738 options: &DrawOptions,
739 ) {
740 let mut combined_bounds = euclid::Rect::zero();
741 for (id, position) in ids.iter().zip(positions.iter()) {
742 let bounds = font.raster_bounds(
743 *id,
744 point_size,
745 &fk::FontTransform::new(self.transform.m11, self.transform.m21, self.transform.m12, self.transform.m22),
746 &(self.transform.transform_point(*position)),
747 fk::HintingOptions::None,
748 fk::RasterizationOptions::GrayscaleAa,
749 );
750 combined_bounds = match bounds {
751 Ok(bounds) => {
752 combined_bounds.union(&bounds)
753 }
754 _ => panic!(),
755 }
756 }
757
758 /*let mut canvas = Canvas::new(&euclid::Size2D::new(combined_bounds.size.width as u32,
759 combined_bounds.size.height as u32), Format::A8);*/
760 let mut canvas = fk::Canvas::new(
761 &euclid::Size2D::new(combined_bounds.size.width as u32, combined_bounds.size.height as u32),
762 fk::Format::A8,
763 );
764 for (id, position) in ids.iter().zip(positions.iter()) {
765 let mut position = self.transform.transform_point(*position);
766 position.x -= combined_bounds.origin.x as f32;
767 position.y -= combined_bounds.origin.y as f32;
768 font.rasterize_glyph(
769 &mut canvas,
770 *id,
771 point_size,
772 &fk::FontTransform::new(self.transform.m11, self.transform.m21, self.transform.m12, self.transform.m22),
773 &position,
774 fk::HintingOptions::None,
775 fk::RasterizationOptions::GrayscaleAa,
776 ).unwrap();
777 }
778
779 self.composite(
780 src,
781 Some(&canvas.pixels),
782 combined_bounds.to_box2d(),
783 combined_bounds.to_box2d(),
784 options.blend_mode,
785 1.,
786 );
787 }
788
789
790
791
choose_blitter<'a, 'b, 'c>(mask: Option<&[u8]>, clip_stack: &'a Vec<Clip>, blitter_storage: &'b mut ShaderBlitterStorage<'a>, shader: &'a dyn Shader, blend: BlendMode, dest: &'a mut [u32], dest_bounds: IntRect, width: i32) -> &'b mut dyn Blitter792 fn choose_blitter<'a, 'b, 'c>(mask: Option<&[u8]>, clip_stack: &'a Vec<Clip>, blitter_storage: &'b mut ShaderBlitterStorage<'a>, shader: &'a dyn Shader, blend: BlendMode, dest: &'a mut [u32], dest_bounds: IntRect, width: i32) -> &'b mut dyn Blitter {
793 let blitter: &mut dyn Blitter;
794
795 match (mask, clip_stack.last()) {
796 (Some(_mask), Some(Clip {
797 rect: _,
798 mask: Some(clip),
799 })) => {
800 if blend == BlendMode::SrcOver {
801 let scb = ShaderClipMaskBlitter {
802 x: dest_bounds.min.x,
803 y: dest_bounds.min.y,
804 shader: shader,
805 tmp: vec![0; width as usize],
806 dest,
807 dest_stride: dest_bounds.size().width,
808 clip,
809 clip_stride: width,
810 };
811 *blitter_storage = ShaderBlitterStorage::ShaderClipMaskBlitter(scb);
812 blitter = match blitter_storage { ShaderBlitterStorage::ShaderClipMaskBlitter(s) => s, _ => panic!() };
813 } else {
814 let blend_fn = build_blend_proc::<BlendRowMaskClip>(blend);
815 let scb_blend = ShaderClipBlendMaskBlitter {
816 x: dest_bounds.min.x,
817 y: dest_bounds.min.y,
818 shader: shader,
819 tmp: vec![0; width as usize],
820 dest,
821 dest_stride: dest_bounds.size().width,
822 clip,
823 clip_stride: width,
824 blend_fn
825 };
826
827 *blitter_storage = ShaderBlitterStorage::ShaderClipBlendMaskBlitter(scb_blend);
828 blitter = match blitter_storage {
829 ShaderBlitterStorage::ShaderClipBlendMaskBlitter(s) => s,
830 _ => panic!()
831 };
832 }
833 }
834 (Some(_mask), _) => {
835 if blend == BlendMode::SrcOver {
836 let sb = ShaderMaskBlitter {
837 x: dest_bounds.min.x,
838 y: dest_bounds.min.y,
839 shader: &*shader,
840 tmp: vec![0; width as usize],
841 dest,
842 dest_stride: dest_bounds.size().width,
843 };
844 *blitter_storage = ShaderBlitterStorage::ShaderMaskBlitter(sb);
845 blitter = match blitter_storage { ShaderBlitterStorage::ShaderMaskBlitter(s) => s, _ => panic!() };
846 } else {
847 let blend_fn = build_blend_proc::<BlendRowMask>(blend);
848 let sb_blend = ShaderBlendMaskBlitter {
849 x: dest_bounds.min.x,
850 y: dest_bounds.min.y,
851 shader: &*shader,
852 tmp: vec![0; width as usize],
853 dest,
854 dest_stride: dest_bounds.size().width,
855 blend_fn,
856 };
857 *blitter_storage = ShaderBlitterStorage::ShaderBlendMaskBlitter(sb_blend);
858 blitter = match blitter_storage {
859 ShaderBlitterStorage::ShaderBlendMaskBlitter(s) => s,
860 _ => panic!()
861 };
862 }
863 }
864 (None, _) => {
865 let blend_fn = build_blend_proc::<BlendRow>(blend);
866 let sb_blend = ShaderBlendBlitter {
867 x: dest_bounds.min.x,
868 y: dest_bounds.min.y,
869 shader: &*shader,
870 tmp: vec![0; width as usize],
871 dest,
872 dest_stride: dest_bounds.size().width,
873 blend_fn,
874 };
875 *blitter_storage = ShaderBlitterStorage::ShaderBlendBlitter(sb_blend);
876 blitter = match blitter_storage {
877 ShaderBlitterStorage::ShaderBlendBlitter(s) => s,
878 _ => panic!()
879 };
880 }
881 }
882 blitter
883 }
884
885 /// `mask_rect` is in DrawTarget space. i.e size is the size of the mask and origin is the position.
886 /// you can not render a part of the mask
composite(&mut self, src: &Source, mask: Option<&[u8]>, mask_rect: IntRect, mut rect: IntRect, blend: BlendMode, alpha: f32)887 fn composite(&mut self, src: &Source, mask: Option<&[u8]>, mask_rect: IntRect, mut rect: IntRect, blend: BlendMode, alpha: f32) {
888 let ti = self.transform.inverse();
889 let ti = if let Some(ti) = ti {
890 ti
891 } else {
892 // the transform is not invertible so we have nothing to draw
893 return;
894 };
895
896 let clip_bounds = self.clip_bounds();
897
898 let (dest, dest_bounds) = match self.layer_stack.last_mut() {
899 Some(layer) => (&mut layer.buf[..], layer.rect),
900 None => (&mut self.buf[..], intrect(0, 0, self.width, self.height))
901 };
902
903 rect = rect
904 .intersection(&clip_bounds)
905 .intersection(&dest_bounds)
906 .intersection(&mask_rect);
907 if rect.is_negative() {
908 return;
909 }
910
911 let mut shader_storage: ShaderStorage = ShaderStorage::None;
912 let shader = choose_shader(&ti, src, alpha, &mut shader_storage);
913
914 let mut blitter_storage: ShaderBlitterStorage = ShaderBlitterStorage::None;
915 let blitter = DrawTarget::choose_blitter(mask, &self.clip_stack, &mut blitter_storage, shader, blend, dest, dest_bounds, self.width);
916
917 match mask {
918 Some(mask) => {
919 for y in rect.min.y..rect.max.y {
920 let mask_row = (y - mask_rect.min.y) * mask_rect.size().width;
921 let mask_start = (mask_row + rect.min.x - mask_rect.min.x) as usize;
922 let mask_end = (mask_row + rect.max.x - mask_rect.min.x) as usize;
923 blitter.blit_span(y, rect.min.x, rect.max.x, &mask[mask_start..mask_end]);
924 }
925 }
926 None => {
927 for y in rect.min.y..rect.max.y {
928 let empty_mask = [];
929 blitter.blit_span(y, rect.min.x, rect.max.x, &empty_mask[..]);
930 }
931 }
932 };
933 }
934
935 /// Draws `src_rect` of `src` at `dst`. The current transform and clip are ignored
composite_surface<F: Fn(&[u32], &mut [u32])>(&mut self, src: &DrawTarget, src_rect: IntRect, dst: IntPoint, f: F)936 pub fn composite_surface<F: Fn(&[u32], &mut [u32])>(&mut self, src: &DrawTarget, src_rect: IntRect, dst: IntPoint, f: F) {
937 let dst_rect = intrect(0, 0, self.width, self.height);
938
939 // intersect the src_rect with the source size so that we don't go out of bounds
940 let src_rect = src_rect.intersection(&intrect(0, 0, src.width, src.height));
941
942 let src_rect = dst_rect
943 .intersection(&src_rect.translate(dst.to_vector())).translate(-dst.to_vector());
944
945 // clamp requires Float so open code it
946 let dst = IntPoint::new(dst.x.max(dst_rect.min.x).min(dst_rect.max.x),
947 dst.y.max(dst_rect.min.y).min(dst_rect.max.y));
948
949 if src_rect.is_negative() {
950 return;
951 }
952
953 for y in src_rect.min.y..src_rect.max.y {
954 let dst_row_start = (dst.x + (dst.y + y - src_rect.min.y) * self.width) as usize;
955 let dst_row_end = dst_row_start + src_rect.size().width as usize;
956 let src_row_start = (src_rect.min.x + y * src.width) as usize;
957 let src_row_end = src_row_start + src_rect.size().width as usize;
958 f(&src.buf[src_row_start..src_row_end], &mut self.buf[dst_row_start..dst_row_end]);
959 }
960 }
961
962 /// Draws `src_rect` of `src` at `dst`. The current transform and clip are ignored.
963 /// `src_rect` is clamped to (0, 0, src.width, src.height).
copy_surface(&mut self, src: &DrawTarget, src_rect: IntRect, dst: IntPoint)964 pub fn copy_surface(&mut self, src: &DrawTarget, src_rect: IntRect, dst: IntPoint) {
965 self.composite_surface(src, src_rect, dst, |src, dst| {
966 dst.copy_from_slice(src)
967 })
968 }
969
970 /// Blends `src_rect` of `src` at `dst`using `blend` mode.
971 /// The current transform and clip are ignored.
972 /// `src_rect` is clamped to (0, 0, `src.width`, `src.height`).
blend_surface(&mut self, src: &DrawTarget, src_rect: IntRect, dst: IntPoint, blend: BlendMode)973 pub fn blend_surface(&mut self, src: &DrawTarget, src_rect: IntRect, dst: IntPoint, blend: BlendMode) {
974 let blend_fn = build_blend_proc::<BlendRow>(blend);
975 self.composite_surface(src, src_rect, dst, |src, dst| {
976 blend_fn(src, dst);
977 });
978 }
979
980 /// Blends `src_rect` of `src` at `dst` using `alpha`. The current transform and clip are ignored.
981 /// `src_rect` is clamped to (0, 0, `src.width`, `src.height`).
blend_surface_with_alpha(&mut self, src: &DrawTarget, src_rect: IntRect, dst: IntPoint, alpha: f32)982 pub fn blend_surface_with_alpha(&mut self, src: &DrawTarget, src_rect: IntRect, dst: IntPoint, alpha: f32) {
983 let alpha = (alpha * 255. + 0.5) as u8;
984
985 self.composite_surface(src, src_rect, dst, |src, dst| {
986 over_in_row(src, dst, alpha as u32);
987 });
988 }
989
990 /// Returns a reference to the underlying pixel data
get_data(&self) -> &[u32]991 pub fn get_data(&self) -> &[u32] {
992 &self.buf
993 }
994
995 /// Returns a mut reference to the underlying pixel data as ARGB with a representation
996 /// like: (A << 24) | (R << 16) | (G << 8) | B
get_data_mut(&mut self) -> &mut [u32]997 pub fn get_data_mut(&mut self) -> &mut [u32] {
998 &mut self.buf
999 }
1000
1001 /// Returns a reference to the underlying pixel data as individual bytes with the order BGRA
1002 /// on little endian.
get_data_u8(&self) -> &[u8]1003 pub fn get_data_u8(&self) -> &[u8] {
1004 let p = self.buf[..].as_ptr();
1005 let len = self.buf[..].len();
1006 // we want to return an [u8] slice instead of a [u32] slice. This is a safe thing to
1007 // do because requirements of a [u32] slice are stricter.
1008 unsafe { std::slice::from_raw_parts(p as *const u8, len * std::mem::size_of::<u32>()) }
1009 }
1010
1011 /// Returns a mut reference to the underlying pixel data as individual bytes with the order BGRA
1012 /// on little endian.
get_data_u8_mut(&mut self) -> &mut [u8]1013 pub fn get_data_u8_mut(&mut self) -> &mut [u8] {
1014 let p = self.buf[..].as_mut_ptr();
1015 let len = self.buf[..].len();
1016 // we want to return an [u8] slice instead of a [u32] slice. This is a safe thing to
1017 // do because requirements of a [u32] slice are stricter.
1018 unsafe { std::slice::from_raw_parts_mut(p as *mut u8, len * std::mem::size_of::<u32>()) }
1019 }
1020
1021 /// Take ownership of the buffer backing the DrawTarget
into_vec(self) -> Vec<u32>1022 pub fn into_vec(self) -> Vec<u32> {
1023 self.buf
1024 }
1025
1026
1027 /// Saves the current pixel to a png file at `path`
write_png<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), png::EncodingError>1028 pub fn write_png<P: AsRef<std::path::Path>>(&self, path: P) -> Result<(), png::EncodingError> {
1029 let file = File::create(path)?;
1030
1031 let ref mut w = BufWriter::new(file);
1032
1033 let mut encoder = png::Encoder::new(w, self.width as u32, self.height as u32);
1034 encoder.set_color(png::ColorType::RGBA);
1035 encoder.set_depth(png::BitDepth::Eight);
1036 let mut writer = encoder.write_header()?;
1037 let mut output = Vec::with_capacity(self.buf.len() * 4);
1038
1039 for pixel in &self.buf {
1040 let a = (pixel >> 24) & 0xffu32;
1041 let mut r = (pixel >> 16) & 0xffu32;
1042 let mut g = (pixel >> 8) & 0xffu32;
1043 let mut b = (pixel >> 0) & 0xffu32;
1044
1045 if a > 0u32 {
1046 r = r * 255u32 / a;
1047 g = g * 255u32 / a;
1048 b = b * 255u32 / a;
1049 }
1050
1051 output.push(r as u8);
1052 output.push(g as u8);
1053 output.push(b as u8);
1054 output.push(a as u8);
1055 }
1056
1057 writer.write_image_data(&output)
1058 }
1059 }
1060