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