应该如何使用Raw数据填充生成QImage图像

5 篇文章 0 订阅

当我们有raw数据,想存成QImage图像数据时,往往使用下列函数进行生成:

QImage(uchar *data, int width, int height, int bytesPerLine, Format format, QImageCleanupFunction cleanupFunction = nullptr, void *cleanupInfo = nullptr);

或者这样:

    uchar* imgBuf = ...//假设imgBuf的位深度已经与下面设置QImage::Format_Grayscale8一致
    int width = 300, height = 300;
    QImage img = QImage(width, height, QImage::Format_Grayscale8);
    uchar* pLine = img.scanLine(0); //获取图像数据指针基地址
    memcpy(pLine, imgBuf, width * height);

但能看到有的新手是这样写的:

uchar* buf = ... //已有的raw数据缓存
QImage img = QImage(300, 300, QImage::Format_Grayscale8);
for(int y = 0; y < 300; y++)
    for(int x = 0; x < 300; x++)
        img.setPixel(x, y, buf[y*300 + x]);

这样写确实也能实现功能,但这样效率会很低,而且可能低的吓人。

//(Qt5.12.1 源码摘录)
void QImage::setPixel(int x, int y, uint index_or_rgb)
{
    if (!d || x < 0 || x >= width() || y < 0 || y >= height()) {
        qWarning("QImage::setPixel: coordinate (%d,%d) out of range", x, y);
        return;
    }
    // detach is called from within scanLine
    uchar * s = scanLine(y);
    switch(d->format) {
    case Format_Mono:
    case Format_MonoLSB:
        if (index_or_rgb > 1) {
            qWarning("QImage::setPixel: Index %d out of range", index_or_rgb);
        } else if (format() == Format_MonoLSB) {
            if (index_or_rgb==0)
                *(s + (x >> 3)) &= ~(1 << (x & 7));
            else
                *(s + (x >> 3)) |= (1 << (x & 7));
        } else {
            if (index_or_rgb==0)
                *(s + (x >> 3)) &= ~(1 << (7-(x & 7)));
            else
                *(s + (x >> 3)) |= (1 << (7-(x & 7)));
        }
        return;
    case Format_Indexed8:
        if (index_or_rgb >= (uint)d->colortable.size()) {
            qWarning("QImage::setPixel: Index %d out of range", index_or_rgb);
            return;
        }
        s[x] = index_or_rgb;
        return;
    case Format_RGB32:
        //make sure alpha is 255, we depend on it in qdrawhelper for cases
        // when image is set as a texture pattern on a qbrush
        ((uint *)s)[x] = 0xff000000 | index_or_rgb;
        return;
    case Format_ARGB32:
    case Format_ARGB32_Premultiplied:
        ((uint *)s)[x] = index_or_rgb;
        return;
    case Format_RGB16:
        ((quint16 *)s)[x] = qConvertRgb32To16(qUnpremultiply(index_or_rgb));
        return;
    case Format_RGBX8888:
        ((uint *)s)[x] = ARGB2RGBA(0xff000000 | index_or_rgb);
        return;
    case Format_RGBA8888:
    case Format_RGBA8888_Premultiplied:
        ((uint *)s)[x] = ARGB2RGBA(index_or_rgb);
        return;
    case Format_BGR30:
        ((uint *)s)[x] = qConvertRgb32ToRgb30<PixelOrderBGR>(index_or_rgb);
        return;
    case Format_A2BGR30_Premultiplied:
        ((uint *)s)[x] = qConvertArgb32ToA2rgb30<PixelOrderBGR>(index_or_rgb);
        return;
    case Format_RGB30:
        ((uint *)s)[x] = qConvertRgb32ToRgb30<PixelOrderRGB>(index_or_rgb);
        return;
    case Format_A2RGB30_Premultiplied:
        ((uint *)s)[x] = qConvertArgb32ToA2rgb30<PixelOrderRGB>(index_or_rgb);
        return;
    case Format_Invalid:
    case NImageFormats:
        Q_ASSERT(false);
        return;
    default:
        break;
    }

    const QPixelLayout *layout = &qPixelLayouts[d->format];
    layout->storeFromARGB32PM(s, &index_or_rgb, x, 1, nullptr, nullptr);

这里最耗时的可能还不是中间那一大串的switch语句。首先是scanLine(),其中会调用detach(),该函数 在最坏的情况下,会进行整个类的深拷贝:

//(Qt5.12.1 源码摘录)
void QImage::detach()
{
    if (d) {
        if (d->is_cached && d->ref.load() == 1)
            QImagePixmapCleanupHooks::executeImageHooks(cacheKey());

        if (d->ref.load() != 1 || d->ro_data)
            *this = copy();

        if (d)
            ++d->detach_no;
    }
}

当然,上面的例子不符合深拷贝的条件,但是storeFromARGB32PM会进行不同像素数据格式转换,一次两次没啥问题,如果放在循环里面,真就是大可不必。

  • 1
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值