Week 4 recap - Kisbrushop and drawDDAline

·

3 min read

Currently, my goal is to implement the pixel-perfect algorithm on just brushes that have sharpness checked and are no bigger than 1 pixel before moving on to other brush options. If we look at KisBrushOp::paintLine here:

void KisBrushOp::paintLine(const KisPaintInformation& pi1, const KisPaintInformation& pi2, KisDistanceInformation *currentDistance)
{
    //qDebug() << "KisBrushOp::paintLine";
    if (m_sharpnessOption.isChecked() && m_brush && (m_brush->width() == 1) && (m_brush->height() == 1)) {

        if (!m_lineCacheDevice) {
            m_lineCacheDevice = source()->createCompositionSourceDevice();
        }
        else {
            m_lineCacheDevice->clear();
        }

        KisPainter p(m_lineCacheDevice);
        p.setPaintColor(painter()->paintColor());
        p.drawDDALine(pi1.pos(), pi2.pos());

        QRect rc = m_lineCacheDevice->extent();
        painter()->bitBlt(rc.x(), rc.y(), m_lineCacheDevice, rc.x(), rc.y(), rc.width(), rc.height());
    //fixes Bug 338011
    painter()->renderMirrorMask(rc, m_lineCacheDevice);
    }
    else {
        KisPaintOp::paintLine(pi1, pi2, currentDistance);
    }
}

this specific criteria will lead to calling drawDDALine which is a function that handles plotting points based on a start and end. This is gonna be the core mechanic I have to work with because pixel-perfect involves direct manipulation of plotted points.

Right now I'm working on a few different approaches. Ricky Han's concept of using a vector of points and filtering out the corners in any L-shaped lines seems nice but, the problem I discovered going with this approach is that drawDDALine is often called so fast that it is just plotting each individual point when drawing slow strokes so we can't really just filter out points in a line here. These functions take position 1(start) and position 2(end) as their parameters and not necessarily vectors of points so at the moment, I'm not sure what is the best way to handle all these micro calls that have start and end coordinates. (Noting here that maybe we can use this filtered vector approach in a different/higher function on the logic flow of KisToolFreeHand::doStroke -> KisPainter::drawDDALine where the parameter/input is in the form of a vector?)

Another idea I had is that since KisBrushOp::paintLine takes/feeds KisPaintInformation& pi1, KisPaintInformation& pi2 which is just the beginning coordinates and ending coordinates of the stroke we can just see if there are any forbidden corner pixels in that line and since this is called upon so often we can look for L shape lines, but this also doesn't work because a generated line from one point to another will always be pixel-perfect already and it seems completely dependent on the speed of the stroke whether or not we end up getting a diagonal L shape inside this two coordinate system.

The latest approach I'm working on is Tom Cantwell's method. In his program, it seems that he also just feeds a list of points points (which is also what Aseprite does) to what is drawing but something we can use from his implementation is the tracking of coordinates. I have been trying to figure out a way to use static variables to track the current, waiting, and last drawn pixel and only draw the waiting pixel when the current pixel is adjacent or in the same direction as the last drawn pixel. The method seems promising but again the main problem is for every paintLine call the input isn't just the line from the beginning of mouse click to the end of the mouse letting go, but instead just a constant stream of coordinates (sometimes overlapping). I'm sure there is a work around and I'll be working on this.

I think with this approach the program doesn't have to have a backlog of points and instead proactively chooses what gets drawn which might work. Still trying to wrap my head around a solution that involves using the KisPaintInformation& pi1,KisPaintInformation& pi2 that is consistently getting fetched but I think I'm close. Stay tuned!