1use crate::*;
8
9use tiny_skia_path::{PathStroker, Scalar, SCALAR_MAX};
10
11use crate::geom::ScreenIntRect;
12use crate::mask::SubMaskRef;
13use crate::pipeline::{RasterPipelineBlitter, RasterPipelineBuilder};
14use crate::pixmap::SubPixmapMut;
15use crate::scan;
16
17use crate::geom::IntSizeExt;
18#[cfg(all(not(feature = "std"), feature = "no-std-float"))]
19use tiny_skia_path::NoStdFloat;
20
21#[derive(Copy, Clone, Default, PartialEq, Debug)]
23pub enum FillRule {
24 #[default]
26 Winding,
27 EvenOdd,
29}
30
31#[derive(Clone, PartialEq, Debug)]
33pub struct Paint<'a> {
34 pub shader: Shader<'a>,
38
39 pub blend_mode: BlendMode,
43
44 pub anti_alias: bool,
48
49 pub colorspace: ColorSpace,
57
58 pub force_hq_pipeline: bool,
75}
76
77impl Default for Paint<'_> {
78 fn default() -> Self {
79 Paint {
80 shader: Shader::SolidColor(Color::BLACK),
81 blend_mode: BlendMode::default(),
82 anti_alias: true,
83 colorspace: ColorSpace::default(),
84 force_hq_pipeline: false,
85 }
86 }
87}
88
89impl Paint<'_> {
90 pub fn set_color(&mut self, color: Color) {
92 self.shader = Shader::SolidColor(color);
93 }
94
95 pub fn set_color_rgba8(&mut self, r: u8, g: u8, b: u8, a: u8) {
99 self.set_color(Color::from_rgba8(r, g, b, a))
100 }
101
102 pub fn is_solid_color(&self) -> bool {
104 matches!(self.shader, Shader::SolidColor(_))
105 }
106}
107
108impl Pixmap {
109 pub fn fill_rect(
113 &mut self,
114 rect: Rect,
115 paint: &Paint,
116 transform: Transform,
117 mask: Option<&Mask>,
118 ) {
119 self.as_mut().fill_rect(rect, paint, transform, mask);
120 }
121
122 pub fn fill_path(
126 &mut self,
127 path: &Path,
128 paint: &Paint,
129 fill_rule: FillRule,
130 transform: Transform,
131 mask: Option<&Mask>,
132 ) {
133 self.as_mut()
134 .fill_path(path, paint, fill_rule, transform, mask);
135 }
136
137 pub fn stroke_path(
141 &mut self,
142 path: &Path,
143 paint: &Paint,
144 stroke: &Stroke,
145 transform: Transform,
146 mask: Option<&Mask>,
147 ) {
148 self.as_mut()
149 .stroke_path(path, paint, stroke, transform, mask);
150 }
151
152 pub fn draw_pixmap(
156 &mut self,
157 x: i32,
158 y: i32,
159 pixmap: PixmapRef,
160 paint: &PixmapPaint,
161 transform: Transform,
162 mask: Option<&Mask>,
163 ) {
164 self.as_mut()
165 .draw_pixmap(x, y, pixmap, paint, transform, mask);
166 }
167
168 pub fn apply_mask(&mut self, mask: &Mask) {
172 self.as_mut().apply_mask(mask);
173 }
174}
175
176impl PixmapMut<'_> {
177 pub fn fill_rect(
186 &mut self,
187 rect: Rect,
188 paint: &Paint,
189 transform: Transform,
190 mask: Option<&Mask>,
191 ) {
192 if transform.is_identity() && !DrawTiler::required(self.width(), self.height()) {
194 let clip = self.size().to_screen_int_rect(0, 0);
197
198 let mask = mask.map(|mask| mask.as_submask());
199 let mut subpix = self.as_subpixmap();
200 let mut blitter = match RasterPipelineBlitter::new(paint, mask, &mut subpix) {
201 Some(v) => v,
202 None => return, };
204
205 if paint.anti_alias {
206 scan::fill_rect_aa(&rect, &clip, &mut blitter);
207 } else {
208 scan::fill_rect(&rect, &clip, &mut blitter);
209 }
210 } else {
211 let path = PathBuilder::from_rect(rect);
212 self.fill_path(&path, paint, FillRule::Winding, transform, mask);
213 }
214 }
215
216 pub fn fill_path(
218 &mut self,
219 path: &Path,
220 paint: &Paint,
221 fill_rule: FillRule,
222 transform: Transform,
223 mask: Option<&Mask>,
224 ) {
225 if transform.is_identity() {
226 let path_bounds = path.bounds();
230 if path_bounds.width().is_nearly_zero() || path_bounds.height().is_nearly_zero() {
231 log::warn!("empty paths and horizontal/vertical lines cannot be filled");
232 return;
233 }
234
235 if is_too_big_for_math(path) {
236 log::warn!("path coordinates are too big");
237 return;
238 }
239
240 if let Some(tiler) = DrawTiler::new(self.width(), self.height()) {
243 let mut path = path.clone(); let mut paint = paint.clone();
245
246 for tile in tiler {
247 let ts = Transform::from_translate(-(tile.x() as f32), -(tile.y() as f32));
248 path = match path.transform(ts) {
249 Some(v) => v,
250 None => {
251 log::warn!("path transformation failed");
252 return;
253 }
254 };
255 paint.shader.transform(ts);
256
257 let clip_rect = tile.size().to_screen_int_rect(0, 0);
258 let mut subpix = match self.subpixmap(tile.to_int_rect()) {
259 Some(v) => v,
260 None => continue, };
262
263 let submask = mask.and_then(|mask| mask.submask(tile.to_int_rect()));
264 let mut blitter = match RasterPipelineBlitter::new(&paint, submask, &mut subpix)
265 {
266 Some(v) => v,
267 None => continue, };
269
270 if paint.anti_alias {
274 scan::path_aa::fill_path(&path, fill_rule, &clip_rect, &mut blitter);
275 } else {
276 scan::path::fill_path(&path, fill_rule, &clip_rect, &mut blitter);
277 }
278
279 let ts = Transform::from_translate(tile.x() as f32, tile.y() as f32);
280 path = match path.transform(ts) {
281 Some(v) => v,
282 None => return, };
284 paint.shader.transform(ts);
285 }
286 } else {
287 let clip_rect = self.size().to_screen_int_rect(0, 0);
288 let submask = mask.map(|mask| mask.as_submask());
289 let mut subpix = self.as_subpixmap();
290 let mut blitter = match RasterPipelineBlitter::new(paint, submask, &mut subpix) {
291 Some(v) => v,
292 None => return, };
294
295 if paint.anti_alias {
296 scan::path_aa::fill_path(path, fill_rule, &clip_rect, &mut blitter);
297 } else {
298 scan::path::fill_path(path, fill_rule, &clip_rect, &mut blitter);
299 }
300 }
301 } else {
302 let path = match path.clone().transform(transform) {
303 Some(v) => v,
304 None => {
305 log::warn!("path transformation failed");
306 return;
307 }
308 };
309
310 let mut paint = paint.clone();
311 paint.shader.transform(transform);
312
313 self.fill_path(&path, &paint, fill_rule, Transform::identity(), mask)
314 }
315 }
316
317 pub fn stroke_path(
331 &mut self,
332 path: &Path,
333 paint: &Paint,
334 stroke: &Stroke,
335 transform: Transform,
336 mask: Option<&Mask>,
337 ) {
338 if stroke.width < 0.0 {
339 log::warn!("negative stroke width isn't allowed");
340 return;
341 }
342
343 let res_scale = PathStroker::compute_resolution_scale(&transform);
344
345 let dash_path;
346 let path = if let Some(ref dash) = stroke.dash {
347 dash_path = match path.dash(dash, res_scale) {
348 Some(v) => v,
349 None => {
350 log::warn!("path dashing failed");
351 return;
352 }
353 };
354 &dash_path
355 } else {
356 path
357 };
358
359 if let Some(coverage) = treat_as_hairline(paint, stroke, transform) {
360 let mut paint = paint.clone();
361 if coverage == 1.0 {
362 } else if paint.blend_mode.should_pre_scale_coverage() {
364 let scale = (coverage * 256.0) as i32;
368 let new_alpha = (255 * scale) >> 8;
369 paint.shader.apply_opacity(new_alpha as f32 / 255.0);
370 }
371
372 if let Some(tiler) = DrawTiler::new(self.width(), self.height()) {
373 let mut path = path.clone(); let mut paint = paint.clone();
375
376 if !transform.is_identity() {
377 paint.shader.transform(transform);
378 path = match path.transform(transform) {
379 Some(v) => v,
380 None => {
381 log::warn!("path transformation failed");
382 return;
383 }
384 };
385 }
386
387 for tile in tiler {
388 let ts = Transform::from_translate(-(tile.x() as f32), -(tile.y() as f32));
389 path = match path.transform(ts) {
390 Some(v) => v,
391 None => {
392 log::warn!("path transformation failed");
393 return;
394 }
395 };
396 paint.shader.transform(ts);
397
398 let mut subpix = match self.subpixmap(tile.to_int_rect()) {
399 Some(v) => v,
400 None => continue, };
402 let submask = mask.and_then(|mask| mask.submask(tile.to_int_rect()));
403
404 Self::stroke_hairline(&path, &paint, stroke.line_cap, submask, &mut subpix);
408
409 let ts = Transform::from_translate(tile.x() as f32, tile.y() as f32);
410 path = match path.transform(ts) {
411 Some(v) => v,
412 None => return,
413 };
414 paint.shader.transform(ts);
415 }
416 } else {
417 let subpix = &mut self.as_subpixmap();
418 let submask = mask.map(|mask| mask.as_submask());
419 if !transform.is_identity() {
420 paint.shader.transform(transform);
421
422 let path = match path.clone().transform(transform) {
424 Some(v) => v,
425 None => {
426 log::warn!("path transformation failed");
427 return;
428 }
429 };
430
431 Self::stroke_hairline(&path, &paint, stroke.line_cap, submask, subpix);
432 } else {
433 Self::stroke_hairline(path, &paint, stroke.line_cap, submask, subpix);
434 }
435 }
436 } else {
437 let path = match path.stroke(stroke, res_scale) {
438 Some(v) => v,
439 None => {
440 log::warn!("path stroking failed");
441 return;
442 }
443 };
444
445 self.fill_path(&path, paint, FillRule::Winding, transform, mask);
446 }
447 }
448
449 fn stroke_hairline(
451 path: &Path,
452 paint: &Paint,
453 line_cap: LineCap,
454 mask: Option<SubMaskRef>,
455 pixmap: &mut SubPixmapMut,
456 ) {
457 let clip = pixmap.size.to_screen_int_rect(0, 0);
458 let mut blitter = match RasterPipelineBlitter::new(paint, mask, pixmap) {
459 Some(v) => v,
460 None => return, };
462 if paint.anti_alias {
463 scan::hairline_aa::stroke_path(path, line_cap, &clip, &mut blitter);
464 } else {
465 scan::hairline::stroke_path(path, line_cap, &clip, &mut blitter);
466 }
467 }
468
469 pub fn draw_pixmap(
473 &mut self,
474 x: i32,
475 y: i32,
476 pixmap: PixmapRef,
477 paint: &PixmapPaint,
478 transform: Transform,
479 mask: Option<&Mask>,
480 ) {
481 let rect = pixmap.size().to_int_rect(x, y).to_rect();
482
483 let patt_transform = Transform::from_translate(x as f32, y as f32);
489
490 let paint = Paint {
491 shader: Pattern::new(
492 pixmap,
493 SpreadMode::Pad, paint.quality,
495 paint.opacity,
496 patt_transform,
497 ),
498 blend_mode: paint.blend_mode,
499 anti_alias: false, force_hq_pipeline: false, colorspace: ColorSpace::default(),
502 };
503
504 self.fill_rect(rect, &paint, transform, mask);
505 }
506
507 pub fn apply_mask(&mut self, mask: &Mask) {
519 if self.size() != mask.size() {
520 log::warn!("Pixmap and Mask are expected to have the same size");
521 return;
522 }
523
524 let pixmap_src = PixmapRef::from_bytes(&[0, 0, 0, 0], 1, 1).unwrap();
526
527 let mut p = RasterPipelineBuilder::new();
528 p.push(pipeline::Stage::LoadMaskU8);
529 p.push(pipeline::Stage::LoadDestination);
530 p.push(pipeline::Stage::DestinationIn);
531 p.push(pipeline::Stage::Store);
532 let mut p = p.compile();
533 let rect = self.size().to_screen_int_rect(0, 0);
534 p.run(
535 &rect,
536 pipeline::AAMaskCtx::default(),
537 mask.as_submask().mask_ctx(),
538 pixmap_src,
539 &mut self.as_subpixmap(),
540 );
541 }
542}
543
544fn treat_as_hairline(paint: &Paint, stroke: &Stroke, mut ts: Transform) -> Option<f32> {
545 fn fast_len(p: Point) -> f32 {
546 let mut x = p.x.abs();
547 let mut y = p.y.abs();
548 if x < y {
549 core::mem::swap(&mut x, &mut y);
550 }
551
552 x + y.half()
553 }
554
555 debug_assert!(stroke.width >= 0.0);
556
557 if stroke.width == 0.0 {
558 return Some(1.0);
559 }
560
561 if !paint.anti_alias {
562 return None;
563 }
564
565 ts.tx = 0.0;
567 ts.ty = 0.0;
568
569 let mut points = [
571 Point::from_xy(stroke.width, 0.0),
572 Point::from_xy(0.0, stroke.width),
573 ];
574 ts.map_points(&mut points);
575
576 let len0 = fast_len(points[0]);
577 let len1 = fast_len(points[1]);
578
579 if len0 <= 1.0 && len1 <= 1.0 {
580 return Some(len0.ave(len1));
581 }
582
583 None
584}
585
586pub(crate) fn is_too_big_for_math(path: &Path) -> bool {
593 const SCALE_DOWN_TO_ALLOW_FOR_SMALL_MULTIPLIES: f32 = 0.25;
596 const MAX: f32 = SCALAR_MAX * SCALE_DOWN_TO_ALLOW_FOR_SMALL_MULTIPLIES;
597
598 let b = path.bounds();
599
600 !(b.left() >= -MAX && b.top() >= -MAX && b.right() <= MAX && b.bottom() <= MAX)
602}
603
604pub(crate) struct DrawTiler {
613 image_width: u32,
614 image_height: u32,
615 x_offset: u32,
616 y_offset: u32,
617 finished: bool,
618}
619
620impl DrawTiler {
621 const MAX_DIMENSIONS: u32 = 8192 - 1;
623
624 fn required(image_width: u32, image_height: u32) -> bool {
625 image_width > Self::MAX_DIMENSIONS || image_height > Self::MAX_DIMENSIONS
626 }
627
628 pub(crate) fn new(image_width: u32, image_height: u32) -> Option<Self> {
629 if Self::required(image_width, image_height) {
630 Some(DrawTiler {
631 image_width,
632 image_height,
633 x_offset: 0,
634 y_offset: 0,
635 finished: false,
636 })
637 } else {
638 None
639 }
640 }
641}
642
643impl Iterator for DrawTiler {
644 type Item = ScreenIntRect;
645
646 fn next(&mut self) -> Option<Self::Item> {
647 if self.finished {
648 return None;
649 }
650
651 if self.x_offset < self.image_width && self.y_offset < self.image_height {
654 let h = if self.y_offset < self.image_height {
655 (self.image_height - self.y_offset).min(Self::MAX_DIMENSIONS)
656 } else {
657 self.image_height
658 };
659
660 let r = ScreenIntRect::from_xywh(
661 self.x_offset,
662 self.y_offset,
663 (self.image_width - self.x_offset).min(Self::MAX_DIMENSIONS),
664 h,
665 );
666
667 self.x_offset += Self::MAX_DIMENSIONS;
668 if self.x_offset >= self.image_width {
669 self.x_offset = 0;
670 self.y_offset += Self::MAX_DIMENSIONS;
671 }
672
673 return r;
674 }
675
676 None
677 }
678}
679
680#[cfg(test)]
681mod tests {
682 use super::*;
683 const MAX_DIM: u32 = DrawTiler::MAX_DIMENSIONS;
684
685 #[test]
686 fn skip() {
687 assert!(DrawTiler::new(100, 500).is_none());
688 }
689
690 #[test]
691 fn horizontal() {
692 let mut iter = DrawTiler::new(10000, 500).unwrap();
693 assert_eq!(iter.next(), ScreenIntRect::from_xywh(0, 0, MAX_DIM, 500));
694 assert_eq!(
695 iter.next(),
696 ScreenIntRect::from_xywh(MAX_DIM, 0, 10000 - MAX_DIM, 500)
697 );
698 assert_eq!(iter.next(), None);
699 }
700
701 #[test]
702 fn vertical() {
703 let mut iter = DrawTiler::new(500, 10000).unwrap();
704 assert_eq!(iter.next(), ScreenIntRect::from_xywh(0, 0, 500, MAX_DIM));
705 assert_eq!(
706 iter.next(),
707 ScreenIntRect::from_xywh(0, MAX_DIM, 500, 10000 - MAX_DIM)
708 );
709 assert_eq!(iter.next(), None);
710 }
711
712 #[test]
713 fn rect() {
714 let mut iter = DrawTiler::new(10000, 10000).unwrap();
715 assert_eq!(
717 iter.next(),
718 ScreenIntRect::from_xywh(0, 0, MAX_DIM, MAX_DIM)
719 );
720 assert_eq!(
721 iter.next(),
722 ScreenIntRect::from_xywh(MAX_DIM, 0, 10000 - MAX_DIM, MAX_DIM)
723 );
724 assert_eq!(
726 iter.next(),
727 ScreenIntRect::from_xywh(0, MAX_DIM, MAX_DIM, 10000 - MAX_DIM)
728 );
729 assert_eq!(
730 iter.next(),
731 ScreenIntRect::from_xywh(MAX_DIM, MAX_DIM, 10000 - MAX_DIM, 10000 - MAX_DIM)
732 );
733 assert_eq!(iter.next(), None);
734 }
735}