src/gui/image/qjpeghandler.cpp
changeset 33 3e2da88830cd
child 37 758a864f9613
equal deleted inserted replaced
30:5dc02b23752f 33:3e2da88830cd
       
     1 /****************************************************************************
       
     2 **
       
     3 ** Copyright (C) 2010 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_p.h"
       
    43 
       
    44 #include <qimage.h>
       
    45 #include <qvariant.h>
       
    46 #include <qvector.h>
       
    47 #include <qbuffer.h>
       
    48 
       
    49 #include <stdio.h>      // jpeglib needs this to be pre-included
       
    50 #include <setjmp.h>
       
    51 
       
    52 #ifdef FAR
       
    53 #undef FAR
       
    54 #endif
       
    55 
       
    56 // including jpeglib.h seems to be a little messy
       
    57 extern "C" {
       
    58 // mingw includes rpcndr.h but does not define boolean
       
    59 #if defined(Q_OS_WIN) && defined(Q_CC_GNU)
       
    60 #   if defined(__RPCNDR_H__) && !defined(boolean)
       
    61         typedef unsigned char boolean;
       
    62 #       define HAVE_BOOLEAN
       
    63 #   endif
       
    64 #endif
       
    65 
       
    66 #define XMD_H           // shut JPEGlib up
       
    67 #if defined(Q_OS_UNIXWARE)
       
    68 #  define HAVE_BOOLEAN  // libjpeg under Unixware seems to need this
       
    69 #endif
       
    70 #include <jpeglib.h>
       
    71 #ifdef const
       
    72 #  undef const          // remove crazy C hackery in jconfig.h
       
    73 #endif
       
    74 }
       
    75 
       
    76 QT_BEGIN_NAMESPACE
       
    77 
       
    78 struct my_error_mgr : public jpeg_error_mgr {
       
    79     jmp_buf setjmp_buffer;
       
    80 };
       
    81 
       
    82 #if defined(Q_C_CALLBACKS)
       
    83 extern "C" {
       
    84 #endif
       
    85 
       
    86 static void my_error_exit (j_common_ptr cinfo)
       
    87 {
       
    88     my_error_mgr* myerr = (my_error_mgr*) cinfo->err;
       
    89     char buffer[JMSG_LENGTH_MAX];
       
    90     (*cinfo->err->format_message)(cinfo, buffer);
       
    91     qWarning("%s", buffer);
       
    92     longjmp(myerr->setjmp_buffer, 1);
       
    93 }
       
    94 
       
    95 #if defined(Q_C_CALLBACKS)
       
    96 }
       
    97 #endif
       
    98 
       
    99 
       
   100 static const int max_buf = 4096;
       
   101 
       
   102 struct my_jpeg_source_mgr : public jpeg_source_mgr {
       
   103     // Nothing dynamic - cannot rely on destruction over longjump
       
   104     QIODevice *device;
       
   105     JOCTET buffer[max_buf];
       
   106     const QBuffer *memDevice;
       
   107 
       
   108 public:
       
   109     my_jpeg_source_mgr(QIODevice *device);
       
   110 };
       
   111 
       
   112 #if defined(Q_C_CALLBACKS)
       
   113 extern "C" {
       
   114 #endif
       
   115 
       
   116 static void qt_init_source(j_decompress_ptr)
       
   117 {
       
   118 }
       
   119 
       
   120 static boolean qt_fill_input_buffer(j_decompress_ptr cinfo)
       
   121 {
       
   122     my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
       
   123     if (src->memDevice) {
       
   124         src->next_input_byte = (const JOCTET *)(src->memDevice->data().constData() + src->memDevice->pos());
       
   125         src->bytes_in_buffer = (size_t)(src->memDevice->data().size() - src->memDevice->pos());
       
   126         return true;
       
   127     }
       
   128     src->next_input_byte = src->buffer;
       
   129     int num_read = src->device->read((char*)src->buffer, max_buf);
       
   130     if (num_read <= 0) {
       
   131         // Insert a fake EOI marker - as per jpeglib recommendation
       
   132         src->buffer[0] = (JOCTET) 0xFF;
       
   133         src->buffer[1] = (JOCTET) JPEG_EOI;
       
   134         src->bytes_in_buffer = 2;
       
   135     } else {
       
   136         src->bytes_in_buffer = num_read;
       
   137     }
       
   138 #if defined(Q_OS_UNIXWARE)
       
   139     return B_TRUE;
       
   140 #else
       
   141     return true;
       
   142 #endif
       
   143 }
       
   144 
       
   145 static void qt_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
       
   146 {
       
   147     my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
       
   148 
       
   149     // `dumb' implementation from jpeglib
       
   150 
       
   151     /* Just a dumb implementation for now.  Could use fseek() except
       
   152      * it doesn't work on pipes.  Not clear that being smart is worth
       
   153      * any trouble anyway --- large skips are infrequent.
       
   154      */
       
   155     if (num_bytes > 0) {
       
   156         while (num_bytes > (long) src->bytes_in_buffer) {  // Should not happen in case of memDevice
       
   157             num_bytes -= (long) src->bytes_in_buffer;
       
   158             (void) qt_fill_input_buffer(cinfo);
       
   159             /* note we assume that qt_fill_input_buffer will never return false,
       
   160             * so suspension need not be handled.
       
   161             */
       
   162         }
       
   163         src->next_input_byte += (size_t) num_bytes;
       
   164         src->bytes_in_buffer -= (size_t) num_bytes;
       
   165     }
       
   166 }
       
   167 
       
   168 static void qt_term_source(j_decompress_ptr cinfo)
       
   169 {
       
   170     my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
       
   171     if (!src->device->isSequential())
       
   172     {
       
   173         // read() isn't used for memDevice, so seek past everything that was used
       
   174         if (src->memDevice)
       
   175             src->device->seek(src->device->pos() + (src->memDevice->data().size() - src->memDevice->pos() - src->bytes_in_buffer));
       
   176         else
       
   177             src->device->seek(src->device->pos() - src->bytes_in_buffer);
       
   178     }
       
   179 }
       
   180 
       
   181 #if defined(Q_C_CALLBACKS)
       
   182 }
       
   183 #endif
       
   184 
       
   185 inline my_jpeg_source_mgr::my_jpeg_source_mgr(QIODevice *device)
       
   186 {
       
   187     jpeg_source_mgr::init_source = qt_init_source;
       
   188     jpeg_source_mgr::fill_input_buffer = qt_fill_input_buffer;
       
   189     jpeg_source_mgr::skip_input_data = qt_skip_input_data;
       
   190     jpeg_source_mgr::resync_to_restart = jpeg_resync_to_restart;
       
   191     jpeg_source_mgr::term_source = qt_term_source;
       
   192     this->device = device;
       
   193     memDevice = qobject_cast<QBuffer *>(device);
       
   194     bytes_in_buffer = 0;
       
   195     next_input_byte = buffer;
       
   196 }
       
   197 
       
   198 
       
   199 inline static bool read_jpeg_size(int &w, int &h, j_decompress_ptr cinfo)
       
   200 {
       
   201     (void) jpeg_calc_output_dimensions(cinfo);
       
   202 
       
   203     w = cinfo->output_width;
       
   204     h = cinfo->output_height;
       
   205     return true;
       
   206 }
       
   207 
       
   208 #define HIGH_QUALITY_THRESHOLD 50
       
   209 
       
   210 inline static bool read_jpeg_format(QImage::Format &format, j_decompress_ptr cinfo)
       
   211 {
       
   212 
       
   213     bool result = true;
       
   214     switch (cinfo->output_components) {
       
   215     case 1:
       
   216         format = QImage::Format_Indexed8;
       
   217         break;
       
   218     case 3:
       
   219     case 4:
       
   220         format = QImage::Format_RGB32;
       
   221         break;
       
   222     default:
       
   223         result = false;
       
   224         break;
       
   225     }
       
   226     cinfo->output_scanline = cinfo->output_height;
       
   227     return result;
       
   228 }
       
   229 
       
   230 static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info,
       
   231                              const QSize& size)
       
   232 {
       
   233     QImage::Format format;
       
   234     switch (info->output_components) {
       
   235     case 1:
       
   236         format = QImage::Format_Indexed8;
       
   237         break;
       
   238     case 3:
       
   239     case 4:
       
   240         format = QImage::Format_RGB32;
       
   241         break;
       
   242     default:
       
   243         return false; // unsupported format
       
   244     }
       
   245 
       
   246     if (dest->size() != size || dest->format() != format) {
       
   247         *dest = QImage(size, format);
       
   248 
       
   249         if (format == QImage::Format_Indexed8) {
       
   250             dest->setColorCount(256);
       
   251             for (int i = 0; i < 256; i++)
       
   252                 dest->setColor(i, qRgb(i,i,i));
       
   253         }
       
   254     }
       
   255 
       
   256     return !dest->isNull();
       
   257 }
       
   258 
       
   259 static bool read_jpeg_image(QImage *outImage,
       
   260                             QSize scaledSize, QRect scaledClipRect,
       
   261                             QRect clipRect, int inQuality, j_decompress_ptr info, struct my_error_mgr* err  )
       
   262 {
       
   263     if (!setjmp(err->setjmp_buffer)) {
       
   264         // -1 means default quality.
       
   265         int quality = inQuality;
       
   266         if (quality < 0)
       
   267             quality = 75;
       
   268 
       
   269         // If possible, merge the scaledClipRect into either scaledSize
       
   270         // or clipRect to avoid doing a separate scaled clipping pass.
       
   271         // Best results are achieved by clipping before scaling, not after.
       
   272         if (!scaledClipRect.isEmpty()) {
       
   273             if (scaledSize.isEmpty() && clipRect.isEmpty()) {
       
   274                 // No clipping or scaling before final clip.
       
   275                 clipRect = scaledClipRect;
       
   276                 scaledClipRect = QRect();
       
   277             } else if (scaledSize.isEmpty()) {
       
   278                 // Clipping, but no scaling: combine the clip regions.
       
   279                 scaledClipRect.translate(clipRect.topLeft());
       
   280                 clipRect = scaledClipRect.intersected(clipRect);
       
   281                 scaledClipRect = QRect();
       
   282             } else if (clipRect.isEmpty()) {
       
   283                 // No clipping, but scaling: if we can map back to an
       
   284                 // integer pixel boundary, then clip before scaling.
       
   285                 if ((info->image_width % scaledSize.width()) == 0 &&
       
   286                         (info->image_height % scaledSize.height()) == 0) {
       
   287                     int x = scaledClipRect.x() * info->image_width /
       
   288                             scaledSize.width();
       
   289                     int y = scaledClipRect.y() * info->image_height /
       
   290                             scaledSize.height();
       
   291                     int width = (scaledClipRect.right() + 1) *
       
   292                                 info->image_width / scaledSize.width() - x;
       
   293                     int height = (scaledClipRect.bottom() + 1) *
       
   294                                  info->image_height / scaledSize.height() - y;
       
   295                     clipRect = QRect(x, y, width, height);
       
   296                     scaledSize = scaledClipRect.size();
       
   297                     scaledClipRect = QRect();
       
   298                 }
       
   299             } else {
       
   300                 // Clipping and scaling: too difficult to figure out,
       
   301                 // and not a likely use case, so do it the long way.
       
   302             }
       
   303         }
       
   304 
       
   305         // Determine the scale factor to pass to libjpeg for quick downscaling.
       
   306         if (!scaledSize.isEmpty()) {
       
   307             if (clipRect.isEmpty()) {
       
   308                 info->scale_denom =
       
   309                     qMin(info->image_width / scaledSize.width(),
       
   310                          info->image_height / scaledSize.height());
       
   311             } else {
       
   312                 info->scale_denom =
       
   313                     qMin(clipRect.width() / scaledSize.width(),
       
   314                          clipRect.height() / scaledSize.height());
       
   315             }
       
   316             if (info->scale_denom < 2) {
       
   317                 info->scale_denom = 1;
       
   318             } else if (info->scale_denom < 4) {
       
   319                 info->scale_denom = 2;
       
   320             } else if (info->scale_denom < 8) {
       
   321                 info->scale_denom = 4;
       
   322             } else {
       
   323                 info->scale_denom = 8;
       
   324             }
       
   325             info->scale_num = 1;
       
   326             if (!clipRect.isEmpty()) {
       
   327                 // Correct the scale factor so that we clip accurately.
       
   328                 // It is recommended that the clip rectangle be aligned
       
   329                 // on an 8-pixel boundary for best performance.
       
   330                 while (info->scale_denom > 1 &&
       
   331                        ((clipRect.x() % info->scale_denom) != 0 ||
       
   332                         (clipRect.y() % info->scale_denom) != 0 ||
       
   333                         (clipRect.width() % info->scale_denom) != 0 ||
       
   334                         (clipRect.height() % info->scale_denom) != 0)) {
       
   335                     info->scale_denom /= 2;
       
   336                 }
       
   337             }
       
   338         }
       
   339 
       
   340         // If high quality not required, use fast decompression
       
   341         if( quality < HIGH_QUALITY_THRESHOLD ) {
       
   342             info->dct_method = JDCT_IFAST;
       
   343             info->do_fancy_upsampling = FALSE;
       
   344         }
       
   345 
       
   346         (void) jpeg_calc_output_dimensions(info);
       
   347 
       
   348         // Determine the clip region to extract.
       
   349         QRect imageRect(0, 0, info->output_width, info->output_height);
       
   350         QRect clip;
       
   351         if (clipRect.isEmpty()) {
       
   352             clip = imageRect;
       
   353         } else if (info->scale_denom == info->scale_num) {
       
   354             clip = clipRect.intersected(imageRect);
       
   355         } else {
       
   356             // The scale factor was corrected above to ensure that
       
   357             // we don't miss pixels when we scale the clip rectangle.
       
   358             clip = QRect(clipRect.x() / int(info->scale_denom),
       
   359                          clipRect.y() / int(info->scale_denom),
       
   360                          clipRect.width() / int(info->scale_denom),
       
   361                          clipRect.height() / int(info->scale_denom));
       
   362             clip = clip.intersected(imageRect);
       
   363         }
       
   364 
       
   365         // Allocate memory for the clipped QImage.
       
   366         if (!ensureValidImage(outImage, info, clip.size()))
       
   367             longjmp(err->setjmp_buffer, 1);
       
   368 
       
   369         // Avoid memcpy() overhead if grayscale with no clipping.
       
   370         bool quickGray = (info->output_components == 1 &&
       
   371                           clip == imageRect);
       
   372         if (!quickGray) {
       
   373             // Ask the jpeg library to allocate a temporary row.
       
   374             // The library will automatically delete it for us later.
       
   375             // The libjpeg docs say we should do this before calling
       
   376             // jpeg_start_decompress().  We can't use "new" here
       
   377             // because we are inside the setjmp() block and an error
       
   378             // in the jpeg input stream would cause a memory leak.
       
   379             JSAMPARRAY rows = (info->mem->alloc_sarray)
       
   380                               ((j_common_ptr)info, JPOOL_IMAGE,
       
   381                                info->output_width * info->output_components, 1);
       
   382 
       
   383             (void) jpeg_start_decompress(info);
       
   384 
       
   385             while (info->output_scanline < info->output_height) {
       
   386                 int y = int(info->output_scanline) - clip.y();
       
   387                 if (y >= clip.height())
       
   388                     break;      // We've read the entire clip region, so abort.
       
   389 
       
   390                 (void) jpeg_read_scanlines(info, rows, 1);
       
   391 
       
   392                 if (y < 0)
       
   393                     continue;   // Haven't reached the starting line yet.
       
   394 
       
   395                 if (info->output_components == 3) {
       
   396                     // Expand 24->32 bpp.
       
   397                     uchar *in = rows[0] + clip.x() * 3;
       
   398                     QRgb *out = (QRgb*)outImage->scanLine(y);
       
   399                     for (int i = 0; i < clip.width(); ++i) {
       
   400                         *out++ = qRgb(in[0], in[1], in[2]);
       
   401                         in += 3;
       
   402                     }
       
   403                 } else if (info->out_color_space == JCS_CMYK) {
       
   404                     // Convert CMYK->RGB.
       
   405                     uchar *in = rows[0] + clip.x() * 4;
       
   406                     QRgb *out = (QRgb*)outImage->scanLine(y);
       
   407                     for (int i = 0; i < clip.width(); ++i) {
       
   408                         int k = in[3];
       
   409                         *out++ = qRgb(k * in[0] / 255, k * in[1] / 255,
       
   410                                       k * in[2] / 255);
       
   411                         in += 4;
       
   412                     }
       
   413                 } else if (info->output_components == 1) {
       
   414                     // Grayscale.
       
   415                     memcpy(outImage->scanLine(y),
       
   416                            rows[0] + clip.x(), clip.width());
       
   417                 }
       
   418             }
       
   419         } else {
       
   420             // Load unclipped grayscale data directly into the QImage.
       
   421             (void) jpeg_start_decompress(info);
       
   422             while (info->output_scanline < info->output_height) {
       
   423                 uchar *row = outImage->scanLine(info->output_scanline);
       
   424                 (void) jpeg_read_scanlines(info, &row, 1);
       
   425             }
       
   426         }
       
   427 
       
   428         if (info->output_scanline == info->output_height)
       
   429             (void) jpeg_finish_decompress(info);
       
   430 
       
   431         if (info->density_unit == 1) {
       
   432             outImage->setDotsPerMeterX(int(100. * info->X_density / 2.54));
       
   433             outImage->setDotsPerMeterY(int(100. * info->Y_density / 2.54));
       
   434         } else if (info->density_unit == 2) {
       
   435             outImage->setDotsPerMeterX(int(100. * info->X_density));
       
   436             outImage->setDotsPerMeterY(int(100. * info->Y_density));
       
   437         }
       
   438 
       
   439         if (scaledSize.isValid() && scaledSize != clip.size()) {
       
   440             *outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, quality >= HIGH_QUALITY_THRESHOLD ? Qt::SmoothTransformation : Qt::FastTransformation);
       
   441         }
       
   442 
       
   443         if (!scaledClipRect.isEmpty())
       
   444             *outImage = outImage->copy(scaledClipRect);
       
   445         return !outImage->isNull();
       
   446     }
       
   447     else
       
   448         return false;
       
   449 }
       
   450 
       
   451 struct my_jpeg_destination_mgr : public jpeg_destination_mgr {
       
   452     // Nothing dynamic - cannot rely on destruction over longjump
       
   453     QIODevice *device;
       
   454     JOCTET buffer[max_buf];
       
   455 
       
   456 public:
       
   457     my_jpeg_destination_mgr(QIODevice *);
       
   458 };
       
   459 
       
   460 
       
   461 #if defined(Q_C_CALLBACKS)
       
   462 extern "C" {
       
   463 #endif
       
   464 
       
   465 static void qt_init_destination(j_compress_ptr)
       
   466 {
       
   467 }
       
   468 
       
   469 static boolean qt_empty_output_buffer(j_compress_ptr cinfo)
       
   470 {
       
   471     my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest;
       
   472 
       
   473     int written = dest->device->write((char*)dest->buffer, max_buf);
       
   474     if (written == -1)
       
   475         (*cinfo->err->error_exit)((j_common_ptr)cinfo);
       
   476 
       
   477     dest->next_output_byte = dest->buffer;
       
   478     dest->free_in_buffer = max_buf;
       
   479 
       
   480 #if defined(Q_OS_UNIXWARE)
       
   481     return B_TRUE;
       
   482 #else
       
   483     return true;
       
   484 #endif
       
   485 }
       
   486 
       
   487 static void qt_term_destination(j_compress_ptr cinfo)
       
   488 {
       
   489     my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest;
       
   490     qint64 n = max_buf - dest->free_in_buffer;
       
   491 
       
   492     qint64 written = dest->device->write((char*)dest->buffer, n);
       
   493     if (written == -1)
       
   494         (*cinfo->err->error_exit)((j_common_ptr)cinfo);
       
   495 }
       
   496 
       
   497 #if defined(Q_C_CALLBACKS)
       
   498 }
       
   499 #endif
       
   500 
       
   501 inline my_jpeg_destination_mgr::my_jpeg_destination_mgr(QIODevice *device)
       
   502 {
       
   503     jpeg_destination_mgr::init_destination = qt_init_destination;
       
   504     jpeg_destination_mgr::empty_output_buffer = qt_empty_output_buffer;
       
   505     jpeg_destination_mgr::term_destination = qt_term_destination;
       
   506     this->device = device;
       
   507     next_output_byte = buffer;
       
   508     free_in_buffer = max_buf;
       
   509 }
       
   510 
       
   511 static bool can_write_format(QImage::Format fmt)
       
   512 {
       
   513     switch (fmt) {
       
   514     case QImage::Format_Mono:
       
   515     case QImage::Format_MonoLSB:
       
   516     case QImage::Format_Indexed8:
       
   517     case QImage::Format_RGB888:
       
   518     case QImage::Format_RGB32:
       
   519     case QImage::Format_ARGB32:
       
   520     case QImage::Format_ARGB32_Premultiplied:
       
   521         return true;
       
   522         break;
       
   523     default:
       
   524         break;
       
   525     }
       
   526     return false;
       
   527 }
       
   528 
       
   529 static bool write_jpeg_image(const QImage &sourceImage, QIODevice *device, int sourceQuality)
       
   530 {
       
   531     bool success = false;
       
   532     const QImage image = can_write_format(sourceImage.format()) ?
       
   533                          sourceImage : sourceImage.convertToFormat(QImage::Format_RGB888);
       
   534     const QVector<QRgb> cmap = image.colorTable();
       
   535 
       
   536     struct jpeg_compress_struct cinfo;
       
   537     JSAMPROW row_pointer[1];
       
   538     row_pointer[0] = 0;
       
   539 
       
   540     struct my_jpeg_destination_mgr *iod_dest = new my_jpeg_destination_mgr(device);
       
   541     struct my_error_mgr jerr;
       
   542 
       
   543     cinfo.err = jpeg_std_error(&jerr);
       
   544     jerr.error_exit = my_error_exit;
       
   545 
       
   546     if (!setjmp(jerr.setjmp_buffer)) {
       
   547         // WARNING:
       
   548         // this if loop is inside a setjmp/longjmp branch
       
   549         // do not create C++ temporaries here because the destructor may never be called
       
   550         // if you allocate memory, make sure that you can free it (row_pointer[0])
       
   551         jpeg_create_compress(&cinfo);
       
   552 
       
   553         cinfo.dest = iod_dest;
       
   554 
       
   555         cinfo.image_width = image.width();
       
   556         cinfo.image_height = image.height();
       
   557 
       
   558         bool gray=false;
       
   559         switch (image.format()) {
       
   560         case QImage::Format_Mono:
       
   561         case QImage::Format_MonoLSB:
       
   562         case QImage::Format_Indexed8:
       
   563             gray = true;
       
   564             for (int i = image.colorCount(); gray && i--;) {
       
   565                 gray = gray & (qRed(cmap[i]) == qGreen(cmap[i]) &&
       
   566                                qRed(cmap[i]) == qBlue(cmap[i]));
       
   567             }
       
   568             cinfo.input_components = gray ? 1 : 3;
       
   569             cinfo.in_color_space = gray ? JCS_GRAYSCALE : JCS_RGB;
       
   570             break;
       
   571         default:
       
   572             cinfo.input_components = 3;
       
   573             cinfo.in_color_space = JCS_RGB;
       
   574         }
       
   575 
       
   576         jpeg_set_defaults(&cinfo);
       
   577 
       
   578         qreal diffInch = qAbs(image.dotsPerMeterX()*2.54/100. - qRound(image.dotsPerMeterX()*2.54/100.))
       
   579                          + qAbs(image.dotsPerMeterY()*2.54/100. - qRound(image.dotsPerMeterY()*2.54/100.));
       
   580         qreal diffCm = (qAbs(image.dotsPerMeterX()/100. - qRound(image.dotsPerMeterX()/100.))
       
   581                         + qAbs(image.dotsPerMeterY()/100. - qRound(image.dotsPerMeterY()/100.)))*2.54;
       
   582         if (diffInch < diffCm) {
       
   583             cinfo.density_unit = 1; // dots/inch
       
   584             cinfo.X_density = qRound(image.dotsPerMeterX()*2.54/100.);
       
   585             cinfo.Y_density = qRound(image.dotsPerMeterY()*2.54/100.);
       
   586         } else {
       
   587             cinfo.density_unit = 2; // dots/cm
       
   588             cinfo.X_density = (image.dotsPerMeterX()+50) / 100;
       
   589             cinfo.Y_density = (image.dotsPerMeterY()+50) / 100;
       
   590         }
       
   591 
       
   592 
       
   593         int quality = sourceQuality >= 0 ? qMin(sourceQuality,100) : 75;
       
   594 #if defined(Q_OS_UNIXWARE)
       
   595         jpeg_set_quality(&cinfo, quality, B_TRUE /* limit to baseline-JPEG values */);
       
   596         jpeg_start_compress(&cinfo, B_TRUE);
       
   597 #else
       
   598         jpeg_set_quality(&cinfo, quality, true /* limit to baseline-JPEG values */);
       
   599         jpeg_start_compress(&cinfo, true);
       
   600 #endif
       
   601 
       
   602         row_pointer[0] = new uchar[cinfo.image_width*cinfo.input_components];
       
   603         int w = cinfo.image_width;
       
   604         while (cinfo.next_scanline < cinfo.image_height) {
       
   605             uchar *row = row_pointer[0];
       
   606             switch (image.format()) {
       
   607             case QImage::Format_Mono:
       
   608             case QImage::Format_MonoLSB:
       
   609                 if (gray) {
       
   610                     const uchar* data = image.scanLine(cinfo.next_scanline);
       
   611                     if (image.format() == QImage::Format_MonoLSB) {
       
   612                         for (int i=0; i<w; i++) {
       
   613                             bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
       
   614                             row[i] = qRed(cmap[bit]);
       
   615                         }
       
   616                     } else {
       
   617                         for (int i=0; i<w; i++) {
       
   618                             bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
       
   619                             row[i] = qRed(cmap[bit]);
       
   620                         }
       
   621                     }
       
   622                 } else {
       
   623                     const uchar* data = image.scanLine(cinfo.next_scanline);
       
   624                     if (image.format() == QImage::Format_MonoLSB) {
       
   625                         for (int i=0; i<w; i++) {
       
   626                             bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
       
   627                             *row++ = qRed(cmap[bit]);
       
   628                             *row++ = qGreen(cmap[bit]);
       
   629                             *row++ = qBlue(cmap[bit]);
       
   630                         }
       
   631                     } else {
       
   632                         for (int i=0; i<w; i++) {
       
   633                             bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
       
   634                             *row++ = qRed(cmap[bit]);
       
   635                             *row++ = qGreen(cmap[bit]);
       
   636                             *row++ = qBlue(cmap[bit]);
       
   637                         }
       
   638                     }
       
   639                 }
       
   640                 break;
       
   641             case QImage::Format_Indexed8:
       
   642                 if (gray) {
       
   643                     const uchar* pix = image.scanLine(cinfo.next_scanline);
       
   644                     for (int i=0; i<w; i++) {
       
   645                         *row = qRed(cmap[*pix]);
       
   646                         ++row; ++pix;
       
   647                     }
       
   648                 } else {
       
   649                     const uchar* pix = image.scanLine(cinfo.next_scanline);
       
   650                     for (int i=0; i<w; i++) {
       
   651                         *row++ = qRed(cmap[*pix]);
       
   652                         *row++ = qGreen(cmap[*pix]);
       
   653                         *row++ = qBlue(cmap[*pix]);
       
   654                         ++pix;
       
   655                     }
       
   656                 }
       
   657                 break;
       
   658             case QImage::Format_RGB888:
       
   659                 memcpy(row, image.scanLine(cinfo.next_scanline), w * 3);
       
   660                 break;
       
   661             case QImage::Format_RGB32:
       
   662             case QImage::Format_ARGB32:
       
   663             case QImage::Format_ARGB32_Premultiplied: {
       
   664                 QRgb* rgb = (QRgb*)image.scanLine(cinfo.next_scanline);
       
   665                 for (int i=0; i<w; i++) {
       
   666                     *row++ = qRed(*rgb);
       
   667                     *row++ = qGreen(*rgb);
       
   668                     *row++ = qBlue(*rgb);
       
   669                     ++rgb;
       
   670                 }
       
   671                 break;
       
   672             }
       
   673             default:
       
   674                 qWarning("QJpegHandler: unable to write image of format %i",
       
   675                          image.format());
       
   676                 break;
       
   677             }
       
   678             jpeg_write_scanlines(&cinfo, row_pointer, 1);
       
   679         }
       
   680 
       
   681         jpeg_finish_compress(&cinfo);
       
   682         jpeg_destroy_compress(&cinfo);
       
   683         success = true;
       
   684     } else {
       
   685         jpeg_destroy_compress(&cinfo);
       
   686         success = false;
       
   687     }
       
   688 
       
   689     delete iod_dest;
       
   690     delete [] row_pointer[0];
       
   691     return success;
       
   692 }
       
   693 
       
   694 class QJpegHandlerPrivate
       
   695 {
       
   696 public:
       
   697     enum State {
       
   698         Ready,
       
   699         ReadHeader,
       
   700         Error
       
   701     };
       
   702 
       
   703     QJpegHandlerPrivate(QJpegHandler *qq)
       
   704         : quality(75), iod_src(0), state(Ready), q(qq)
       
   705     {}
       
   706 
       
   707     ~QJpegHandlerPrivate()
       
   708     {
       
   709         if(iod_src)
       
   710         {
       
   711             jpeg_destroy_decompress(&info);
       
   712             delete iod_src;
       
   713             iod_src = 0;
       
   714         }
       
   715     }
       
   716 
       
   717     bool readJpegHeader(QIODevice*);
       
   718     bool read(QImage *image);
       
   719 
       
   720     int quality;
       
   721     QVariant size;
       
   722     QImage::Format format;
       
   723     QSize scaledSize;
       
   724     QRect scaledClipRect;
       
   725     QRect clipRect;
       
   726     struct jpeg_decompress_struct info;
       
   727     struct my_jpeg_source_mgr * iod_src;
       
   728     struct my_error_mgr err;
       
   729 
       
   730     State state;
       
   731 
       
   732     QJpegHandler *q;
       
   733 };
       
   734 
       
   735 /*!
       
   736     \internal
       
   737 */
       
   738 bool QJpegHandlerPrivate::readJpegHeader(QIODevice *device)
       
   739 {
       
   740     if(state == Ready)
       
   741     {
       
   742         state = Error;
       
   743         iod_src = new my_jpeg_source_mgr(device);
       
   744 
       
   745         jpeg_create_decompress(&info);
       
   746         info.src = iod_src;
       
   747         info.err = jpeg_std_error(&err);
       
   748         err.error_exit = my_error_exit;
       
   749 
       
   750         if (!setjmp(err.setjmp_buffer)) {
       
   751     #if defined(Q_OS_UNIXWARE)
       
   752             (void) jpeg_read_header(&info, B_TRUE);
       
   753     #else
       
   754             (void) jpeg_read_header(&info, true);
       
   755     #endif
       
   756 
       
   757             int width = 0;
       
   758             int height = 0;
       
   759             read_jpeg_size(width, height, &info);
       
   760             size = QSize(width, height);
       
   761 
       
   762             format = QImage::Format_Invalid;
       
   763             read_jpeg_format(format, &info);
       
   764             state = ReadHeader;
       
   765             return true;
       
   766         }
       
   767         else
       
   768         {
       
   769             return false;
       
   770         }
       
   771     }
       
   772     else if(state == Error)
       
   773         return false;
       
   774     return true;
       
   775 }
       
   776 
       
   777 bool QJpegHandlerPrivate::read(QImage *image)
       
   778 {
       
   779     if(state == Ready)
       
   780         readJpegHeader(q->device());
       
   781 
       
   782     if(state == ReadHeader)
       
   783     {
       
   784         bool success = read_jpeg_image(image, scaledSize, scaledClipRect, clipRect, quality,  &info, &err);
       
   785         state = success ? Ready : Error;
       
   786         return success;
       
   787     }
       
   788 
       
   789     return false;
       
   790 
       
   791 }
       
   792 
       
   793 QJpegHandler::QJpegHandler()
       
   794     : d(new QJpegHandlerPrivate(this))
       
   795 {
       
   796 }
       
   797 
       
   798 QJpegHandler::~QJpegHandler()
       
   799 {
       
   800     delete d;
       
   801 }
       
   802 
       
   803 bool QJpegHandler::canRead() const
       
   804 {
       
   805     if(d->state == QJpegHandlerPrivate::Ready && !canRead(device()))
       
   806         return false;
       
   807 
       
   808     if (d->state != QJpegHandlerPrivate::Error) {
       
   809         setFormat("jpeg");
       
   810         return true;
       
   811     }
       
   812 
       
   813     return false;
       
   814 }
       
   815 
       
   816 bool QJpegHandler::canRead(QIODevice *device)
       
   817 {
       
   818     if (!device) {
       
   819         qWarning("QJpegHandler::canRead() called with no device");
       
   820         return false;
       
   821     }
       
   822 
       
   823     char buffer[2];
       
   824     if (device->peek(buffer, 2) != 2)
       
   825         return false;
       
   826     return uchar(buffer[0]) == 0xff && uchar(buffer[1]) == 0xd8;
       
   827 }
       
   828 
       
   829 bool QJpegHandler::read(QImage *image)
       
   830 {
       
   831     if (!canRead())
       
   832         return false;
       
   833     return d->read(image);
       
   834 }
       
   835 
       
   836 bool QJpegHandler::write(const QImage &image)
       
   837 {
       
   838     return write_jpeg_image(image, device(), d->quality);
       
   839 }
       
   840 
       
   841 bool QJpegHandler::supportsOption(ImageOption option) const
       
   842 {
       
   843     return option == Quality
       
   844         || option == ScaledSize
       
   845         || option == ScaledClipRect
       
   846         || option == ClipRect
       
   847         || option == Size
       
   848         || option == ImageFormat;
       
   849 }
       
   850 
       
   851 QVariant QJpegHandler::option(ImageOption option) const
       
   852 {
       
   853     switch(option) {
       
   854     case Quality:
       
   855         return d->quality;
       
   856     case ScaledSize:
       
   857         return d->scaledSize;
       
   858     case ScaledClipRect:
       
   859         return d->scaledClipRect;
       
   860     case ClipRect:
       
   861         return d->clipRect;
       
   862     case Size:
       
   863         d->readJpegHeader(device());
       
   864         return d->size;
       
   865     case ImageFormat:
       
   866         d->readJpegHeader(device());
       
   867         return d->format;
       
   868     default:
       
   869         return QVariant();
       
   870     }
       
   871 }
       
   872 
       
   873 void QJpegHandler::setOption(ImageOption option, const QVariant &value)
       
   874 {
       
   875     switch(option) {
       
   876     case Quality:
       
   877         d->quality = value.toInt();
       
   878         break;
       
   879     case ScaledSize:
       
   880         d->scaledSize = value.toSize();
       
   881         break;
       
   882     case ScaledClipRect:
       
   883         d->scaledClipRect = value.toRect();
       
   884         break;
       
   885     case ClipRect:
       
   886         d->clipRect = value.toRect();
       
   887         break;
       
   888     default:
       
   889         break;
       
   890     }
       
   891 }
       
   892 
       
   893 QByteArray QJpegHandler::name() const
       
   894 {
       
   895     return "jpeg";
       
   896 }
       
   897 
       
   898 
       
   899 
       
   900 
       
   901 QT_END_NAMESPACE