Skip to main content

orbclient/
renderer.rs

1// SPDX-License-Identifier: MIT
2
3use core::cell::Cell;
4use core::cmp;
5
6#[cfg(feature = "std")]
7use crate::blur;
8use crate::color::Color;
9use crate::graphicspath::GraphicsPath;
10use crate::graphicspath::PointType;
11use crate::Mode;
12
13/// The trait to allow rendering code to be placed.
14/// All rendering in this trait is done in software.
15pub trait Renderer {
16    /// Get width
17    fn width(&self) -> u32;
18
19    /// Get height
20    fn height(&self) -> u32;
21
22    /// Access the pixel buffer
23    fn data(&self) -> &[Color];
24
25    /// Access the pixel buffer mutably
26    fn data_mut(&mut self) -> &mut [Color];
27
28    /// Update the hardware buffer
29    fn sync(&mut self) -> bool;
30
31    /// Update the software buffer
32    fn update(&mut self) -> bool;
33
34    /// Update the specified software buffer region (x, y, w, h)
35    fn update_rects(&mut self, rects: &[(i32, i32, u32, u32)]) -> bool;
36
37    /// Set/get drawing mode
38    fn mode(&self) -> &Cell<Mode>;
39
40    ///Draw a pixel
41    //faster pixel implementation (multiplexing)
42    fn pixel(&mut self, x: i32, y: i32, color: Color) {
43        let replace = match self.mode().get() {
44            Mode::Blend => false,
45            Mode::Overwrite => true,
46        };
47        let w = self.width() as i32;
48        let h = self.height() as i32;
49        let data = self.data_mut();
50
51        if x >= 0 && y >= 0 && x < w && y < h {
52            let new = color.data;
53            let alpha = (new >> 24) & 0xFF;
54            let old = &mut data[(y * w + x) as usize].data;
55
56            if alpha >= 255 || replace {
57                *old = new;
58            } else if alpha > 0 {
59                let n_alpha = 255 - alpha;
60                let rb = ((n_alpha * (*old & 0x00FF00FF)) + (alpha * (new & 0x00FF00FF))) >> 8;
61                let ag = (n_alpha * ((*old & 0xFF00FF00) >> 8))
62                    + (alpha * (0x01000000 | ((new & 0x0000FF00) >> 8)));
63
64                *old = (rb & 0x00FF00FF) | (ag & 0xFF00FF00);
65            }
66        }
67    }
68
69    /// Draw a piece of an arc. Negative radius will fill in the inside
70    fn arc(&mut self, x0: i32, y0: i32, radius: i32, parts: u8, color: Color) {
71        let mut x = radius.abs();
72        let mut y = 0;
73        let mut err = 0;
74
75        // https://github.com/rust-lang/rust-clippy/issues/5354
76        #[allow(clippy::comparison_chain)]
77        while x >= y {
78            if radius < 0 {
79                if parts & 1 << 0 != 0 {
80                    self.rect(x0 - x, y0 + y, x as u32, 1, color);
81                }
82                if parts & 1 << 1 != 0 {
83                    self.rect(x0, y0 + y, x as u32 + 1, 1, color);
84                }
85                if parts & 1 << 2 != 0 {
86                    self.rect(x0 - y, y0 + x, y as u32, 1, color);
87                }
88                if parts & 1 << 3 != 0 {
89                    self.rect(x0, y0 + x, y as u32 + 1, 1, color);
90                }
91                if parts & 1 << 4 != 0 {
92                    self.rect(x0 - x, y0 - y, x as u32, 1, color);
93                }
94                if parts & 1 << 5 != 0 {
95                    self.rect(x0, y0 - y, x as u32 + 1, 1, color);
96                }
97                if parts & 1 << 6 != 0 {
98                    self.rect(x0 - y, y0 - x, y as u32, 1, color);
99                }
100                if parts & 1 << 7 != 0 {
101                    self.rect(x0, y0 - x, y as u32 + 1, 1, color);
102                }
103            } else if radius == 0 {
104                self.pixel(x0, y0, color);
105            } else {
106                if parts & 1 << 0 != 0 {
107                    self.pixel(x0 - x, y0 + y, color);
108                }
109                if parts & 1 << 1 != 0 {
110                    self.pixel(x0 + x, y0 + y, color);
111                }
112                if parts & 1 << 2 != 0 {
113                    self.pixel(x0 - y, y0 + x, color);
114                }
115                if parts & 1 << 3 != 0 {
116                    self.pixel(x0 + y, y0 + x, color);
117                }
118                if parts & 1 << 4 != 0 {
119                    self.pixel(x0 - x, y0 - y, color);
120                }
121                if parts & 1 << 5 != 0 {
122                    self.pixel(x0 + x, y0 - y, color);
123                }
124                if parts & 1 << 6 != 0 {
125                    self.pixel(x0 - y, y0 - x, color);
126                }
127                if parts & 1 << 7 != 0 {
128                    self.pixel(x0 + y, y0 - x, color);
129                }
130            }
131
132            y += 1;
133            err += 1 + 2 * y;
134            if 2 * (err - x) + 1 > 0 {
135                x -= 1;
136                err += 1 - 2 * x;
137            }
138        }
139    }
140
141    /// Draw a circle. Negative radius will fill in the inside
142    fn circle(&mut self, x0: i32, y0: i32, radius: i32, color: Color) {
143        let mut x = radius.abs();
144        let mut y = 0;
145        let mut err = -radius.abs();
146
147        match radius {
148            radius if radius > 0 => {
149                err = 0;
150                while x >= y {
151                    self.pixel(x0 - x, y0 + y, color);
152                    self.pixel(x0 + x, y0 + y, color);
153                    self.pixel(x0 - y, y0 + x, color);
154                    self.pixel(x0 + y, y0 + x, color);
155                    self.pixel(x0 - x, y0 - y, color);
156                    self.pixel(x0 + x, y0 - y, color);
157                    self.pixel(x0 - y, y0 - x, color);
158                    self.pixel(x0 + y, y0 - x, color);
159
160                    y += 1;
161                    err += 1 + 2 * y;
162                    if 2 * (err - x) + 1 > 0 {
163                        x -= 1;
164                        err += 1 - 2 * x;
165                    }
166                }
167            }
168
169            radius if radius < 0 => {
170                while x >= y {
171                    let lasty = y;
172                    err += y;
173                    y += 1;
174                    err += y;
175                    self.line4points(x0, y0, x, lasty, color);
176                    if err >= 0 {
177                        if x != lasty {
178                            self.line4points(x0, y0, lasty, x, color);
179                        }
180                        err -= x;
181                        x -= 1;
182                        err -= x;
183                    }
184                }
185            }
186            _ => {
187                self.pixel(x0, y0, color);
188            }
189        }
190    }
191
192    fn line4points(&mut self, x0: i32, y0: i32, x: i32, y: i32, color: Color) {
193        //self.line(x0 - x, y0 + y, (x+x0), y0 + y, color);
194        self.rect(x0 - x, y0 + y, x as u32 * 2 + 1, 1, color);
195        if y != 0 {
196            //self.line(x0 - x, y0 - y, (x+x0), y0-y , color);
197            self.rect(x0 - x, y0 - y, x as u32 * 2 + 1, 1, color);
198        }
199    }
200
201    /// Draw a line
202    fn line(&mut self, argx1: i32, argy1: i32, argx2: i32, argy2: i32, color: Color) {
203        let mut x = argx1;
204        let mut y = argy1;
205
206        let dx = if argx1 > argx2 {
207            argx1 - argx2
208        } else {
209            argx2 - argx1
210        };
211        let dy = if argy1 > argy2 {
212            argy1 - argy2
213        } else {
214            argy2 - argy1
215        };
216
217        let sx = if argx1 < argx2 { 1 } else { -1 };
218        let sy = if argy1 < argy2 { 1 } else { -1 };
219
220        let mut err = if dx > dy { dx } else { -dy } / 2;
221        let mut err_tolerance;
222
223        loop {
224            self.pixel(x, y, color);
225
226            if x == argx2 && y == argy2 {
227                break;
228            };
229
230            err_tolerance = 2 * err;
231
232            if err_tolerance > -dx {
233                err -= dy;
234                x += sx;
235            }
236            if err_tolerance < dy {
237                err += dx;
238                y += sy;
239            }
240        }
241    }
242
243    fn lines(&mut self, points: &[[i32; 2]], color: Color) {
244        if points.is_empty() {
245            // when no points given, do nothing
246        } else if points.len() == 1 {
247            self.pixel(points[0][0], points[0][1], color);
248        } else {
249            for i in 0..points.len() - 1 {
250                self.line(
251                    points[i][0],
252                    points[i][1],
253                    points[i + 1][0],
254                    points[i + 1][1],
255                    color,
256                );
257            }
258        }
259    }
260
261    /// Draw a path (GraphicsPath)
262    fn draw_path_stroke(&mut self, graphicspath: GraphicsPath, color: Color) {
263        let mut x: i32 = 0;
264        let mut y: i32 = 0;
265
266        for point in graphicspath.points {
267            if let PointType::Connect = point.2 {
268                self.line(x, y, point.0, point.1, color)
269            }
270            x = point.0;
271            y = point.1;
272        }
273    }
274
275    /// Draw a character, using the loaded font
276    #[cfg(feature = "unifont")]
277    fn char(&mut self, x: i32, y: i32, c: char, color: Color) {
278        let mut offset = (c as usize) * 16;
279        for row in 0..16 {
280            let row_data = if offset < crate::FONT.len() {
281                crate::FONT[offset]
282            } else {
283                0
284            };
285
286            for col in 0..8 {
287                let pixel = (row_data >> (7 - col)) & 1;
288                if pixel > 0 {
289                    self.pixel(x + col, y + row, color);
290                }
291            }
292            offset += 1;
293        }
294    }
295
296    /// Set entire window to a color
297    fn set(&mut self, color: Color) {
298        let data = self.data_mut();
299        let data_ptr = data.as_mut_ptr();
300        for i in 0..data.len() as isize {
301            unsafe { *data_ptr.offset(i) = color }
302        }
303    }
304
305    /// Sets the whole window to black
306    fn clear(&mut self) {
307        self.set(Color::rgb(0, 0, 0));
308    }
309
310    fn rect(&mut self, x: i32, y: i32, w: u32, h: u32, color: Color) {
311        let replace = match self.mode().get() {
312            Mode::Blend => false,
313            Mode::Overwrite => true,
314        };
315        let self_w = self.width();
316        let self_h = self.height();
317
318        let start_y = cmp::max(0, cmp::min(self_h as i32 - 1, y));
319        let end_y = cmp::max(start_y, cmp::min(self_h as i32, y + h as i32));
320
321        let start_x = cmp::max(0, cmp::min(self_w as i32 - 1, x));
322        let len = cmp::max(start_x, cmp::min(self_w as i32, x + w as i32)) - start_x;
323
324        let alpha = (color.data >> 24) & 0xFF;
325        //if alpha > 0 {
326        if alpha >= 255 || replace {
327            let data = self.data_mut();
328            let data_ptr = data.as_mut_ptr();
329            for y in start_y..end_y {
330                let start = (y * self_w as i32 + start_x) as isize;
331                let end = start + len as isize;
332                for i in start..end {
333                    unsafe {
334                        *data_ptr.offset(i) = color;
335                    }
336                }
337            }
338        } else {
339            for y in start_y..end_y {
340                for x in start_x..start_x + len {
341                    self.pixel(x, y, color);
342                }
343            }
344        }
345        //}
346    }
347
348    #[cfg(feature = "std")]
349    fn box_blur(&mut self, x: i32, y: i32, w: u32, h: u32, r: i32) {
350        let self_w = self.width();
351        let self_h = self.height();
352
353        let start_y = cmp::max(0, cmp::min(self_h as i32 - 1, y));
354        let end_y = cmp::max(start_y, cmp::min(self_h as i32, y + h as i32));
355
356        let start_x = cmp::max(0, cmp::min(self_w as i32 - 1, x));
357        let end_x = cmp::max(start_x, cmp::min(self_w as i32, x + w as i32));
358
359        let data = self.data_mut();
360        let mut blur_data: Vec<Color> = Vec::new();
361        for y in start_y..end_y {
362            for x in start_x..end_x {
363                let old = data[y as usize * self_w as usize + x as usize];
364                blur_data.push(old);
365            }
366        }
367        let real_w = end_x - start_x;
368        let real_h = end_y - start_y;
369        blur::gauss_blur(&mut blur_data, real_w as u32, real_h as u32, r as f32);
370
371        let mut counter: u32 = 0;
372        for y in start_y..end_y {
373            for x in start_x..end_x {
374                let a = blur_data[counter as usize];
375                let old = &mut data[y as usize * self_w as usize + x as usize].data;
376
377                *old = a.data;
378                counter += 1;
379            }
380        }
381    }
382
383    #[allow(clippy::too_many_arguments)]
384    #[cfg(feature = "std")]
385    fn box_shadow(
386        &mut self,
387        x: i32,
388        y: i32,
389        w: u32,
390        h: u32,
391        offset_x: i32,
392        offset_y: i32,
393        r: i32,
394        color: Color,
395    ) {
396        let real_w = w + (2 * r as u32);
397        let real_h = h + (2 * r as u32);
398
399        let mut blur_data: Vec<Color> = Vec::new();
400        for new_x in x..x + real_w as i32 {
401            for new_y in y..y + real_h as i32 {
402                if new_x < x + r
403                    || new_y < y + r
404                    || new_y >= y + h as i32 + r
405                    || new_x >= x + w as i32 + r
406                {
407                    blur_data.push(Color::rgb(255, 0, 255));
408                } else {
409                    blur_data.push(Color::rgb(0, 0, 0));
410                }
411            }
412        }
413
414        blur::gauss_blur(&mut blur_data, real_w, real_h, r as f32 / 3.0);
415
416        let mut counter: u32 = 0;
417        for new_x in (x - r)..(x + real_w as i32 - r) {
418            for new_y in (y - r)..(y + real_h as i32 - r) {
419                let c = blur_data[counter as usize];
420
421                let alpha: u8 = if color.a() < 255 - c.r() {
422                    color.a()
423                } else {
424                    255 - c.r()
425                };
426                let col = Color::rgba(color.r(), color.g(), color.b(), alpha);
427
428                let new_x_b = new_x + offset_x;
429                let new_y_b = new_y + offset_y;
430                if new_x_b < x
431                    || new_x_b > x + w as i32 - 1
432                    || new_y_b < y
433                    || new_y_b > y + h as i32 - 1
434                {
435                    self.pixel(new_x_b, new_y_b, col);
436                }
437                counter += 1;
438            }
439        }
440    }
441
442    /// Display an image
443    fn image(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, data: &[Color]) {
444        match self.mode().get() {
445            Mode::Blend => self.image_fast(start_x, start_y, w, h, data),
446            Mode::Overwrite => self.image_opaque(start_x, start_y, w, h, data),
447        }
448    }
449
450    // TODO: Improve speed
451    #[inline(always)]
452    fn image_legacy(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, data: &[Color]) {
453        let mut i = 0;
454        for y in start_y..start_y + h as i32 {
455            for x in start_x..start_x + w as i32 {
456                if i < data.len() {
457                    self.pixel(x, y, data[i])
458                }
459                i += 1;
460            }
461        }
462    }
463
464    ///Display an image overwriting a portion of window starting at given line : very quick!!
465    fn image_over(&mut self, start: i32, image_data: &[Color]) {
466        let start = start as usize * self.width() as usize;
467        let window_data = self.data_mut();
468        let stop = cmp::min(start + image_data.len(), window_data.len());
469        let end = cmp::min(image_data.len(), window_data.len() - start);
470
471        window_data[start..stop].copy_from_slice(&image_data[..end]);
472    }
473
474    ///Display an image using non transparent method
475    #[inline(always)]
476    fn image_opaque(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, image_data: &[Color]) {
477        let w = w as usize;
478        let mut h = h as usize;
479        let width = self.width() as usize;
480        let height = self.height() as usize;
481        let start_x = start_x as usize;
482        let start_y = start_y as usize;
483
484        //check boundaries
485        if start_x >= width || start_y >= height {
486            return;
487        }
488        if h + start_y > height {
489            h = height - start_y;
490        }
491        let window_data = self.data_mut();
492        let offset = start_y * width + start_x;
493        //copy image slices to window line by line
494        for l in 0..h {
495            let start = offset + l * width;
496            let mut stop = start + w;
497            let begin = l * w;
498            let mut end = begin + w;
499            //check boundaries
500            if start_x + w > width {
501                stop = (start_y + l + 1) * width - 1;
502                end = begin + stop - start;
503            }
504            window_data[start..stop].copy_from_slice(&image_data[begin..end]);
505        }
506    }
507
508    // Speed improved, image can be outside of window boundary
509    #[inline(always)]
510    fn image_fast(&mut self, start_x: i32, start_y: i32, w: u32, h: u32, image_data: &[Color]) {
511        let w = w as usize;
512        let h = h as usize;
513        let width = self.width() as usize;
514        let start_x = start_x as usize;
515        let start_y = start_y as usize;
516
517        //simply return if image is outside of window
518        if start_x >= width || start_y >= self.height() as usize {
519            return;
520        }
521        let window_data = self.data_mut();
522        let offset = start_y * width + start_x;
523
524        //copy image slices to window line by line
525        for l in 0..h {
526            let start = offset + l * width;
527            let mut stop = start + w;
528            let begin = l * w;
529            let end = begin + w;
530
531            //check boundaries
532            if start_x + w > width {
533                stop = (start_y + l + 1) * width;
534            }
535            let mut k = 0;
536            for i in begin..end {
537                if i < image_data.len() {
538                    let new = image_data[i].data;
539                    let alpha = (new >> 24) & 0xFF;
540                    if alpha > 0 && (start + k) < window_data.len() && (start + k) < stop {
541                        let old = &mut window_data[start + k].data;
542                        if alpha >= 255 {
543                            *old = new;
544                        } else {
545                            let n_alpha = 255 - alpha;
546                            let rb = ((n_alpha * (*old & 0x00FF00FF))
547                                + (alpha * (new & 0x00FF00FF)))
548                                >> 8;
549                            let ag = (n_alpha * ((*old & 0xFF00FF00) >> 8))
550                                + (alpha * (0x01000000 | ((new & 0x0000FF00) >> 8)));
551
552                            *old = (rb & 0x00FF00FF) | (ag & 0xFF00FF00);
553                        }
554                    }
555                    k += 1;
556                }
557            }
558        }
559    }
560
561    /// Draw a linear gradient in a rectangular region
562    #[allow(clippy::too_many_arguments)]
563    #[cfg(feature = "std")]
564    fn linear_gradient(
565        &mut self,
566        rect_x: i32,
567        rect_y: i32,
568        rect_width: u32,
569        rect_height: u32,
570        start_x: i32,
571        start_y: i32,
572        end_x: i32,
573        end_y: i32,
574        start_color: Color,
575        end_color: Color,
576    ) {
577        fn clamp(proj: f64) -> f64 {
578            if proj < 0.0 {
579                0.0
580            } else if proj > 1.0 {
581                1.0
582            } else {
583                proj
584            }
585        }
586
587        if (start_x == end_x) && (start_y == end_y) {
588            // Degenerate gradient
589            self.rect(rect_x, rect_y, rect_width, rect_height, start_color);
590        } else if start_x == end_x {
591            // Vertical gradient
592            let y_factor = 1.0 / (end_y - start_y) as f64;
593            for y in rect_y..(rect_y + rect_height as i32) {
594                let proj = (y - start_y) as f64 * y_factor;
595                let scale = clamp(proj);
596                let color = Color::interpolate(start_color, end_color, scale);
597                self.line(rect_x, y, rect_x + rect_width as i32 - 1, y, color);
598            }
599        } else if start_y == end_y {
600            // Horizontal gradient
601            let x_factor = 1.0 / (end_x - start_x) as f64;
602            for x in rect_x..(rect_x + rect_width as i32) {
603                let proj = (x - start_x) as f64 * x_factor;
604                let scale = clamp(proj);
605                let color = Color::interpolate(start_color, end_color, scale);
606                self.line(x, rect_y, x, rect_y + rect_height as i32 - 1, color);
607            }
608        } else {
609            // Non axis-aligned gradient
610            // Gradient vector
611            let grad_x = (end_x - start_x) as f64;
612            let grad_y = (end_y - start_y) as f64;
613            let grad_len = 1.0 / (grad_x * grad_x + grad_y * grad_y);
614
615            for y in rect_y..(rect_y + rect_height as i32) {
616                for x in rect_x..(rect_x + rect_width as i32) {
617                    // Pixel vector
618                    let pix_x = (x - start_x) as f64;
619                    let pix_y = (y - start_y) as f64;
620                    // Scalar projection
621                    let proj = (pix_x * grad_x + pix_y * grad_y) * grad_len;
622                    // Saturation
623                    let scale = clamp(proj);
624                    // Interpolation
625                    let color = Color::interpolate(start_color, end_color, scale);
626                    self.pixel(x, y, color);
627                }
628            }
629        }
630    }
631
632    /// Draw a rect with rounded corners
633    #[allow(clippy::too_many_arguments)]
634    fn rounded_rect(
635        &mut self,
636        x: i32,
637        y: i32,
638        w: u32,
639        h: u32,
640        radius: u32,
641        filled: bool,
642        color: Color,
643    ) {
644        let w = w as i32;
645        let h = h as i32;
646        let r = radius as i32;
647
648        if filled {
649            //Draw inside corners
650            self.arc(x + r, y + r, -r, 1 << 4 | 1 << 6, color);
651            self.arc(x + w - 1 - r, y + r, -r, 1 << 5 | 1 << 7, color);
652            self.arc(x + r, y + h - 1 - r, -r, 1 << 0 | 1 << 2, color);
653            self.arc(x + w - 1 - r, y + h - 1 - r, -r, 1 << 1 | 1 << 3, color);
654
655            // Draw inside rectangles
656            self.rect(x + r, y, (w - 1 - r * 2) as u32, r as u32 + 1, color);
657            self.rect(
658                x + r,
659                y + h - 1 - r,
660                (w - 1 - r * 2) as u32,
661                r as u32 + 1,
662                color,
663            );
664            self.rect(x, y + r + 1, w as u32, (h - 2 - r * 2) as u32, color);
665        } else {
666            //Draw outside corners
667            self.arc(x + r, y + r, r, 1 << 4 | 1 << 6, color);
668            self.arc(x + w - 1 - r, y + r, r, 1 << 5 | 1 << 7, color);
669            self.arc(x + r, y + h - 1 - r, r, 1 << 0 | 1 << 2, color);
670            self.arc(x + w - 1 - r, y + h - 1 - r, r, 1 << 1 | 1 << 3, color);
671
672            // Draw outside rectangles
673            self.rect(x + r + 1, y, (w - 2 - r * 2) as u32, 1, color);
674            self.rect(x + r + 1, y + h - 1, (w - 2 - r * 2) as u32, 1, color);
675            self.rect(x, y + r + 1, 1, (h - 2 - r * 2) as u32, color);
676            self.rect(x + w - 1, y + r + 1, 1, (h - 2 - r * 2) as u32, color);
677        }
678    }
679
680    /// Draws antialiased line
681    #[cfg(feature = "std")]
682    fn wu_line(&mut self, x0: i32, y0: i32, x1: i32, y1: i32, color: Color) {
683        //adapted from https://rosettacode.org/wiki/Xiaolin_Wu's_line_algorithm#C.23
684        let mut x0 = x0 as f64;
685        let mut y0 = y0 as f64;
686        let mut x1 = x1 as f64;
687        let mut y1 = y1 as f64;
688        let r = color.r();
689        let g = color.g();
690        let b = color.b();
691        let a = color.a() as f64;
692
693        fn ipart(x: f64) -> i32 {
694            x.trunc() as i32
695        }
696
697        fn fpart(x: f64) -> f64 {
698            if x < 0.0 {
699                return 1.0 - (x - x.floor());
700            }
701            x - x.floor()
702        }
703
704        fn rfpart(x: f64) -> f64 {
705            1.0 - fpart(x)
706        }
707
708        fn chkalpha(mut alpha: f64) -> u8 {
709            if alpha > 255.0 {
710                alpha = 255.0
711            };
712            if alpha < 0.0 {
713                alpha = 0.0
714            };
715            alpha as u8
716        }
717
718        let steep: bool = (y1 - y0).abs() > (x1 - x0).abs();
719        let mut temp;
720        if steep {
721            temp = x0;
722            x0 = y0;
723            y0 = temp;
724            temp = x1;
725            x1 = y1;
726            y1 = temp;
727        }
728        if x0 > x1 {
729            temp = x0;
730            x0 = x1;
731            x1 = temp;
732            temp = y0;
733            y0 = y1;
734            y1 = temp;
735        }
736        let dx = x1 - x0;
737        let dy = y1 - y0;
738        let gradient = dy / dx;
739
740        let mut xend: f64 = x0.round();
741        let mut yend: f64 = y0 + gradient * (xend - x0);
742        let mut xgap: f64 = rfpart(x0 + 0.5);
743        let xpixel1 = xend as i32;
744        let ypixel1 = ipart(yend);
745
746        if steep {
747            self.pixel(
748                ypixel1,
749                xpixel1,
750                Color::rgba(r, g, b, chkalpha(rfpart(yend) * xgap * a)),
751            );
752            self.pixel(
753                ypixel1 + 1,
754                xpixel1,
755                Color::rgba(r, g, b, chkalpha(fpart(yend) * xgap * a)),
756            );
757        } else {
758            self.pixel(
759                xpixel1,
760                ypixel1,
761                Color::rgba(r, g, b, chkalpha(rfpart(yend) * xgap * a)),
762            );
763            self.pixel(
764                xpixel1 + 1,
765                ypixel1,
766                Color::rgba(r, g, b, chkalpha(fpart(yend) * xgap * a)),
767            );
768        }
769        let mut intery: f64 = yend + gradient;
770        xend = x1.round();
771        yend = y1 + gradient * (xend - x1);
772        xgap = fpart(x1 + 0.5);
773        let xpixel2 = xend as i32;
774        let ypixel2 = ipart(yend);
775        if steep {
776            self.pixel(
777                ypixel2,
778                xpixel2,
779                Color::rgba(r, g, b, chkalpha(rfpart(yend) * xgap * a)),
780            );
781            self.pixel(
782                ypixel2 + 1,
783                xpixel2,
784                Color::rgba(r, g, b, chkalpha(fpart(yend) * xgap * a)),
785            );
786        } else {
787            self.pixel(
788                xpixel2,
789                ypixel2,
790                Color::rgba(r, g, b, chkalpha(rfpart(yend) * xgap * a)),
791            );
792            self.pixel(
793                xpixel2 + 1,
794                ypixel2,
795                Color::rgba(r, g, b, chkalpha(fpart(yend) * xgap * a)),
796            );
797        }
798        if steep {
799            for x in (xpixel1 + 1)..(xpixel2) {
800                self.pixel(
801                    ipart(intery),
802                    x,
803                    Color::rgba(r, g, b, chkalpha(a * rfpart(intery))),
804                );
805                self.pixel(
806                    ipart(intery) + 1,
807                    x,
808                    Color::rgba(r, g, b, chkalpha(a * fpart(intery))),
809                );
810                intery += gradient;
811            }
812        } else {
813            for x in (xpixel1 + 1)..(xpixel2) {
814                self.pixel(
815                    x,
816                    ipart(intery),
817                    Color::rgba(r, g, b, chkalpha(a * rfpart(intery))),
818                );
819                self.pixel(
820                    x,
821                    ipart(intery) + 1,
822                    Color::rgba(r, g, b, chkalpha(a * fpart(intery))),
823                );
824                intery += gradient;
825            }
826        }
827    }
828
829    ///Draws antialiased circle
830    #[cfg(feature = "std")]
831    fn wu_circle(&mut self, x0: i32, y0: i32, radius: i32, color: Color) {
832        let r = color.r();
833        let g = color.g();
834        let b = color.b();
835        let a = color.a();
836        let mut y = 0;
837        let mut x = radius;
838        let mut d = 0_f64;
839
840        self.pixel(x0 + x, y0 + y, color);
841        self.pixel(x0 - x, y0 - y, color);
842        self.pixel(x0 + y, y0 - x, color);
843        self.pixel(x0 - y, y0 + x, color);
844
845        while x > y {
846            let di = dist(radius, y);
847            if di < d {
848                x -= 1;
849            }
850            let col = Color::rgba(r, g, b, (a as f64 * (1.0 - di)) as u8);
851            let col2 = Color::rgba(r, g, b, (a as f64 * di) as u8);
852
853            self.pixel(x0 + x, y0 + y, col);
854            self.pixel(x0 + x - 1, y0 + y, col2); //-
855            self.pixel(x0 - x, y0 + y, col);
856            self.pixel(x0 - x + 1, y0 + y, col2); //+
857            self.pixel(x0 + x, y0 - y, col);
858            self.pixel(x0 + x - 1, y0 - y, col2); //-
859            self.pixel(x0 - x, y0 - y, col);
860            self.pixel(x0 - x + 1, y0 - y, col2); //+
861
862            self.pixel(x0 + y, y0 + x, col);
863            self.pixel(x0 + y, y0 + x - 1, col2);
864            self.pixel(x0 - y, y0 + x, col);
865            self.pixel(x0 - y, y0 + x - 1, col2);
866            self.pixel(x0 + y, y0 - x, col);
867            self.pixel(x0 + y, y0 - x + 1, col2);
868            self.pixel(x0 - y, y0 - x, col);
869            self.pixel(x0 - y, y0 - x + 1, col2);
870            d = di;
871            y += 1;
872        }
873
874        fn dist(r: i32, y: i32) -> f64 {
875            let x: f64 = ((r * r - y * y) as f64).sqrt();
876            x.ceil() - x
877        }
878    }
879
880    ///Gets pixel color at x,y position
881    fn getpixel(&self, x: i32, y: i32) -> Color {
882        let p = (self.width() as i32 * y + x) as usize;
883        if p >= self.data().len() {
884            return Color::rgba(0, 0, 0, 0);
885        }
886        self.data()[p]
887    }
888}