src/plugins/imageformats/jpeg/qjpeghandler.cpp
branchRCL_3
changeset 4 3b1da2848fc7
parent 3 41300fa6a67c
child 7 3f74d0d4af4c
equal deleted inserted replaced
3:41300fa6a67c 4:3b1da2848fc7
     1 /****************************************************************************
     1 /****************************************************************************
     2 **
     2 **
     3 ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
     3 ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies).
     4 ** All rights reserved.
     4 ** All rights reserved.
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
     5 ** Contact: Nokia Corporation (qt-info@nokia.com)
     6 **
     6 **
     7 ** This file is part of the plugins of the Qt Toolkit.
     7 ** This file is part of the plugins of the Qt Toolkit.
     8 **
     8 **
    82 class QImageSmoothScaler
    82 class QImageSmoothScaler
    83 {
    83 {
    84 public:
    84 public:
    85     QImageSmoothScaler(const int w, const int h, const QImage &src);
    85     QImageSmoothScaler(const int w, const int h, const QImage &src);
    86     QImageSmoothScaler(const int srcWidth, const int srcHeight,
    86     QImageSmoothScaler(const int srcWidth, const int srcHeight,
    87                        const char *parameters);
    87                        const int dstWidth, const int dstHeight);
    88 
    88 
    89     virtual ~QImageSmoothScaler(void);
    89     virtual ~QImageSmoothScaler(void);
    90 
    90 
    91     QImage  scale();
    91     QImage  scale();
    92 
       
    93 protected:
       
    94     int scaledWidth(void) const;
       
    95 
    92 
    96 private:
    93 private:
    97     QImageSmoothScalerPrivate	*d;
    94     QImageSmoothScalerPrivate	*d;
    98     virtual QRgb *scanLine(const int line = 0, const QImage *src = 0);
    95     virtual QRgb *scanLine(const int line = 0, const QImage *src = 0);
    99 };
    96 };
   121     d->setup(src.width(), src.height(), w, h, src.hasAlphaChannel() );
   118     d->setup(src.width(), src.height(), w, h, src.hasAlphaChannel() );
   122     this->d->src = &src;
   119     this->d->src = &src;
   123 }
   120 }
   124 
   121 
   125 QImageSmoothScaler::QImageSmoothScaler(const int srcWidth, const int srcHeight,
   122 QImageSmoothScaler::QImageSmoothScaler(const int srcWidth, const int srcHeight,
   126                                        const char *parameters)
   123                                        const int dstWidth, const int dstHeight)
   127 {
   124 {
   128     char    sModeStr[1024];
       
   129     int	    t1;
       
   130     int	    t2;
       
   131     int	    dstWidth;
       
   132     int	    dstHeight;
       
   133 
       
   134     sModeStr[0] = '\0';
       
   135 
       
   136     d = new QImageSmoothScalerPrivate;
   125     d = new QImageSmoothScalerPrivate;
   137 #if defined(Q_OS_WIN) && !defined(Q_OS_WINCE) && defined(_MSC_VER) && _MSC_VER >= 1400
       
   138     sscanf_s(parameters, "Scale( %i, %i, %1023s )", &dstWidth, &dstHeight, sModeStr, sizeof(sModeStr));
       
   139 #else
       
   140     sscanf(parameters, "Scale( %i, %i, %s )", &dstWidth, &dstHeight, sModeStr);
       
   141 #endif
       
   142     QString sModeQStr = QString::fromLatin1(sModeStr);
       
   143 
       
   144     t1 = srcWidth * dstHeight;
       
   145     t2 = srcHeight * dstWidth;
       
   146 
       
   147     if (((sModeQStr == QLatin1String("ScaleMin")) && (t1 > t2)) || ((sModeQStr == QLatin1String("ScaleMax")) && (t2 < t2))) {
       
   148 	dstHeight = t2 / srcWidth;
       
   149     } else if (sModeQStr != QLatin1String("ScaleFree")) {
       
   150 	dstWidth = t1 / srcHeight;
       
   151     }
       
   152 
       
   153     d->setup(srcWidth, srcHeight, dstWidth, dstHeight, 0);
   126     d->setup(srcWidth, srcHeight, dstWidth, dstHeight, 0);
   154 }
   127 }
   155 
   128 
   156 void QImageSmoothScalerPrivate::setup(const int srcWidth, const int srcHeight,
   129 void QImageSmoothScalerPrivate::setup(const int srcWidth, const int srcHeight,
   157                                       const int dstWidth, const int dstHeight,
   130                                       const int dstWidth, const int dstHeight,
   160     cols = srcWidth;
   133     cols = srcWidth;
   161     rows = srcHeight;
   134     rows = srcHeight;
   162     newcols = dstWidth;
   135     newcols = dstWidth;
   163     newrows = dstHeight;
   136     newrows = dstHeight;
   164     hasAlpha = hasAlphaChannel;
   137     hasAlpha = hasAlphaChannel;
   165 }
       
   166 
       
   167 int QImageSmoothScaler::scaledWidth() const
       
   168 {
       
   169     return d->cols;
       
   170 }
   138 }
   171 
   139 
   172 QImageSmoothScaler::~QImageSmoothScaler()
   140 QImageSmoothScaler::~QImageSmoothScaler()
   173 {
   141 {
   174     delete d;
   142     delete d;
   465 }
   433 }
   466 
   434 
   467 class jpegSmoothScaler : public QImageSmoothScaler
   435 class jpegSmoothScaler : public QImageSmoothScaler
   468 {
   436 {
   469 public:
   437 public:
   470     jpegSmoothScaler(struct jpeg_decompress_struct *info, const char *params):
   438     jpegSmoothScaler(struct jpeg_decompress_struct *info, const QSize& dstSize, const QRect& clipRect)
   471 	QImageSmoothScaler(info->output_width, info->output_height, params)
   439         : QImageSmoothScaler(clipRect.width(), clipRect.height(),
       
   440                              dstSize.width(), dstSize.height())
   472     {
   441     {
   473 	cinfo = info;
   442         cinfo = info;
   474 	cols24Bit = scaledWidth() * 3;
   443         clip = clipRect;
   475 
   444         imageCache = QImage(info->output_width, 1, QImage::Format_RGB32);
   476 	cacheHeight = 1;
       
   477 	imageCache = QImage( info->output_width, cacheHeight, QImage::Format_RGB32 );
       
   478     }
   445     }
   479 
   446 
   480 private:
   447 private:
   481     int	    cols24Bit;
   448     QRect   clip;
   482     QImage  imageCache;
   449     QImage  imageCache;
   483     int	    cacheHeight;
       
   484     struct jpeg_decompress_struct *cinfo;
   450     struct jpeg_decompress_struct *cinfo;
   485 
   451 
   486     QRgb *scanLine(const int line = 0, const QImage *src = 0)
   452     QRgb *scanLine(const int line = 0, const QImage *src = 0)
   487     {
   453     {
   488 	QRgb    *out;
   454 	QRgb    *out;
   490 
   456 
   491 	Q_UNUSED(line);
   457 	Q_UNUSED(line);
   492 	Q_UNUSED(src);
   458 	Q_UNUSED(src);
   493 
   459 
   494         uchar* data = imageCache.bits();
   460         uchar* data = imageCache.bits();
       
   461 
       
   462         // Read ahead if we haven't reached the first clipped scanline yet.
       
   463         while (int(cinfo->output_scanline) < clip.y() &&
       
   464                cinfo->output_scanline < cinfo->output_height)
       
   465 	    jpeg_read_scanlines(cinfo, &data, 1);
       
   466 
       
   467         // Read the next scanline.  We assume that "line"
       
   468         // will never be >= clip.height().
   495 	jpeg_read_scanlines(cinfo, &data, 1);
   469 	jpeg_read_scanlines(cinfo, &data, 1);
   496 	out = (QRgb*)imageCache.scanLine(0);
   470         if (cinfo->output_scanline == cinfo->output_height)
       
   471             jpeg_finish_decompress(cinfo);
       
   472 
       
   473 	out = ((QRgb*)data) + clip.x();
   497 
   474 
   498 	//
   475 	//
   499 	// The smooth scale algorithm only works on 32-bit images;
   476 	// The smooth scale algorithm only works on 32-bit images;
   500 	// convert from (8|24) bits to 32.
   477 	// convert from (8|24) bits to 32.
   501 	//
   478 	//
   502 	if (cinfo->output_components == 1) {
   479 	if (cinfo->output_components == 1) {
   503 	    in = (uchar*)out + scaledWidth();
   480 	    in = data + clip.right();
   504 	    for (uint i = scaledWidth(); i--; ) {
   481 	    for (int i = clip.width(); i--; ) {
       
   482 		out[i] = qRgb(*in, *in, *in);
   505 		in--;
   483 		in--;
   506 		out[i] = qRgb(*in, *in, *in);
       
   507 	    }
   484 	    }
   508        } else if (cinfo->out_color_space == JCS_CMYK) {
   485         } else if (cinfo->out_color_space == JCS_CMYK) {
   509            int cols32Bit = scaledWidth() * 4;
   486             in = data + clip.right() * 4;
   510            in = (uchar*)out + cols32Bit;
   487             for (int i = clip.width(); i--; ) {
   511            for (uint i = scaledWidth(); i--; ) {
   488                 int k = in[3];
   512                in -= 4;
   489                 out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
   513                int k = in[3];
   490                 in -= 4;
   514                out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
   491             }
   515                //out[i] = qRgb(in[0], in[1], in[2]);
   492         } else {
   516            }
   493 	    in = data + clip.right() * 3;
   517        } else {
   494 	    for (int i = clip.width(); i--; ) {
   518 	    in = (uchar*)out + cols24Bit;
   495 		out[i] = qRgb(in[0], in[1], in[2]);
   519 	    for (uint i = scaledWidth(); i--; ) {
       
   520 		in -= 3;
   496 		in -= 3;
   521 		out[i] = qRgb(in[0], in[1], in[2]);
       
   522 	    }
   497 	    }
   523 	}
   498 	}
   524 
   499 
   525 	return out;
   500 	return out;
   526     }
   501     }
   634     this->device = device;
   609     this->device = device;
   635     bytes_in_buffer = 0;
   610     bytes_in_buffer = 0;
   636     next_input_byte = buffer;
   611     next_input_byte = buffer;
   637 }
   612 }
   638 
   613 
   639 
       
   640 static void scaleSize(int &reqW, int &reqH, int imgW, int imgH, Qt::AspectRatioMode mode)
       
   641 {
       
   642     if (mode == Qt::IgnoreAspectRatio)
       
   643         return;
       
   644     int t1 = imgW * reqH;
       
   645     int t2 = reqW * imgH;
       
   646     if ((mode == Qt::KeepAspectRatio && (t1 > t2)) || (mode == Qt::KeepAspectRatioByExpanding && (t1 < t2)))
       
   647         reqH = t2 / imgW;
       
   648     else
       
   649         reqW = t1 / imgH;
       
   650 }
       
   651 
   614 
   652 static bool read_jpeg_size(QIODevice *device, int &w, int &h)
   615 static bool read_jpeg_size(QIODevice *device, int &w, int &h)
   653 {
   616 {
   654     bool rt = false;
   617     bool rt = false;
   655     struct jpeg_decompress_struct cinfo;
   618     struct jpeg_decompress_struct cinfo;
   727     delete iod_src;
   690     delete iod_src;
   728     return result;
   691     return result;
   729 }
   692 }
   730 
   693 
   731 static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info,
   694 static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info,
   732                              bool dummy = false)
   695                              const QSize& size)
   733 {
   696 {
   734     QImage::Format format;
   697     QImage::Format format;
   735     switch (info->output_components) {
   698     switch (info->output_components) {
   736     case 1:
   699     case 1:
   737         format = QImage::Format_Indexed8;
   700         format = QImage::Format_Indexed8;
   742         break;
   705         break;
   743     default:
   706     default:
   744         return false; // unsupported format
   707         return false; // unsupported format
   745     }
   708     }
   746 
   709 
   747     const QSize size(info->output_width, info->output_height);
       
   748     if (dest->size() != size || dest->format() != format) {
   710     if (dest->size() != size || dest->format() != format) {
   749         static uchar dummyImage[1];
   711         *dest = QImage(size, format);
   750         if (dummy) // Create QImage but don't read the pixels
       
   751             *dest = QImage(dummyImage, size.width(), size.height(), format);
       
   752         else
       
   753             *dest = QImage(size, format);
       
   754 
   712 
   755         if (format == QImage::Format_Indexed8) {
   713         if (format == QImage::Format_Indexed8) {
   756             dest->setColorCount(256);
   714             dest->setColorCount(256);
   757             for (int i = 0; i < 256; i++)
   715             for (int i = 0; i < 256; i++)
   758                 dest->setColor(i, qRgb(i,i,i));
   716                 dest->setColor(i, qRgb(i,i,i));
   761 
   719 
   762     return !dest->isNull();
   720     return !dest->isNull();
   763 }
   721 }
   764 
   722 
   765 static bool read_jpeg_image(QIODevice *device, QImage *outImage,
   723 static bool read_jpeg_image(QIODevice *device, QImage *outImage,
   766                             const QByteArray &parameters, QSize scaledSize,
   724                             QSize scaledSize, QRect scaledClipRect,
   767                             int inQuality )
   725                             QRect clipRect, int inQuality )
   768 {
   726 {
   769 #ifdef QT_NO_IMAGE_SMOOTHSCALE
       
   770     Q_UNUSED( scaledSize );
       
   771 #endif
       
   772 
       
   773     struct jpeg_decompress_struct cinfo;
   727     struct jpeg_decompress_struct cinfo;
   774 
   728 
   775     struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(device);
   729     struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(device);
   776     struct my_error_mgr jerr;
   730     struct my_error_mgr jerr;
   777 
   731 
   792         // -1 means default quality.
   746         // -1 means default quality.
   793         int quality = inQuality;
   747         int quality = inQuality;
   794         if (quality < 0)
   748         if (quality < 0)
   795             quality = 75;
   749             quality = 75;
   796 
   750 
   797         QString params = QString::fromLatin1(parameters);
   751         // If possible, merge the scaledClipRect into either scaledSize
   798         params.simplified();
   752         // or clipRect to avoid doing a separate scaled clipping pass.
   799         int sWidth = 0, sHeight = 0;
   753         // Best results are achieved by clipping before scaling, not after.
   800         char sModeStr[1024] = "";
   754         if (!scaledClipRect.isEmpty()) {
   801         Qt::AspectRatioMode sMode;
   755             if (scaledSize.isEmpty() && clipRect.isEmpty()) {
   802 
   756                 // No clipping or scaling before final clip.
   803 #ifndef QT_NO_IMAGE_SMOOTHSCALE
   757                 clipRect = scaledClipRect;
   804         // If high quality not required, shrink image during decompression
   758                 scaledClipRect = QRect();
   805         if (scaledSize.isValid() && !scaledSize.isEmpty() && quality < HIGH_QUALITY_THRESHOLD
   759             } else if (scaledSize.isEmpty()) {
   806             && !params.contains(QLatin1String("GetHeaderInformation")) ) {
   760                 // Clipping, but no scaling: combine the clip regions.
   807             cinfo.scale_denom = qMin(cinfo.image_width / scaledSize.width(),
   761                 scaledClipRect.translate(clipRect.topLeft());
   808                                      cinfo.image_width / scaledSize.height());
   762                 clipRect = scaledClipRect.intersected(clipRect);
       
   763                 scaledClipRect = QRect();
       
   764             } else if (clipRect.isEmpty()) {
       
   765                 // No clipping, but scaling: if we can map back to an
       
   766                 // integer pixel boundary, then clip before scaling.
       
   767                 if ((cinfo.image_width % scaledSize.width()) == 0 &&
       
   768                         (cinfo.image_height % scaledSize.height()) == 0) {
       
   769                     int x = scaledClipRect.x() * cinfo.image_width /
       
   770                             scaledSize.width();
       
   771                     int y = scaledClipRect.y() * cinfo.image_height /
       
   772                             scaledSize.height();
       
   773                     int width = (scaledClipRect.right() + 1) *
       
   774                                 cinfo.image_width / scaledSize.width() - x;
       
   775                     int height = (scaledClipRect.bottom() + 1) *
       
   776                                  cinfo.image_height / scaledSize.height() - y;
       
   777                     clipRect = QRect(x, y, width, height);
       
   778                     scaledSize = scaledClipRect.size();
       
   779                     scaledClipRect = QRect();
       
   780                 }
       
   781             } else {
       
   782                 // Clipping and scaling: too difficult to figure out,
       
   783                 // and not a likely use case, so do it the long way.
       
   784             }
       
   785         }
       
   786 
       
   787         // Determine the scale factor to pass to libjpeg for quick downscaling.
       
   788         if (!scaledSize.isEmpty()) {
       
   789             if (clipRect.isEmpty()) {
       
   790                 cinfo.scale_denom =
       
   791                     qMin(cinfo.image_width / scaledSize.width(),
       
   792                          cinfo.image_height / scaledSize.height());
       
   793             } else {
       
   794                 cinfo.scale_denom =
       
   795                     qMin(clipRect.width() / scaledSize.width(),
       
   796                          clipRect.height() / scaledSize.height());
       
   797             }
   809             if (cinfo.scale_denom < 2) {
   798             if (cinfo.scale_denom < 2) {
   810                 cinfo.scale_denom = 1;
   799                 cinfo.scale_denom = 1;
   811             } else if (cinfo.scale_denom < 4) {
   800             } else if (cinfo.scale_denom < 4) {
   812                 cinfo.scale_denom = 2;
   801                 cinfo.scale_denom = 2;
   813             } else if (cinfo.scale_denom < 8) {
   802             } else if (cinfo.scale_denom < 8) {
   814                 cinfo.scale_denom = 4;
   803                 cinfo.scale_denom = 4;
   815             } else {
   804             } else {
   816                 cinfo.scale_denom = 8;
   805                 cinfo.scale_denom = 8;
   817             }
   806             }
   818             cinfo.scale_num = 1;
   807             cinfo.scale_num = 1;
   819         }
   808             if (!clipRect.isEmpty()) {
   820 #endif
   809                 // Correct the scale factor so that we clip accurately.
   821 
   810                 // It is recommended that the clip rectangle be aligned
       
   811                 // on an 8-pixel boundary for best performance.
       
   812                 while (cinfo.scale_denom > 1 &&
       
   813                        ((clipRect.x() % cinfo.scale_denom) != 0 ||
       
   814                         (clipRect.y() % cinfo.scale_denom) != 0 ||
       
   815                         (clipRect.width() % cinfo.scale_denom) != 0 ||
       
   816                         (clipRect.height() % cinfo.scale_denom) != 0)) {
       
   817                     cinfo.scale_denom /= 2;
       
   818                 }
       
   819             }
       
   820         }
   822 
   821 
   823         // If high quality not required, use fast decompression
   822         // If high quality not required, use fast decompression
   824         if( quality < HIGH_QUALITY_THRESHOLD ) {
   823         if( quality < HIGH_QUALITY_THRESHOLD ) {
   825             cinfo.dct_method = JDCT_IFAST;
   824             cinfo.dct_method = JDCT_IFAST;
   826             cinfo.do_fancy_upsampling = FALSE;
   825             cinfo.do_fancy_upsampling = FALSE;
   827         }
   826         }
   828 
   827 
   829 
   828         (void) jpeg_calc_output_dimensions(&cinfo);
   830         (void) jpeg_start_decompress(&cinfo);
   829 
   831 
   830         // Determine the clip region to extract.
   832         if (params.contains(QLatin1String("GetHeaderInformation"))) {
   831         QRect imageRect(0, 0, cinfo.output_width, cinfo.output_height);
   833             if (!ensureValidImage(outImage, &cinfo, true))
   832         QRect clip;
       
   833         if (clipRect.isEmpty()) {
       
   834             clip = imageRect;
       
   835         } else if (cinfo.scale_denom == 1) {
       
   836             clip = clipRect.intersected(imageRect);
       
   837         } else {
       
   838             // The scale factor was corrected above to ensure that
       
   839             // we don't miss pixels when we scale the clip rectangle.
       
   840             clip = QRect(clipRect.x() / int(cinfo.scale_denom),
       
   841                          clipRect.y() / int(cinfo.scale_denom),
       
   842                          clipRect.width() / int(cinfo.scale_denom),
       
   843                          clipRect.height() / int(cinfo.scale_denom));
       
   844             clip = clip.intersected(imageRect);
       
   845         }
       
   846 
       
   847 #ifndef QT_NO_IMAGE_SMOOTHSCALE
       
   848         if (scaledSize.isValid() && scaledSize != clip.size()
       
   849             && quality >= HIGH_QUALITY_THRESHOLD) {
       
   850 
       
   851             (void) jpeg_start_decompress(&cinfo);
       
   852 
       
   853             jpegSmoothScaler scaler(&cinfo, scaledSize, clip);
       
   854             *outImage = scaler.scale();
       
   855         } else
       
   856 #endif
       
   857         {
       
   858             // Allocate memory for the clipped QImage.
       
   859             if (!ensureValidImage(outImage, &cinfo, clip.size()))
   834                 longjmp(jerr.setjmp_buffer, 1);
   860                 longjmp(jerr.setjmp_buffer, 1);
   835         } else if (params.contains(QLatin1String("Scale"))) {
   861 
   836 #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(Q_OS_WINCE)
   862             // Avoid memcpy() overhead if grayscale with no clipping.
   837             sscanf_s(params.toLatin1().data(), "Scale(%i, %i, %1023s)",
   863             bool quickGray = (cinfo.output_components == 1 &&
   838                      &sWidth, &sHeight, sModeStr, sizeof(sModeStr));
   864                               clip == imageRect);
   839 #else
   865             if (!quickGray) {
   840             sscanf(params.toLatin1().data(), "Scale(%i, %i, %1023s)",
   866                 // Ask the jpeg library to allocate a temporary row.
   841                    &sWidth, &sHeight, sModeStr);
   867                 // The library will automatically delete it for us later.
   842 #endif
   868                 // The libjpeg docs say we should do this before calling
   843 
   869                 // jpeg_start_decompress().  We can't use "new" here
   844             QString sModeQStr(QString::fromLatin1(sModeStr));
   870                 // because we are inside the setjmp() block and an error
   845             if (sModeQStr == QLatin1String("IgnoreAspectRatio")) {
   871                 // in the jpeg input stream would cause a memory leak.
   846                 sMode = Qt::IgnoreAspectRatio;
   872                 JSAMPARRAY rows = (cinfo.mem->alloc_sarray)
   847             } else if (sModeQStr == QLatin1String("KeepAspectRatio")) {
   873                     ((j_common_ptr)&cinfo, JPOOL_IMAGE,
   848                 sMode = Qt::KeepAspectRatio;
   874                      cinfo.output_width * cinfo.output_components, 1);
   849             } else if (sModeQStr == QLatin1String("KeepAspectRatioByExpanding")) {
   875 
   850                 sMode = Qt::KeepAspectRatioByExpanding;
   876                 (void) jpeg_start_decompress(&cinfo);
   851             } else {
   877 
   852                 qDebug("read_jpeg_image: invalid aspect ratio mode \"%s\", see QImage::AspectRatioMode documentation", sModeStr);
       
   853                 sMode = Qt::KeepAspectRatio;
       
   854             }
       
   855 
       
   856 //            qDebug("Parameters ask to scale the image to %i x %i AspectRatioMode: %s", sWidth, sHeight, sModeStr);
       
   857             scaleSize(sWidth, sHeight, cinfo.output_width, cinfo.output_height, sMode);
       
   858 //            qDebug("Scaling the jpeg to %i x %i", sWidth, sHeight, sModeStr);
       
   859 
       
   860             if (cinfo.output_components == 3 || cinfo.output_components == 4) {
       
   861                 if (outImage->size() != QSize(sWidth, sHeight) || outImage->format() != QImage::Format_RGB32)
       
   862                     *outImage = QImage(sWidth, sHeight, QImage::Format_RGB32);
       
   863             } else if (cinfo.output_components == 1) {
       
   864                 if (outImage->size() != QSize(sWidth, sHeight) || outImage->format() != QImage::Format_Indexed8)
       
   865                     *outImage = QImage(sWidth, sHeight, QImage::Format_Indexed8);
       
   866                 outImage->setColorCount(256);
       
   867                 for (int i = 0; i < 256; ++i)
       
   868                     outImage->setColor(i, qRgb(i,i,i));
       
   869             } else {
       
   870                 // Unsupported format
       
   871             }
       
   872             if (outImage->isNull())
       
   873                 longjmp(jerr.setjmp_buffer, 1);
       
   874 
       
   875             if (!outImage->isNull()) {
       
   876                 QImage tmpImage(cinfo.output_width, 1, QImage::Format_RGB32);
       
   877                 uchar* inData = tmpImage.bits();
       
   878                 uchar* outData = outImage->bits();
       
   879                 int out_bpl = outImage->bytesPerLine();
       
   880                 while (cinfo.output_scanline < cinfo.output_height) {
   878                 while (cinfo.output_scanline < cinfo.output_height) {
   881                     int outputLine = sHeight * cinfo.output_scanline / cinfo.output_height;
   879                     int y = int(cinfo.output_scanline) - clip.y();
   882                     (void) jpeg_read_scanlines(&cinfo, &inData, 1);
   880                     if (y >= clip.height())
       
   881                         break;      // We've read the entire clip region, so abort.
       
   882 
       
   883                     (void) jpeg_read_scanlines(&cinfo, rows, 1);
       
   884 
       
   885                     if (y < 0)
       
   886                         continue;   // Haven't reached the starting line yet.
       
   887 
   883                     if (cinfo.output_components == 3) {
   888                     if (cinfo.output_components == 3) {
   884                         uchar *in = inData;
   889                         // Expand 24->32 bpp.
   885                         QRgb *out = (QRgb*)outData + outputLine * out_bpl;
   890                         uchar *in = rows[0] + clip.x() * 3;
   886                         for (uint i=0; i<cinfo.output_width; i++) {
   891                         QRgb *out = (QRgb*)outImage->scanLine(y);
   887 // ### Only scaling down an image works, I don't think scaling up will work at the moment
   892                         for (int i = 0; i < clip.width(); ++i) {
   888 // ### An idea I have to make this a smooth scale is to progressively add the pixel values up
   893                             *out++ = qRgb(in[0], in[1], in[2]);
   889 // When scaling down, multiple values are being over drawn in to the output buffer.
       
   890 // Instead, a weighting based on the distance the line or pixel is from the output pixel determines
       
   891 // the weight of it when added to the output buffer. At present it is a non-smooth scale which is
       
   892 // inefficently implemented, it still uncompresses all the jpeg, an optimization for progressive
       
   893 // jpegs could be made if scaling by say 50% or some other special cases
       
   894                             out[sWidth * i / cinfo.output_width] = qRgb(in[0], in[1], in[2]);
       
   895                             in += 3;
   894                             in += 3;
   896                         }
   895                         }
   897                     } else {
   896                     } else if (cinfo.out_color_space == JCS_CMYK) {
   898 // ### Need to test the case where the jpeg is grayscale, need some black and white jpegs to test
   897                         // Convert CMYK->RGB.
   899 // this code. (also only scales down and probably won't scale to a larger size)
   898                         uchar *in = rows[0] + clip.x() * 4;
   900                         uchar *in = inData;
   899                         QRgb *out = (QRgb*)outImage->scanLine(y);
   901                         uchar *out = outData + outputLine*out_bpl;
   900                         for (int i = 0; i < clip.width(); ++i) {
   902                         for (uint i=0; i<cinfo.output_width; i++) {
   901                             int k = in[3];
   903                             out[sWidth * i / cinfo.output_width] = in[i];
   902                             *out++ = qRgb(k * in[0] / 255, k * in[1] / 255,
       
   903                                           k * in[2] / 255);
       
   904                             in += 4;
   904                         }
   905                         }
       
   906                     } else if (cinfo.output_components == 1) {
       
   907                         // Grayscale.
       
   908                         memcpy(outImage->scanLine(y),
       
   909                                rows[0] + clip.x(), clip.width());
   905                     }
   910                     }
   906                 }
   911                 }
       
   912             } else {
       
   913                 // Load unclipped grayscale data directly into the QImage.
       
   914                 (void) jpeg_start_decompress(&cinfo);
       
   915                 while (cinfo.output_scanline < cinfo.output_height) {
       
   916                     uchar *row = outImage->scanLine(cinfo.output_scanline);
       
   917                     (void) jpeg_read_scanlines(&cinfo, &row, 1);
       
   918                 }
       
   919             }
       
   920 
       
   921             if (cinfo.output_scanline == cinfo.output_height)
   907                 (void) jpeg_finish_decompress(&cinfo);
   922                 (void) jpeg_finish_decompress(&cinfo);
   908             }
   923 
   909 #ifndef QT_NO_IMAGE_SMOOTHSCALE
       
   910         } else if (scaledSize.isValid() && scaledSize != QSize(cinfo.output_width, cinfo.output_height)
       
   911             && quality >= HIGH_QUALITY_THRESHOLD) {
       
   912 
       
   913             jpegSmoothScaler scaler(&cinfo, QString().sprintf("Scale( %d, %d, ScaleFree )",
       
   914                                                               scaledSize.width(),
       
   915                                                               scaledSize.height()).toLatin1().data());
       
   916             *outImage = scaler.scale();
       
   917 #endif
       
   918         } else {
       
   919             if (!ensureValidImage(outImage, &cinfo))
       
   920                 longjmp(jerr.setjmp_buffer, 1);
       
   921 
       
   922             uchar* data = outImage->bits();
       
   923             int bpl = outImage->bytesPerLine();
       
   924             while (cinfo.output_scanline < cinfo.output_height) {
       
   925                 uchar *d = data + cinfo.output_scanline * bpl;
       
   926                 (void) jpeg_read_scanlines(&cinfo,
       
   927                                            &d,
       
   928                                            1);
       
   929             }
       
   930             (void) jpeg_finish_decompress(&cinfo);
       
   931 
       
   932             if (cinfo.output_components == 3) {
       
   933                 // Expand 24->32 bpp.
       
   934                 for (uint j=0; j<cinfo.output_height; j++) {
       
   935                     uchar *in = outImage->scanLine(j) + cinfo.output_width * 3;
       
   936                     QRgb *out = (QRgb*)outImage->scanLine(j);
       
   937 
       
   938                     for (uint i=cinfo.output_width; i--;) {
       
   939                         in-=3;
       
   940                         out[i] = qRgb(in[0], in[1], in[2]);
       
   941                     }
       
   942                 }
       
   943             } else if (cinfo.out_color_space == JCS_CMYK) {
       
   944                 for (uint j = 0; j < cinfo.output_height; ++j) {
       
   945                     uchar *in = outImage->scanLine(j) + cinfo.output_width * 4;
       
   946                     QRgb *out = (QRgb*)outImage->scanLine(j);
       
   947 
       
   948                     for (uint i = cinfo.output_width; i--; ) {
       
   949                         in-=4;
       
   950                         int k = in[3];
       
   951                         out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
       
   952                     }
       
   953                 }
       
   954             }
       
   955             if (cinfo.density_unit == 1) {
   924             if (cinfo.density_unit == 1) {
   956                 outImage->setDotsPerMeterX(int(100. * cinfo.X_density / 2.54));
   925                 outImage->setDotsPerMeterX(int(100. * cinfo.X_density / 2.54));
   957                 outImage->setDotsPerMeterY(int(100. * cinfo.Y_density / 2.54));
   926                 outImage->setDotsPerMeterY(int(100. * cinfo.Y_density / 2.54));
   958             } else if (cinfo.density_unit == 2) {
   927             } else if (cinfo.density_unit == 2) {
   959                 outImage->setDotsPerMeterX(int(100. * cinfo.X_density));
   928                 outImage->setDotsPerMeterX(int(100. * cinfo.X_density));
   960                 outImage->setDotsPerMeterY(int(100. * cinfo.Y_density));
   929                 outImage->setDotsPerMeterY(int(100. * cinfo.Y_density));
   961             }
   930             }
   962 
   931 
   963             if (scaledSize.isValid() && scaledSize != QSize(cinfo.output_width, cinfo.output_height))
   932             if (scaledSize.isValid() && scaledSize != clip.size())
   964                 *outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::FastTransformation);
   933                 *outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::FastTransformation);
   965         }
   934         }
   966     }
   935     }
   967 
   936 
   968     jpeg_destroy_decompress(&cinfo);
   937     jpeg_destroy_decompress(&cinfo);
   969     delete iod_src;
   938     delete iod_src;
       
   939     if (!scaledClipRect.isEmpty())
       
   940         *outImage = outImage->copy(scaledClipRect);
   970     return !outImage->isNull();
   941     return !outImage->isNull();
   971 }
   942 }
   972 
   943 
   973 
   944 
   974 struct my_jpeg_destination_mgr : public jpeg_destination_mgr {
   945 struct my_jpeg_destination_mgr : public jpeg_destination_mgr {
  1222 
  1193 
  1223 bool QJpegHandler::read(QImage *image)
  1194 bool QJpegHandler::read(QImage *image)
  1224 {
  1195 {
  1225     if (!canRead())
  1196     if (!canRead())
  1226         return false;
  1197         return false;
  1227     return read_jpeg_image(device(), image, parameters, scaledSize, quality);
  1198     return read_jpeg_image(device(), image, scaledSize, scaledClipRect, clipRect, quality);
  1228 }
  1199 }
  1229 
  1200 
  1230 bool QJpegHandler::write(const QImage &image)
  1201 bool QJpegHandler::write(const QImage &image)
  1231 {
  1202 {
  1232     return write_jpeg_image(image, device(), quality);
  1203     return write_jpeg_image(image, device(), quality);
  1233 }
  1204 }
  1234 
  1205 
  1235 bool QJpegHandler::supportsOption(ImageOption option) const
  1206 bool QJpegHandler::supportsOption(ImageOption option) const
  1236 {
  1207 {
  1237     return option == Quality
  1208     return option == Quality
  1238 #ifndef QT_NO_IMAGE_SMOOTHSCALE
       
  1239         || option == ScaledSize
  1209         || option == ScaledSize
  1240 #endif
  1210         || option == ScaledClipRect
       
  1211         || option == ClipRect
  1241         || option == Size
  1212         || option == Size
  1242         || option == ImageFormat;
  1213         || option == ImageFormat;
  1243 }
  1214 }
  1244 
  1215 
  1245 QVariant QJpegHandler::option(ImageOption option) const
  1216 QVariant QJpegHandler::option(ImageOption option) const
  1246 {
  1217 {
  1247     if (option == Quality) {
  1218     if (option == Quality) {
  1248         return quality;
  1219         return quality;
  1249 #ifndef QT_NO_IMAGE_SMOOTHSCALE
       
  1250     } else if  (option == ScaledSize) {
  1220     } else if  (option == ScaledSize) {
  1251         return scaledSize;
  1221         return scaledSize;
  1252 #endif
  1222     } else if  (option == ScaledClipRect) {
       
  1223         return scaledClipRect;
       
  1224     } else if  (option == ClipRect) {
       
  1225         return clipRect;
  1253     } else if (option == Size) {
  1226     } else if (option == Size) {
  1254         if (canRead() && !device()->isSequential()) {
  1227         if (canRead() && !device()->isSequential()) {
  1255             qint64 pos = device()->pos();
  1228             qint64 pos = device()->pos();
  1256             int width = 0;
  1229             int width = 0;
  1257             int height = 0;
  1230             int height = 0;
  1274 
  1247 
  1275 void QJpegHandler::setOption(ImageOption option, const QVariant &value)
  1248 void QJpegHandler::setOption(ImageOption option, const QVariant &value)
  1276 {
  1249 {
  1277     if (option == Quality)
  1250     if (option == Quality)
  1278         quality = value.toInt();
  1251         quality = value.toInt();
  1279 #ifndef QT_NO_IMAGE_SMOOTHSCALE
       
  1280     else if ( option == ScaledSize )
  1252     else if ( option == ScaledSize )
  1281         scaledSize = value.toSize();
  1253         scaledSize = value.toSize();
  1282 #endif
  1254     else if ( option == ScaledClipRect )
       
  1255         scaledClipRect = value.toRect();
       
  1256     else if ( option == ClipRect )
       
  1257         clipRect = value.toRect();
  1283 }
  1258 }
  1284 
  1259 
  1285 QByteArray QJpegHandler::name() const
  1260 QByteArray QJpegHandler::name() const
  1286 {
  1261 {
  1287     return "jpeg";
  1262     return "jpeg";