[Qt] Pictures in png and jpg formats (2)

background

In the previous article 【Qt】Pictures in png and jpg formats (1) - Nuggets (juejin.cn), the author explained jpgand the two formats, but there is still no explanation about the problem of Qt opening and changing the suffix file name of the picture png. To explore why Qt cannot open the picture file with the suffix changed, this still needs to be located in many ways. As mentioned above, QLabelthe pictures of are set in three ways.

Scene recurrence

Here is how to reproduce the scene where the setting is unsuccessful:

  1. Find a .jpgpicture of and modify the suffix, that is, the file attribute to.png
  2. Use any of the following three methods to set it QLabelas a picture.

The Qt version is5.9

The compiler tried MSVCand mingwdid not work.

QLabelSet picture method

  1. Image via QPixmapsettingsQLabel
QPixmap img(":/Win11.png");

ui->label->setPixmap(img);
ui->label->setScaledContents(true);
  1. Image via QImagesettingsQLabel
QImage img;
img.load(":/Win11.png");
ui->label->setPixmap(QPixmap::fromImage(img))
  1. via QLabel.setStyleSheet()pictures
ui->label->setStyleSheet(QString("QLabel{"
                                 "border-image:url(:/Win11.png) 4 4 4 4 stretch stretch;"
                                 "}"));

Of course, there is a fourth method, by QSvgRenderersetting:

#include <QSvgRenderer>

QSvgRenderer svgRender(QString(":/Win11.svg"));
QPixmap pixmap(20,20);
QPainter painter(&pixmap);
svgRender.render(&painter);
ui->label->setPixmap(pixmap);

The scene is described, you can try it yourself

operate

The next step is to check how the source code in several implementation methods is set up. Let’s guess the conclusion first.

That is png, jpgthe algorithm of the format is different from that of the format, and the reading algorithm caused by the format problem is inconsistent. Therefore, the algorithm for reading pictures implemented inside Qt can only read the algorithm according to the algorithm corresponding to the suffix of the picture file, and the algorithm is also based on such a logic setStyleSheet. , so the three reading methods are all unsuccessful.

Regarding the problem of the image algorithm, it was also slightly mentioned in the previous article. We don’t do in-depth research on this, we only need to know that jpgand pngare not the same algorithm, and they are not universal.

preliminary verification

Because the author has tried all the above steps, we don't worry about which method to use during the verification process, and just look at the results.

  1. Then how to verify it, we still look at the code, this time we remove the suffix name of the picture in the Qt qrcfile, and read the picture without the suffix attribute to see if it can be read successfully.

Remove the suffix attribute

operation result: QLabelread successfully

As shown in the figure above, it is still successful qrcto remove the suffix of the picture in the file and set it to read the picture .QLabel

  1. Let's go ahead and modify the image suffix in the code pngto see if it can be read successfully this time.

Modify the suffix to png

operation result: read failed

As shown in the figure above, after adding the suffix, it still fails to display.

The results of the preliminary verification

This basically says:

Inside Qtthere is a high probability that the file suffix is ​​used to determine which image reading algorithm to call. In other words, when you artificially modify the png->jpg, Qtthere will be a problem of failing to set the picture in . At present, it seems that if there is no problem with the code, but the picture is not displayed after setting, the best way is to remove the suffix of the picture and let the user decide Qtwhich algorithm to call to read the picture.

Source code analysis

Here is a description of Qtthe image I\O, you can see the documentation on image file reading and writing in the Qt help manual.

Read and write image files

In the source code part, we only need Qtto look at two classes, and I personally feel that we can know just by looking at one. The algorithm for identifying pictures between the two should be the same. The interface including setStyleSheet()setting pictures in the interface should be consistent.

  1. QPixmap depends on the drawing engine of the platform where it is located, so some effects such as anti-aliasing may have different display effects on different platforms. QImage uses Qt's own drawing engine to have the same display effect on different platforms.
  2. The current Qt will store QPixmap in graphics memory, and QImage is stored on the client side, which is independent of hardware. On X11, Mac and Symbian platforms, QPixmap is stored on the server side, while QImage is stored on the client side. On the Windows platform, both QPixmap and QImage are stored on the client side without using any GDI resources.
  3. Since QImage is independent of hardware and is also a kind of QPaintDevice, we can draw it in another thread without processing it in the GUI thread. Using this method can greatly improve the UI response speed.

QPixmap

Query the source file of QPixmap, which is generally stored in Qtthe installation directory ${安装目录}\5.9.9\Src\qtbase\src\gui\image, and the file name isqpixmap.h qpixmap.cpp

First look at QPixmapthe method of reading image files in the constructor of .

  1. QPixmap::QPixmap(const QString& fileName, const char *format, Qt::ImageConversionFlags flags)source code

    QPixmap::QPixmap(const QString& fileName, const char *format, Qt::ImageConversionFlags flags)
        : QPaintDevice()
       {
          
          
            doInit(0, 0, QPlatformPixmap::PixmapType);
            if (!qt_pixmap_thread_test())
                return;
    
            load(fileName, format, flags); // 这里看到调用了load()的接口,接着查看load是如何实现的
        }
    
  2. load()Function source code

    /*!
        Loads a pixmap from the file with the given \a fileName. Returns
        true if the pixmap was successfully loaded; otherwise invalidates
        the pixmap and returns \c false.
    
        The loader attempts to read the pixmap using the specified \a
        format. If the \a format is not specified (which is the default),
        the loader probes the file for a header to guess the file format.
    
        The file name can either refer to an actual file on disk or to one
        of the application's embedded resources. See the
        \l{resources.html}{Resource System} overview for details on how to
        embed pixmaps and other resource files in the application's
        executable.
    
        If the data needs to be modified to fit in a lower-resolution
        result (e.g. converting from 32-bit to 8-bit), use the \a flags to
        control the conversion.
    
        Note that QPixmaps are automatically added to the QPixmapCache
        when loaded from a file; the key used is internal and can not
        be acquired.
    
        \sa loadFromData(), {QPixmap#Reading and Writing Image
        Files}{Reading and Writing Image Files}
    */
    
    /* 翻译过来就是
    从给定的文件名的文件中加载一个像素图。如果pixmap成功加载,则为True;否则返回无效pixmap并返回\c false。
    
    加载器尝试使用指定的\a读取pixmap格式。如果没有指定\a格式(这是默认的),
    加载器探测文件的头,以猜测文件的格式。
    
    文件名可以指向磁盘上的实际文件,也可以指向磁盘上的实际文件应用程序的嵌入式资源。看到\l{resources.html}{Resource System}概述如何嵌入pixmap和其他资源文件在应用程序的可执行文件。
    
    如果数据需要修改以适应低分辨率结果(例如从32位转换到8位),使用\a标志来控制转换。
    
    注意,qpixmap会自动添加到QPixmapCache中
    当从文件加载时;使用的密钥是内部的,不能被收购。
    
    \sa loadFromData(), {QPixmap#读写图像读写图像文件}
    */
    
    // loadFromdata的源码我也补充到了文末
    bool QPixmap::load(const QString &fileName, const char *format, Qt::ImageConversionFlags flags)
    {
          
          
        if (!fileName.isEmpty()) {
          
          
    
            QFileInfo info(fileName);
            // Note: If no extension is provided, we try to match the
            // file against known plugin extensions
            if (info.completeSuffix().isEmpty() || info.exists()) {
          
          
    
                QString key = QLatin1String("qt_pixmap")
                        % info.absoluteFilePath()
                        % HexString<uint>(info.lastModified().toSecsSinceEpoch())
                        % HexString<quint64>(info.size())
                        % HexString<uint>(data ? data->pixelType() : QPlatformPixmap::PixmapType);
    
                if (QPixmapCache::find(key, this))
                    return true;
    
                data = QPlatformPixmap::create(0, 0, data ? data->pixelType() : QPlatformPixmap::PixmapType);
    
                if (data->fromFile(fileName, format, flags)) {
          
          
                    QPixmapCache::insert(key, *this);
                    return true;
                }
            }
        }
    
        if (!isNull()) {
          
          
            if (isQBitmap())
                *this = QBitmap();
            else
                data.reset();
        }
        return false;
    }
    

See if the source code is much clearer. In load()the implementation of the function: ① judge whether the file name is empty; ② when it is not empty, the first thing is to read the suffix of the file. Here we can check in detail what kind of operation is performed in the second type of ifjudgment .QStringkey

QString key = QLatin1String("qt_pixmap")
                    % info.absoluteFilePath() // 返回文件名的绝对路径
                    % HexString<uint>(info.lastModified().toSecsSinceEpoch()) // 返回文件最后一次修改的日期和时间
                    % HexString<quint64>(info.size()) // 返回文件的大小
                    % HexString<uint>(data ? data->pixelType() : QPlatformPixmap::PixmapType); // 这里的data是成员变量,就是说如果设置了data的pixelType的值就读取,没设置的话就是默认值 QPlatformPixmap::PixmapType

The above HexString<type>is the ASCII array form, 16进制the array. What is the operation in the code above %if not remainder. If it is asking for a surplus, QStringwill it report an error? ? ?

In the end, check whether the current file is a picture by setting and checking the flag in QPixmapthe built-in variable dataof , if it is a picture, the current QPixmappointer will be modified, and if not, it will returnfalse

if (QPixmapCache::find(key, this))
                return true;

Here is a QPixmapCachedemo to add:

/*在缓存中查找与给定键关联的缓存pixmap。如果找到了pixmap,函数将pixmap设置为该pixmap并返回true;否则,它将保留pixmap并返回false。*/
QPixmap pm;
if (!QPixmapCache::find("my_big_image", &pm)) {
    
    
    pm.load("bigimage.png");
    QPixmapCache::insert("my_big_image", pm);
}
painter->drawPixmap(0, 0, pm);

The final judgment of whether the current file is a picture should be data->fromFile()realized in .

The definition of this data:

QExplicitlySharedDataPointer<QPlatformPixmap> data;

In the next article, we will continue to study the code for reading pictures inside Qt.

Replenish

Add

load()It is necessary to read the function.

QPixmap::loadFromData()source code.

/*!
    \fn bool QPixmap::loadFromData(const uchar *data, uint len, const char *format, Qt::ImageConversionFlags flags)

    Loads a pixmap from the \a len first bytes of the given binary \a
    data.  Returns \c true if the pixmap was loaded successfully;
    otherwise invalidates the pixmap and returns \c false.

    The loader attempts to read the pixmap using the specified \a
    format. If the \a format is not specified (which is the default),
    the loader probes the file for a header to guess the file format.

    If the data needs to be modified to fit in a lower-resolution
    result (e.g. converting from 32-bit to 8-bit), use the \a flags to
    control the conversion.

    \sa load(), {QPixmap#Reading and Writing Image Files}{Reading and
    Writing Image Files}
*/

bool QPixmap::loadFromData(const uchar *buf, uint len, const char *format, Qt::ImageConversionFlags flags)
{
     
     
    if (len == 0 || buf == 0) {
     
     
        data.reset();
        return false;
    }

    data = QPlatformPixmap::create(0, 0, QPlatformPixmap::PixmapType);

    if (data->fromData(buf, len, format, flags))
        return true;

    data.reset();
    return false;
}

Guess you like

Origin blog.csdn.net/Fuel_Ming/article/details/124397625