source: trunk/src/plugins/imageformats/jpeg/qjpeghandler.cpp@ 497

Last change on this file since 497 was 2, checked in by Dmitry A. Kuminov, 16 years ago

Initially imported qt-all-opensource-src-4.5.1 from Trolltech.

File size: 39.0 KB
Line 
1/****************************************************************************
2**
3** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies).
4** Contact: Qt Software Information ([email protected])
5**
6** This file is part of the plugins of the Qt Toolkit.
7**
8** $QT_BEGIN_LICENSE:LGPL$
9** Commercial Usage
10** Licensees holding valid Qt Commercial licenses may use this file in
11** accordance with the Qt Commercial License Agreement provided with the
12** Software or, alternatively, in accordance with the terms contained in
13** a written agreement between you and Nokia.
14**
15** GNU Lesser General Public License Usage
16** Alternatively, this file may be used under the terms of the GNU Lesser
17** General Public License version 2.1 as published by the Free Software
18** Foundation and appearing in the file LICENSE.LGPL included in the
19** packaging of this file. Please review the following information to
20** ensure the GNU Lesser General Public License version 2.1 requirements
21** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
22**
23** In addition, as a special exception, Nokia gives you certain
24** additional rights. These rights are described in the Nokia Qt LGPL
25** Exception version 1.0, included in the file LGPL_EXCEPTION.txt in this
26** package.
27**
28** GNU General Public License Usage
29** Alternatively, this file may be used under the terms of the GNU
30** General Public License version 3.0 as published by the Free Software
31** Foundation and appearing in the file LICENSE.GPL included in the
32** packaging of this file. Please review the following information to
33** ensure the GNU General Public License version 3.0 requirements will be
34** met: http://www.gnu.org/copyleft/gpl.html.
35**
36** If you are unsure which license is appropriate for your use, please
37** contact the sales department at [email protected].
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
58extern "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
77QT_BEGIN_NAMESPACE
78
79//#define QT_NO_IMAGE_SMOOTHSCALE
80#ifndef QT_NO_IMAGE_SMOOTHSCALE
81class QImageSmoothScalerPrivate;
82class QImageSmoothScaler
83{
84public:
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
93protected:
94 int scaledWidth(void) const;
95
96private:
97 QImageSmoothScalerPrivate *d;
98 virtual QRgb *scanLine(const int line = 0, const QImage *src = 0);
99};
100
101class QImageSmoothScalerPrivate
102{
103public:
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
116QImageSmoothScaler::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
125QImageSmoothScaler::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
156void 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
167int QImageSmoothScaler::scaledWidth() const
168{
169 return d->cols;
170}
171
172QImageSmoothScaler::~QImageSmoothScaler()
173{
174 delete d;
175}
176
177inline 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
197QImage 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
455class jpegSmoothScaler : public QImageSmoothScaler
456{
457public:
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
468private:
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 {
497 in = (uchar*)out + cols24Bit;
498 for (uint i = scaledWidth(); i--; ) {
499 in -= 3;
500 out[i] = qRgb(in[0], in[1], in[2]);
501 }
502 }
503
504 return out;
505 }
506
507};
508#endif
509
510struct my_error_mgr : public jpeg_error_mgr {
511 jmp_buf setjmp_buffer;
512};
513
514#if defined(Q_C_CALLBACKS)
515extern "C" {
516#endif
517
518static void my_error_exit (j_common_ptr cinfo)
519{
520 my_error_mgr* myerr = (my_error_mgr*) cinfo->err;
521 char buffer[JMSG_LENGTH_MAX];
522 (*cinfo->err->format_message)(cinfo, buffer);
523 qWarning("%s", buffer);
524 longjmp(myerr->setjmp_buffer, 1);
525}
526
527#if defined(Q_C_CALLBACKS)
528}
529#endif
530
531
532static const int max_buf = 4096;
533
534struct my_jpeg_source_mgr : public jpeg_source_mgr {
535 // Nothing dynamic - cannot rely on destruction over longjump
536 QIODevice *device;
537 JOCTET buffer[max_buf];
538
539public:
540 my_jpeg_source_mgr(QIODevice *device);
541};
542
543#if defined(Q_C_CALLBACKS)
544extern "C" {
545#endif
546
547static void qt_init_source(j_decompress_ptr)
548{
549}
550
551static boolean qt_fill_input_buffer(j_decompress_ptr cinfo)
552{
553 int num_read;
554 my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
555 src->next_input_byte = src->buffer;
556 num_read = src->device->read((char*)src->buffer, max_buf);
557 if (num_read <= 0) {
558 // Insert a fake EOI marker - as per jpeglib recommendation
559 src->buffer[0] = (JOCTET) 0xFF;
560 src->buffer[1] = (JOCTET) JPEG_EOI;
561 src->bytes_in_buffer = 2;
562 } else {
563 src->bytes_in_buffer = num_read;
564 }
565#if defined(Q_OS_UNIXWARE)
566 return B_TRUE;
567#else
568 return true;
569#endif
570}
571
572static void qt_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
573{
574 my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
575
576 // `dumb' implementation from jpeglib
577
578 /* Just a dumb implementation for now. Could use fseek() except
579 * it doesn't work on pipes. Not clear that being smart is worth
580 * any trouble anyway --- large skips are infrequent.
581 */
582 if (num_bytes > 0) {
583 while (num_bytes > (long) src->bytes_in_buffer) {
584 num_bytes -= (long) src->bytes_in_buffer;
585 (void) qt_fill_input_buffer(cinfo);
586 /* note we assume that qt_fill_input_buffer will never return false,
587 * so suspension need not be handled.
588 */
589 }
590 src->next_input_byte += (size_t) num_bytes;
591 src->bytes_in_buffer -= (size_t) num_bytes;
592 }
593}
594
595static void qt_term_source(j_decompress_ptr cinfo)
596{
597 my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
598 if (!src->device->isSequential())
599 src->device->seek(src->device->pos() - src->bytes_in_buffer);
600}
601
602#if defined(Q_C_CALLBACKS)
603}
604#endif
605
606inline my_jpeg_source_mgr::my_jpeg_source_mgr(QIODevice *device)
607{
608 jpeg_source_mgr::init_source = qt_init_source;
609 jpeg_source_mgr::fill_input_buffer = qt_fill_input_buffer;
610 jpeg_source_mgr::skip_input_data = qt_skip_input_data;
611 jpeg_source_mgr::resync_to_restart = jpeg_resync_to_restart;
612 jpeg_source_mgr::term_source = qt_term_source;
613 this->device = device;
614 bytes_in_buffer = 0;
615 next_input_byte = buffer;
616}
617
618
619static void scaleSize(int &reqW, int &reqH, int imgW, int imgH, Qt::AspectRatioMode mode)
620{
621 if (mode == Qt::IgnoreAspectRatio)
622 return;
623 int t1 = imgW * reqH;
624 int t2 = reqW * imgH;
625 if ((mode == Qt::KeepAspectRatio && (t1 > t2)) || (mode == Qt::KeepAspectRatioByExpanding && (t1 < t2)))
626 reqH = t2 / imgW;
627 else
628 reqW = t1 / imgH;
629}
630
631static bool read_jpeg_size(QIODevice *device, int &w, int &h)
632{
633 bool rt = false;
634 struct jpeg_decompress_struct cinfo;
635
636 struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(device);
637 struct my_error_mgr jerr;
638
639 jpeg_create_decompress(&cinfo);
640
641 cinfo.src = iod_src;
642
643 cinfo.err = jpeg_std_error(&jerr);
644 jerr.error_exit = my_error_exit;
645
646 if (!setjmp(jerr.setjmp_buffer)) {
647#if defined(Q_OS_UNIXWARE)
648 (void) jpeg_read_header(&cinfo, B_TRUE);
649#else
650 (void) jpeg_read_header(&cinfo, true);
651#endif
652 (void) jpeg_calc_output_dimensions(&cinfo);
653
654 w = cinfo.output_width;
655 h = cinfo.output_height;
656 rt = true;
657 }
658 jpeg_destroy_decompress(&cinfo);
659 delete iod_src;
660 return rt;
661}
662
663#define HIGH_QUALITY_THRESHOLD 50
664
665static bool read_jpeg_format(QIODevice *device, QImage::Format &format)
666{
667 bool result = false;
668 struct jpeg_decompress_struct cinfo;
669
670 struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(device);
671 struct my_error_mgr jerr;
672
673 jpeg_create_decompress(&cinfo);
674
675 cinfo.src = iod_src;
676
677 cinfo.err = jpeg_std_error(&jerr);
678 jerr.error_exit = my_error_exit;
679
680 if (!setjmp(jerr.setjmp_buffer)) {
681#if defined(Q_OS_UNIXWARE)
682 (void) jpeg_read_header(&cinfo, B_TRUE);
683#else
684 (void) jpeg_read_header(&cinfo, true);
685#endif
686 // This does not allocate memory for the whole image
687 // or such, so we are safe.
688 (void) jpeg_start_decompress(&cinfo);
689 result = true;
690 switch (cinfo.output_components) {
691 case 1:
692 format = QImage::Format_Indexed8;
693 break;
694 case 3:
695 case 4:
696 format = QImage::Format_RGB32;
697 break;
698 default:
699 result = false;
700 break;
701 }
702 cinfo.output_scanline = cinfo.output_height;
703 (void) jpeg_finish_decompress(&cinfo);
704 }
705 jpeg_destroy_decompress(&cinfo);
706 delete iod_src;
707 return result;
708}
709
710static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info,
711 bool dummy = false)
712{
713 QImage::Format format;
714 switch (info->output_components) {
715 case 1:
716 format = QImage::Format_Indexed8;
717 break;
718 case 3:
719 case 4:
720 format = QImage::Format_RGB32;
721 break;
722 default:
723 return false; // unsupported format
724 }
725
726 const QSize size(info->output_width, info->output_height);
727 if (dest->size() != size || dest->format() != format) {
728 static uchar dummyImage[1];
729 if (dummy) // Create QImage but don't read the pixels
730 *dest = QImage(dummyImage, size.width(), size.height(), format);
731 else
732 *dest = QImage(size, format);
733
734 if (format == QImage::Format_Indexed8) {
735 dest->setNumColors(256);
736 for (int i = 0; i < 256; i++)
737 dest->setColor(i, qRgb(i,i,i));
738 }
739 }
740
741 return !dest->isNull();
742}
743
744static bool read_jpeg_image(QIODevice *device, QImage *outImage,
745 const QByteArray &parameters, QSize scaledSize,
746 int inQuality )
747{
748#ifdef QT_NO_IMAGE_SMOOTHSCALE
749 Q_UNUSED( scaledSize );
750#endif
751
752 struct jpeg_decompress_struct cinfo;
753
754 struct my_jpeg_source_mgr *iod_src = new my_jpeg_source_mgr(device);
755 struct my_error_mgr jerr;
756
757 jpeg_create_decompress(&cinfo);
758
759 cinfo.src = iod_src;
760
761 cinfo.err = jpeg_std_error(&jerr);
762 jerr.error_exit = my_error_exit;
763
764 if (!setjmp(jerr.setjmp_buffer)) {
765#if defined(Q_OS_UNIXWARE)
766 (void) jpeg_read_header(&cinfo, B_TRUE);
767#else
768 (void) jpeg_read_header(&cinfo, true);
769#endif
770
771 // -1 means default quality.
772 int quality = inQuality;
773 if (quality < 0)
774 quality = 75;
775
776 QString params = QString::fromLatin1(parameters);
777 params.simplified();
778 int sWidth = 0, sHeight = 0;
779 char sModeStr[1024] = "";
780 Qt::AspectRatioMode sMode;
781
782#ifndef QT_NO_IMAGE_SMOOTHSCALE
783 // If high quality not required, shrink image during decompression
784 if (scaledSize.isValid() && quality < HIGH_QUALITY_THRESHOLD && !params.contains(QLatin1String("GetHeaderInformation")) ) {
785 cinfo.scale_denom = qMin(cinfo.image_width / scaledSize.width(),
786 cinfo.image_width / scaledSize.height());
787 if (cinfo.scale_denom < 2) {
788 cinfo.scale_denom = 1;
789 } else if (cinfo.scale_denom < 4) {
790 cinfo.scale_denom = 2;
791 } else if (cinfo.scale_denom < 8) {
792 cinfo.scale_denom = 4;
793 } else {
794 cinfo.scale_denom = 8;
795 }
796 cinfo.scale_num = 1;
797 }
798#endif
799
800
801 // If high quality not required, use fast decompression
802 if( quality < HIGH_QUALITY_THRESHOLD ) {
803 cinfo.dct_method = JDCT_IFAST;
804 cinfo.do_fancy_upsampling = FALSE;
805 }
806
807
808 (void) jpeg_start_decompress(&cinfo);
809
810 if (params.contains(QLatin1String("GetHeaderInformation"))) {
811 if (!ensureValidImage(outImage, &cinfo, true))
812 longjmp(jerr.setjmp_buffer, 1);
813 } else if (params.contains(QLatin1String("Scale"))) {
814#if defined(_MSC_VER) && _MSC_VER >= 1400 && !defined(Q_OS_WINCE)
815 sscanf_s(params.toLatin1().data(), "Scale(%i, %i, %1023s)",
816 &sWidth, &sHeight, sModeStr, sizeof(sModeStr));
817#else
818 sscanf(params.toLatin1().data(), "Scale(%i, %i, %1023s)",
819 &sWidth, &sHeight, sModeStr);
820#endif
821
822 QString sModeQStr(QString::fromLatin1(sModeStr));
823 if (sModeQStr == QLatin1String("IgnoreAspectRatio")) {
824 sMode = Qt::IgnoreAspectRatio;
825 } else if (sModeQStr == QLatin1String("KeepAspectRatio")) {
826 sMode = Qt::KeepAspectRatio;
827 } else if (sModeQStr == QLatin1String("KeepAspectRatioByExpanding")) {
828 sMode = Qt::KeepAspectRatioByExpanding;
829 } else {
830 qDebug("read_jpeg_image: invalid aspect ratio mode \"%s\", see QImage::AspectRatioMode documentation", sModeStr);
831 sMode = Qt::KeepAspectRatio;
832 }
833
834// qDebug("Parameters ask to scale the image to %i x %i AspectRatioMode: %s", sWidth, sHeight, sModeStr);
835 scaleSize(sWidth, sHeight, cinfo.output_width, cinfo.output_height, sMode);
836// qDebug("Scaling the jpeg to %i x %i", sWidth, sHeight, sModeStr);
837
838 if (cinfo.output_components == 3 || cinfo.output_components == 4) {
839 if (outImage->size() != QSize(sWidth, sHeight) || outImage->format() != QImage::Format_RGB32)
840 *outImage = QImage(sWidth, sHeight, QImage::Format_RGB32);
841 } else if (cinfo.output_components == 1) {
842 if (outImage->size() != QSize(sWidth, sHeight) || outImage->format() != QImage::Format_Indexed8)
843 *outImage = QImage(sWidth, sHeight, QImage::Format_Indexed8);
844 outImage->setNumColors(256);
845 for (int i = 0; i < 256; ++i)
846 outImage->setColor(i, qRgb(i,i,i));
847 } else {
848 // Unsupported format
849 }
850 if (outImage->isNull())
851 longjmp(jerr.setjmp_buffer, 1);
852
853 if (!outImage->isNull()) {
854 QImage tmpImage(cinfo.output_width, 1, QImage::Format_RGB32);
855 uchar* inData = tmpImage.bits();
856 uchar* outData = outImage->bits();
857 int out_bpl = outImage->bytesPerLine();
858 while (cinfo.output_scanline < cinfo.output_height) {
859 int outputLine = sHeight * cinfo.output_scanline / cinfo.output_height;
860 (void) jpeg_read_scanlines(&cinfo, &inData, 1);
861 if (cinfo.output_components == 3) {
862 uchar *in = inData;
863 QRgb *out = (QRgb*)outData + outputLine * out_bpl;
864 for (uint i=0; i<cinfo.output_width; i++) {
865// ### Only scaling down an image works, I don't think scaling up will work at the moment
866// ### An idea I have to make this a smooth scale is to progressively add the pixel values up
867// When scaling down, multiple values are being over drawn in to the output buffer.
868// Instead, a weighting based on the distance the line or pixel is from the output pixel determines
869// the weight of it when added to the output buffer. At present it is a non-smooth scale which is
870// inefficently implemented, it still uncompresses all the jpeg, an optimization for progressive
871// jpegs could be made if scaling by say 50% or some other special cases
872 out[sWidth * i / cinfo.output_width] = qRgb(in[0], in[1], in[2]);
873 in += 3;
874 }
875 } else {
876// ### Need to test the case where the jpeg is grayscale, need some black and white jpegs to test
877// this code. (also only scales down and probably won't scale to a larger size)
878 uchar *in = inData;
879 uchar *out = outData + outputLine*out_bpl;
880 for (uint i=0; i<cinfo.output_width; i++) {
881 out[sWidth * i / cinfo.output_width] = in[i];
882 }
883 }
884 }
885 (void) jpeg_finish_decompress(&cinfo);
886 }
887#ifndef QT_NO_IMAGE_SMOOTHSCALE
888 } else if (scaledSize.isValid()) {
889
890 jpegSmoothScaler scaler(&cinfo, QString().sprintf("Scale( %d, %d, ScaleFree )",
891 scaledSize.width(),
892 scaledSize.height()).toLatin1().data());
893 *outImage = scaler.scale();
894#endif
895 } else {
896 if (!ensureValidImage(outImage, &cinfo))
897 longjmp(jerr.setjmp_buffer, 1);
898
899 uchar* data = outImage->bits();
900 int bpl = outImage->bytesPerLine();
901 while (cinfo.output_scanline < cinfo.output_height) {
902 uchar *d = data + cinfo.output_scanline * bpl;
903 (void) jpeg_read_scanlines(&cinfo,
904 &d,
905 1);
906 }
907 (void) jpeg_finish_decompress(&cinfo);
908
909 if (cinfo.output_components == 3) {
910 // Expand 24->32 bpp.
911 for (uint j=0; j<cinfo.output_height; j++) {
912 uchar *in = outImage->scanLine(j) + cinfo.output_width * 3;
913 QRgb *out = (QRgb*)outImage->scanLine(j);
914
915 for (uint i=cinfo.output_width; i--;) {
916 in-=3;
917 out[i] = qRgb(in[0], in[1], in[2]);
918 }
919 }
920 } else if (cinfo.out_color_space == JCS_CMYK) {
921 for (uint j = 0; j < cinfo.output_height; ++j) {
922 uchar *in = outImage->scanLine(j) + cinfo.output_width * 4;
923 QRgb *out = (QRgb*)outImage->scanLine(j);
924
925 for (uint i = cinfo.output_width; i--; ) {
926 in-=4;
927 int k = in[3];
928 out[i] = qRgb(k * in[0] / 255, k * in[1] / 255, k * in[2] / 255);
929 }
930 }
931 }
932 if (cinfo.density_unit == 1) {
933 outImage->setDotsPerMeterX(int(100. * cinfo.X_density / 2.54));
934 outImage->setDotsPerMeterY(int(100. * cinfo.Y_density / 2.54));
935 } else if (cinfo.density_unit == 2) {
936 outImage->setDotsPerMeterX(int(100. * cinfo.X_density));
937 outImage->setDotsPerMeterY(int(100. * cinfo.Y_density));
938 }
939 }
940 }
941
942 jpeg_destroy_decompress(&cinfo);
943 delete iod_src;
944 return !outImage->isNull();
945}
946
947
948struct my_jpeg_destination_mgr : public jpeg_destination_mgr {
949 // Nothing dynamic - cannot rely on destruction over longjump
950 QIODevice *device;
951 JOCTET buffer[max_buf];
952
953public:
954 my_jpeg_destination_mgr(QIODevice *);
955};
956
957
958#if defined(Q_C_CALLBACKS)
959extern "C" {
960#endif
961
962static void qt_init_destination(j_compress_ptr)
963{
964}
965
966static boolean qt_empty_output_buffer(j_compress_ptr cinfo)
967{
968 my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest;
969
970 int written = dest->device->write((char*)dest->buffer, max_buf);
971 if (written == -1)
972 (*cinfo->err->error_exit)((j_common_ptr)cinfo);
973
974 dest->next_output_byte = dest->buffer;
975 dest->free_in_buffer = max_buf;
976
977#if defined(Q_OS_UNIXWARE)
978 return B_TRUE;
979#else
980 return true;
981#endif
982}
983
984static void qt_term_destination(j_compress_ptr cinfo)
985{
986 my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest;
987 qint64 n = max_buf - dest->free_in_buffer;
988
989 qint64 written = dest->device->write((char*)dest->buffer, n);
990 if (written == -1)
991 (*cinfo->err->error_exit)((j_common_ptr)cinfo);
992}
993
994#if defined(Q_C_CALLBACKS)
995}
996#endif
997
998inline my_jpeg_destination_mgr::my_jpeg_destination_mgr(QIODevice *device)
999{
1000 jpeg_destination_mgr::init_destination = qt_init_destination;
1001 jpeg_destination_mgr::empty_output_buffer = qt_empty_output_buffer;
1002 jpeg_destination_mgr::term_destination = qt_term_destination;
1003 this->device = device;
1004 next_output_byte = buffer;
1005 free_in_buffer = max_buf;
1006}
1007
1008
1009static bool write_jpeg_image(const QImage &sourceImage, QIODevice *device, int sourceQuality)
1010{
1011 bool success = false;
1012 const QImage image = sourceImage;
1013 const QVector<QRgb> cmap = image.colorTable();
1014
1015 struct jpeg_compress_struct cinfo;
1016 JSAMPROW row_pointer[1];
1017 row_pointer[0] = 0;
1018
1019 struct my_jpeg_destination_mgr *iod_dest = new my_jpeg_destination_mgr(device);
1020 struct my_error_mgr jerr;
1021
1022 cinfo.err = jpeg_std_error(&jerr);
1023 jerr.error_exit = my_error_exit;
1024
1025 if (!setjmp(jerr.setjmp_buffer)) {
1026 // WARNING:
1027 // this if loop is inside a setjmp/longjmp branch
1028 // do not create C++ temporaries here because the destructor may never be called
1029 // if you allocate memory, make sure that you can free it (row_pointer[0])
1030 jpeg_create_compress(&cinfo);
1031
1032 cinfo.dest = iod_dest;
1033
1034 cinfo.image_width = image.width();
1035 cinfo.image_height = image.height();
1036
1037 bool gray=false;
1038 switch (image.format()) {
1039 case QImage::Format_Mono:
1040 case QImage::Format_MonoLSB:
1041 case QImage::Format_Indexed8:
1042 gray = true;
1043 for (int i = image.numColors(); gray && i--;) {
1044 gray = gray & (qRed(cmap[i]) == qGreen(cmap[i]) &&
1045 qRed(cmap[i]) == qBlue(cmap[i]));
1046 }
1047 cinfo.input_components = gray ? 1 : 3;
1048 cinfo.in_color_space = gray ? JCS_GRAYSCALE : JCS_RGB;
1049 break;
1050 default:
1051 cinfo.input_components = 3;
1052 cinfo.in_color_space = JCS_RGB;
1053 }
1054
1055 jpeg_set_defaults(&cinfo);
1056
1057 qreal diffInch = qAbs(image.dotsPerMeterX()*2.54/100. - qRound(image.dotsPerMeterX()*2.54/100.))
1058 + qAbs(image.dotsPerMeterY()*2.54/100. - qRound(image.dotsPerMeterY()*2.54/100.));
1059 qreal diffCm = (qAbs(image.dotsPerMeterX()/100. - qRound(image.dotsPerMeterX()/100.))
1060 + qAbs(image.dotsPerMeterY()/100. - qRound(image.dotsPerMeterY()/100.)))*2.54;
1061 if (diffInch < diffCm) {
1062 cinfo.density_unit = 1; // dots/inch
1063 cinfo.X_density = qRound(image.dotsPerMeterX()*2.54/100.);
1064 cinfo.Y_density = qRound(image.dotsPerMeterY()*2.54/100.);
1065 } else {
1066 cinfo.density_unit = 2; // dots/cm
1067 cinfo.X_density = (image.dotsPerMeterX()+50) / 100;
1068 cinfo.Y_density = (image.dotsPerMeterY()+50) / 100;
1069 }
1070
1071
1072 int quality = sourceQuality >= 0 ? qMin(sourceQuality,100) : 75;
1073#if defined(Q_OS_UNIXWARE)
1074 jpeg_set_quality(&cinfo, quality, B_TRUE /* limit to baseline-JPEG values */);
1075 jpeg_start_compress(&cinfo, B_TRUE);
1076#else
1077 jpeg_set_quality(&cinfo, quality, true /* limit to baseline-JPEG values */);
1078 jpeg_start_compress(&cinfo, true);
1079#endif
1080
1081 row_pointer[0] = new uchar[cinfo.image_width*cinfo.input_components];
1082 int w = cinfo.image_width;
1083 while (cinfo.next_scanline < cinfo.image_height) {
1084 uchar *row = row_pointer[0];
1085 switch (image.format()) {
1086 case QImage::Format_Mono:
1087 case QImage::Format_MonoLSB:
1088 if (gray) {
1089 const uchar* data = image.scanLine(cinfo.next_scanline);
1090 if (image.format() == QImage::Format_MonoLSB) {
1091 for (int i=0; i<w; i++) {
1092 bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
1093 row[i] = qRed(cmap[bit]);
1094 }
1095 } else {
1096 for (int i=0; i<w; i++) {
1097 bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
1098 row[i] = qRed(cmap[bit]);
1099 }
1100 }
1101 } else {
1102 const uchar* data = image.scanLine(cinfo.next_scanline);
1103 if (image.format() == QImage::Format_MonoLSB) {
1104 for (int i=0; i<w; i++) {
1105 bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
1106 *row++ = qRed(cmap[bit]);
1107 *row++ = qGreen(cmap[bit]);
1108 *row++ = qBlue(cmap[bit]);
1109 }
1110 } else {
1111 for (int i=0; i<w; i++) {
1112 bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
1113 *row++ = qRed(cmap[bit]);
1114 *row++ = qGreen(cmap[bit]);
1115 *row++ = qBlue(cmap[bit]);
1116 }
1117 }
1118 }
1119 break;
1120 case QImage::Format_Indexed8:
1121 if (gray) {
1122 const uchar* pix = image.scanLine(cinfo.next_scanline);
1123 for (int i=0; i<w; i++) {
1124 *row = qRed(cmap[*pix]);
1125 ++row; ++pix;
1126 }
1127 } else {
1128 const uchar* pix = image.scanLine(cinfo.next_scanline);
1129 for (int i=0; i<w; i++) {
1130 *row++ = qRed(cmap[*pix]);
1131 *row++ = qGreen(cmap[*pix]);
1132 *row++ = qBlue(cmap[*pix]);
1133 ++pix;
1134 }
1135 }
1136 break;
1137 case QImage::Format_RGB888:
1138 memcpy(row, image.scanLine(cinfo.next_scanline), w * 3);
1139 break;
1140 case QImage::Format_RGB32:
1141 case QImage::Format_ARGB32:
1142 case QImage::Format_ARGB32_Premultiplied: {
1143 QRgb* rgb = (QRgb*)image.scanLine(cinfo.next_scanline);
1144 for (int i=0; i<w; i++) {
1145 *row++ = qRed(*rgb);
1146 *row++ = qGreen(*rgb);
1147 *row++ = qBlue(*rgb);
1148 ++rgb;
1149 }
1150 break;
1151 }
1152 default:
1153 qWarning("QJpegHandler: unable to write image of format %i",
1154 image.format());
1155 break;
1156 }
1157 jpeg_write_scanlines(&cinfo, row_pointer, 1);
1158 }
1159
1160 jpeg_finish_compress(&cinfo);
1161 jpeg_destroy_compress(&cinfo);
1162 success = true;
1163 } else {
1164 jpeg_destroy_compress(&cinfo);
1165 success = false;
1166 }
1167
1168 delete iod_dest;
1169 delete [] row_pointer[0];
1170 return success;
1171}
1172
1173QJpegHandler::QJpegHandler()
1174{
1175 quality = 75;
1176}
1177
1178bool QJpegHandler::canRead() const
1179{
1180 if (canRead(device())) {
1181 setFormat("jpeg");
1182 return true;
1183 }
1184 return false;
1185}
1186
1187bool QJpegHandler::canRead(QIODevice *device)
1188{
1189 if (!device) {
1190 qWarning("QJpegHandler::canRead() called with no device");
1191 return false;
1192 }
1193
1194 return device->peek(2) == "\xFF\xD8";
1195}
1196
1197bool QJpegHandler::read(QImage *image)
1198{
1199 if (!canRead())
1200 return false;
1201 return read_jpeg_image(device(), image, parameters, scaledSize, quality);
1202}
1203
1204bool QJpegHandler::write(const QImage &image)
1205{
1206 return write_jpeg_image(image, device(), quality);
1207}
1208
1209bool QJpegHandler::supportsOption(ImageOption option) const
1210{
1211 return option == Quality
1212#ifndef QT_NO_IMAGE_SMOOTHSCALE
1213 || option == ScaledSize
1214#endif
1215 || option == Size
1216 || option == ImageFormat;
1217}
1218
1219QVariant QJpegHandler::option(ImageOption option) const
1220{
1221 if (option == Quality) {
1222 return quality;
1223#ifndef QT_NO_IMAGE_SMOOTHSCALE
1224 } else if (option == ScaledSize) {
1225 return scaledSize;
1226#endif
1227 } else if (option == Size) {
1228 if (canRead() && !device()->isSequential()) {
1229 qint64 pos = device()->pos();
1230 int width = 0;
1231 int height = 0;
1232 read_jpeg_size(device(), width, height);
1233 device()->seek(pos);
1234 return QSize(width, height);
1235 }
1236 } else if (option == ImageFormat) {
1237 if (canRead() && !device()->isSequential()) {
1238 qint64 pos = device()->pos();
1239 QImage::Format format = QImage::Format_Invalid;
1240 read_jpeg_format(device(), format);
1241 device()->seek(pos);
1242 return format;
1243 }
1244 return QImage::Format_Invalid;
1245 }
1246 return QVariant();
1247}
1248
1249void QJpegHandler::setOption(ImageOption option, const QVariant &value)
1250{
1251 if (option == Quality)
1252 quality = value.toInt();
1253#ifndef QT_NO_IMAGE_SMOOTHSCALE
1254 else if ( option == ScaledSize )
1255 scaledSize = value.toSize();
1256#endif
1257}
1258
1259QByteArray QJpegHandler::name() const
1260{
1261 return "jpeg";
1262}
1263
1264QT_END_NAMESPACE
Note: See TracBrowser for help on using the repository browser.