Pixels problem in softbuffer and skia

Hello, I apologize for the many questions lately, I am a bit of a beginner.

When I run the script:

use std::num::NonZeroU32;
use softbuffer::{Context, Surface};
use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;
use skia_safe::{surfaces,Color, Color4f, Paint,PaintStyle, Font, Typeface, FontStyle, Data, FontMgr, ColorType, ImageInfo, AlphaType, ColorSpace, SurfaceProps, SurfacePropsFlags,PixelGeometry};
use std::fs::{File, read};

fn main(){

    let event_loop = EventLoop::new();
    // event_loop.set_control_flow(ControlFlow::Wait);
    let window = WindowBuilder::new().build(&event_loop).unwrap();
    let context = unsafe { Context::new(&window) }.unwrap();
    let mut surface = unsafe { Surface::new(&context, &window) }.unwrap();


    // Set pixel's color variables
    let mut last_pixel = Color4f{r:0.0 , g:0.0 , b:0.0 , a:0.0};
    let mut _current_pixel  = Color4f{r:0.0 , g:0.0 , b:0.0 , a:0.0};

    let (width, height) = {
                    let size = window.inner_size();
                    (size.width, size.height)
    };


    let ii = ImageInfo::new((width as i32,height as i32), ColorType::RGBA8888, AlphaType::Premul, ColorSpace::new_srgb());
    let mut visible_buffer = surfaces::raster(&ii,(0) as usize, Some(&SurfaceProps::default())).expect("surface");

    // let mut pixmap = visible_buffer.canvas().peek_pixels().unwrap().bytes().unwrap();

        // Run event loop
    event_loop.run(move |event, _, control_flow| {
        *control_flow = ControlFlow::Wait;
    
        match event {
             // When window opened or changed its size, redraw its graphics content
            Event::RedrawRequested(window_id) if window_id == window.id() => {
                let (width, height) = {
                    let size = window.inner_size();
                    (size.width, size.height)
                };
                surface
                    .resize(
                        NonZeroU32::new(width).unwrap(),
                        NonZeroU32::new(height).unwrap(),
                    )
                    .unwrap();

                let ii = ImageInfo::new((width as i32,height as i32), ColorType::RGBA8888, AlphaType::Premul, ColorSpace::new_srgb());
                let mut visible_buffer = surfaces::raster(&ii,(0) as usize, Some(&SurfaceProps::default())).expect("surface");
                visible_buffer.canvas().clear(Color::WHITE);
                
                let mut paint = Paint::default();
                paint.set_color(Color::BLUE);
                paint.set_anti_alias(true);
                paint.set_style(PaintStyle::Fill);
                
                visible_buffer.canvas().draw_circle(((width/2) as f32, (height/2) as f32),100.0 , &paint);

                let mut fpaint = Paint::default();
                fpaint.set_color(Color::RED);
                fpaint.set_style(PaintStyle::Fill);

                // let font = Font::from_typeface(Typeface::from_name("Times", FontStyle::bold_italic()).unwrap(), 20.0);
                // let font_mgr = FontMgr::new();
                let font_raw = std::fs::read("src/fonts/ColombiaItalic-BWGZB.ttf").unwrap();
                let font_data = unsafe { Data::new_bytes(font_raw.as_slice()) };
                let font = Font::from_typeface(Typeface::from_data(font_data, None).unwrap(), 30.0);

                visible_buffer.canvas().draw_str("string works??", ((width/2) as f32, (height/2) as f32), &font, &fpaint);
                
                
                
                let pixmap = visible_buffer.canvas().peek_pixels().unwrap().bytes().unwrap();


                let mut buffer = surface.buffer_mut().unwrap();
                for index in 0..(width * height) as usize {
                    buffer[index] = pixmap[index * 4 + 2] as u32
                        | (pixmap[index * 4 + 1] as u32) << 8
                        | (pixmap[index * 4] as u32) << 16;
                }
                buffer.present().unwrap();
            }

            Event::WindowEvent { event, .. } => match event {
                // Close the software when window closed
                WindowEvent::CloseRequested => control_flow.set_exit(),
                WindowEvent::CursorMoved { position,device_id , modifiers } 
                    => {
                        let x = position.x as i32;
                        let y = position.y as i32;
                        
                        // thread 'main' panicked at 'assertion failed: p.x >= 0 && p.x < self.width()'
                        _current_pixel = visible_buffer.canvas().peek_pixels().unwrap().get_color_4f((x,y));
                        if last_pixel != _current_pixel {
                            last_pixel = _current_pixel;
                            println!("mouse pixel: {last_pixel:?}")
                        };
                    }
            _ => {}
        }_ => {}



    };
    });
    }

I get an error message in the terminal:

thread 'main' panicked at 'assertion failed: p.x >= 0 && p.x < self.width()'

I think the problem is caused by this part of the script:

                        let x = position.x as i32;
                        let y = position.y as i32;
                        
                        // thread 'main' panicked at 'assertion failed: p.x >= 0 && p.x < self.width()'
                        _current_pixel = visible_buffer.canvas().peek_pixels().unwrap().get_color_4f((x,y));
                        if last_pixel != _current_pixel {
                            last_pixel = _current_pixel;
                            println!("mouse pixel: {last_pixel:?}")
                        };

If there are any improvements or any other possible errors, I would really appreciate it if you let me know.

You should probably include a backtrace in your messages when you get a panic. Otherwise it can be difficult to reproduce the error to help diagnose it. I had to reverse engineer the dependencies you were using (skia-safe@0.68, softbuffer@0.3, winit@0.28) to capture the backtrace:

Click to expand
assertion failed: p.x >= 0 && p.x < self.width()
stack backtrace:
   0: std::panicking::begin_panic_handler
             at /rustc/df871fbf053de3a855398964cd05fadbe91cf4fd/library\std\src\panicking.rs:597
   1: core::panicking::panic_fmt
             at /rustc/df871fbf053de3a855398964cd05fadbe91cf4fd/library\core\src\panicking.rs:72
   2: core::panicking::panic
             at /rustc/df871fbf053de3a855398964cd05fadbe91cf4fd/library\core\src\panicking.rs:127
   3: skia_safe::core::pixmap::Pixmap::assert_pixel_exists<skia_safe::core::point::IPoint>
             at C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f\skia-safe-0.68.0\src\core\pixmap.rs:161
   4: skia_safe::core::pixmap::Pixmap::get_color_4f<tuple$<i32,i32> >
             at C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f\skia-safe-0.68.0\src\core\pixmap.rs:147
   5: sbskia::main::closure$0
             at .\src\main.rs:134
   6: winit::platform_impl::platform::event_loop::impl$3::run_return::closure$0<tuple$<>,sbskia::main::closure_env$0>
             at C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f\winit-0.28.7\src\platform_impl\windows\event_loop.rs:260
   7: alloc::boxed::impl$48::call_mut<tuple$<enum2$<winit::event::Event<tuple$<> > >,ref_mut$<enum2$<winit::event_loop::ControlFlow> > >,dyn$<core::ops::function::FnMut<tuple$<enum2$<winit::event::Event<tuple$<> > >,ref_mut$<enum2$<winit::event_loop::ControlFlo
             at /rustc/df871fbf053de3a855398964cd05fadbe91cf4fd\library\alloc\src\boxed.rs:2014
   8: winit::platform_impl::platform::event_loop::runner::impl$3::call_event_handler::closure$0<tuple$<> >
             at C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f\winit-0.28.7\src\platform_impl\windows\event_loop\runner.rs:250
   9: core::panic::unwind_safe::impl$23::call_once<tuple$<>,winit::platform_impl::platform::event_loop::runner::impl$3::call_event_handler::closure_env$0<tuple$<> > >
             at /rustc/df871fbf053de3a855398964cd05fadbe91cf4fd\library\core\src\panic\unwind_safe.rs:271
  10: std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<winit::platform_impl::platform::event_loop::runner::impl$3::call_event_handler::closure_env$0<tuple$<> > >,tuple$<> >
             at /rustc/df871fbf053de3a855398964cd05fadbe91cf4fd\library\std\src\panicking.rs:504
  11: alloc::collections::vec_deque::impl$1::drop<enum2$<winit::platform_impl::platform::event_loop::runner::BufferedEvent<tuple$<> > >,alloc::alloc::Global>
  12: std::panicking::try<tuple$<>,core::panic::unwind_safe::AssertUnwindSafe<winit::platform_impl::platform::event_loop::runner::impl$3::call_event_handler::closure_env$0<tuple$<> > > >
             at /rustc/df871fbf053de3a855398964cd05fadbe91cf4fd\library\std\src\panicking.rs:468
  13: std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<winit::platform_impl::platform::event_loop::runner::impl$3::call_event_handler::closure_env$0<tuple$<> > >,tuple$<> >
             at /rustc/df871fbf053de3a855398964cd05fadbe91cf4fd\library\std\src\panic.rs:142
  14: winit::platform_impl::platform::event_loop::runner::EventLoopRunner<tuple$<> >::catch_unwind<tuple$<>,tuple$<>,winit::platform_impl::platform::event_loop::runner::impl$3::call_event_handler::closure_env$0<tuple$<> > >
             at C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f\winit-0.28.7\src\platform_impl\windows\event_loop\runner.rs:157
  15: winit::platform_impl::platform::event_loop::runner::EventLoopRunner<tuple$<> >::call_event_handler<tuple$<> >
             at C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f\winit-0.28.7\src\platform_impl\windows\event_loop\runner.rs:242
  16: winit::platform_impl::platform::event_loop::runner::EventLoopRunner<tuple$<> >::send_event<tuple$<> >
             at C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f\winit-0.28.7\src\platform_impl\windows\event_loop\runner.rs:224
  17: winit::platform_impl::platform::event_loop::WindowData<tuple$<> >::send_event<tuple$<> >
             at C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f\winit-0.28.7\src\platform_impl\windows\event_loop.rs:142
  18: winit::platform_impl::platform::event_loop::public_window_callback_inner::closure$0<tuple$<> >
             at C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f\winit-0.28.7\src\platform_impl\windows\event_loop.rs:1475
  19: core::ops::function::FnOnce::call_once<winit::platform_impl::platform::event_loop::public_window_callback_inner::closure_env$0<tuple$<> >,tuple$<> >
             at /rustc/df871fbf053de3a855398964cd05fadbe91cf4fd\library\core\src\ops\function.rs:250
  20: core::panic::unwind_safe::impl$23::call_once<isize,winit::platform_impl::platform::event_loop::public_window_callback_inner::closure_env$0<tuple$<> > >
             at /rustc/df871fbf053de3a855398964cd05fadbe91cf4fd\library\core\src\panic\unwind_safe.rs:271
  21: std::panicking::try::do_call<core::panic::unwind_safe::AssertUnwindSafe<winit::platform_impl::platform::event_loop::public_window_callback_inner::closure_env$0<tuple$<> > >,isize>
             at /rustc/df871fbf053de3a855398964cd05fadbe91cf4fd\library\std\src\panicking.rs:504
  22: std::panicking::try::do_catch<core::panic::unwind_safe::AssertUnwindSafe<winit::platform_impl::platform::event_loop::public_window_callback_inner::closure_env$0<tuple$<> > >,isize>
  23: std::panicking::try<isize,core::panic::unwind_safe::AssertUnwindSafe<winit::platform_impl::platform::event_loop::public_window_callback_inner::closure_env$0<tuple$<> > > >
             at /rustc/df871fbf053de3a855398964cd05fadbe91cf4fd\library\std\src\panicking.rs:468
  24: std::panic::catch_unwind<core::panic::unwind_safe::AssertUnwindSafe<winit::platform_impl::platform::event_loop::public_window_callback_inner::closure_env$0<tuple$<> > >,isize>
             at /rustc/df871fbf053de3a855398964cd05fadbe91cf4fd\library\std\src\panic.rs:142
  25: winit::platform_impl::platform::event_loop::runner::EventLoopRunner<tuple$<> >::catch_unwind<tuple$<>,isize,winit::platform_impl::platform::event_loop::public_window_callback_inner::closure_env$0<tuple$<> > >
             at C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f\winit-0.28.7\src\platform_impl\windows\event_loop\runner.rs:157
  26: winit::platform_impl::platform::event_loop::public_window_callback_inner<tuple$<> >
             at C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f\winit-0.28.7\src\platform_impl\windows\event_loop.rs:2320
  27: winit::platform_impl::platform::event_loop::public_window_callback<tuple$<> >
             at C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f\winit-0.28.7\src\platform_impl\windows\event_loop.rs:994
  28: DispatchMessageW
  29: DispatchMessageW
  30: winit::platform_impl::platform::event_loop::EventLoop<tuple$<> >::run_return<tuple$<>,sbskia::main::closure_env$0>
             at C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f\winit-0.28.7\src\platform_impl\windows\event_loop.rs:282
  31: winit::platform_impl::platform::event_loop::EventLoop<tuple$<> >::run<tuple$<>,sbskia::main::closure_env$0>
             at C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f\winit-0.28.7\src\platform_impl\windows\event_loop.rs:245
  32: winit::event_loop::EventLoop<tuple$<> >::run<tuple$<>,sbskia::main::closure_env$0>
             at C:\Users\user\.cargo\registry\src\index.crates.io-6f17d22bba15001f\winit-0.28.7\src\event_loop.rs:305
  33: sbskia::main
             at .\src\main.rs:50
  34: core::ops::function::FnOnce::call_once<void (*)(),tuple$<> >
             at /rustc/df871fbf053de3a855398964cd05fadbe91cf4fd\library\core\src\ops\function.rs:250

This points at the .get_color_4f((x,y)) as you suspected. And I can reproduce the panic by resizing the window to a larger size and then moving the mouse inside the window at the lower-right corner.

A much scarier thing happens with this app though: moving the mouse into the window from the left side causes a segfault, even without resizing the window. And this code doesn't have the unsafe keyword anywhere. Edit: Yes, it does! My grep didn't find it (lol?). I'm not going to be debugging the segfault. But heads up, this app has some unsoundness.

As for why it panics when moving the mouse, it's because you are creating the surface when the app starts, using the window's default size, then you resize the surface for redrawing, but you don't update the variable binding for the cursor move event. Remove the let mut binding on line 52 to fix the panic. This updates the variable in the outer scope, which will then be visible to the cursor move handler.

3 Likes

Well, I think I managed to come up with a solution, however I'm not sure if it's the perfect solution:

use std::num::NonZeroU32;
use softbuffer::{Context, Surface};
use winit::event::{Event, WindowEvent};
use winit::event_loop::{ControlFlow, EventLoop};
use winit::window::WindowBuilder;
use skia_safe::{surfaces,Color, Color4f, Paint,PaintStyle, Font, Typeface, FontStyle, Data, FontMgr, ColorType, ImageInfo, AlphaType, ColorSpace, SurfaceProps, SurfacePropsFlags,PixelGeometry};
use std::fs::{File, read};

fn main(){
    let event_loop = EventLoop::new();
    // event_loop.set_control_flow(ControlFlow::Wait);
    let window = WindowBuilder::new().build(&event_loop).unwrap();
    let context = unsafe { Context::new(&window) }.unwrap();
    let mut surface = unsafe { Surface::new(&context, &window) }.unwrap();


    // Set pixel's color variables
    let mut last_pixel = Color::from_argb(0 , 0 , 0 , 0);
    let mut _current_pixel  = Color::from_argb(0 , 0 , 0 , 0);

    let (width, height) = {
                    let size = window.inner_size();
                    (size.width, size.height)
    };


    let mut ii = ImageInfo::new((width as i32,height as i32), ColorType::RGBA8888, AlphaType::Premul, ColorSpace::new_srgb());
    let mut visible_buffer = surfaces::raster(&ii,(0) as usize, Some(&SurfaceProps::default())).expect("surface");

        // Run event loop
    event_loop.run(move |event, _, control_flow| {
        *control_flow = ControlFlow::Wait;
    
        match event {
             // When window opened or changed its size, redraw its graphics content
            Event::RedrawRequested(window_id) if window_id == window.id() => {
                let (width, height) = {
                    let size = window.inner_size();
                    (size.width, size.height)
                };
                surface
                    .resize(
                        NonZeroU32::new(width).unwrap(),
                        NonZeroU32::new(height).unwrap(),
                    )
                    .unwrap();

                let iii = ImageInfo::new((width as i32,height as i32), ColorType::RGBA8888, AlphaType::Premul, ColorSpace::new_srgb());
                visible_buffer = surfaces::raster(&iii,(0) as usize, Some(&SurfaceProps::default())).expect("surface");
                visible_buffer.canvas().clear(Color::WHITE);
                
                let mut paint = Paint::default();
                paint.set_color(Color::BLUE);
                paint.set_anti_alias(true);
                paint.set_style(PaintStyle::Fill);
                
                visible_buffer.canvas().draw_circle(((width/2) as f32, (height/2) as f32),100.0 , &paint);

                let mut fpaint = Paint::default();
                fpaint.set_color(Color::RED);
                fpaint.set_style(PaintStyle::Fill);

                // let font = Font::from_typeface(Typeface::from_name("Times", FontStyle::bold_italic()).unwrap(), 20.0);
                // let font_mgr = FontMgr::new();
                let font_raw = std::fs::read("src/fonts/ColombiaItalic-BWGZB.ttf").unwrap();
                let font_data = unsafe { Data::new_bytes(font_raw.as_slice()) };
                let font = Font::from_typeface(Typeface::from_data(font_data, None).unwrap(), 30.0);

                visible_buffer.canvas().draw_str("string works??", ((width/2) as f32, (height/2) as f32), &font, &fpaint);
                
                let pixmap = visible_buffer.canvas().peek_pixels().unwrap().bytes().unwrap();


                let mut buffer = surface.buffer_mut().unwrap();
                for index in 0..(width * height) as usize {  
                    buffer[index] = pixmap[index * 4 + 2] as u32
                        | (pixmap[index * 4 + 1] as u32) << 8
                        | (pixmap[index * 4] as u32) << 16;
                }
                buffer.present().unwrap();
            }

            Event::WindowEvent { event, .. } => match event {
                // Close the software when window closed
                WindowEvent::CloseRequested => control_flow.set_exit(),
                WindowEvent::CursorMoved { position,device_id , modifiers } 
                    => {
                        let x = position.x as i32;
                        let y = position.y as i32;
                        
                        // println!("mouse position: {}, canvas size: {}", (x*y), visible_buffer.canvas().image_info().bounds().width()*visible_buffer.canvas().image_info().bounds().height());
                        if (x <=  visible_buffer.canvas().image_info().bounds().width()) &&(y <= visible_buffer.canvas().image_info().bounds().height()) && (x>0) && (y>0){
                            _current_pixel = visible_buffer.canvas().peek_pixels().unwrap().get_color((x,y));
                        }
                        // _current_pixel = visible_buffer.canvas().peek_pixels().unwrap().get_color((x,y));
                        if last_pixel != _current_pixel {
                            last_pixel = _current_pixel;
                            println!("mouse pixel: {last_pixel:?}")
                        };
                    }
                // WindowEvent::CursorMoved { position,device_id , modifiers } 
                // => {}
            _ => {}
        }_ => {}



    };
    });
    }

I set a condition so that pixels are not read when the mouse cursor coordinates are outside the window range.

The reason for this is that when I read the location of the mouse cursor in relation to the window:

// println!("mouse position: {}, canvas size: {}", (x*y), visible_buffer.canvas().image_info().bounds().width()*visible_buffer.canvas().image_info().bounds().height());

it is correct, except when I minimize the window and show the window again, the mouse coordinates are either larger than the window size, or the value of the mouse coordinates is negative.

Right now, I'm not sure about anything since I'm still a beginner.