Qt Raster模式下直线的光栅化算法

Qt绘制直线算法底层使用的还是DDA算法,用F26Dot6和FDot16两种格式将小数计算转换成整数计算。下面就是对源代码进行一下注释。(F26Dot格式指的是将32位的int,前26位表示一个数的整数部分,后6位表示小数部分。FDot16格式指的是32位int,前16位表示整数,后16位表示小数)

static bool drawLine(QCosmeticStroker *stroker, qreal rx1, qreal ry1, qreal rx2, qreal ry2, int caps)
{
    bool didDraw = qAbs(rx2 - rx1) + qAbs(ry2 - ry1) >= 1.0;

    if (stroker->clipLine(rx1, ry1, rx2, ry2))
        return true;

    int x1 = toF26Dot6(rx1);  // 这里是转换成F26Dot6的格式,
    int y1 = toF26Dot6(ry1);  // F26Dot6 是将32位的int 前26为作为整数部分,后6位作为小数部分进行存储
    int x2 = toF26Dot6(rx2);  // 整数的计算会比小数快很多
    int y2 = toF26Dot6(ry2);

    int dx = qAbs(x2 - x1);
    int dy = qAbs(y2 - y1);

    QCosmeticStroker::Point last = stroker->lastPixel;

//    qDebug() << "stroke" << x1/64. << y1/64. << x2/64. << y2/64.;

    if (dx < dy) {
        // vertical
        QCosmeticStroker::Direction dir = QCosmeticStroker::TopToBottom;

        bool swapped = false;
        if (y1 > y2) {
            swapped = true;
            qSwap(y1, y2);
            qSwap(x1, x2);
            caps = swapCaps(caps);
            dir = QCosmeticStroker::BottomToTop;
        }
        FDot16 xinc = FDot16FixedDiv(x2 - x1, y2 - y1);
        FDot16 x = FDot16(x1) * (1<<10);

        if ((stroker->lastDir ^ QCosmeticStroker::VerticalMask) == dir)
            caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin;

        capAdjust(caps, y1, y2, x, xinc);

        int y = (y1 + 32) >> 6;
        int ys = (y2 + 32) >> 6;
        int round = (xinc > 0) ? 32 : 0;

        // If capAdjust made us round away from what calculateLastPoint gave us,
        // round back the other way so we start and end on the right point.
        if ((caps & QCosmeticStroker::CapBegin) && stroker->lastPixel.y == y + 1)
           y++;

        if (y != ys) {
            x += ((y * (1<<6)) + round - y1) * xinc >> 6;

            // calculate first and last pixel and perform dropout control
            QCosmeticStroker::Point first;
            first.x = x >> 16;
            first.y = y;
            last.x = (x + (ys - y - 1)*xinc) >> 16;
            last.y = ys - 1;
            if (swapped)
                qSwap(first, last);

            bool axisAligned = qAbs(xinc) < (1 << 14);
            if (stroker->lastPixel.x > INT_MIN) {
                if (first.x == stroker->lastPixel.x &&
                    first.y == stroker->lastPixel.y) {
                    // remove duplicated pixel
                    if (swapped) {
                        --ys;
                    } else {
                        ++y;
                        x += xinc;
                    }
                } else if (stroker->lastDir != dir &&
                           (((axisAligned && stroker->lastAxisAligned) &&
                             stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
                            (qAbs(stroker->lastPixel.x - first.x) > 1 ||
                             qAbs(stroker->lastPixel.y - first.y) > 1))) {
                    // have a missing pixel, insert it
                    if (swapped) {
                        ++ys;
                    } else {
                        --y;
                        x -= xinc;
                    }
                } else if (stroker->lastDir == dir &&
                           ((qAbs(stroker->lastPixel.x - first.x) <= 1 &&
                             qAbs(stroker->lastPixel.y - first.y) > 1))) {
                    x += xinc >> 1;
                    if (swapped)
                        last.x = (x >> 16);
                    else
                        last.x = (x + (ys - y - 1)*xinc) >> 16;
                }
            }
            stroker->lastDir = dir;
            stroker->lastAxisAligned = axisAligned;

            Dasher dasher(stroker, swapped, y * (1<<6), ys * (1<<6));

            do {
                if (dasher.on())
                    drawPixel(stroker, x >> 16, y, 255);
                dasher.adjust();
                x += xinc;
            } while (++y < ys);
            didDraw = true;
        }
    } else {
        // horizontal
        if (!dx)
            return true;

        QCosmeticStroker::Direction dir = QCosmeticStroker::LeftToRight;

        bool swapped = false;
        if (x1 > x2) {
            swapped = true;
            qSwap(x1, x2);
            qSwap(y1, y2);
            caps = swapCaps(caps);
            dir = QCosmeticStroker::RightToLeft;
        }
        // 这里是以FDot16格式算出直线的斜率
        // FDot16格式指的是整数占16位,小数占16位
        FDot16 yinc = FDot16FixedDiv(y2 - y1, x2 - x1);
        
        // 这一步的意思是将F26Dot6格式的y1转成FDot16格式
        // 乘1<<10 表示向左移动10位,dot6向左移动10位就变成了dot16
        FDot16 y = FDot16(y1) * (1<<10);

        if ((stroker->lastDir ^ QCosmeticStroker::HorizontalMask) == dir)
            caps |= swapped ? QCosmeticStroker::CapEnd : QCosmeticStroker::CapBegin;

        capAdjust(caps, x1, x2, y, yinc);
        
        // 这里的意思是把当前点往前走半个像素,然后转换成取整数部分(x1是F26Dot6)
        int x = (x1 + 32) >> 6;
        int xs = (x2 + 32) >> 6;
        int round = (yinc > 0) ? 32 : 0;

        // If capAdjust made us round away from what calculateLastPoint gave us,
        // round back the other way so we start and end on the right point.
        if ((caps & QCosmeticStroker::CapBegin) && stroker->lastPixel.x == x + 1)
            x++;

        if (x != xs) {
            y += ((x * (1<<6)) + round - x1) * yinc >> 6;

            // calculate first and last pixel to perform dropout control
            QCosmeticStroker::Point first;、
            // 计算起点和终点都是只保留了整数
            first.x = x;
            first.y = y >> 16;
            last.x = xs - 1;
            last.y = (y + (xs - x - 1)*yinc) >> 16;
            if (swapped)
                qSwap(first, last);

            bool axisAligned = qAbs(yinc) < (1 << 14);
            if (stroker->lastPixel.x > INT_MIN) {
                if (first.x == stroker->lastPixel.x && first.y == stroker->lastPixel.y) {
                    // remove duplicated pixel
                    if (swapped) {
                        --xs;
                    } else {
                        ++x;
                        y += yinc;
                    }
                } else if (stroker->lastDir != dir &&
                           (((axisAligned && stroker->lastAxisAligned) &&
                             stroker->lastPixel.x != first.x && stroker->lastPixel.y != first.y) ||
                            (qAbs(stroker->lastPixel.x - first.x) > 1 ||
                             qAbs(stroker->lastPixel.y - first.y) > 1))) {
                    // have a missing pixel, insert it
                    if (swapped) {
                        ++xs;
                    } else {
                        --x;
                        y -= yinc;
                    }
                } else if (stroker->lastDir == dir &&
                           ((qAbs(stroker->lastPixel.x - first.x) <= 1 &&
                             qAbs(stroker->lastPixel.y - first.y) > 1))) {
                    y += yinc >> 1;
                    if (swapped)
                        last.y = (y >> 16);
                    else
                        last.y = (y + (xs - x - 1)*yinc) >> 16;
                }
            }
            stroker->lastDir = dir;
            stroker->lastAxisAligned = axisAligned;

            Dasher dasher(stroker, swapped, x * (1<<6), xs * (1<<6));

            do {
                if (dasher.on())
                    drawPixel(stroker, x, y >> 16, 255);
                dasher.adjust();
                y += yinc;
            } while (++x < xs);
            didDraw = true;
        }
    }
    stroker->lastPixel = last;
    return didDraw;
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值