Uploaded image for project: 'Qt'
  1. Qt
  2. QTBUG-40890

Buggy conversion between QImage (QPixmap) and image data of Windows clipboard

    XMLWordPrintable

Details

    • Bug
    • Resolution: Done
    • P2: Important
    • 5.5.1
    • 5.3.0
    • Image formats, QPA: Windows
    • None
    • Windows 7; MS visual studio 2013;
    • d99c9bcf2c4e14d3db02ad375aa624a0695e838d

    Description

      On the Windows platform, if the bitmap in the system clipboard has the type of compression being BI_BITFIELDS, Qt fails to read the image data correctly.

      According to BITMAPINFOHEADER, if a bitmap is compressed in BI_BITFIELDS, its color table will contains three DWORD color masks. The three DWORD should be handled before we can read the colormap, but Qt doesn't.

      A simple way to read the image contained in the clipboard would be:

      QPixmap pixmap = QApplication::clipboard()->pixmap();
      

      If a bitmap like this:
      (Image A)
      is compressed in BI_BITFIELDS, the resulting QPixmap would be:
      (Image B)
      Notice that three columns of pixels are shifted from the right border to the left border.

      This bug can be fixed by replacing the line 311 in gui\image\qbmphandler.cpp:

      d->seek(startpos + BMP_FILEHDR_SIZE + (bi.biSize >= BMP_WIN4 ? BMP_WIN : bi.biSize)); // goto start of colormap
      

      by:

      d->seek(startpos + BMP_FILEHDR_SIZE + (comp == BMP_BITFIELDS ? sizeof(unsigned long) * 3 : 0) + (bi.biSize >= BMP_WIN4 ? BMP_WIN : bi.biSize)); // goto start of colormap i
      

      as DWORD is a typedef of 'unsigned long'.

      Such code solves the problem for me, but it may not be a perfect solution as I am not familiar about the bitmap thing. You may also read the example code given by Microsoft.
      In the example, the function BitmapFromDIB() reads from the dib and returns a HBITMAP. It can get us Image A if Image A is put in the clipboard. BitmapFromDIB() calls another function ColorTableSize(). If we replace the function ColorTableSize() by a value of 0, the bitmap returned changes to Image B (as Qt does), which explains my solution above.


      Besides, I believe similar problems also exist when Qt writes images into the Windows clipboard. Suppose that I have a QPixmap pixmap whose content is Image A and I put it into the clipboard using the following code:

      QClipboard *clipboard = QApplication::clipboard();
      clipboard->setPixmap(pixmap); // or: clipboard->setImage(pixmap.toImage());
      

      Then I paste it out using some other non-Qt program, it's likely that I get Image B! Yes, this is the inverse problem of the former one.

      Fortunately, most programs can paste the image out just like Image A, e.g. the MS office apps, while other few programs fail to get me Image A. I think the problem still belongs to Qt because if modify the code to:

      QClipboard *clipboard = QApplication::clipboard();
      clipboard->setImage(pixmap.toImage().convertToFormat(QImage::Format_RGB32));
      

      The problem disappears. So there may be something wrong when Qt writes a QImage into the clipboard, whose format is QImage::Format_ARGB32_Premultiplied. I tried to figure out where the bugged code lies but failed, so I have to use the convertToFormat() as a workaround.



      As I have pointed out, (I) Qt reads and converts the image data in the Windows clipboard correctly for the MOST of time; (II) when Qt puts an image into the system clipboard, MOST software can also read that correctly. Thus it's a bit difficult to find some common software to reproduce the bug, but finally I find one and it's a system app.

      1) On Windows 7, 'Start menu' - 'All programs' - 'Accessories' - 'Snipping tool' (SnippingTool.exe).
      2) Snip a rectangle area of the screen using the snipping tool. A rectangle with different colors on its left border and right border would be nice. For example, the Image A above.
      3) Copy it to the clipboard using ctrl-c or via the snipping tool's menubar - 'Edit' - 'Copy'.
      4) Read from the clipboard and save it to a file using the following code:

      QImage image = QApplication::clipboard()->image();
      image.save("b.png");
      

      5) Open the saved image "b.png". Now you are likely to see that it's different from what you just snipped. Similar to Image A -> Image B.

      As to the inverse problem, I have not found proper common software to reproduce it, but it does exist and the solution does not lie in qbmphandler.cpp.

      Attachments

        1. src.jpg
          src.jpg
          1 kB
        2. qtbug40890.zip
          2 kB
        3. qtbug40890_diag.diff
          1 kB
        4. qtbug40890_compressed.bmp
          100 kB
        5. dst.png
          dst.png
          0.4 kB

        Issue Links

          No reviews matched the request. Check your Options in the drop-down menu of this sections header.

          Activity

            People

              vgt Eirik Aavitsland
              liulios Le Liu
              Votes:
              0 Vote for this issue
              Watchers:
              6 Start watching this issue

              Dates

                Created:
                Updated:
                Resolved:

                Gerrit Reviews

                  There are no open Gerrit changes