src/plugins/imageformats/tiff/qtiffhandler.cpp
changeset 0 1918ee327afb
child 3 41300fa6a67c
equal deleted inserted replaced
-1:000000000000 0:1918ee327afb
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
       
     4 ** All rights reserved.
       
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
       
     6 **
       
     7 ** This file is part of the plugins of the Qt Toolkit.
       
     8 **
       
     9 ** $QT_BEGIN_LICENSE:LGPL$
       
    10 ** No Commercial Usage
       
    11 ** This file contains pre-release code and may not be distributed.
       
    12 ** You may use this file in accordance with the terms and conditions
       
    13 ** contained in the Technology Preview License Agreement accompanying
       
    14 ** this package.
       
    15 **
       
    16 ** GNU Lesser General Public License Usage
       
    17 ** Alternatively, this file may be used under the terms of the GNU Lesser
       
    18 ** General Public License version 2.1 as published by the Free Software
       
    19 ** Foundation and appearing in the file LICENSE.LGPL included in the
       
    20 ** packaging of this file.  Please review the following information to
       
    21 ** ensure the GNU Lesser General Public License version 2.1 requirements
       
    22 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
       
    23 **
       
    24 ** In addition, as a special exception, Nokia gives you certain additional
       
    25 ** rights.  These rights are described in the Nokia Qt LGPL Exception
       
    26 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
       
    27 **
       
    28 ** If you have questions regarding the use of this file, please contact
       
    29 ** Nokia at qt-info@nokia.com.
       
    30 **
       
    31 **
       
    32 **
       
    33 **
       
    34 **
       
    35 **
       
    36 **
       
    37 **
       
    38 ** $QT_END_LICENSE$
       
    39 **
       
    40 ****************************************************************************/
       
    41 
       
    42 #include "qtiffhandler.h"
       
    43 #include <qvariant.h>
       
    44 #include <qdebug.h>
       
    45 #include <qimage.h>
       
    46 #include <qglobal.h>
       
    47 extern "C" {
       
    48 #include "tiffio.h"
       
    49 }
       
    50 
       
    51 QT_BEGIN_NAMESPACE
       
    52 
       
    53 tsize_t qtiffReadProc(thandle_t fd, tdata_t buf, tsize_t size)
       
    54 {
       
    55     QIODevice* device = static_cast<QTiffHandler*>(fd)->device();
       
    56     return device->isReadable() ? device->read(static_cast<char *>(buf), size) : -1;
       
    57 }
       
    58 
       
    59 tsize_t qtiffWriteProc(thandle_t fd, tdata_t buf, tsize_t size)
       
    60 {
       
    61     return static_cast<QTiffHandler*>(fd)->device()->write(static_cast<char *>(buf), size);
       
    62 }
       
    63 
       
    64 toff_t qtiffSeekProc(thandle_t fd, toff_t off, int whence)
       
    65 {
       
    66     QIODevice *device = static_cast<QTiffHandler*>(fd)->device();
       
    67     switch (whence) {
       
    68     case SEEK_SET:
       
    69         device->seek(off);
       
    70         break;
       
    71     case SEEK_CUR:
       
    72         device->seek(device->pos() + off);
       
    73         break;
       
    74     case SEEK_END:
       
    75         device->seek(device->size() + off);
       
    76         break;
       
    77     }
       
    78 
       
    79     return device->pos();
       
    80 }
       
    81 
       
    82 int qtiffCloseProc(thandle_t /*fd*/)
       
    83 {
       
    84     return 0;
       
    85 }
       
    86 
       
    87 toff_t qtiffSizeProc(thandle_t fd)
       
    88 {
       
    89     return static_cast<QTiffHandler*>(fd)->device()->size();
       
    90 }
       
    91 
       
    92 int qtiffMapProc(thandle_t /*fd*/, tdata_t* /*pbase*/, toff_t* /*psize*/)
       
    93 {
       
    94     return 0;
       
    95 }
       
    96 
       
    97 void qtiffUnmapProc(thandle_t /*fd*/, tdata_t /*base*/, toff_t /*size*/)
       
    98 {
       
    99 }
       
   100 
       
   101 // for 32 bits images
       
   102 inline void rotate_right_mirror_horizontal(QImage *const image)// rotate right->mirrored horizontal
       
   103 {
       
   104     const int height = image->height();
       
   105     const int width = image->width();
       
   106     QImage generated(/* width = */ height, /* height = */ width, image->format());
       
   107     const uint32 *originalPixel = reinterpret_cast<const uint32*>(image->bits());
       
   108     uint32 *const generatedPixels = reinterpret_cast<uint32*>(generated.bits());
       
   109     for (int row=0; row < height; ++row) {
       
   110         for (int col=0; col < width; ++col) {
       
   111             int idx = col * height + row;
       
   112             generatedPixels[idx] = *originalPixel;
       
   113             ++originalPixel;
       
   114         }
       
   115     }
       
   116     *image = generated;
       
   117 }
       
   118 
       
   119 inline void rotate_right_mirror_vertical(QImage *const image) // rotate right->mirrored vertical
       
   120 {
       
   121     const int height = image->height();
       
   122     const int width = image->width();
       
   123     QImage generated(/* width = */ height, /* height = */ width, image->format());
       
   124     const int lastCol = width - 1;
       
   125     const int lastRow = height - 1;
       
   126     const uint32 *pixel = reinterpret_cast<const uint32*>(image->bits());
       
   127     uint32 *const generatedBits = reinterpret_cast<uint32*>(generated.bits());
       
   128     for (int row=0; row < height; ++row) {
       
   129         for (int col=0; col < width; ++col) {
       
   130             int idx = (lastCol - col) * height + (lastRow - row);
       
   131             generatedBits[idx] = *pixel;
       
   132             ++pixel;
       
   133         }
       
   134     }
       
   135     *image = generated;
       
   136 }
       
   137 
       
   138 QTiffHandler::QTiffHandler() : QImageIOHandler()
       
   139 {
       
   140     compression = NoCompression;
       
   141 }
       
   142 
       
   143 bool QTiffHandler::canRead() const
       
   144 {
       
   145     if (canRead(device())) {
       
   146         setFormat("tiff");
       
   147         return true;
       
   148     }
       
   149     return false;
       
   150 }
       
   151 
       
   152 bool QTiffHandler::canRead(QIODevice *device)
       
   153 {
       
   154     if (!device) {
       
   155         qWarning("QTiffHandler::canRead() called with no device");
       
   156         return false;
       
   157     }
       
   158 
       
   159     // current implementation uses TIFFClientOpen which needs to be
       
   160     // able to seek, so sequential devices are not supported
       
   161     QByteArray header = device->peek(4);
       
   162     return header == QByteArray::fromRawData("\x49\x49\x2A\x00", 4)
       
   163            || header == QByteArray::fromRawData("\x4D\x4D\x00\x2A", 4);
       
   164 }
       
   165 
       
   166 bool QTiffHandler::read(QImage *image)
       
   167 {
       
   168     if (!canRead())
       
   169         return false;
       
   170 
       
   171     TIFF *const tiff = TIFFClientOpen("foo",
       
   172                                       "r",
       
   173                                       this,
       
   174                                       qtiffReadProc,
       
   175                                       qtiffWriteProc,
       
   176                                       qtiffSeekProc,
       
   177                                       qtiffCloseProc,
       
   178                                       qtiffSizeProc,
       
   179                                       qtiffMapProc,
       
   180                                       qtiffUnmapProc);
       
   181 
       
   182     if (!tiff) {
       
   183         return false;
       
   184     }
       
   185     uint32 width;
       
   186     uint32 height;
       
   187     uint16 photometric;
       
   188     if (!TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width)
       
   189         || !TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height)
       
   190         || !TIFFGetField(tiff, TIFFTAG_PHOTOMETRIC, &photometric)) {
       
   191         TIFFClose(tiff);
       
   192         return false;
       
   193     }
       
   194 
       
   195     if (photometric == PHOTOMETRIC_MINISBLACK || photometric == PHOTOMETRIC_MINISWHITE) {
       
   196         if (image->size() != QSize(width, height) || image->format() != QImage::Format_Mono)
       
   197             *image = QImage(width, height, QImage::Format_Mono);
       
   198         QVector<QRgb> colortable(2);
       
   199         if (photometric == PHOTOMETRIC_MINISBLACK) {
       
   200             colortable[0] = 0xff000000;
       
   201             colortable[1] = 0xffffffff;
       
   202         } else {
       
   203             colortable[0] = 0xffffffff;
       
   204             colortable[1] = 0xff000000;
       
   205         }
       
   206         image->setColorTable(colortable);
       
   207 
       
   208         if (!image->isNull()) {
       
   209             for (uint32 y=0; y<height; ++y) {
       
   210                 if (TIFFReadScanline(tiff, image->scanLine(y), y, 0) < 0) {
       
   211                         TIFFClose(tiff);
       
   212                         return false;
       
   213                 }
       
   214             }
       
   215         }
       
   216     } else {
       
   217         uint16 bitPerSample;
       
   218         if (!TIFFGetField(tiff, TIFFTAG_BITSPERSAMPLE, &bitPerSample)) {
       
   219             TIFFClose(tiff);
       
   220             return false;
       
   221         }
       
   222         if (photometric == PHOTOMETRIC_PALETTE && bitPerSample == 8) {
       
   223             if (image->size() != QSize(width, height) || image->format() != QImage::Format_Indexed8)
       
   224                 *image = QImage(width, height, QImage::Format_Indexed8);
       
   225             if (!image->isNull()) {
       
   226                 // create the color table
       
   227                 const uint16 tableSize = 256;
       
   228                 uint16 *redTable = static_cast<uint16 *>(qMalloc(tableSize * sizeof(uint16)));
       
   229                 uint16 *greenTable = static_cast<uint16 *>(qMalloc(tableSize * sizeof(uint16)));
       
   230                 uint16 *blueTable = static_cast<uint16 *>(qMalloc(tableSize * sizeof(uint16)));
       
   231                 if (!redTable || !greenTable || !blueTable) {
       
   232                     TIFFClose(tiff);
       
   233                     return false;
       
   234                 }
       
   235                 if (!TIFFGetField(tiff, TIFFTAG_COLORMAP, &redTable, &greenTable, &blueTable)) {
       
   236                     TIFFClose(tiff);
       
   237                     return false;
       
   238                 }
       
   239 
       
   240                 QVector<QRgb> qtColorTable(tableSize);
       
   241                 for (int i = 0; i<tableSize ;++i) {
       
   242                     const int red = redTable[i] / 257;
       
   243                     const int green = greenTable[i] / 257;
       
   244                     const int blue = blueTable[i] / 257;
       
   245                     qtColorTable[i] = qRgb(red, green, blue);
       
   246 
       
   247                 }
       
   248 
       
   249                 image->setColorTable(qtColorTable);
       
   250                 for (uint32 y=0; y<height; ++y) {
       
   251                     if (TIFFReadScanline(tiff, image->scanLine(y), y, 0) < 0) {
       
   252                         TIFFClose(tiff);
       
   253                         return false;
       
   254                     }
       
   255                 }
       
   256 
       
   257                 // free redTable, greenTable and greenTable done by libtiff
       
   258             }
       
   259         } else {
       
   260             if (image->size() != QSize(width, height) || image->format() != QImage::Format_ARGB32)
       
   261                 *image = QImage(width, height, QImage::Format_ARGB32);
       
   262             if (!image->isNull()) {
       
   263                 const int stopOnError = 1;
       
   264                 if (TIFFReadRGBAImageOriented(tiff, width, height, reinterpret_cast<uint32 *>(image->bits()), ORIENTATION_TOPLEFT, stopOnError)) {
       
   265                     for (uint32 y=0; y<height; ++y)
       
   266                         convert32BitOrder(image->scanLine(y), width);
       
   267                 } else {
       
   268                     TIFFClose(tiff);
       
   269                     return false;
       
   270                 }
       
   271             }
       
   272         }
       
   273     }
       
   274 
       
   275     if (image->isNull()) {
       
   276         TIFFClose(tiff);
       
   277         return false;
       
   278     }
       
   279 
       
   280     float resX = 0;
       
   281     float resY = 0;
       
   282     uint16 resUnit = RESUNIT_NONE;
       
   283     if (TIFFGetField(tiff, TIFFTAG_RESOLUTIONUNIT, &resUnit)
       
   284         && TIFFGetField(tiff, TIFFTAG_XRESOLUTION, &resX)
       
   285         && TIFFGetField(tiff, TIFFTAG_YRESOLUTION, &resY)) {
       
   286 
       
   287         switch(resUnit) {
       
   288         case RESUNIT_CENTIMETER:
       
   289             image->setDotsPerMeterX(qRound(resX * 100));
       
   290             image->setDotsPerMeterY(qRound(resY * 100));
       
   291             break;
       
   292         case RESUNIT_INCH:
       
   293             image->setDotsPerMeterX(qRound(resX * (100 / 2.54)));
       
   294             image->setDotsPerMeterY(qRound(resY * (100 / 2.54)));
       
   295             break;
       
   296         default:
       
   297             // do nothing as defaults have already
       
   298             // been set within the QImage class
       
   299             break;
       
   300         }
       
   301     }
       
   302 
       
   303     // rotate the image if the orientation is defined in the file
       
   304     uint16 orientationTag;
       
   305     if (TIFFGetField(tiff, TIFFTAG_ORIENTATION, &orientationTag)) {
       
   306         if (image->format() == QImage::Format_ARGB32) {
       
   307             // TIFFReadRGBAImageOriented() flip the image but does not rotate them
       
   308             switch (orientationTag) {
       
   309             case 5:
       
   310                 rotate_right_mirror_horizontal(image);
       
   311                 break;
       
   312             case 6:
       
   313                 rotate_right_mirror_vertical(image);
       
   314                 break;
       
   315             case 7:
       
   316                 rotate_right_mirror_horizontal(image);
       
   317                 break;
       
   318             case 8:
       
   319                 rotate_right_mirror_vertical(image);
       
   320                 break;
       
   321             }
       
   322         } else {
       
   323             switch (orientationTag) {
       
   324             case 1: // default orientation
       
   325                 break;
       
   326             case 2: // mirror horizontal
       
   327                 *image = image->mirrored(true, false);
       
   328                 break;
       
   329             case 3: // mirror both
       
   330                 *image = image->mirrored(true, true);
       
   331                 break;
       
   332             case 4: // mirror vertical
       
   333                 *image = image->mirrored(false, true);
       
   334                 break;
       
   335             case 5: // rotate right mirror horizontal
       
   336                 {
       
   337                     QMatrix transformation;
       
   338                     transformation.rotate(90);
       
   339                     *image = image->transformed(transformation);
       
   340                     *image = image->mirrored(true, false);
       
   341                     break;
       
   342                 }
       
   343             case 6: // rotate right
       
   344                 {
       
   345                     QMatrix transformation;
       
   346                     transformation.rotate(90);
       
   347                     *image = image->transformed(transformation);
       
   348                     break;
       
   349                 }
       
   350             case 7: // rotate right, mirror vertical
       
   351                 {
       
   352                     QMatrix transformation;
       
   353                     transformation.rotate(90);
       
   354                     *image = image->transformed(transformation);
       
   355                     *image = image->mirrored(false, true);
       
   356                     break;
       
   357                 }
       
   358             case 8: // rotate left
       
   359                 {
       
   360                     QMatrix transformation;
       
   361                     transformation.rotate(270);
       
   362                     *image = image->transformed(transformation);
       
   363                     break;
       
   364                 }
       
   365             }
       
   366         }
       
   367     }
       
   368 
       
   369 
       
   370     TIFFClose(tiff);
       
   371     return true;
       
   372 }
       
   373 
       
   374 bool QTiffHandler::write(const QImage &image)
       
   375 {
       
   376     if (!device()->isWritable())
       
   377         return false;
       
   378 
       
   379     TIFF *const tiff = TIFFClientOpen("foo",
       
   380                                       "w",
       
   381                                       this,
       
   382                                       qtiffReadProc,
       
   383                                       qtiffWriteProc,
       
   384                                       qtiffSeekProc,
       
   385                                       qtiffCloseProc,
       
   386                                       qtiffSizeProc,
       
   387                                       qtiffMapProc,
       
   388                                       qtiffUnmapProc);
       
   389     if (!tiff)
       
   390         return false;
       
   391 
       
   392     const int width = image.width();
       
   393     const int height = image.height();
       
   394 
       
   395     if (!TIFFSetField(tiff, TIFFTAG_IMAGEWIDTH, width)
       
   396         || !TIFFSetField(tiff, TIFFTAG_IMAGELENGTH, height)
       
   397         || !TIFFSetField(tiff, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG)) {
       
   398         TIFFClose(tiff);
       
   399         return false;
       
   400     }
       
   401 
       
   402     // set the resolution
       
   403     bool  resolutionSet = false;
       
   404     const int dotPerMeterX = image.dotsPerMeterX();
       
   405     const int dotPerMeterY = image.dotsPerMeterY();
       
   406     if ((dotPerMeterX % 100) == 0
       
   407         && (dotPerMeterY % 100) == 0) {
       
   408         resolutionSet = TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_CENTIMETER)
       
   409                         && TIFFSetField(tiff, TIFFTAG_XRESOLUTION, dotPerMeterX/100.0)
       
   410                         && TIFFSetField(tiff, TIFFTAG_YRESOLUTION, dotPerMeterY/100.0);
       
   411     } else {
       
   412         resolutionSet = TIFFSetField(tiff, TIFFTAG_RESOLUTIONUNIT, RESUNIT_INCH)
       
   413                         && TIFFSetField(tiff, TIFFTAG_XRESOLUTION, static_cast<float>(image.logicalDpiX()))
       
   414                         && TIFFSetField(tiff, TIFFTAG_YRESOLUTION, static_cast<float>(image.logicalDpiY()));
       
   415     }
       
   416     if (!resolutionSet) {
       
   417         TIFFClose(tiff);
       
   418         return false;
       
   419     }
       
   420 
       
   421     // configure image depth
       
   422     const QImage::Format format = image.format();
       
   423     if (format == QImage::Format_Mono || format == QImage::Format_MonoLSB) {
       
   424         uint16 photometric = PHOTOMETRIC_MINISBLACK;
       
   425         if (image.colorTable().at(0) == 0xffffffff)
       
   426             photometric = PHOTOMETRIC_MINISWHITE;
       
   427         if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, photometric)
       
   428             || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_CCITTRLE)) {
       
   429             TIFFClose(tiff);
       
   430             return false;
       
   431         }
       
   432 
       
   433         // try to do the conversion in chunks no greater than 16 MB
       
   434         int chunks = (width * height / (1024 * 1024 * 16)) + 1;
       
   435         int chunkHeight = qMax(height / chunks, 1);
       
   436 
       
   437         int y = 0;
       
   438         while (y < height) {
       
   439             QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y)).convertToFormat(QImage::Format_Mono);
       
   440 
       
   441             int chunkStart = y;
       
   442             int chunkEnd = y + chunk.height();
       
   443             while (y < chunkEnd) {
       
   444                 if (TIFFWriteScanline(tiff, reinterpret_cast<uint32 *>(chunk.scanLine(y - chunkStart)), y) != 1) {
       
   445                     TIFFClose(tiff);
       
   446                     return false;
       
   447                 }
       
   448                 ++y;
       
   449             }
       
   450         }
       
   451         TIFFClose(tiff);
       
   452     } else if (format == QImage::Format_Indexed8) {
       
   453         if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_PALETTE)
       
   454             || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_PACKBITS)
       
   455             || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)) {
       
   456             TIFFClose(tiff);
       
   457             return false;
       
   458         }
       
   459         //// write the color table
       
   460         // allocate the color tables
       
   461         uint16 *redTable = static_cast<uint16 *>(qMalloc(256 * sizeof(uint16)));
       
   462         uint16 *greenTable = static_cast<uint16 *>(qMalloc(256 * sizeof(uint16)));
       
   463         uint16 *blueTable = static_cast<uint16 *>(qMalloc(256 * sizeof(uint16)));
       
   464         if (!redTable || !greenTable || !blueTable) {
       
   465             TIFFClose(tiff);
       
   466             return false;
       
   467         }
       
   468 
       
   469         // set the color table
       
   470         const QVector<QRgb> colorTable = image.colorTable();
       
   471 
       
   472         const int tableSize = colorTable.size();
       
   473         Q_ASSERT(tableSize <= 256);
       
   474         for (int i = 0; i<tableSize; ++i) {
       
   475             const QRgb color = colorTable.at(i);
       
   476             redTable[i] = qRed(color) * 257;
       
   477             greenTable[i] = qGreen(color) * 257;
       
   478             blueTable[i] = qBlue(color) * 257;
       
   479         }
       
   480 
       
   481         const bool setColorTableSuccess = TIFFSetField(tiff, TIFFTAG_COLORMAP, redTable, greenTable, blueTable);
       
   482 
       
   483         qFree(redTable);
       
   484         qFree(greenTable);
       
   485         qFree(blueTable);
       
   486 
       
   487         if (!setColorTableSuccess) {
       
   488             TIFFClose(tiff);
       
   489             return false;
       
   490         }
       
   491 
       
   492         //// write the data
       
   493         // try to do the conversion in chunks no greater than 16 MB
       
   494         int chunks = (width * height/ (1024 * 1024 * 16)) + 1;
       
   495         int chunkHeight = qMax(height / chunks, 1);
       
   496 
       
   497         int y = 0;
       
   498         while (y < height) {
       
   499             QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y));
       
   500 
       
   501             int chunkStart = y;
       
   502             int chunkEnd = y + chunk.height();
       
   503             while (y < chunkEnd) {
       
   504                 if (TIFFWriteScanline(tiff, reinterpret_cast<uint32 *>(chunk.scanLine(y - chunkStart)), y) != 1) {
       
   505                     TIFFClose(tiff);
       
   506                     return false;
       
   507                 }
       
   508                 ++y;
       
   509             }
       
   510         }
       
   511         TIFFClose(tiff);
       
   512 
       
   513     } else {
       
   514         if (!TIFFSetField(tiff, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB)
       
   515             || !TIFFSetField(tiff, TIFFTAG_COMPRESSION, compression == NoCompression ? COMPRESSION_NONE : COMPRESSION_LZW)
       
   516             || !TIFFSetField(tiff, TIFFTAG_SAMPLESPERPIXEL, 4)
       
   517             || !TIFFSetField(tiff, TIFFTAG_BITSPERSAMPLE, 8)) {
       
   518             TIFFClose(tiff);
       
   519             return false;
       
   520         }
       
   521         // try to do the ARGB32 conversion in chunks no greater than 16 MB
       
   522         int chunks = (width * height * 4 / (1024 * 1024 * 16)) + 1;
       
   523         int chunkHeight = qMax(height / chunks, 1);
       
   524 
       
   525         int y = 0;
       
   526         while (y < height) {
       
   527             QImage chunk = image.copy(0, y, width, qMin(chunkHeight, height - y)).convertToFormat(QImage::Format_ARGB32);
       
   528 
       
   529             int chunkStart = y;
       
   530             int chunkEnd = y + chunk.height();
       
   531             while (y < chunkEnd) {
       
   532                 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian)
       
   533                     convert32BitOrder(chunk.scanLine(y - chunkStart), width);
       
   534                 else
       
   535                     convert32BitOrderBigEndian(chunk.scanLine(y - chunkStart), width);
       
   536 
       
   537                 if (TIFFWriteScanline(tiff, reinterpret_cast<uint32 *>(chunk.scanLine(y - chunkStart)), y) != 1) {
       
   538                     TIFFClose(tiff);
       
   539                     return false;
       
   540                 }
       
   541                 ++y;
       
   542             }
       
   543         }
       
   544         TIFFClose(tiff);
       
   545     }
       
   546 
       
   547     return true;
       
   548 }
       
   549 
       
   550 QByteArray QTiffHandler::name() const
       
   551 {
       
   552     return "tiff";
       
   553 }
       
   554 
       
   555 QVariant QTiffHandler::option(ImageOption option) const
       
   556 {
       
   557     if (option == Size && canRead()) {
       
   558         QSize imageSize;
       
   559         qint64 pos = device()->pos();
       
   560         TIFF *tiff = TIFFClientOpen("foo",
       
   561                                     "r",
       
   562                                     const_cast<QTiffHandler*>(this),
       
   563                                     qtiffReadProc,
       
   564                                     qtiffWriteProc,
       
   565                                     qtiffSeekProc,
       
   566                                     qtiffCloseProc,
       
   567                                     qtiffSizeProc,
       
   568                                     qtiffMapProc,
       
   569                                     qtiffUnmapProc);
       
   570 
       
   571         if (tiff) {
       
   572             uint32 width = 0;
       
   573             uint32 height = 0;
       
   574             TIFFGetField(tiff, TIFFTAG_IMAGEWIDTH, &width);
       
   575             TIFFGetField(tiff, TIFFTAG_IMAGELENGTH, &height);
       
   576             imageSize = QSize(width, height);
       
   577         }
       
   578         device()->seek(pos);
       
   579         if (imageSize.isValid())
       
   580             return imageSize;
       
   581     } else if (option == CompressionRatio) {
       
   582         return compression;
       
   583     } else if (option == ImageFormat) {
       
   584         return QImage::Format_ARGB32;
       
   585     }
       
   586     return QVariant();
       
   587 }
       
   588 
       
   589 void QTiffHandler::setOption(ImageOption option, const QVariant &value)
       
   590 {
       
   591     if (option == CompressionRatio && value.type() == QVariant::Int)
       
   592         compression = value.toInt();
       
   593 }
       
   594 
       
   595 bool QTiffHandler::supportsOption(ImageOption option) const
       
   596 {
       
   597     return option == CompressionRatio
       
   598             || option == Size
       
   599             || option == ImageFormat;
       
   600 }
       
   601 
       
   602 void QTiffHandler::convert32BitOrder(void *buffer, int width)
       
   603 {
       
   604     uint32 *target = reinterpret_cast<uint32 *>(buffer);
       
   605     for (int32 x=0; x<width; ++x) {
       
   606         uint32 p = target[x];
       
   607         // convert between ARGB and ABGR
       
   608         target[x] = (p & 0xff000000)
       
   609                     | ((p & 0x00ff0000) >> 16)
       
   610                     | (p & 0x0000ff00)
       
   611                     | ((p & 0x000000ff) << 16);
       
   612     }
       
   613 }
       
   614 
       
   615 void QTiffHandler::convert32BitOrderBigEndian(void *buffer, int width)
       
   616 {
       
   617     uint32 *target = reinterpret_cast<uint32 *>(buffer);
       
   618     for (int32 x=0; x<width; ++x) {
       
   619         uint32 p = target[x];
       
   620         target[x] = (p & 0xff000000) >> 24
       
   621                     | (p & 0x00ff0000) << 8
       
   622                     | (p & 0x0000ff00) << 8
       
   623                     | (p & 0x000000ff) << 8;
       
   624     }
       
   625 }
       
   626 
       
   627 QT_END_NAMESPACE