Important: Please read the Qt Code of Conduct - https://forum.qt.io/topic/113070/qt-code-of-conduct

Making a Paint Program: Drawing brush preview near cursor efficiently



  • Hello everybody,

    I'm working on an art editor - image Microsoft Paint but for index painting. Here's my setup:

    • I have a class called ImageModel, which holds a QImage for the pixel data and a QVector<QRgb> for the color palette. The QImage uses QImage::Format::Format_Indexed8.

    • The central widget of my main window is a QScrollArea that holds my ImageWidget. This class visually represents the image that the user is working on, the class inherits from QWidget and intercepts mouse events.

    • If the user has clicked on the ImageWidget, it calls a function in the ImageModel, which makes changes according to the user input to its QImage. The ImageModel then emits a signal that is connected to a slot in the Image and causes a partial repaint , updating the changed area of the ImageWidget. I use the rect parameter of the repaint() function to achieve this.

    And here is my issue: most art editors draw a 'preview' of your brush/line/etc on the image as you hover your mouse over the image. This could be a single pixel in your selected color or a round brush for example. I want to recreate this effect but am having trouble with my chosen setup.

    • I don't want to make actual changes to my ImageModel, since the preview does not actually draw to the image document. Instead I create and overlap a PreviewWidget just to render the effect.

    • Calling repaint() without the rect parameter is surprisingly slow. I'd estimate 1-2 fps for a tiny 128x128 at scale x4. This means that the pixel following the cursor lags far behind the actual mouse position and drawing rounded shapes like this becomes impossible.

    • Using the rect parameter to just update the pixels around the cursor means that I leave a trace behind, as it appears that only calling repaint() without the rect parameter clears the entire image. All my attempts to manually erase anything outside a given rect have been unsuccessful.

    I am kinda out of ideas at this point, any input would be greatly appreciated.


  • Moderators

    @nihil said in Making a Paint Program: Drawing brush preview near cursor efficiently:

    Using the rect parameter to just update the pixels around the cursor means that I leave a trace behind, as it appears that only calling repaint() without the rect parameter clears the entire image. All my attempts to manually erase anything outside a given rect have been unsuccessful.

    you could simply increase the rect, by expanding it to a bounding rect of your current position and the (stored) previous position.

    Alternatively you could paint a QPixmap on the fly whenever you change your brush and create a QCursor and apply it on your paint widget.



  • Thanks for your reply.

    @raven-worx said in Making a Paint Program: Drawing brush preview near cursor efficiently:

    you could simply increase the rect, by expanding it to a bounding rect of your current position and the (stored) previous position.

    I like this idea. It's going to be slow if the cursor leaves the ImageWidget near the top-left corner and re-enters from the bottom-right for example, but at least that is only a single slow draw call.

    @raven-worx said in Making a Paint Program: Drawing brush preview near cursor efficiently:

    Alternatively you could paint a QPixmap on the fly whenever you change your brush and create a QCursor and apply it on your paint widget.

    My problem with this approach is that I also still want a cursor to be displayed and the preview also needs to work at different zoom levels (and snap to the image pixel grid), which is difficult to achieve with the mouse cursor I believe.

    I'm going to give your first idea a shot, thanks again! If there are any further ideas, please don't hesitate to reply.


  • Moderators

    @nihil said in Making a Paint Program: Drawing brush preview near cursor efficiently:

    I like this idea. It's going to be slow if the cursor leaves the ImageWidget near the top-left corner and re-enters from the bottom-right for example, but at least that is only a single slow draw call.

    then issue 2 redraws with 2 minimal rects from both positions



  • @raven-worx said in Making a Paint Program: Drawing brush preview near cursor efficiently:

    @nihil said in Making a Paint Program: Drawing brush preview near cursor efficiently:

    I like this idea. It's going to be slow if the cursor leaves the ImageWidget near the top-left corner and re-enters from the bottom-right for example, but at least that is only a single slow draw call.

    then issue 2 redraws with 2 minimal rects from both positions

    Of course, why didn't I think of this? :) Thanks!



  • @raven-worx I finally had some time to implement this, it works beautifully and is very fast. Thank you so much for your help. I will mark the topic solved.


Log in to reply