Qt 4.8
qjpeghandler.cpp
Go to the documentation of this file.
1 /****************************************************************************
2 **
3 ** Copyright (C) 2014 Digia Plc and/or its subsidiary(-ies).
4 ** Contact: http://www.qt-project.org/legal
5 **
6 ** This file is part of the plugins of the Qt Toolkit.
7 **
8 ** $QT_BEGIN_LICENSE:LGPL$
9 ** Commercial License Usage
10 ** Licensees holding valid commercial Qt licenses may use this file in
11 ** accordance with the commercial license agreement provided with the
12 ** Software or, alternatively, in accordance with the terms contained in
13 ** a written agreement between you and Digia. For licensing terms and
14 ** conditions see http://qt.digia.com/licensing. For further information
15 ** use the contact form at http://qt.digia.com/contact-us.
16 **
17 ** GNU Lesser General Public License Usage
18 ** Alternatively, this file may be used under the terms of the GNU Lesser
19 ** General Public License version 2.1 as published by the Free Software
20 ** Foundation and appearing in the file LICENSE.LGPL included in the
21 ** packaging of this file. Please review the following information to
22 ** ensure the GNU Lesser General Public License version 2.1 requirements
23 ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html.
24 **
25 ** In addition, as a special exception, Digia gives you certain additional
26 ** rights. These rights are described in the Digia Qt LGPL Exception
27 ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package.
28 **
29 ** GNU General Public License Usage
30 ** Alternatively, this file may be used under the terms of the GNU
31 ** General Public License version 3.0 as published by the Free Software
32 ** Foundation and appearing in the file LICENSE.GPL included in the
33 ** packaging of this file. Please review the following information to
34 ** ensure the GNU General Public License version 3.0 requirements will be
35 ** met: http://www.gnu.org/copyleft/gpl.html.
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 #include <private/qsimd_p.h>
49 
50 #include <stdio.h> // jpeglib needs this to be pre-included
51 #include <setjmp.h>
52 
53 #ifdef FAR
54 #undef FAR
55 #endif
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 #if defined(JPEG_TRUE) && !defined(HAVE_BOOLEAN)
78 // this jpeglib.h uses JPEG_boolean
79 typedef JPEG_boolean boolean;
80 #endif
81 
83 
84 void QT_FASTCALL convert_rgb888_to_rgb32_C(quint32 *dst, const uchar *src, int len)
85 {
86  // Expand 24->32 bpp.
87  for (int i = 0; i < len; ++i) {
88  *dst++ = qRgb(src[0], src[1], src[2]);
89  src += 3;
90  }
91 }
92 
93 typedef void (QT_FASTCALL *Rgb888ToRgb32Converter)(quint32 *dst, const uchar *src, int len);
94 
96 
97 struct my_error_mgr : public jpeg_error_mgr {
98  jmp_buf setjmp_buffer;
99 };
100 
101 #if defined(Q_C_CALLBACKS)
102 extern "C" {
103 #endif
104 
105 static void my_error_exit (j_common_ptr cinfo)
106 {
107  my_error_mgr* myerr = (my_error_mgr*) cinfo->err;
108  char buffer[JMSG_LENGTH_MAX];
109  (*cinfo->err->format_message)(cinfo, buffer);
110  qWarning("%s", buffer);
111  longjmp(myerr->setjmp_buffer, 1);
112 }
113 
114 #if defined(Q_C_CALLBACKS)
115 }
116 #endif
117 
118 
119 static const int max_buf = 4096;
120 
121 struct my_jpeg_source_mgr : public jpeg_source_mgr {
122  // Nothing dynamic - cannot rely on destruction over longjump
124  JOCTET buffer[max_buf];
126 
127 public:
128  my_jpeg_source_mgr(QIODevice *device);
129 };
130 
131 #if defined(Q_C_CALLBACKS)
132 extern "C" {
133 #endif
134 
135 static void qt_init_source(j_decompress_ptr)
136 {
137 }
138 
139 static boolean qt_fill_input_buffer(j_decompress_ptr cinfo)
140 {
141  my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
142  qint64 num_read = 0;
143  if (src->memDevice) {
144  src->next_input_byte = (const JOCTET *)(src->memDevice->data().constData() + src->memDevice->pos());
145  num_read = src->memDevice->data().size() - src->memDevice->pos();
146  src->device->seek(src->memDevice->data().size());
147  } else {
148  src->next_input_byte = src->buffer;
149  num_read = src->device->read((char*)src->buffer, max_buf);
150  }
151  if (num_read <= 0) {
152  // Insert a fake EOI marker - as per jpeglib recommendation
153  src->next_input_byte = src->buffer;
154  src->buffer[0] = (JOCTET) 0xFF;
155  src->buffer[1] = (JOCTET) JPEG_EOI;
156  src->bytes_in_buffer = 2;
157  } else {
158  src->bytes_in_buffer = num_read;
159  }
160  return TRUE;
161 }
162 
163 static void qt_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
164 {
165  my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
166 
167  // `dumb' implementation from jpeglib
168 
169  /* Just a dumb implementation for now. Could use fseek() except
170  * it doesn't work on pipes. Not clear that being smart is worth
171  * any trouble anyway --- large skips are infrequent.
172  */
173  if (num_bytes > 0) {
174  while (num_bytes > (long) src->bytes_in_buffer) { // Should not happen in case of memDevice
175  num_bytes -= (long) src->bytes_in_buffer;
176  (void) qt_fill_input_buffer(cinfo);
177  /* note we assume that qt_fill_input_buffer will never return false,
178  * so suspension need not be handled.
179  */
180  }
181  src->next_input_byte += (size_t) num_bytes;
182  src->bytes_in_buffer -= (size_t) num_bytes;
183  }
184 }
185 
186 static void qt_term_source(j_decompress_ptr cinfo)
187 {
188  my_jpeg_source_mgr* src = (my_jpeg_source_mgr*)cinfo->src;
189  if (!src->device->isSequential())
190  src->device->seek(src->device->pos() - src->bytes_in_buffer);
191 }
192 
193 #if defined(Q_C_CALLBACKS)
194 }
195 #endif
196 
198 {
199  jpeg_source_mgr::init_source = qt_init_source;
200  jpeg_source_mgr::fill_input_buffer = qt_fill_input_buffer;
201  jpeg_source_mgr::skip_input_data = qt_skip_input_data;
202  jpeg_source_mgr::resync_to_restart = jpeg_resync_to_restart;
203  jpeg_source_mgr::term_source = qt_term_source;
204  this->device = device;
205  memDevice = qobject_cast<QBuffer *>(device);
206  bytes_in_buffer = 0;
207  next_input_byte = buffer;
208 }
209 
210 
211 inline static bool read_jpeg_size(int &w, int &h, j_decompress_ptr cinfo)
212 {
213  (void) jpeg_calc_output_dimensions(cinfo);
214 
215  w = cinfo->output_width;
216  h = cinfo->output_height;
217  return true;
218 }
219 
220 #define HIGH_QUALITY_THRESHOLD 50
221 
222 inline static bool read_jpeg_format(QImage::Format &format, j_decompress_ptr cinfo)
223 {
224 
225  bool result = true;
226  switch (cinfo->output_components) {
227  case 1:
228  format = QImage::Format_Indexed8;
229  break;
230  case 3:
231  case 4:
232  format = QImage::Format_RGB32;
233  break;
234  default:
235  result = false;
236  break;
237  }
238  cinfo->output_scanline = cinfo->output_height;
239  return result;
240 }
241 
242 static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info,
243  const QSize& size)
244 {
246  switch (info->output_components) {
247  case 1:
248  format = QImage::Format_Indexed8;
249  break;
250  case 3:
251  case 4:
252  format = QImage::Format_RGB32;
253  break;
254  default:
255  return false; // unsupported format
256  }
257 
258  if (dest->size() != size || dest->format() != format) {
259  *dest = QImage(size, format);
260 
261  if (format == QImage::Format_Indexed8) {
262  dest->setColorCount(256);
263  for (int i = 0; i < 256; i++)
264  dest->setColor(i, qRgb(i,i,i));
265  }
266  }
267 
268  return !dest->isNull();
269 }
270 
271 static bool read_jpeg_image(QImage *outImage,
272  QSize scaledSize, QRect scaledClipRect,
273  QRect clipRect, int inQuality, j_decompress_ptr info, struct my_error_mgr* err )
274 {
275  if (!setjmp(err->setjmp_buffer)) {
276  // -1 means default quality.
277  int quality = inQuality;
278  if (quality < 0)
279  quality = 75;
280 
281  // If possible, merge the scaledClipRect into either scaledSize
282  // or clipRect to avoid doing a separate scaled clipping pass.
283  // Best results are achieved by clipping before scaling, not after.
284  if (!scaledClipRect.isEmpty()) {
285  if (scaledSize.isEmpty() && clipRect.isEmpty()) {
286  // No clipping or scaling before final clip.
287  clipRect = scaledClipRect;
288  scaledClipRect = QRect();
289  } else if (scaledSize.isEmpty()) {
290  // Clipping, but no scaling: combine the clip regions.
291  scaledClipRect.translate(clipRect.topLeft());
292  clipRect = scaledClipRect.intersected(clipRect);
293  scaledClipRect = QRect();
294  } else if (clipRect.isEmpty()) {
295  // No clipping, but scaling: if we can map back to an
296  // integer pixel boundary, then clip before scaling.
297  if ((info->image_width % scaledSize.width()) == 0 &&
298  (info->image_height % scaledSize.height()) == 0) {
299  int x = scaledClipRect.x() * info->image_width /
300  scaledSize.width();
301  int y = scaledClipRect.y() * info->image_height /
302  scaledSize.height();
303  int width = (scaledClipRect.right() + 1) *
304  info->image_width / scaledSize.width() - x;
305  int height = (scaledClipRect.bottom() + 1) *
306  info->image_height / scaledSize.height() - y;
307  clipRect = QRect(x, y, width, height);
308  scaledSize = scaledClipRect.size();
309  scaledClipRect = QRect();
310  }
311  } else {
312  // Clipping and scaling: too difficult to figure out,
313  // and not a likely use case, so do it the long way.
314  }
315  }
316 
317  // Determine the scale factor to pass to libjpeg for quick downscaling.
318  if (!scaledSize.isEmpty()) {
319  if (clipRect.isEmpty()) {
320  info->scale_denom =
321  qMin(info->image_width / scaledSize.width(),
322  info->image_height / scaledSize.height());
323  } else {
324  info->scale_denom =
325  qMin(clipRect.width() / scaledSize.width(),
326  clipRect.height() / scaledSize.height());
327  }
328  if (info->scale_denom < 2) {
329  info->scale_denom = 1;
330  } else if (info->scale_denom < 4) {
331  info->scale_denom = 2;
332  } else if (info->scale_denom < 8) {
333  info->scale_denom = 4;
334  } else {
335  info->scale_denom = 8;
336  }
337  info->scale_num = 1;
338  if (!clipRect.isEmpty()) {
339  // Correct the scale factor so that we clip accurately.
340  // It is recommended that the clip rectangle be aligned
341  // on an 8-pixel boundary for best performance.
342  while (info->scale_denom > 1 &&
343  ((clipRect.x() % info->scale_denom) != 0 ||
344  (clipRect.y() % info->scale_denom) != 0 ||
345  (clipRect.width() % info->scale_denom) != 0 ||
346  (clipRect.height() % info->scale_denom) != 0)) {
347  info->scale_denom /= 2;
348  }
349  }
350  }
351 
352  // If high quality not required, use fast decompression
353  if( quality < HIGH_QUALITY_THRESHOLD ) {
354  info->dct_method = JDCT_IFAST;
355  info->do_fancy_upsampling = FALSE;
356  }
357 
358  (void) jpeg_calc_output_dimensions(info);
359 
360  // Determine the clip region to extract.
361  QRect imageRect(0, 0, info->output_width, info->output_height);
362  QRect clip;
363  if (clipRect.isEmpty()) {
364  clip = imageRect;
365  } else if (info->scale_denom == info->scale_num) {
366  clip = clipRect.intersected(imageRect);
367  } else {
368  // The scale factor was corrected above to ensure that
369  // we don't miss pixels when we scale the clip rectangle.
370  clip = QRect(clipRect.x() / int(info->scale_denom),
371  clipRect.y() / int(info->scale_denom),
372  clipRect.width() / int(info->scale_denom),
373  clipRect.height() / int(info->scale_denom));
374  clip = clip.intersected(imageRect);
375  }
376 
377  // Allocate memory for the clipped QImage.
378  if (!ensureValidImage(outImage, info, clip.size()))
379  longjmp(err->setjmp_buffer, 1);
380 
381  // Avoid memcpy() overhead if grayscale with no clipping.
382  bool quickGray = (info->output_components == 1 &&
383  clip == imageRect);
384  if (!quickGray) {
385  // Ask the jpeg library to allocate a temporary row.
386  // The library will automatically delete it for us later.
387  // The libjpeg docs say we should do this before calling
388  // jpeg_start_decompress(). We can't use "new" here
389  // because we are inside the setjmp() block and an error
390  // in the jpeg input stream would cause a memory leak.
391  JSAMPARRAY rows = (info->mem->alloc_sarray)
392  ((j_common_ptr)info, JPOOL_IMAGE,
393  info->output_width * info->output_components, 1);
394 
395  (void) jpeg_start_decompress(info);
396 
397  while (info->output_scanline < info->output_height) {
398  int y = int(info->output_scanline) - clip.y();
399  if (y >= clip.height())
400  break; // We've read the entire clip region, so abort.
401 
402  (void) jpeg_read_scanlines(info, rows, 1);
403 
404  if (y < 0)
405  continue; // Haven't reached the starting line yet.
406 
407  if (info->output_components == 3) {
408  uchar *in = rows[0] + clip.x() * 3;
409  QRgb *out = (QRgb*)outImage->scanLine(y);
410  rgb888ToRgb32ConverterPtr(out, in, clip.width());
411  } else if (info->out_color_space == JCS_CMYK) {
412  // Convert CMYK->RGB.
413  uchar *in = rows[0] + clip.x() * 4;
414  QRgb *out = (QRgb*)outImage->scanLine(y);
415  for (int i = 0; i < clip.width(); ++i) {
416  int k = in[3];
417  *out++ = qRgb(k * in[0] / 255, k * in[1] / 255,
418  k * in[2] / 255);
419  in += 4;
420  }
421  } else if (info->output_components == 1) {
422  // Grayscale.
423  memcpy(outImage->scanLine(y),
424  rows[0] + clip.x(), clip.width());
425  }
426  }
427  } else {
428  // Load unclipped grayscale data directly into the QImage.
429  (void) jpeg_start_decompress(info);
430  while (info->output_scanline < info->output_height) {
431  uchar *row = outImage->scanLine(info->output_scanline);
432  (void) jpeg_read_scanlines(info, &row, 1);
433  }
434  }
435 
436  if (info->output_scanline == info->output_height)
437  (void) jpeg_finish_decompress(info);
438 
439  if (info->density_unit == 1) {
440  outImage->setDotsPerMeterX(int(100. * info->X_density / 2.54));
441  outImage->setDotsPerMeterY(int(100. * info->Y_density / 2.54));
442  } else if (info->density_unit == 2) {
443  outImage->setDotsPerMeterX(int(100. * info->X_density));
444  outImage->setDotsPerMeterY(int(100. * info->Y_density));
445  }
446 
447  if (scaledSize.isValid() && scaledSize != clip.size()) {
449  }
450 
451  if (!scaledClipRect.isEmpty())
452  *outImage = outImage->copy(scaledClipRect);
453  return !outImage->isNull();
454  }
455  else
456  return false;
457 }
458 
459 struct my_jpeg_destination_mgr : public jpeg_destination_mgr {
460  // Nothing dynamic - cannot rely on destruction over longjump
462  JOCTET buffer[max_buf];
463 
464 public:
466 };
467 
468 
469 #if defined(Q_C_CALLBACKS)
470 extern "C" {
471 #endif
472 
473 static void qt_init_destination(j_compress_ptr)
474 {
475 }
476 
477 static boolean qt_empty_output_buffer(j_compress_ptr cinfo)
478 {
479  my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest;
480 
481  int written = dest->device->write((char*)dest->buffer, max_buf);
482  if (written == -1)
483  (*cinfo->err->error_exit)((j_common_ptr)cinfo);
484 
485  dest->next_output_byte = dest->buffer;
486  dest->free_in_buffer = max_buf;
487 
488  return TRUE;
489 }
490 
491 static void qt_term_destination(j_compress_ptr cinfo)
492 {
493  my_jpeg_destination_mgr* dest = (my_jpeg_destination_mgr*)cinfo->dest;
494  qint64 n = max_buf - dest->free_in_buffer;
495 
496  qint64 written = dest->device->write((char*)dest->buffer, n);
497  if (written == -1)
498  (*cinfo->err->error_exit)((j_common_ptr)cinfo);
499 }
500 
501 #if defined(Q_C_CALLBACKS)
502 }
503 #endif
504 
506 {
507  jpeg_destination_mgr::init_destination = qt_init_destination;
508  jpeg_destination_mgr::empty_output_buffer = qt_empty_output_buffer;
509  jpeg_destination_mgr::term_destination = qt_term_destination;
510  this->device = device;
511  next_output_byte = buffer;
512  free_in_buffer = max_buf;
513 }
514 
515 
516 static bool write_jpeg_image(const QImage &image, QIODevice *device, int sourceQuality)
517 {
518  bool success = false;
519  const QVector<QRgb> cmap = image.colorTable();
520 
521  struct jpeg_compress_struct cinfo;
522  JSAMPROW row_pointer[1];
523  row_pointer[0] = 0;
524 
525  struct my_jpeg_destination_mgr *iod_dest = new my_jpeg_destination_mgr(device);
526  struct my_error_mgr jerr;
527 
528  cinfo.err = jpeg_std_error(&jerr);
529  jerr.error_exit = my_error_exit;
530 
531  if (!setjmp(jerr.setjmp_buffer)) {
532  // WARNING:
533  // this if loop is inside a setjmp/longjmp branch
534  // do not create C++ temporaries here because the destructor may never be called
535  // if you allocate memory, make sure that you can free it (row_pointer[0])
536  jpeg_create_compress(&cinfo);
537 
538  cinfo.dest = iod_dest;
539 
540  cinfo.image_width = image.width();
541  cinfo.image_height = image.height();
542 
543  bool gray=false;
544  switch (image.format()) {
545  case QImage::Format_Mono:
548  gray = true;
549  for (int i = image.colorCount(); gray && i--;) {
550  gray = gray & (qRed(cmap[i]) == qGreen(cmap[i]) &&
551  qRed(cmap[i]) == qBlue(cmap[i]));
552  }
553  cinfo.input_components = gray ? 1 : 3;
554  cinfo.in_color_space = gray ? JCS_GRAYSCALE : JCS_RGB;
555  break;
556  default:
557  cinfo.input_components = 3;
558  cinfo.in_color_space = JCS_RGB;
559  }
560 
561  jpeg_set_defaults(&cinfo);
562 
563  qreal diffInch = qAbs(image.dotsPerMeterX()*2.54/100. - qRound(image.dotsPerMeterX()*2.54/100.))
564  + qAbs(image.dotsPerMeterY()*2.54/100. - qRound(image.dotsPerMeterY()*2.54/100.));
565  qreal diffCm = (qAbs(image.dotsPerMeterX()/100. - qRound(image.dotsPerMeterX()/100.))
566  + qAbs(image.dotsPerMeterY()/100. - qRound(image.dotsPerMeterY()/100.)))*2.54;
567  if (diffInch < diffCm) {
568  cinfo.density_unit = 1; // dots/inch
569  cinfo.X_density = qRound(image.dotsPerMeterX()*2.54/100.);
570  cinfo.Y_density = qRound(image.dotsPerMeterY()*2.54/100.);
571  } else {
572  cinfo.density_unit = 2; // dots/cm
573  cinfo.X_density = (image.dotsPerMeterX()+50) / 100;
574  cinfo.Y_density = (image.dotsPerMeterY()+50) / 100;
575  }
576 
577 
578  int quality = sourceQuality >= 0 ? qMin(sourceQuality,100) : 75;
579  jpeg_set_quality(&cinfo, quality, TRUE /* limit to baseline-JPEG values */);
580  jpeg_start_compress(&cinfo, TRUE);
581 
582  row_pointer[0] = new uchar[cinfo.image_width*cinfo.input_components];
583  int w = cinfo.image_width;
584  while (cinfo.next_scanline < cinfo.image_height) {
585  uchar *row = row_pointer[0];
586  switch (image.format()) {
587  case QImage::Format_Mono:
589  if (gray) {
590  const uchar* data = image.constScanLine(cinfo.next_scanline);
591  if (image.format() == QImage::Format_MonoLSB) {
592  for (int i=0; i<w; i++) {
593  bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
594  row[i] = qRed(cmap[bit]);
595  }
596  } else {
597  for (int i=0; i<w; i++) {
598  bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
599  row[i] = qRed(cmap[bit]);
600  }
601  }
602  } else {
603  const uchar* data = image.constScanLine(cinfo.next_scanline);
604  if (image.format() == QImage::Format_MonoLSB) {
605  for (int i=0; i<w; i++) {
606  bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
607  *row++ = qRed(cmap[bit]);
608  *row++ = qGreen(cmap[bit]);
609  *row++ = qBlue(cmap[bit]);
610  }
611  } else {
612  for (int i=0; i<w; i++) {
613  bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
614  *row++ = qRed(cmap[bit]);
615  *row++ = qGreen(cmap[bit]);
616  *row++ = qBlue(cmap[bit]);
617  }
618  }
619  }
620  break;
622  if (gray) {
623  const uchar* pix = image.constScanLine(cinfo.next_scanline);
624  for (int i=0; i<w; i++) {
625  *row = qRed(cmap[*pix]);
626  ++row; ++pix;
627  }
628  } else {
629  const uchar* pix = image.constScanLine(cinfo.next_scanline);
630  for (int i=0; i<w; i++) {
631  *row++ = qRed(cmap[*pix]);
632  *row++ = qGreen(cmap[*pix]);
633  *row++ = qBlue(cmap[*pix]);
634  ++pix;
635  }
636  }
637  break;
639  memcpy(row, image.constScanLine(cinfo.next_scanline), w * 3);
640  break;
644  {
645  const QRgb* rgb = (const QRgb*)image.constScanLine(cinfo.next_scanline);
646  for (int i=0; i<w; i++) {
647  *row++ = qRed(*rgb);
648  *row++ = qGreen(*rgb);
649  *row++ = qBlue(*rgb);
650  ++rgb;
651  }
652  }
653  break;
654  default:
655  {
656  // (Testing shows that this way is actually faster than converting to RGB888 + memcpy)
657  QImage rowImg = image.copy(0, cinfo.next_scanline, w, 1).convertToFormat(QImage::Format_RGB32);
658  const QRgb* rgb = (const QRgb*)rowImg.constScanLine(0);
659  for (int i=0; i<w; i++) {
660  *row++ = qRed(*rgb);
661  *row++ = qGreen(*rgb);
662  *row++ = qBlue(*rgb);
663  ++rgb;
664  }
665  }
666  break;
667  }
668  jpeg_write_scanlines(&cinfo, row_pointer, 1);
669  }
670 
671  jpeg_finish_compress(&cinfo);
672  jpeg_destroy_compress(&cinfo);
673  success = true;
674  } else {
675  jpeg_destroy_compress(&cinfo);
676  success = false;
677  }
678 
679  delete iod_dest;
680  delete [] row_pointer[0];
681  return success;
682 }
683 
685 {
686 public:
687  enum State {
691  };
692 
694  : quality(75), iod_src(0), state(Ready), q(qq)
695  {}
696 
698  {
699  if(iod_src)
700  {
701  jpeg_destroy_decompress(&info);
702  delete iod_src;
703  iod_src = 0;
704  }
705  }
706 
707  bool readJpegHeader(QIODevice*);
708  bool read(QImage *image);
709 
710  int quality;
716  struct jpeg_decompress_struct info;
718  struct my_error_mgr err;
719 
721 
723 };
724 
729 {
730  if(state == Ready)
731  {
732  state = Error;
733  iod_src = new my_jpeg_source_mgr(device);
734 
735  jpeg_create_decompress(&info);
736  info.src = iod_src;
737  info.err = jpeg_std_error(&err);
738  err.error_exit = my_error_exit;
739 
740  if (!setjmp(err.setjmp_buffer)) {
741  (void) jpeg_read_header(&info, TRUE);
742 
743  int width = 0;
744  int height = 0;
745  read_jpeg_size(width, height, &info);
746  size = QSize(width, height);
747 
750  state = ReadHeader;
751  return true;
752  }
753  else
754  {
755  return false;
756  }
757  }
758  else if(state == Error)
759  return false;
760  return true;
761 }
762 
764 {
765  if(state == Ready)
766  readJpegHeader(q->device());
767 
768  if(state == ReadHeader)
769  {
770  bool success = read_jpeg_image(image, scaledSize, scaledClipRect, clipRect, quality, &info, &err);
771  state = success ? Ready : Error;
772  return success;
773  }
774 
775  return false;
776 
777 }
778 
780  : d(new QJpegHandlerPrivate(this))
781 {
782  const uint features = qDetectCPUFeatures();
783  Q_UNUSED(features);
784 #if defined(QT_HAVE_NEON)
785  // from qimage_neon.cpp
786  Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_neon(quint32 *dst, const uchar *src, int len);
787 
788  if (features & NEON)
789  rgb888ToRgb32ConverterPtr = qt_convert_rgb888_to_rgb32_neon;
790 #endif // QT_HAVE_NEON
791 #if defined(QT_HAVE_SSSE3)
792  // from qimage_ssse3.cpp
793  Q_GUI_EXPORT void QT_FASTCALL qt_convert_rgb888_to_rgb32_ssse3(quint32 *dst, const uchar *src, int len);
794 
795  if (features & SSSE3)
796  rgb888ToRgb32ConverterPtr = qt_convert_rgb888_to_rgb32_ssse3;
797 #endif // QT_HAVE_SSSE3
798 }
799 
801 {
802  delete d;
803 }
804 
806 {
808  return false;
809 
811  setFormat("jpeg");
812  return true;
813  }
814 
815  return false;
816 }
817 
819 {
820  if (!device) {
821  qWarning("QJpegHandler::canRead() called with no device");
822  return false;
823  }
824 
825  char buffer[2];
826  if (device->peek(buffer, 2) != 2)
827  return false;
828  return uchar(buffer[0]) == 0xff && uchar(buffer[1]) == 0xd8;
829 }
830 
832 {
833  if (!canRead())
834  return false;
835  return d->read(image);
836 }
837 
838 bool QJpegHandler::write(const QImage &image)
839 {
840  return write_jpeg_image(image, device(), d->quality);
841 }
842 
844 {
845  return option == Quality
846  || option == ScaledSize
847  || option == ScaledClipRect
848  || option == ClipRect
849  || option == Size
850  || option == ImageFormat;
851 }
852 
854 {
855  switch(option) {
856  case Quality:
857  return d->quality;
858  case ScaledSize:
859  return d->scaledSize;
860  case ScaledClipRect:
861  return d->scaledClipRect;
862  case ClipRect:
863  return d->clipRect;
864  case Size:
865  d->readJpegHeader(device());
866  return d->size;
867  case ImageFormat:
868  d->readJpegHeader(device());
869  return d->format;
870  default:
871  return QVariant();
872  }
873 }
874 
876 {
877  switch(option) {
878  case Quality:
879  d->quality = value.toInt();
880  break;
881  case ScaledSize:
882  d->scaledSize = value.toSize();
883  break;
884  case ScaledClipRect:
885  d->scaledClipRect = value.toRect();
886  break;
887  case ClipRect:
888  d->clipRect = value.toRect();
889  break;
890  default:
891  break;
892  }
893 }
894 
896 {
897  return "jpeg";
898 }
899 
900 
901 
902 
The QVariant class acts like a union for the most common Qt data types.
Definition: qvariant.h:92
T qobject_cast(QObject *object)
Definition: qobject.h:375
double d
Definition: qnumeric_p.h:62
bool read(QImage *image)
Error
Definition: qaudio.h:58
#define HIGH_QUALITY_THRESHOLD
Format
The following image formats are available in Qt.
Definition: qimage.h:91
unsigned int QRgb
Definition: qrgb.h:53
void setFormat(const QByteArray &format)
Sets the format of the QImageIOHandler to format.
QImage copy(const QRect &rect=QRect()) const
Returns a sub-area of the image as a new image.
Definition: qimage.cpp:1410
void setColor(int i, QRgb c)
Sets the color at the given index in the color table, to the given to colorValue. ...
Definition: qimage.cpp:1850
double qreal
Definition: qglobal.h:1193
static void my_error_exit(j_common_ptr cinfo)
static mach_timebase_info_data_t info
Q_DECL_CONSTEXPR const T & qMin(const T &a, const T &b)
Definition: qglobal.h:1215
#define QT_END_NAMESPACE
This macro expands to.
Definition: qglobal.h:90
const QByteArray & data() const
Returns the data contained in the buffer.
Definition: qbuffer.cpp:301
#define Q_GUI_EXPORT
Definition: qglobal.h:1450
bool isNull() const
Returns true if it is a null image, otherwise returns false.
Definition: qimage.cpp:1542
QJpegHandler * q
The QByteArray class provides an array of bytes.
Definition: qbytearray.h:135
static const int max_buf
virtual qint64 pos() const
For random-access devices, this function returns the position that data is written to or read from...
Definition: qiodevice.cpp:624
bool canRead() const
Returns true if an image can be read from the device (i.
static QColor cmap[256]
Definition: qgl_mac.mm:760
int width() const
Returns the width of the rectangle.
Definition: qrect.h:303
qint64 pos() const
Reimplemented Function
Definition: qbuffer.cpp:367
QRect intersected(const QRect &other) const
Returns the intersection of this rectangle and the given rectangle.
Definition: qrect.h:481
int dotsPerMeterY() const
Returns the number of pixels that fit vertically in a physical meter.
Definition: qimage.cpp:5628
The QBuffer class provides a QIODevice interface for a QByteArray.
Definition: qbuffer.h:57
int height() const
Returns the height of the rectangle.
Definition: qrect.h:306
JOCTET buffer[max_buf]
int bottom() const
Returns the y-coordinate of the rectangle&#39;s bottom edge.
Definition: qrect.h:249
jmp_buf setjmp_buffer
Q_DECL_CONSTEXPR T qAbs(const T &t)
Definition: qglobal.h:1201
#define QT_FASTCALL
Definition: qglobal.h:1161
Format format() const
Returns the format of the image.
Definition: qimage.cpp:2305
Q_GUI_EXPORT_INLINE int qRed(QRgb rgb)
Definition: qrgb.h:57
int toInt(bool *ok=0) const
Returns the variant as an int if the variant has type() Int , Bool , ByteArray , Char ...
Definition: qvariant.cpp:2625
qint64 read(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, and returns the number of bytes read...
Definition: qiodevice.cpp:791
bool read(QImage *image)
Read an image from the device, and stores it in image.
unsigned char uchar
Definition: qglobal.h:994
int width() const
Returns the width.
Definition: qsize.h:126
void setColorCount(int)
Resizes the color table to contain colorCount entries.
Definition: qimage.cpp:2275
void setOption(ImageOption option, const QVariant &value)
Sets the option option with the value value.
static bool ensureValidImage(QImage *dest, struct jpeg_decompress_struct *info, const QSize &size)
#define QT_BEGIN_NAMESPACE
This macro expands to.
Definition: qglobal.h:89
static void qt_term_destination(j_compress_ptr cinfo)
static bool write_jpeg_image(const QImage &image, QIODevice *device, int sourceQuality)
static void qt_skip_input_data(j_decompress_ptr cinfo, long num_bytes)
ImageOption
This enum describes the different options supported by QImageIOHandler.
QSize size() const
Returns the size of the rectangle.
Definition: qrect.h:309
qint64 peek(char *data, qint64 maxlen)
Reads at most maxSize bytes from the device into data, without side effects (i.
Definition: qiodevice.cpp:1563
Q_CORE_EXPORT void qWarning(const char *,...)
The QImage class provides a hardware-independent image representation that allows direct access to th...
Definition: qimage.h:87
static const char * data(const QByteArray &arr)
unsigned int uint
Definition: qglobal.h:996
#define FALSE
Synonym for false.
Definition: qglobal.h:1019
void setDotsPerMeterY(int)
Sets the number of pixels that fit vertically in a physical meter, to y.
Definition: qimage.cpp:5667
__int64 qint64
Definition: qglobal.h:942
The State element defines configurations of objects and properties.
void(QT_FASTCALL * Rgb888ToRgb32Converter)(quint32 *dst, const uchar *src, int len)
QImage::Format format
Q_GUI_EXPORT_INLINE int qBlue(QRgb rgb)
Definition: qrgb.h:63
QSize toSize() const
Returns the variant as a QSize if the variant has type() Size ; otherwise returns an invalid QSize...
Definition: qvariant.cpp:2432
my_jpeg_destination_mgr(QIODevice *)
virtual bool isSequential() const
Returns true if this device is sequential; otherwise returns false.
Definition: qiodevice.cpp:454
QSize size() const
Returns the size of the image, i.
Definition: qimage.cpp:1587
#define rgb(r, g, b)
Definition: qcolor_p.cpp:130
const uchar * constScanLine(int) const
Returns a pointer to the pixel data at the scanline with index i.
Definition: qimage.cpp:1926
#define TRUE
Synonym for true.
Definition: qglobal.h:1018
const char * constData() const
Returns a pointer to the data stored in the byte array.
Definition: qbytearray.h:433
bool isEmpty() const
Returns true if the rectangle is empty, otherwise returns false.
Definition: qrect.h:234
QRgb qRgb(int r, int g, int b)
Returns the ARGB quadruplet (255, {r}, {g}, {b}).
Definition: qrgb.h:69
bool write(const QImage &image)
Writes the image image to the assigned device.
int width() const
Returns the width of the image.
Definition: qimage.cpp:1557
static boolean qt_fill_input_buffer(j_decompress_ptr cinfo)
my_jpeg_source_mgr(QIODevice *device)
QImage convertToFormat(Format f, Qt::ImageConversionFlags flags=Qt::AutoColor) const Q_REQUIRED_RESULT
Returns a copy of the image in the given format.
Definition: qimage.cpp:3966
static void qt_init_source(j_decompress_ptr)
int right() const
Returns the x-coordinate of the rectangle&#39;s right edge.
Definition: qrect.h:246
void setDotsPerMeterX(int)
Sets the number of pixels that fit horizontally in a physical meter, to x.
Definition: qimage.cpp:5645
int y() const
Returns the y-coordinate of the rectangle&#39;s top edge.
Definition: qrect.h:255
int colorCount() const
Returns the size of the color table for the image.
Definition: qimage.cpp:1656
uint qDetectCPUFeatures()
Definition: qsimd.cpp:382
QRect toRect() const
Returns the variant as a QRect if the variant has type() Rect ; otherwise returns an invalid QRect...
Definition: qvariant.cpp:2416
int x() const
Returns the x-coordinate of the rectangle&#39;s left edge.
Definition: qrect.h:252
QByteArray name() const
Use format() instead.
Q_GUI_EXPORT_INLINE QRgb qRgb(int r, int g, int b)
Definition: qrgb.h:69
unsigned int quint32
Definition: qglobal.h:938
Definition: qsimd_p.h:222
int height() const
Returns the height.
Definition: qsize.h:129
if(void) toggleToolbarShown
The QRect class defines a rectangle in the plane using integer precision.
Definition: qrect.h:58
bool isValid() const
Returns true if both the width and height is equal to or greater than 0; otherwise returns false...
Definition: qsize.h:123
QIODevice * device() const
Returns the device currently assigned to the QImageIOHandler.
int height() const
Returns the height of the image.
Definition: qimage.cpp:1572
int size() const
Returns the number of bytes in this byte array.
Definition: qbytearray.h:402
Q_GUI_EXPORT_INLINE int qGreen(QRgb rgb)
Definition: qrgb.h:60
static bool read_jpeg_size(int &w, int &h, j_decompress_ptr cinfo)
The QSize class defines the size of a two-dimensional object using integer point precision.
Definition: qsize.h:53
static boolean qt_empty_output_buffer(j_compress_ptr cinfo)
QJpegHandlerPrivate * d
static void qt_term_source(j_decompress_ptr cinfo)
Definition: qsimd_p.h:220
struct my_jpeg_source_mgr * iod_src
static bool read_jpeg_image(QImage *outImage, QSize scaledSize, QRect scaledClipRect, QRect clipRect, int inQuality, j_decompress_ptr info, struct my_error_mgr *err)
bool isEmpty() const
Returns true if either of the width and height is less than or equal to 0; otherwise returns false...
Definition: qsize.h:120
qint64 write(const char *data, qint64 len)
Writes at most maxSize bytes of data from data to the device.
Definition: qiodevice.cpp:1342
static Rgb888ToRgb32Converter rgb888ToRgb32ConverterPtr
QImage scaled(int w, int h, Qt::AspectRatioMode aspectMode=Qt::IgnoreAspectRatio, Qt::TransformationMode mode=Qt::FastTransformation) const
Definition: qimage.h:232
void translate(int dx, int dy)
Moves the rectangle dx along the x axis and dy along the y axis, relative to the current position...
Definition: qrect.h:312
The QIODevice class is the base interface class of all I/O devices in Qt.
Definition: qiodevice.h:66
static void qt_init_destination(j_compress_ptr)
void QT_FASTCALL convert_rgb888_to_rgb32_C(quint32 *dst, const uchar *src, int len)
bool readJpegHeader(QIODevice *)
#define Q_UNUSED(x)
Indicates to the compiler that the parameter with the specified name is not used in the body of a fun...
Definition: qglobal.h:1729
virtual bool seek(qint64 pos)
For random-access devices, this function sets the current position to pos, returning true on success...
Definition: qiodevice.cpp:659
int dotsPerMeterX() const
Returns the number of pixels that fit horizontally in a physical meter.
Definition: qimage.cpp:5615
const QBuffer * memDevice
QVector< QRgb > colorTable() const
Returns a list of the colors contained in the image&#39;s color table, or an empty list if the image does...
Definition: qimage.cpp:1770
static bool read_jpeg_format(QImage::Format &format, j_decompress_ptr cinfo)
JOCTET buffer[max_buf]
Q_DECL_CONSTEXPR int qRound(qreal d)
Definition: qglobal.h:1203
bool supportsOption(ImageOption option) const
Returns true if the QImageIOHandler supports the option option; otherwise returns false...
uchar * scanLine(int)
Returns a pointer to the pixel data at the scanline with index i.
Definition: qimage.cpp:1886
QVariant option(ImageOption option) const
Returns the value assigned to option as a QVariant.
QJpegHandlerPrivate(QJpegHandler *qq)
QPoint topLeft() const
Returns the position of the rectangle&#39;s top-left corner.
Definition: qrect.h:288