src/gui/image/qppmhandler.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 QtGui module 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 "private/qppmhandler_p.h"
       
    43 
       
    44 #ifndef QT_NO_IMAGEFORMAT_PPM
       
    45 
       
    46 #include <qimage.h>
       
    47 #include <qvariant.h>
       
    48 #include <qvector.h>
       
    49 #include <ctype.h>
       
    50 
       
    51 QT_BEGIN_NAMESPACE
       
    52 
       
    53 /*****************************************************************************
       
    54   PBM/PGM/PPM (ASCII and RAW) image read/write functions
       
    55  *****************************************************************************/
       
    56 
       
    57 static int read_pbm_int(QIODevice *d)
       
    58 {
       
    59     char c;
       
    60     int          val = -1;
       
    61     bool  digit;
       
    62     const int buflen = 100;
       
    63     char  buf[buflen];
       
    64     for (;;) {
       
    65         if (!d->getChar(&c))                // end of file
       
    66             break;
       
    67         digit = isdigit((uchar) c);
       
    68         if (val != -1) {
       
    69             if (digit) {
       
    70                 val = 10*val + c - '0';
       
    71                 continue;
       
    72             } else {
       
    73                 if (c == '#')                        // comment
       
    74                     d->readLine(buf, buflen);
       
    75                 break;
       
    76             }
       
    77         }
       
    78         if (digit)                                // first digit
       
    79             val = c - '0';
       
    80         else if (isspace((uchar) c))
       
    81             continue;
       
    82         else if (c == '#')
       
    83             (void)d->readLine(buf, buflen);
       
    84         else
       
    85             break;
       
    86     }
       
    87     return val;
       
    88 }
       
    89 
       
    90 static bool read_pbm_header(QIODevice *device, char& type, int& w, int& h, int& mcc)
       
    91 {
       
    92     char buf[3];
       
    93     if (device->read(buf, 3) != 3)                        // read P[1-6]<white-space>
       
    94         return false;
       
    95 
       
    96     if (!(buf[0] == 'P' && isdigit((uchar) buf[1]) && isspace((uchar) buf[2])))
       
    97         return false;
       
    98 
       
    99     type = buf[1];
       
   100     if (type < '1' || type > '6')
       
   101         return false;
       
   102 
       
   103     w = read_pbm_int(device);                        // get image width
       
   104     h = read_pbm_int(device);                        // get image height
       
   105 
       
   106     if (type == '1' || type == '4')
       
   107         mcc = 1;                                  // ignore max color component
       
   108     else
       
   109         mcc = read_pbm_int(device);               // get max color component
       
   110 
       
   111     if (w <= 0 || w > 32767 || h <= 0 || h > 32767 || mcc <= 0)
       
   112         return false;                                        // weird P.M image
       
   113 
       
   114     return true;
       
   115 }
       
   116 
       
   117 static bool read_pbm_body(QIODevice *device, char type, int w, int h, int mcc, QImage *outImage)
       
   118 {
       
   119     int nbits, y;
       
   120     int pbm_bpl;
       
   121     bool raw;
       
   122 
       
   123     QImage::Format format;
       
   124     switch (type) {
       
   125         case '1':                                // ascii PBM
       
   126         case '4':                                // raw PBM
       
   127             nbits = 1;
       
   128             format = QImage::Format_Mono;
       
   129             break;
       
   130         case '2':                                // ascii PGM
       
   131         case '5':                                // raw PGM
       
   132             nbits = 8;
       
   133             format = QImage::Format_Indexed8;
       
   134             break;
       
   135         case '3':                                // ascii PPM
       
   136         case '6':                                // raw PPM
       
   137             nbits = 32;
       
   138             format = QImage::Format_RGB32;
       
   139             break;
       
   140         default:
       
   141             return false;
       
   142     }
       
   143     raw = type >= '4';
       
   144 
       
   145     int maxc = mcc;
       
   146     if (maxc > 255)
       
   147         maxc = 255;
       
   148     if (outImage->size() != QSize(w, h) || outImage->format() != format) {
       
   149         *outImage = QImage(w, h, format);
       
   150         if (outImage->isNull())
       
   151             return false;
       
   152     }
       
   153 
       
   154     pbm_bpl = (nbits*w+7)/8;                        // bytes per scanline in PBM
       
   155 
       
   156     if (raw) {                                // read raw data
       
   157         if (nbits == 32) {                        // type 6
       
   158             pbm_bpl = mcc < 256 ? 3*w : 6*w;
       
   159             uchar *buf24 = new uchar[pbm_bpl], *b;
       
   160             QRgb  *p;
       
   161             QRgb  *end;
       
   162             for (y=0; y<h; y++) {
       
   163                 if (device->read((char *)buf24, pbm_bpl) != pbm_bpl) {
       
   164                     delete[] buf24;
       
   165                     return false;
       
   166                 }
       
   167                 p = (QRgb *)outImage->scanLine(y);
       
   168                 end = p + w;
       
   169                 b = buf24;
       
   170                 while (p < end) {
       
   171                     if (mcc < 256) {
       
   172                         *p++ = qRgb(b[0],b[1],b[2]);
       
   173                         b += 3;
       
   174                     } else {
       
   175                         *p++ = qRgb(((int(b[0]) * 256 + int(b[1]) + 1) * 256) / (mcc + 1) - 1,
       
   176                                     ((int(b[2]) * 256 + int(b[3]) + 1) * 256) / (mcc + 1) - 1,
       
   177                                     ((int(b[4]) * 256 + int(b[5]) + 1) * 256) / (mcc + 1) - 1);
       
   178                         b += 6;
       
   179                     }
       
   180                 }
       
   181             }
       
   182             delete[] buf24;
       
   183         } else {                                // type 4,5
       
   184             for (y=0; y<h; y++) {
       
   185                 if (device->read((char *)outImage->scanLine(y), pbm_bpl)
       
   186                         != pbm_bpl)
       
   187                     return false;
       
   188             }
       
   189         }
       
   190     } else {                                        // read ascii data
       
   191         register uchar *p;
       
   192         int n;
       
   193         for (y=0; y<h; y++) {
       
   194             p = outImage->scanLine(y);
       
   195             n = pbm_bpl;
       
   196             if (nbits == 1) {
       
   197                 int b;
       
   198                 int bitsLeft = w;
       
   199                 while (n--) {
       
   200                     b = 0;
       
   201                     for (int i=0; i<8; i++) {
       
   202                         if (i < bitsLeft)
       
   203                             b = (b << 1) | (read_pbm_int(device) & 1);
       
   204                         else
       
   205                             b = (b << 1) | (0 & 1); // pad it our self if we need to
       
   206                     }
       
   207                     bitsLeft -= 8;
       
   208                     *p++ = b;
       
   209                 }
       
   210             } else if (nbits == 8) {
       
   211                 if (mcc == maxc) {
       
   212                     while (n--) {
       
   213                         *p++ = read_pbm_int(device);
       
   214                     }
       
   215                 } else {
       
   216                     while (n--) {
       
   217                         *p++ = read_pbm_int(device) * maxc / mcc;
       
   218                     }
       
   219                 }
       
   220             } else {                                // 32 bits
       
   221                 n /= 4;
       
   222                 int r, g, b;
       
   223                 if (mcc == maxc) {
       
   224                     while (n--) {
       
   225                         r = read_pbm_int(device);
       
   226                         g = read_pbm_int(device);
       
   227                         b = read_pbm_int(device);
       
   228                         *((QRgb*)p) = qRgb(r, g, b);
       
   229                         p += 4;
       
   230                     }
       
   231                 } else {
       
   232                     while (n--) {
       
   233                         r = read_pbm_int(device) * maxc / mcc;
       
   234                         g = read_pbm_int(device) * maxc / mcc;
       
   235                         b = read_pbm_int(device) * maxc / mcc;
       
   236                         *((QRgb*)p) = qRgb(r, g, b);
       
   237                         p += 4;
       
   238                     }
       
   239                 }
       
   240             }
       
   241         }
       
   242     }
       
   243 
       
   244     if (nbits == 1) {                                // bitmap
       
   245         outImage->setNumColors(2);
       
   246         outImage->setColor(0, qRgb(255,255,255)); // white
       
   247         outImage->setColor(1, qRgb(0,0,0));        // black
       
   248     } else if (nbits == 8) {                        // graymap
       
   249         outImage->setNumColors(maxc+1);
       
   250         for (int i=0; i<=maxc; i++)
       
   251             outImage->setColor(i, qRgb(i*255/maxc,i*255/maxc,i*255/maxc));
       
   252     }
       
   253 
       
   254     return true;
       
   255 }
       
   256 
       
   257 static bool write_pbm_image(QIODevice *out, const QImage &sourceImage, const QByteArray &sourceFormat)
       
   258 {
       
   259     QByteArray str;
       
   260     QImage image = sourceImage;
       
   261     QByteArray format = sourceFormat;
       
   262 
       
   263     format = format.left(3);                        // ignore RAW part
       
   264     bool gray = format == "pgm";
       
   265 
       
   266     if (format == "pbm") {
       
   267         image = image.convertToFormat(QImage::Format_MonoLSB);
       
   268     } else if (image.depth() == 1) {
       
   269         image = image.convertToFormat(QImage::Format_Indexed8);
       
   270     } else {
       
   271         switch (image.format()) {
       
   272         case QImage::Format_RGB16:
       
   273         case QImage::Format_RGB666:
       
   274         case QImage::Format_RGB555:
       
   275         case QImage::Format_RGB888:
       
   276         case QImage::Format_RGB444:
       
   277             image = image.convertToFormat(QImage::Format_RGB32);
       
   278             break;
       
   279         case QImage::Format_ARGB8565_Premultiplied:
       
   280         case QImage::Format_ARGB6666_Premultiplied:
       
   281         case QImage::Format_ARGB8555_Premultiplied:
       
   282         case QImage::Format_ARGB4444_Premultiplied:
       
   283             image = image.convertToFormat(QImage::Format_ARGB32);
       
   284             break;
       
   285         default:
       
   286             break;
       
   287         }
       
   288     }
       
   289 
       
   290     if (image.depth() == 1 && image.numColors() == 2) {
       
   291         if (qGray(image.color(0)) < qGray(image.color(1))) {
       
   292             // 0=dark/black, 1=light/white - invert
       
   293             image.detach();
       
   294             for (int y=0; y<image.height(); y++) {
       
   295                 uchar *p = image.scanLine(y);
       
   296                 uchar *end = p + image.bytesPerLine();
       
   297                 while (p < end)
       
   298                     *p++ ^= 0xff;
       
   299             }
       
   300         }
       
   301     }
       
   302 
       
   303     uint w = image.width();
       
   304     uint h = image.height();
       
   305 
       
   306     str = "P\n";
       
   307     str += QByteArray::number(w);
       
   308     str += ' ';
       
   309     str += QByteArray::number(h);
       
   310     str += '\n';
       
   311 
       
   312     switch (image.depth()) {
       
   313         case 1: {
       
   314             str.insert(1, '4');
       
   315             if (out->write(str, str.length()) != str.length())
       
   316                 return false;
       
   317             w = (w+7)/8;
       
   318             for (uint y=0; y<h; y++) {
       
   319                 uchar* line = image.scanLine(y);
       
   320                 if (w != (uint)out->write((char*)line, w))
       
   321                     return false;
       
   322             }
       
   323             }
       
   324             break;
       
   325 
       
   326         case 8: {
       
   327             str.insert(1, gray ? '5' : '6');
       
   328             str.append("255\n");
       
   329             if (out->write(str, str.length()) != str.length())
       
   330                 return false;
       
   331             QVector<QRgb> color = image.colorTable();
       
   332             uint bpl = w*(gray ? 1 : 3);
       
   333             uchar *buf   = new uchar[bpl];
       
   334             for (uint y=0; y<h; y++) {
       
   335                 uchar *b = image.scanLine(y);
       
   336                 uchar *p = buf;
       
   337                 uchar *end = buf+bpl;
       
   338                 if (gray) {
       
   339                     while (p < end) {
       
   340                         uchar g = (uchar)qGray(color[*b++]);
       
   341                         *p++ = g;
       
   342                     }
       
   343                 } else {
       
   344                     while (p < end) {
       
   345                         QRgb rgb = color[*b++];
       
   346                         *p++ = qRed(rgb);
       
   347                         *p++ = qGreen(rgb);
       
   348                         *p++ = qBlue(rgb);
       
   349                     }
       
   350                 }
       
   351                 if (bpl != (uint)out->write((char*)buf, bpl))
       
   352                     return false;
       
   353             }
       
   354             delete [] buf;
       
   355             }
       
   356             break;
       
   357 
       
   358         case 32: {
       
   359             str.insert(1, gray ? '5' : '6');
       
   360             str.append("255\n");
       
   361             if (out->write(str, str.length()) != str.length())
       
   362                 return false;
       
   363             uint bpl = w*(gray ? 1 : 3);
       
   364             uchar *buf = new uchar[bpl];
       
   365             for (uint y=0; y<h; y++) {
       
   366                 QRgb  *b = (QRgb*)image.scanLine(y);
       
   367                 uchar *p = buf;
       
   368                 uchar *end = buf+bpl;
       
   369                 if (gray) {
       
   370                     while (p < end) {
       
   371                         uchar g = (uchar)qGray(*b++);
       
   372                         *p++ = g;
       
   373                     }
       
   374                 } else {
       
   375                     while (p < end) {
       
   376                         QRgb rgb = *b++;
       
   377                         *p++ = qRed(rgb);
       
   378                         *p++ = qGreen(rgb);
       
   379                         *p++ = qBlue(rgb);
       
   380                     }
       
   381                 }
       
   382                 if (bpl != (uint)out->write((char*)buf, bpl))
       
   383                     return false;
       
   384             }
       
   385             delete [] buf;
       
   386             }
       
   387             break;
       
   388 
       
   389     default:
       
   390         return false;
       
   391     }
       
   392 
       
   393     return true;
       
   394 }
       
   395 
       
   396 QPpmHandler::QPpmHandler()
       
   397     : state(Ready)
       
   398 {
       
   399 }
       
   400 
       
   401 bool QPpmHandler::readHeader()
       
   402 {
       
   403     state = Error;
       
   404     if (!read_pbm_header(device(), type, width, height, mcc))
       
   405         return false;
       
   406     state = ReadHeader;
       
   407     return true;
       
   408 }
       
   409 
       
   410 bool QPpmHandler::canRead() const
       
   411 {
       
   412     if (state == Ready) {
       
   413         if (!canRead(device(), &subType))
       
   414             return false;
       
   415         setFormat(subType);
       
   416         return true;
       
   417     }
       
   418     return state != Error;
       
   419 }
       
   420 
       
   421 bool QPpmHandler::canRead(QIODevice *device, QByteArray *subType)
       
   422 {
       
   423     if (!device) {
       
   424         qWarning("QPpmHandler::canRead() called with no device");
       
   425         return false;
       
   426     }
       
   427 
       
   428     char head[2];
       
   429     if (device->peek(head, sizeof(head)) != sizeof(head))
       
   430         return false;
       
   431 
       
   432     if (head[0] != 'P')
       
   433         return false;
       
   434 
       
   435     if (head[1] == '1' || head[1] == '4') {
       
   436         if (subType)
       
   437             *subType = "pbm";
       
   438     } else if (head[1] == '2' || head[1] == '5') {
       
   439         if (subType)
       
   440             *subType = "pgm";
       
   441     } else if (head[1] == '3' || head[1] == '6') {
       
   442         if (subType)
       
   443             *subType = "ppm";
       
   444     } else {
       
   445         return false;
       
   446     }
       
   447     return true;
       
   448 }
       
   449 
       
   450 bool QPpmHandler::read(QImage *image)
       
   451 {
       
   452     if (state == Error)
       
   453         return false;
       
   454 
       
   455     if (state == Ready && !readHeader()) {
       
   456         state = Error;
       
   457         return false;
       
   458     }
       
   459 
       
   460     if (!read_pbm_body(device(), type, width, height, mcc, image)) {
       
   461         state = Error;
       
   462         return false;
       
   463     }
       
   464 
       
   465     state = Ready;
       
   466     return true;
       
   467 }
       
   468 
       
   469 bool QPpmHandler::write(const QImage &image)
       
   470 {
       
   471     return write_pbm_image(device(), image, subType);
       
   472 }
       
   473 
       
   474 bool QPpmHandler::supportsOption(ImageOption option) const
       
   475 {
       
   476     return option == SubType
       
   477         || option == Size
       
   478         || option == ImageFormat;
       
   479 }
       
   480 
       
   481 QVariant QPpmHandler::option(ImageOption option) const
       
   482 {
       
   483     if (option == SubType) {
       
   484         return subType;
       
   485     } else if (option == Size) {
       
   486         if (state == Error)
       
   487             return QVariant();
       
   488         if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
       
   489             return QVariant();
       
   490         return QSize(width, height);
       
   491     } else if (option == ImageFormat) {
       
   492         if (state == Error)
       
   493             return QVariant();
       
   494         if (state == Ready && !const_cast<QPpmHandler*>(this)->readHeader())
       
   495             return QVariant();
       
   496         QImage::Format format = QImage::Format_Invalid;
       
   497         switch (type) {
       
   498             case '1':                                // ascii PBM
       
   499             case '4':                                // raw PBM
       
   500             format = QImage::Format_Mono;
       
   501             break;
       
   502             case '2':                                // ascii PGM
       
   503             case '5':                                // raw PGM
       
   504                 format = QImage::Format_Indexed8;
       
   505                 break;
       
   506             case '3':                                // ascii PPM
       
   507             case '6':                                // raw PPM
       
   508                 format = QImage::Format_RGB32;
       
   509                 break;
       
   510             default:
       
   511                 break;
       
   512         }
       
   513         return format;
       
   514     }
       
   515     return QVariant();
       
   516 }
       
   517 
       
   518 void QPpmHandler::setOption(ImageOption option, const QVariant &value)
       
   519 {
       
   520     if (option == SubType)
       
   521         subType = value.toByteArray().toLower();
       
   522 }
       
   523 
       
   524 QByteArray QPpmHandler::name() const
       
   525 {
       
   526     return subType.isEmpty() ? QByteArray("ppm") : subType;
       
   527 }
       
   528 
       
   529 QT_END_NAMESPACE
       
   530 
       
   531 #endif // QT_NO_IMAGEFORMAT_PPM