src/plugins/imageformats/jpeg/qjpeghandler.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 "qjpeghandler.h"
       
    43 
       
    44 #include <qimage.h>
       
    45 #include <qvariant.h>
       
    46 #include <qvector.h>
       
    47 
       
    48 #include <stdio.h>      // jpeglib needs this to be pre-included
       
    49 #include <setjmp.h>
       
    50 
       
    51 #ifdef FAR
       
    52 #undef FAR
       
    53 #endif
       
    54 
       
    55 // hw: optimize smoothscaler for returning 24-bit images
       
    56 
       
    57 // including jpeglib.h seems to be a little messy
       
    58 extern "C" {
       
    59 // mingw includes rpcndr.h but does not define boolean
       
    60 #if defined(Q_OS_WIN) && defined(Q_CC_GNU)
       
    61 #   if defined(__RPCNDR_H__) && !defined(boolean)
       
    62         typedef unsigned char boolean;
       
    63 #       define HAVE_BOOLEAN
       
    64 #   endif
       
    65 #endif
       
    66 
       
    67 #define XMD_H           // shut JPEGlib up
       
    68 #if defined(Q_OS_UNIXWARE)
       
    69 #  define HAVE_BOOLEAN  // libjpeg under Unixware seems to need this
       
    70 #endif
       
    71 #include <jpeglib.h>
       
    72 #ifdef const
       
    73 #  undef const          // remove crazy C hackery in jconfig.h
       
    74 #endif
       
    75 }
       
    76 
       
    77 QT_BEGIN_NAMESPACE
       
    78 
       
    79 //#define QT_NO_IMAGE_SMOOTHSCALE
       
    80 #ifndef QT_NO_IMAGE_SMOOTHSCALE
       
    81 class QImageSmoothScalerPrivate;
       
    82 class QImageSmoothScaler
       
    83 {
       
    84 public:
       
    85     QImageSmoothScaler(const int w, const int h, const QImage &src);
       
    86     QImageSmoothScaler(const int srcWidth, const int srcHeight,
       
    87                        const char *parameters);
       
    88 
       
    89     virtual ~QImageSmoothScaler(void);
       
    90 
       
    91     QImage  scale();
       
    92 
       
    93 protected:
       
    94     int scaledWidth(void) const;
       
    95 
       
    96 private:
       
    97     QImageSmoothScalerPrivate	*d;
       
    98     virtual QRgb *scanLine(const int line = 0, const QImage *src = 0);
       
    99 };
       
   100 
       
   101 class QImageSmoothScalerPrivate
       
   102 {
       
   103 public:
       
   104     int	    cols;
       
   105     int	    newcols;
       
   106     int	    rows;
       
   107     int	    newrows;
       
   108     bool    hasAlpha;
       
   109 
       
   110     const QImage  *src;
       
   111 
       
   112     void setup(const int srcWidth, const int srcHeight, const int dstWidth,
       
   113                const int dstHeight, bool hasAlphaChannel);
       
   114 };
       
   115 
       
   116 QImageSmoothScaler::QImageSmoothScaler(const int w, const int h,
       
   117                                        const QImage &src)
       
   118 {
       
   119     d = new QImageSmoothScalerPrivate;
       
   120 
       
   121     d->setup(src.width(), src.height(), w, h, src.hasAlphaChannel() );
       
   122     this->d->src = &src;
       
   123 }
       
   124 
       
   125 QImageSmoothScaler::QImageSmoothScaler(const int srcWidth, const int srcHeight,
       
   126                                        const char *parameters)
       
   127 {
       
   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;
       
   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);
       
   154 }
       
   155 
       
   156 void QImageSmoothScalerPrivate::setup(const int srcWidth, const int srcHeight,
       
   157                                       const int dstWidth, const int dstHeight,
       
   158                                       bool hasAlphaChannel)
       
   159 {
       
   160     cols = srcWidth;
       
   161     rows = srcHeight;
       
   162     newcols = dstWidth;
       
   163     newrows = dstHeight;
       
   164     hasAlpha = hasAlphaChannel;
       
   165 }
       
   166 
       
   167 int QImageSmoothScaler::scaledWidth() const
       
   168 {
       
   169     return d->cols;
       
   170 }
       
   171 
       
   172 QImageSmoothScaler::~QImageSmoothScaler()
       
   173 {
       
   174     delete d;
       
   175 }
       
   176 
       
   177 inline QRgb *QImageSmoothScaler::scanLine(const int line, const QImage *src)
       
   178 {
       
   179     return (QRgb*)src->scanLine(line);
       
   180 }
       
   181 
       
   182 /*
       
   183   This function uses code based on pnmscale.c by Jef Poskanzer.
       
   184 
       
   185   pnmscale.c - read a portable anymap and scale it
       
   186 
       
   187   Copyright (C) 1989, 1991 by Jef Poskanzer.
       
   188 
       
   189   Permission to use, copy, modify, and distribute this software and its
       
   190   documentation for any purpose and without fee is hereby granted, provided
       
   191   that the above copyright notice appear in all copies and that both that
       
   192   copyright notice and this permission notice appear in supporting
       
   193   documentation.  This software is provided "as is" without express or
       
   194   implied warranty.
       
   195 */
       
   196 
       
   197 QImage QImageSmoothScaler::scale()
       
   198 {
       
   199     long    SCALE;
       
   200     long    HALFSCALE;
       
   201     QRgb    *xelrow = 0;
       
   202     QRgb    *tempxelrow = 0;
       
   203     QRgb    *xP;
       
   204     QRgb    *nxP;
       
   205     int	    row, rowsread;
       
   206     int	    col, needtoreadrow;
       
   207     uchar   maxval = 255;
       
   208     qreal  xscale, yscale;
       
   209     long    sxscale, syscale;
       
   210     long    fracrowtofill, fracrowleft;
       
   211     long    *as;
       
   212     long    *rs;
       
   213     long    *gs;
       
   214     long    *bs;
       
   215     int	    rowswritten = 0;
       
   216     QImage  dst;
       
   217 
       
   218     if (d->cols > 4096) {
       
   219 	SCALE = 4096;
       
   220 	HALFSCALE = 2048;
       
   221     } else {
       
   222 	int fac = 4096;
       
   223 	while (d->cols * fac > 4096) {
       
   224 	    fac /= 2;
       
   225 	}
       
   226 
       
   227 	SCALE = fac * d->cols;
       
   228 	HALFSCALE = fac * d->cols / 2;
       
   229     }
       
   230 
       
   231     xscale = (qreal) d->newcols / (qreal) d->cols;
       
   232     yscale = (qreal) d->newrows / (qreal) d->rows;
       
   233     sxscale = (long)(xscale * SCALE);
       
   234     syscale = (long)(yscale * SCALE);
       
   235 
       
   236     if ( d->newrows != d->rows )	/* shortcut Y scaling if possible */
       
   237 	tempxelrow = new QRgb[d->cols];
       
   238 
       
   239     if ( d->hasAlpha ) {
       
   240 	as = new long[d->cols];
       
   241 	for ( col = 0; col < d->cols; ++col )
       
   242 	    as[col] = HALFSCALE;
       
   243     } else {
       
   244 	as = 0;
       
   245     }
       
   246     rs = new long[d->cols];
       
   247     gs = new long[d->cols];
       
   248     bs = new long[d->cols];
       
   249     rowsread = 0;
       
   250     fracrowleft = syscale;
       
   251     needtoreadrow = 1;
       
   252     for ( col = 0; col < d->cols; ++col )
       
   253 	rs[col] = gs[col] = bs[col] = HALFSCALE;
       
   254     fracrowtofill = SCALE;
       
   255 
       
   256     dst = QImage( d->newcols, d->newrows, d->hasAlpha ? QImage::Format_ARGB32 : QImage::Format_RGB32 );
       
   257 
       
   258     for ( row = 0; row < d->newrows; ++row ) {
       
   259 	/* First scale Y from xelrow into tempxelrow. */
       
   260 	if ( d->newrows == d->rows ) {
       
   261 	    /* shortcut Y scaling if possible */
       
   262 	    tempxelrow = xelrow = scanLine(rowsread++, d->src);
       
   263 	} else {
       
   264 	    while ( fracrowleft < fracrowtofill ) {
       
   265 		if ( needtoreadrow && rowsread < d->rows ) {
       
   266 		    xelrow = scanLine(rowsread++, d->src);
       
   267 		}
       
   268 		for ( col = 0, xP = xelrow; col < d->cols; ++col, ++xP ) {
       
   269 		    if (as) {
       
   270 			as[col] += fracrowleft * qAlpha( *xP );
       
   271 			rs[col] += fracrowleft * qRed( *xP ) * qAlpha( *xP ) / 255;
       
   272 			gs[col] += fracrowleft * qGreen( *xP ) * qAlpha( *xP ) / 255;
       
   273 			bs[col] += fracrowleft * qBlue( *xP ) * qAlpha( *xP ) / 255;
       
   274 		    } else {
       
   275 			rs[col] += fracrowleft * qRed( *xP );
       
   276 			gs[col] += fracrowleft * qGreen( *xP );
       
   277 			bs[col] += fracrowleft * qBlue( *xP );
       
   278 		    }
       
   279 		}
       
   280 		fracrowtofill -= fracrowleft;
       
   281 		fracrowleft = syscale;
       
   282 		needtoreadrow = 1;
       
   283 	    }
       
   284 	    /* Now fracrowleft is >= fracrowtofill, so we can produce a row. */
       
   285 	    if ( needtoreadrow && rowsread < d->rows) {
       
   286 		xelrow = scanLine(rowsread++, d->src);
       
   287 		needtoreadrow = 0;
       
   288 	    }
       
   289 	    for ( col = 0, xP = xelrow, nxP = tempxelrow;
       
   290 		  col < d->cols; ++col, ++xP, ++nxP )
       
   291 	    {
       
   292 		register long a, r, g, b;
       
   293 
       
   294 		if ( as ) {
       
   295 		    r = rs[col] + fracrowtofill * qRed( *xP ) * qAlpha( *xP ) / 255;
       
   296 		    g = gs[col] + fracrowtofill * qGreen( *xP ) * qAlpha( *xP ) / 255;
       
   297 		    b = bs[col] + fracrowtofill * qBlue( *xP ) * qAlpha( *xP ) / 255;
       
   298 		    a = as[col] + fracrowtofill * qAlpha( *xP );
       
   299 		    if ( a ) {
       
   300 			r = r * 255 / a * SCALE;
       
   301 			g = g * 255 / a * SCALE;
       
   302 			b = b * 255 / a * SCALE;
       
   303 		    }
       
   304 		} else {
       
   305 		    r = rs[col] + fracrowtofill * qRed( *xP );
       
   306 		    g = gs[col] + fracrowtofill * qGreen( *xP );
       
   307 		    b = bs[col] + fracrowtofill * qBlue( *xP );
       
   308 		    a = 0; // unwarn
       
   309 		}
       
   310 		r /= SCALE;
       
   311 		if ( r > maxval ) r = maxval;
       
   312 		g /= SCALE;
       
   313 		if ( g > maxval ) g = maxval;
       
   314 		b /= SCALE;
       
   315 		if ( b > maxval ) b = maxval;
       
   316 		if ( as ) {
       
   317 		    a /= SCALE;
       
   318 		    if ( a > maxval ) a = maxval;
       
   319 		    *nxP = qRgba( (int)r, (int)g, (int)b, (int)a );
       
   320 		    as[col] = HALFSCALE;
       
   321 		} else {
       
   322 		    *nxP = qRgb( (int)r, (int)g, (int)b );
       
   323 		}
       
   324 		rs[col] = gs[col] = bs[col] = HALFSCALE;
       
   325 	    }
       
   326 	    fracrowleft -= fracrowtofill;
       
   327 	    if ( fracrowleft == 0 ) {
       
   328 		fracrowleft = syscale;
       
   329 		needtoreadrow = 1;
       
   330 	    }
       
   331 	    fracrowtofill = SCALE;
       
   332 	}
       
   333 
       
   334 	/* Now scale X from tempxelrow into dst and write it out. */
       
   335 	if ( d->newcols == d->cols ) {
       
   336 	    /* shortcut X scaling if possible */
       
   337 	    memcpy(dst.scanLine(rowswritten++), tempxelrow, d->newcols*4);
       
   338 	} else {
       
   339 	    register long a, r, g, b;
       
   340 	    register long fraccoltofill, fraccolleft = 0;
       
   341 	    register int needcol;
       
   342 
       
   343 	    nxP = (QRgb*)dst.scanLine(rowswritten++);
       
   344 	    fraccoltofill = SCALE;
       
   345 	    a = r = g = b = HALFSCALE;
       
   346 	    needcol = 0;
       
   347 	    for ( col = 0, xP = tempxelrow; col < d->cols; ++col, ++xP ) {
       
   348 		fraccolleft = sxscale;
       
   349 		while ( fraccolleft >= fraccoltofill ) {
       
   350 		    if ( needcol ) {
       
   351 			++nxP;
       
   352 			a = r = g = b = HALFSCALE;
       
   353 		    }
       
   354 		    if ( as ) {
       
   355 			r += fraccoltofill * qRed( *xP ) * qAlpha( *xP ) / 255;
       
   356 			g += fraccoltofill * qGreen( *xP ) * qAlpha( *xP ) / 255;
       
   357 			b += fraccoltofill * qBlue( *xP ) * qAlpha( *xP ) / 255;
       
   358 			a += fraccoltofill * qAlpha( *xP );
       
   359 			if ( a ) {
       
   360 			    r = r * 255 / a * SCALE;
       
   361 			    g = g * 255 / a * SCALE;
       
   362 			    b = b * 255 / a * SCALE;
       
   363 			}
       
   364 		    } else {
       
   365 			r += fraccoltofill * qRed( *xP );
       
   366 			g += fraccoltofill * qGreen( *xP );
       
   367 			b += fraccoltofill * qBlue( *xP );
       
   368 		    }
       
   369 		    r /= SCALE;
       
   370 		    if ( r > maxval ) r = maxval;
       
   371 		    g /= SCALE;
       
   372 		    if ( g > maxval ) g = maxval;
       
   373 		    b /= SCALE;
       
   374 		    if ( b > maxval ) b = maxval;
       
   375 		    if (as) {
       
   376 			a /= SCALE;
       
   377 			if ( a > maxval ) a = maxval;
       
   378 			*nxP = qRgba( (int)r, (int)g, (int)b, (int)a );
       
   379 		    } else {
       
   380 			*nxP = qRgb( (int)r, (int)g, (int)b );
       
   381 		    }
       
   382 		    fraccolleft -= fraccoltofill;
       
   383 		    fraccoltofill = SCALE;
       
   384 		    needcol = 1;
       
   385 		}
       
   386 		if ( fraccolleft > 0 ) {
       
   387 		    if ( needcol ) {
       
   388 			++nxP;
       
   389 			a = r = g = b = HALFSCALE;
       
   390 			needcol = 0;
       
   391 		    }
       
   392 		    if (as) {
       
   393 			a += fraccolleft * qAlpha( *xP );
       
   394 			r += fraccolleft * qRed( *xP ) * qAlpha( *xP ) / 255;
       
   395 			g += fraccolleft * qGreen( *xP ) * qAlpha( *xP ) / 255;
       
   396 			b += fraccolleft * qBlue( *xP ) * qAlpha( *xP ) / 255;
       
   397 		    } else {
       
   398 			r += fraccolleft * qRed( *xP );
       
   399 			g += fraccolleft * qGreen( *xP );
       
   400 			b += fraccolleft * qBlue( *xP );
       
   401 		    }
       
   402 		    fraccoltofill -= fraccolleft;
       
   403 		}
       
   404 	    }
       
   405 	    if ( fraccoltofill > 0 ) {
       
   406 		--xP;
       
   407 		if (as) {
       
   408 		    a += fraccolleft * qAlpha( *xP );
       
   409 		    r += fraccoltofill * qRed( *xP ) * qAlpha( *xP ) / 255;
       
   410 		    g += fraccoltofill * qGreen( *xP ) * qAlpha( *xP ) / 255;
       
   411 		    b += fraccoltofill * qBlue( *xP ) * qAlpha( *xP ) / 255;
       
   412 		    if ( a ) {
       
   413 			r = r * 255 / a * SCALE;
       
   414 			g = g * 255 / a * SCALE;
       
   415 			b = b * 255 / a * SCALE;
       
   416 		    }
       
   417 		} else {
       
   418 		    r += fraccoltofill * qRed( *xP );
       
   419 		    g += fraccoltofill * qGreen( *xP );
       
   420 		    b += fraccoltofill * qBlue( *xP );
       
   421 		}
       
   422 	    }
       
   423 	    if ( ! needcol ) {
       
   424 		r /= SCALE;
       
   425 		if ( r > maxval ) r = maxval;
       
   426 		g /= SCALE;
       
   427 		if ( g > maxval ) g = maxval;
       
   428 		b /= SCALE;
       
   429 		if ( b > maxval ) b = maxval;
       
   430 		if (as) {
       
   431 		    a /= SCALE;
       
   432 		    if ( a > maxval ) a = maxval;
       
   433 		    *nxP = qRgba( (int)r, (int)g, (int)b, (int)a );
       
   434 		} else {
       
   435 		    *nxP = qRgb( (int)r, (int)g, (int)b );
       
   436 		}
       
   437 	    }
       
   438 	}
       
   439     }
       
   440 
       
   441     if ( d->newrows != d->rows && tempxelrow )// Robust, tempxelrow might be 0 1 day
       
   442 	delete [] tempxelrow;
       
   443     if ( as )				// Avoid purify complaint
       
   444 	delete [] as;
       
   445     if ( rs )				// Robust, rs might be 0 one day
       
   446 	delete [] rs;
       
   447     if ( gs )				// Robust, gs might be 0 one day
       
   448 	delete [] gs;
       
   449     if ( bs )				// Robust, bs might be 0 one day
       
   450 	delete [] bs;
       
   451 
       
   452     return dst;
       
   453 }
       
   454 
       
   455 class jpegSmoothScaler : public QImageSmoothScaler
       
   456 {
       
   457 public:
       
   458     jpegSmoothScaler(struct jpeg_decompress_struct *info, const char *params):
       
   459 	QImageSmoothScaler(info->output_width, info->output_height, params)
       
   460     {
       
   461 	cinfo = info;
       
   462 	cols24Bit = scaledWidth() * 3;
       
   463 
       
   464 	cacheHeight = 1;
       
   465 	imageCache = QImage( info->output_width, cacheHeight, QImage::Format_RGB32 );
       
   466     }
       
   467 
       
   468 private:
       
   469     int	    cols24Bit;
       
   470     QImage  imageCache;
       
   471     int	    cacheHeight;
       
   472     struct jpeg_decompress_struct *cinfo;
       
   473 
       
   474     QRgb *scanLine(const int line = 0, const QImage *src = 0)
       
   475     {
       
   476 	QRgb    *out;
       
   477 	uchar	*in;
       
   478 
       
   479 	Q_UNUSED(line);
       
   480 	Q_UNUSED(src);
       
   481 
       
   482         uchar* data = imageCache.bits();
       
   483 	jpeg_read_scanlines(cinfo, &data, 1);
       
   484 	out = (QRgb*)imageCache.scanLine(0);
       
   485 
       
   486 	//
       
   487 	// The smooth scale algorithm only works on 32-bit images;
       
   488 	// convert from (8|24) bits to 32.
       
   489 	//
       
   490 	if (cinfo->output_components == 1) {
       
   491 	    in = (uchar*)out + scaledWidth();
       
   492 	    for (uint i = scaledWidth(); i--; ) {
       
   493 		in--;
       
   494 		out[i] = qRgb(*in, *in, *in);
       
   495 	    }
       
   496        } else if (cinfo->out_color_space == JCS_CMYK) {
       
   497            int cols32Bit = scaledWidth() * 4;
       
   498            in = (uchar*)out + cols32Bit;
       
   499            for (uint i = scaledWidth(); i--; ) {
       
   500                in -= 4;
       
   501                int k = in[3];
       
   502                out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
       
   503                //out[i] = qRgb(in[0], in[1], in[2]);
       
   504            }
       
   505        } else {
       
   506 	    in = (uchar*)out + cols24Bit;
       
   507 	    for (uint i = scaledWidth(); i--; ) {
       
   508 		in -= 3;
       
   509 		out[i] = qRgb(in[0], in[1], in[2]);
       
   510 	    }
       
   511 	}
       
   512 
       
   513 	return out;
       
   514     }
       
   515 
       
   516 };
       
   517 #endif
       
   518 
       
   519 struct my_error_mgr : public jpeg_error_mgr {
       
   520     jmp_buf setjmp_buffer;
       
   521 };
       
   522 
       
   523 #if defined(Q_C_CALLBACKS)
       
   524 extern "C" {
       
   525 #endif
       
   526 
       
   527 static void my_error_exit (j_common_ptr cinfo)
       
   528 {
       
   529     my_error_mgr* myerr = (my_error_mgr*) cinfo->err;
       
   530     char buffer[JMSG_LENGTH_MAX];
       
   531     (*cinfo->err->format_message)(cinfo, buffer);
       
   532     qWarning("%s", buffer);
       
   533     longjmp(myerr->setjmp_buffer, 1);
       
   534 }
       
   535 
       
   536 #if defined(Q_C_CALLBACKS)
       
   537 }
       
   538 #endif
       
   539 
       
   540 
       
   541 static const int max_buf = 4096;
       
   542 
       
   543 struct my_jpeg_source_mgr : public jpeg_source_mgr {
       
   544     // Nothing dynamic - cannot rely on destruction over longjump
       
   545     QIODevice *device;
       
   546     JOCTET buffer[max_buf];
       
   547 
       
   548 public:
       
   549     my_jpeg_source_mgr(QIODevice *device);
       
   550 };
       
   551 
       
   552 #if defined(Q_C_CALLBACKS)
       
   553 extern "C" {
       
   554 #endif
       
   555 
       
   556 static void qt_init_source(j_decompress_ptr)
       
   557 {
       
   558 }
       
   559 
       
   560 static boolean qt_fill_input_buffer(j_decompress_ptr cinfo)
       
   561 {
       
   562     int num_read;
       
   563     my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
       
   564     src->next_input_byte = src->buffer;
       
   565     num_read = src->device->read((char*)src->buffer, max_buf);
       
   566     if (num_read <= 0) {
       
   567         // Insert a fake EOI marker - as per jpeglib recommendation
       
   568         src->buffer[0] = (JOCTET) 0xFF;
       
   569         src->buffer[1] = (JOCTET) JPEG_EOI;
       
   570         src->bytes_in_buffer = 2;
       
   571     } else {
       
   572         src->bytes_in_buffer = num_read;
       
   573     }
       
   574 #if defined(Q_OS_UNIXWARE)
       
   575     return B_TRUE;
       
   576 #else
       
   577     return true;
       
   578 #endif
       
   579 }
       
   580 
       
   581 static void qt_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
       
   582 {
       
   583     my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
       
   584 
       
   585     // `dumb' implementation from jpeglib
       
   586 
       
   587     /* Just a dumb implementation for now.  Could use fseek() except
       
   588      * it doesn't work on pipes.  Not clear that being smart is worth
       
   589      * any trouble anyway --- large skips are infrequent.
       
   590      */
       
   591     if (num_bytes > 0) {
       
   592         while (num_bytes > (long) src->bytes_in_buffer) {
       
   593             num_bytes -= (long) src->bytes_in_buffer;
       
   594             (void) qt_fill_input_buffer(cinfo);
       
   595             /* note we assume that qt_fill_input_buffer will never return false,
       
   596             * so suspension need not be handled.
       
   597             */
       
   598         }
       
   599         src->next_input_byte += (size_t) num_bytes;
       
   600         src->bytes_in_buffer -= (size_t) num_bytes;
       
   601     }
       
   602 }
       
   603 
       
   604 static void qt_term_source(j_decompress_ptr cinfo)
       
   605 {
       
   606     my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
       
   607     if (!src->device->isSequential())
       
   608         src->device->seek(src->device->pos() - src->bytes_in_buffer);
       
   609 }
       
   610 
       
   611 #if defined(Q_C_CALLBACKS)
       
   612 }
       
   613 #endif
       
   614 
       
   615 inline my_jpeg_source_mgr::my_jpeg_source_mgr(QIODevice *device)
       
   616 {
       
   617     jpeg_source_mgr::init_source = qt_init_source;
       
   618     jpeg_source_mgr::fill_input_buffer = qt_fill_input_buffer;
       
   619     jpeg_source_mgr::skip_input_data = qt_skip_input_data;
       
   620     jpeg_source_mgr::resync_to_restart = jpeg_resync_to_restart;
       
   621     jpeg_source_mgr::term_source = qt_term_source;
       
   622     this->device = device;
       
   623     bytes_in_buffer = 0;
       
   624     next_input_byte = buffer;
       
   625 }
       
   626 
       
   627 
       
   628 static void scaleSize(int &reqW, int &reqH, int imgW, int imgH, Qt::AspectRatioMode mode)
       
   629 {
       
   630     if (mode == Qt::IgnoreAspectRatio)
       
   631         return;
       
   632     int t1 = imgW * reqH;
       
   633     int t2 = reqW * imgH;
       
   634     if ((mode == Qt::KeepAspectRatio && (t1 > t2)) || (mode == Qt::KeepAspectRatioByExpanding && (t1 < t2)))
       
   635         reqH = t2 / imgW;
       
   636     else
       
   637         reqW = t1 / imgH;
       
   638 }
       
   639 
       
   640 static bool read_jpeg_size(QIODevice *device, int &w, int &h)
       
   641 {
       
   642     bool rt = false;
       
   643     struct jpeg_decompress_struct cinfo;
       
   644 
       
   645     struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(device);
       
   646     struct my_error_mgr jerr;
       
   647 
       
   648     jpeg_create_decompress(&cinfo);
       
   649 
       
   650     cinfo.src = iod_src;
       
   651 
       
   652     cinfo.err = jpeg_std_error(&jerr);
       
   653     jerr.error_exit = my_error_exit;
       
   654 
       
   655     if (!setjmp(jerr.setjmp_buffer)) {
       
   656 #if defined(Q_OS_UNIXWARE)
       
   657         (void) jpeg_read_header(&cinfo, B_TRUE);
       
   658 #else
       
   659         (void) jpeg_read_header(&cinfo, true);
       
   660 #endif
       
   661         (void) jpeg_calc_output_dimensions(&cinfo);
       
   662 
       
   663         w = cinfo.output_width;
       
   664         h = cinfo.output_height;
       
   665         rt = true;
       
   666     }
       
   667     jpeg_destroy_decompress(&cinfo);
       
   668     delete iod_src;
       
   669     return rt;
       
   670 }
       
   671 
       
   672 #define HIGH_QUALITY_THRESHOLD 50
       
   673 
       
   674 static bool read_jpeg_format(QIODevice *device, QImage::Format &format)
       
   675 {
       
   676     bool result = false;
       
   677     struct jpeg_decompress_struct cinfo;
       
   678 
       
   679     struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(device);
       
   680     struct my_error_mgr jerr;
       
   681 
       
   682     jpeg_create_decompress(&cinfo);
       
   683 
       
   684     cinfo.src = iod_src;
       
   685 
       
   686     cinfo.err = jpeg_std_error(&jerr);
       
   687     jerr.error_exit = my_error_exit;
       
   688 
       
   689     if (!setjmp(jerr.setjmp_buffer)) {
       
   690 #if defined(Q_OS_UNIXWARE)
       
   691         (void) jpeg_read_header(&cinfo, B_TRUE);
       
   692 #else
       
   693         (void) jpeg_read_header(&cinfo, true);
       
   694 #endif
       
   695         // This does not allocate memory for the whole image
       
   696         // or such, so we are safe.
       
   697         (void) jpeg_start_decompress(&cinfo);
       
   698         result = true;
       
   699         switch (cinfo.output_components) {
       
   700         case 1:
       
   701             format = QImage::Format_Indexed8;
       
   702             break;
       
   703         case 3:
       
   704         case 4:
       
   705             format = QImage::Format_RGB32;
       
   706             break;
       
   707         default:
       
   708             result = false;
       
   709             break;
       
   710         }
       
   711         cinfo.output_scanline = cinfo.output_height;
       
   712         (void) jpeg_finish_decompress(&cinfo);
       
   713     }
       
   714     jpeg_destroy_decompress(&cinfo);
       
   715     delete iod_src;
       
   716     return result;
       
   717 }
       
   718 
       
   719 static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info,
       
   720                              bool dummy = false)
       
   721 {
       
   722     QImage::Format format;
       
   723     switch (info->output_components) {
       
   724     case 1:
       
   725         format = QImage::Format_Indexed8;
       
   726         break;
       
   727     case 3:
       
   728     case 4:
       
   729         format = QImage::Format_RGB32;
       
   730         break;
       
   731     default:
       
   732         return false; // unsupported format
       
   733     }
       
   734 
       
   735     const QSize size(info->output_width, info->output_height);
       
   736     if (dest->size() != size || dest->format() != format) {
       
   737         static uchar dummyImage[1];
       
   738         if (dummy) // Create QImage but don't read the pixels
       
   739             *dest = QImage(dummyImage, size.width(), size.height(), format);
       
   740         else
       
   741             *dest = QImage(size, format);
       
   742 
       
   743         if (format == QImage::Format_Indexed8) {
       
   744             dest->setNumColors(256);
       
   745             for (int i = 0; i < 256; i++)
       
   746                 dest->setColor(i, qRgb(i,i,i));
       
   747         }
       
   748     }
       
   749 
       
   750     return !dest->isNull();
       
   751 }
       
   752 
       
   753 static bool read_jpeg_image(QIODevice *device, QImage *outImage,
       
   754                             const QByteArray &parameters, QSize scaledSize,
       
   755                             int inQuality )
       
   756 {
       
   757 #ifdef QT_NO_IMAGE_SMOOTHSCALE
       
   758     Q_UNUSED( scaledSize );
       
   759 #endif
       
   760 
       
   761     struct jpeg_decompress_struct cinfo;
       
   762 
       
   763     struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(device);
       
   764     struct my_error_mgr jerr;
       
   765 
       
   766     jpeg_create_decompress(&cinfo);
       
   767 
       
   768     cinfo.src = iod_src;
       
   769 
       
   770     cinfo.err = jpeg_std_error(&jerr);
       
   771     jerr.error_exit = my_error_exit;
       
   772 
       
   773     if (!setjmp(jerr.setjmp_buffer)) {
       
   774 #if defined(Q_OS_UNIXWARE)
       
   775         (void) jpeg_read_header(&cinfo, B_TRUE);
       
   776 #else
       
   777         (void) jpeg_read_header(&cinfo, true);
       
   778 #endif
       
   779 
       
   780         // -1 means default quality.
       
   781         int quality = inQuality;
       
   782         if (quality < 0)
       
   783             quality = 75;
       
   784 
       
   785         QString params = QString::fromLatin1(parameters);
       
   786         params.simplified();
       
   787         int sWidth = 0, sHeight = 0;
       
   788         char sModeStr[1024] = "";
       
   789         Qt::AspectRatioMode sMode;
       
   790 
       
   791 #ifndef QT_NO_IMAGE_SMOOTHSCALE
       
   792         // If high quality not required, shrink image during decompression
       
   793         if (scaledSize.isValid() && !scaledSize.isEmpty() && quality < HIGH_QUALITY_THRESHOLD
       
   794             && !params.contains(QLatin1String("GetHeaderInformation")) ) {
       
   795             cinfo.scale_denom = qMin(cinfo.image_width / scaledSize.width(),
       
   796                                      cinfo.image_width / scaledSize.height());
       
   797             if (cinfo.scale_denom < 2) {
       
   798                 cinfo.scale_denom = 1;
       
   799             } else if (cinfo.scale_denom < 4) {
       
   800                 cinfo.scale_denom = 2;
       
   801             } else if (cinfo.scale_denom < 8) {
       
   802                 cinfo.scale_denom = 4;
       
   803             } else {
       
   804                 cinfo.scale_denom = 8;
       
   805             }
       
   806             cinfo.scale_num = 1;
       
   807         }
       
   808 #endif
       
   809 
       
   810 
       
   811         // If high quality not required, use fast decompression
       
   812         if( quality < HIGH_QUALITY_THRESHOLD ) {
       
   813             cinfo.dct_method = JDCT_IFAST;
       
   814             cinfo.do_fancy_upsampling = FALSE;
       
   815         }
       
   816 
       
   817 
       
   818         (void) jpeg_start_decompress(&cinfo);
       
   819 
       
   820         if (params.contains(QLatin1String("GetHeaderInformation"))) {
       
   821             if (!ensureValidImage(outImage, &cinfo, true))
       
   822                 longjmp(jerr.setjmp_buffer, 1);
       
   823         } else if (params.contains(QLatin1String("Scale"))) {
       
   824 #if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(Q_OS_WINCE)
       
   825             sscanf_s(params.toLatin1().data(), "Scale(%i, %i, %1023s)",
       
   826                      &sWidth, &sHeight, sModeStr, sizeof(sModeStr));
       
   827 #else
       
   828             sscanf(params.toLatin1().data(), "Scale(%i, %i, %1023s)",
       
   829                    &sWidth, &sHeight, sModeStr);
       
   830 #endif
       
   831 
       
   832             QString sModeQStr(QString::fromLatin1(sModeStr));
       
   833             if (sModeQStr == QLatin1String("IgnoreAspectRatio")) {
       
   834                 sMode = Qt::IgnoreAspectRatio;
       
   835             } else if (sModeQStr == QLatin1String("KeepAspectRatio")) {
       
   836                 sMode = Qt::KeepAspectRatio;
       
   837             } else if (sModeQStr == QLatin1String("KeepAspectRatioByExpanding")) {
       
   838                 sMode = Qt::KeepAspectRatioByExpanding;
       
   839             } else {
       
   840                 qDebug("read_jpeg_image: invalid aspect ratio mode \"%s\", see QImage::AspectRatioMode documentation", sModeStr);
       
   841                 sMode = Qt::KeepAspectRatio;
       
   842             }
       
   843 
       
   844 //            qDebug("Parameters ask to scale the image to %i x %i AspectRatioMode: %s", sWidth, sHeight, sModeStr);
       
   845             scaleSize(sWidth, sHeight, cinfo.output_width, cinfo.output_height, sMode);
       
   846 //            qDebug("Scaling the jpeg to %i x %i", sWidth, sHeight, sModeStr);
       
   847 
       
   848             if (cinfo.output_components == 3 || cinfo.output_components == 4) {
       
   849                 if (outImage->size() != QSize(sWidth, sHeight) || outImage->format() != QImage::Format_RGB32)
       
   850                     *outImage = QImage(sWidth, sHeight, QImage::Format_RGB32);
       
   851             } else if (cinfo.output_components == 1) {
       
   852                 if (outImage->size() != QSize(sWidth, sHeight) || outImage->format() != QImage::Format_Indexed8)
       
   853                     *outImage = QImage(sWidth, sHeight, QImage::Format_Indexed8);
       
   854                 outImage->setNumColors(256);
       
   855                 for (int i = 0; i < 256; ++i)
       
   856                     outImage->setColor(i, qRgb(i,i,i));
       
   857             } else {
       
   858                 // Unsupported format
       
   859             }
       
   860             if (outImage->isNull())
       
   861                 longjmp(jerr.setjmp_buffer, 1);
       
   862 
       
   863             if (!outImage->isNull()) {
       
   864                 QImage tmpImage(cinfo.output_width, 1, QImage::Format_RGB32);
       
   865                 uchar* inData = tmpImage.bits();
       
   866                 uchar* outData = outImage->bits();
       
   867                 int out_bpl = outImage->bytesPerLine();
       
   868                 while (cinfo.output_scanline < cinfo.output_height) {
       
   869                     int outputLine = sHeight * cinfo.output_scanline / cinfo.output_height;
       
   870                     (void) jpeg_read_scanlines(&cinfo, &inData, 1);
       
   871                     if (cinfo.output_components == 3) {
       
   872                         uchar *in = inData;
       
   873                         QRgb *out = (QRgb*)outData + outputLine * out_bpl;
       
   874                         for (uint i=0; i<cinfo.output_width; i++) {
       
   875 // ### Only scaling down an image works, I don't think scaling up will work at the moment
       
   876 // ### An idea I have to make this a smooth scale is to progressively add the pixel values up
       
   877 // When scaling down, multiple values are being over drawn in to the output buffer.
       
   878 // Instead, a weighting based on the distance the line or pixel is from the output pixel determines
       
   879 // the weight of it when added to the output buffer. At present it is a non-smooth scale which is
       
   880 // inefficently implemented, it still uncompresses all the jpeg, an optimization for progressive
       
   881 // jpegs could be made if scaling by say 50% or some other special cases
       
   882                             out[sWidth * i / cinfo.output_width] = qRgb(in[0], in[1], in[2]);
       
   883                             in += 3;
       
   884                         }
       
   885                     } else {
       
   886 // ### Need to test the case where the jpeg is grayscale, need some black and white jpegs to test
       
   887 // this code. (also only scales down and probably won't scale to a larger size)
       
   888                         uchar *in = inData;
       
   889                         uchar *out = outData + outputLine*out_bpl;
       
   890                         for (uint i=0; i<cinfo.output_width; i++) {
       
   891                             out[sWidth * i / cinfo.output_width] = in[i];
       
   892                         }
       
   893                     }
       
   894                 }
       
   895                 (void) jpeg_finish_decompress(&cinfo);
       
   896             }
       
   897 #ifndef QT_NO_IMAGE_SMOOTHSCALE
       
   898         } else if (scaledSize.isValid() && scaledSize != QSize(cinfo.output_width, cinfo.output_height)
       
   899             && quality >= HIGH_QUALITY_THRESHOLD) {
       
   900 
       
   901             jpegSmoothScaler scaler(&cinfo, QString().sprintf("Scale( %d, %d, ScaleFree )",
       
   902                                                               scaledSize.width(),
       
   903                                                               scaledSize.height()).toLatin1().data());
       
   904             *outImage = scaler.scale();
       
   905 #endif
       
   906         } else {
       
   907             if (!ensureValidImage(outImage, &cinfo))
       
   908                 longjmp(jerr.setjmp_buffer, 1);
       
   909 
       
   910             uchar* data = outImage->bits();
       
   911             int bpl = outImage->bytesPerLine();
       
   912             while (cinfo.output_scanline < cinfo.output_height) {
       
   913                 uchar *d = data + cinfo.output_scanline * bpl;
       
   914                 (void) jpeg_read_scanlines(&cinfo,
       
   915                                            &d,
       
   916                                            1);
       
   917             }
       
   918             (void) jpeg_finish_decompress(&cinfo);
       
   919 
       
   920             if (cinfo.output_components == 3) {
       
   921                 // Expand 24->32 bpp.
       
   922                 for (uint j=0; j<cinfo.output_height; j++) {
       
   923                     uchar *in = outImage->scanLine(j) + cinfo.output_width * 3;
       
   924                     QRgb *out = (QRgb*)outImage->scanLine(j);
       
   925 
       
   926                     for (uint i=cinfo.output_width; i--;) {
       
   927                         in-=3;
       
   928                         out[i] = qRgb(in[0], in[1], in[2]);
       
   929                     }
       
   930                 }
       
   931             } else if (cinfo.out_color_space == JCS_CMYK) {
       
   932                 for (uint j = 0; j < cinfo.output_height; ++j) {
       
   933                     uchar *in = outImage->scanLine(j) + cinfo.output_width * 4;
       
   934                     QRgb *out = (QRgb*)outImage->scanLine(j);
       
   935 
       
   936                     for (uint i = cinfo.output_width; i--; ) {
       
   937                         in-=4;
       
   938                         int k = in[3];
       
   939                         out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
       
   940                     }
       
   941                 }
       
   942             }
       
   943             if (cinfo.density_unit == 1) {
       
   944                 outImage->setDotsPerMeterX(int(100. * cinfo.X_density / 2.54));
       
   945                 outImage->setDotsPerMeterY(int(100. * cinfo.Y_density / 2.54));
       
   946             } else if (cinfo.density_unit == 2) {
       
   947                 outImage->setDotsPerMeterX(int(100. * cinfo.X_density));
       
   948                 outImage->setDotsPerMeterY(int(100. * cinfo.Y_density));
       
   949             }
       
   950 
       
   951             if (scaledSize.isValid() && scaledSize != QSize(cinfo.output_width, cinfo.output_height))
       
   952                 *outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, Qt::FastTransformation);
       
   953         }
       
   954     }
       
   955 
       
   956     jpeg_destroy_decompress(&cinfo);
       
   957     delete iod_src;
       
   958     return !outImage->isNull();
       
   959 }
       
   960 
       
   961 
       
   962 struct my_jpeg_destination_mgr : public jpeg_destination_mgr {
       
   963     // Nothing dynamic - cannot rely on destruction over longjump
       
   964     QIODevice *device;
       
   965     JOCTET buffer[max_buf];
       
   966 
       
   967 public:
       
   968     my_jpeg_destination_mgr(QIODevice *);
       
   969 };
       
   970 
       
   971 
       
   972 #if defined(Q_C_CALLBACKS)
       
   973 extern "C" {
       
   974 #endif
       
   975 
       
   976 static void qt_init_destination(j_compress_ptr)
       
   977 {
       
   978 }
       
   979 
       
   980 static boolean qt_empty_output_buffer(j_compress_ptr cinfo)
       
   981 {
       
   982     my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest;
       
   983 
       
   984     int written = dest->device->write((char*)dest->buffer, max_buf);
       
   985     if (written == -1)
       
   986         (*cinfo->err->error_exit)((j_common_ptr)cinfo);
       
   987 
       
   988     dest->next_output_byte = dest->buffer;
       
   989     dest->free_in_buffer = max_buf;
       
   990 
       
   991 #if defined(Q_OS_UNIXWARE)
       
   992     return B_TRUE;
       
   993 #else
       
   994     return true;
       
   995 #endif
       
   996 }
       
   997 
       
   998 static void qt_term_destination(j_compress_ptr cinfo)
       
   999 {
       
  1000     my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest;
       
  1001     qint64 n = max_buf - dest->free_in_buffer;
       
  1002 
       
  1003     qint64 written = dest->device->write((char*)dest->buffer, n);
       
  1004     if (written == -1)
       
  1005         (*cinfo->err->error_exit)((j_common_ptr)cinfo);
       
  1006 }
       
  1007 
       
  1008 #if defined(Q_C_CALLBACKS)
       
  1009 }
       
  1010 #endif
       
  1011 
       
  1012 inline my_jpeg_destination_mgr::my_jpeg_destination_mgr(QIODevice *device)
       
  1013 {
       
  1014     jpeg_destination_mgr::init_destination = qt_init_destination;
       
  1015     jpeg_destination_mgr::empty_output_buffer = qt_empty_output_buffer;
       
  1016     jpeg_destination_mgr::term_destination = qt_term_destination;
       
  1017     this->device = device;
       
  1018     next_output_byte = buffer;
       
  1019     free_in_buffer = max_buf;
       
  1020 }
       
  1021 
       
  1022 
       
  1023 static bool write_jpeg_image(const QImage &sourceImage, QIODevice *device, int sourceQuality)
       
  1024 {
       
  1025     bool success = false;
       
  1026     const QImage image = sourceImage;
       
  1027     const QVector<QRgb> cmap = image.colorTable();
       
  1028 
       
  1029     struct jpeg_compress_struct cinfo;
       
  1030     JSAMPROW row_pointer[1];
       
  1031     row_pointer[0] = 0;
       
  1032 
       
  1033     struct my_jpeg_destination_mgr *iod_dest = new my_jpeg_destination_mgr(device);
       
  1034     struct my_error_mgr jerr;
       
  1035 
       
  1036     cinfo.err = jpeg_std_error(&jerr);
       
  1037     jerr.error_exit = my_error_exit;
       
  1038 
       
  1039     if (!setjmp(jerr.setjmp_buffer)) {
       
  1040         // WARNING:
       
  1041         // this if loop is inside a setjmp/longjmp branch
       
  1042         // do not create C++ temporaries here because the destructor may never be called
       
  1043         // if you allocate memory, make sure that you can free it (row_pointer[0])
       
  1044         jpeg_create_compress(&cinfo);
       
  1045 
       
  1046         cinfo.dest = iod_dest;
       
  1047 
       
  1048         cinfo.image_width = image.width();
       
  1049         cinfo.image_height = image.height();
       
  1050 
       
  1051         bool gray=false;
       
  1052         switch (image.format()) {
       
  1053         case QImage::Format_Mono:
       
  1054         case QImage::Format_MonoLSB:
       
  1055         case QImage::Format_Indexed8:
       
  1056             gray = true;
       
  1057             for (int i = image.numColors(); gray && i--;) {
       
  1058                 gray = gray & (qRed(cmap[i]) == qGreen(cmap[i]) &&
       
  1059                                qRed(cmap[i]) == qBlue(cmap[i]));
       
  1060             }
       
  1061             cinfo.input_components = gray ? 1 : 3;
       
  1062             cinfo.in_color_space = gray ? JCS_GRAYSCALE : JCS_RGB;
       
  1063             break;
       
  1064         default:
       
  1065             cinfo.input_components = 3;
       
  1066             cinfo.in_color_space = JCS_RGB;
       
  1067         }
       
  1068 
       
  1069         jpeg_set_defaults(&cinfo);
       
  1070 
       
  1071         qreal diffInch = qAbs(image.dotsPerMeterX()*2.54/100. - qRound(image.dotsPerMeterX()*2.54/100.))
       
  1072                          + qAbs(image.dotsPerMeterY()*2.54/100. - qRound(image.dotsPerMeterY()*2.54/100.));
       
  1073         qreal diffCm = (qAbs(image.dotsPerMeterX()/100. - qRound(image.dotsPerMeterX()/100.))
       
  1074                         + qAbs(image.dotsPerMeterY()/100. - qRound(image.dotsPerMeterY()/100.)))*2.54;
       
  1075         if (diffInch < diffCm) {
       
  1076             cinfo.density_unit = 1; // dots/inch
       
  1077             cinfo.X_density = qRound(image.dotsPerMeterX()*2.54/100.);
       
  1078             cinfo.Y_density = qRound(image.dotsPerMeterY()*2.54/100.);
       
  1079         } else {
       
  1080             cinfo.density_unit = 2; // dots/cm
       
  1081             cinfo.X_density = (image.dotsPerMeterX()+50) / 100;
       
  1082             cinfo.Y_density = (image.dotsPerMeterY()+50) / 100;
       
  1083         }
       
  1084 
       
  1085 
       
  1086         int quality = sourceQuality >= 0 ? qMin(sourceQuality,100) : 75;
       
  1087 #if defined(Q_OS_UNIXWARE)
       
  1088         jpeg_set_quality(&cinfo, quality, B_TRUE /* limit to baseline-JPEG values */);
       
  1089         jpeg_start_compress(&cinfo, B_TRUE);
       
  1090 #else
       
  1091         jpeg_set_quality(&cinfo, quality, true /* limit to baseline-JPEG values */);
       
  1092         jpeg_start_compress(&cinfo, true);
       
  1093 #endif
       
  1094 
       
  1095         row_pointer[0] = new uchar[cinfo.image_width*cinfo.input_components];
       
  1096         int w = cinfo.image_width;
       
  1097         while (cinfo.next_scanline < cinfo.image_height) {
       
  1098             uchar *row = row_pointer[0];
       
  1099             switch (image.format()) {
       
  1100             case QImage::Format_Mono:
       
  1101             case QImage::Format_MonoLSB:
       
  1102                 if (gray) {
       
  1103                     const uchar* data = image.scanLine(cinfo.next_scanline);
       
  1104                     if (image.format() == QImage::Format_MonoLSB) {
       
  1105                         for (int i=0; i<w; i++) {
       
  1106                             bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
       
  1107                             row[i] = qRed(cmap[bit]);
       
  1108                         }
       
  1109                     } else {
       
  1110                         for (int i=0; i<w; i++) {
       
  1111                             bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
       
  1112                             row[i] = qRed(cmap[bit]);
       
  1113                         }
       
  1114                     }
       
  1115                 } else {
       
  1116                     const uchar* data = image.scanLine(cinfo.next_scanline);
       
  1117                     if (image.format() == QImage::Format_MonoLSB) {
       
  1118                         for (int i=0; i<w; i++) {
       
  1119                             bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
       
  1120                             *row++ = qRed(cmap[bit]);
       
  1121                             *row++ = qGreen(cmap[bit]);
       
  1122                             *row++ = qBlue(cmap[bit]);
       
  1123                         }
       
  1124                     } else {
       
  1125                         for (int i=0; i<w; i++) {
       
  1126                             bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
       
  1127                             *row++ = qRed(cmap[bit]);
       
  1128                             *row++ = qGreen(cmap[bit]);
       
  1129                             *row++ = qBlue(cmap[bit]);
       
  1130                         }
       
  1131                     }
       
  1132                 }
       
  1133                 break;
       
  1134             case QImage::Format_Indexed8:
       
  1135                 if (gray) {
       
  1136                     const uchar* pix = image.scanLine(cinfo.next_scanline);
       
  1137                     for (int i=0; i<w; i++) {
       
  1138                         *row = qRed(cmap[*pix]);
       
  1139                         ++row; ++pix;
       
  1140                     }
       
  1141                 } else {
       
  1142                     const uchar* pix = image.scanLine(cinfo.next_scanline);
       
  1143                     for (int i=0; i<w; i++) {
       
  1144                         *row++ = qRed(cmap[*pix]);
       
  1145                         *row++ = qGreen(cmap[*pix]);
       
  1146                         *row++ = qBlue(cmap[*pix]);
       
  1147                         ++pix;
       
  1148                     }
       
  1149                 }
       
  1150                 break;
       
  1151             case QImage::Format_RGB888:
       
  1152                 memcpy(row, image.scanLine(cinfo.next_scanline), w * 3);
       
  1153                 break;
       
  1154             case QImage::Format_RGB32:
       
  1155             case QImage::Format_ARGB32:
       
  1156             case QImage::Format_ARGB32_Premultiplied: {
       
  1157                 QRgb* rgb = (QRgb*)image.scanLine(cinfo.next_scanline);
       
  1158                 for (int i=0; i<w; i++) {
       
  1159                     *row++ = qRed(*rgb);
       
  1160                     *row++ = qGreen(*rgb);
       
  1161                     *row++ = qBlue(*rgb);
       
  1162                     ++rgb;
       
  1163                 }
       
  1164                 break;
       
  1165             }
       
  1166             default:
       
  1167                 qWarning("QJpegHandler: unable to write image of format %i",
       
  1168                          image.format());
       
  1169                 break;
       
  1170             }
       
  1171             jpeg_write_scanlines(&cinfo, row_pointer, 1);
       
  1172         }
       
  1173 
       
  1174         jpeg_finish_compress(&cinfo);
       
  1175         jpeg_destroy_compress(&cinfo);
       
  1176         success = true;
       
  1177     } else {
       
  1178         jpeg_destroy_compress(&cinfo);
       
  1179         success = false;
       
  1180     }
       
  1181 
       
  1182     delete iod_dest;
       
  1183     delete [] row_pointer[0];
       
  1184     return success;
       
  1185 }
       
  1186 
       
  1187 QJpegHandler::QJpegHandler()
       
  1188 {
       
  1189     quality = 75;
       
  1190 }
       
  1191 
       
  1192 bool QJpegHandler::canRead() const
       
  1193 {
       
  1194     if (canRead(device())) {
       
  1195         setFormat("jpeg");
       
  1196         return true;
       
  1197     }
       
  1198     return false;
       
  1199 }
       
  1200 
       
  1201 bool QJpegHandler::canRead(QIODevice *device)
       
  1202 {
       
  1203     if (!device) {
       
  1204         qWarning("QJpegHandler::canRead() called with no device");
       
  1205         return false;
       
  1206     }
       
  1207 
       
  1208     return device->peek(2) == "\xFF\xD8";
       
  1209 }
       
  1210 
       
  1211 bool QJpegHandler::read(QImage *image)
       
  1212 {
       
  1213     if (!canRead())
       
  1214         return false;
       
  1215     return read_jpeg_image(device(), image, parameters, scaledSize, quality);
       
  1216 }
       
  1217 
       
  1218 bool QJpegHandler::write(const QImage &image)
       
  1219 {
       
  1220     return write_jpeg_image(image, device(), quality);
       
  1221 }
       
  1222 
       
  1223 bool QJpegHandler::supportsOption(ImageOption option) const
       
  1224 {
       
  1225     return option == Quality
       
  1226 #ifndef QT_NO_IMAGE_SMOOTHSCALE
       
  1227         || option == ScaledSize
       
  1228 #endif
       
  1229         || option == Size
       
  1230         || option == ImageFormat;
       
  1231 }
       
  1232 
       
  1233 QVariant QJpegHandler::option(ImageOption option) const
       
  1234 {
       
  1235     if (option == Quality) {
       
  1236         return quality;
       
  1237 #ifndef QT_NO_IMAGE_SMOOTHSCALE
       
  1238     } else if  (option == ScaledSize) {
       
  1239         return scaledSize;
       
  1240 #endif
       
  1241     } else if (option == Size) {
       
  1242         if (canRead() && !device()->isSequential()) {
       
  1243             qint64 pos = device()->pos();
       
  1244             int width = 0;
       
  1245             int height = 0;
       
  1246             read_jpeg_size(device(), width, height);
       
  1247             device()->seek(pos);
       
  1248             return QSize(width, height);
       
  1249         }
       
  1250     } else if (option == ImageFormat) {
       
  1251         if (canRead() && !device()->isSequential()) {
       
  1252             qint64 pos = device()->pos();
       
  1253             QImage::Format format = QImage::Format_Invalid;
       
  1254             read_jpeg_format(device(), format);
       
  1255             device()->seek(pos);
       
  1256             return format;
       
  1257         }
       
  1258         return QImage::Format_Invalid;
       
  1259     }
       
  1260     return QVariant();
       
  1261 }
       
  1262 
       
  1263 void QJpegHandler::setOption(ImageOption option, const QVariant &value)
       
  1264 {
       
  1265     if (option == Quality)
       
  1266         quality = value.toInt();
       
  1267 #ifndef QT_NO_IMAGE_SMOOTHSCALE
       
  1268     else if ( option == ScaledSize )
       
  1269         scaledSize = value.toSize();
       
  1270 #endif
       
  1271 }
       
  1272 
       
  1273 QByteArray QJpegHandler::name() const
       
  1274 {
       
  1275     return "jpeg";
       
  1276 }
       
  1277 
       
  1278 QT_END_NAMESPACE