219 QSize scaledSize, QRect scaledClipRect,
220 QRect clipRect,
int quality,
221 Rgb888ToRgb32Converter converter,
222 j_decompress_ptr info,
struct my_error_mgr* err,
bool invertCMYK)
224 if (!setjmp(err->setjmp_buffer)) {
232 if (!scaledClipRect.isEmpty()) {
233 if (scaledSize.isEmpty() && clipRect.isEmpty()) {
235 clipRect = scaledClipRect;
236 scaledClipRect = QRect();
237 }
else if (scaledSize.isEmpty()) {
239 scaledClipRect.translate(clipRect.topLeft());
240 clipRect = scaledClipRect.intersected(clipRect);
241 scaledClipRect = QRect();
242 }
else if (clipRect.isEmpty()) {
245 if ((info->image_width % scaledSize.width()) == 0 &&
246 (info->image_height % scaledSize.height()) == 0) {
247 int x = scaledClipRect.x() * info->image_width /
249 int y = scaledClipRect.y() * info->image_height /
251 int width = (scaledClipRect.right() + 1) *
252 info->image_width / scaledSize.width() - x;
253 int height = (scaledClipRect.bottom() + 1) *
254 info->image_height / scaledSize.height() - y;
255 clipRect = QRect(x, y, width, height);
256 scaledSize = scaledClipRect.size();
257 scaledClipRect = QRect();
266 if (!scaledSize.isEmpty() && info->image_width && info->image_height) {
267 if (clipRect.isEmpty()) {
268 double f = qMin(
double(info->image_width) / scaledSize.width(),
269 double(info->image_height) / scaledSize.height());
273 info->scale_num = qBound(1, qCeil(8/f), 8);
274 info->scale_denom = 8;
276 info->scale_denom = qMin(clipRect.width() / scaledSize.width(),
277 clipRect.height() / scaledSize.height());
281 if (info->scale_denom < 2)
282 info->scale_denom = 1;
283 else if (info->scale_denom < 4)
284 info->scale_denom = 2;
285 else if (info->scale_denom < 8)
286 info->scale_denom = 4;
288 info->scale_denom = 8;
294 while (info->scale_denom > 1 &&
295 ((clipRect.x() % info->scale_denom) != 0 ||
296 (clipRect.y() % info->scale_denom) != 0 ||
297 (clipRect.width() % info->scale_denom) != 0 ||
298 (clipRect.height() % info->scale_denom) != 0)) {
299 info->scale_denom /= 2;
306 info->dct_method = JDCT_IFAST;
307 info->do_fancy_upsampling = FALSE;
310 (
void) jpeg_calc_output_dimensions(info);
313 QRect imageRect(0, 0, info->output_width, info->output_height);
315 if (clipRect.isEmpty()) {
317 }
else if (info->scale_denom == info->scale_num) {
318 clip = clipRect.intersected(imageRect);
322 clip = QRect(clipRect.x() /
int(info->scale_denom),
323 clipRect.y() /
int(info->scale_denom),
324 clipRect.width() /
int(info->scale_denom),
325 clipRect.height() /
int(info->scale_denom));
326 clip = clip.intersected(imageRect);
330 if (!ensureValidImage(outImage, info, clip.size()))
334 bool quickGray = (info->output_components == 1 &&
343 JSAMPARRAY rows = (info->mem->alloc_sarray)
344 ((j_common_ptr)info, JPOOL_IMAGE,
345 info->output_width * info->output_components, 1);
347 (
void) jpeg_start_decompress(info);
349 while (info->output_scanline < info->output_height) {
350 int y =
int(info->output_scanline) - clip.y();
351 if (y >= clip.height())
354 (
void) jpeg_read_scanlines(info, rows, 1);
359 if (info->output_components == 3) {
360 uchar *in = rows[0] + clip.x() * 3;
361 QRgb *out = (QRgb*)outImage->scanLine(y);
362 converter(out, in, clip.width());
363 }
else if (info->out_color_space == JCS_CMYK) {
364 uchar *in = rows[0] + clip.x() * 4;
365 quint32 *out = (quint32*)outImage->scanLine(y);
367 for (
int i = 0; i < clip.width(); ++i) {
368 *out++ = 0xffffffffu - (in[0] | in[1] << 8 | in[2] << 16 | in[3] << 24);
372 memcpy(out, in, clip.width() * 4);
374 }
else if (info->output_components == 1) {
376 memcpy(outImage->scanLine(y),
377 rows[0] + clip.x(), clip.width());
382 (
void) jpeg_start_decompress(info);
383 while (info->output_scanline < info->output_height) {
384 uchar *row = outImage->scanLine(info->output_scanline);
385 (
void) jpeg_read_scanlines(info, &row, 1);
389 if (info->output_scanline == info->output_height)
390 (
void) jpeg_finish_decompress(info);
392 if (info->density_unit == 1) {
393 outImage->setDotsPerMeterX(
int(100. * info->X_density / 2.54));
394 outImage->setDotsPerMeterY(
int(100. * info->Y_density / 2.54));
395 }
else if (info->density_unit == 2) {
396 outImage->setDotsPerMeterX(
int(100. * info->X_density));
397 outImage->setDotsPerMeterY(
int(100. * info->Y_density));
400 if (scaledSize.isValid() && scaledSize != clip.size()) {
401 *outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, quality >=
HIGH_QUALITY_THRESHOLD ? Qt::SmoothTransformation : Qt::FastTransformation);
404 if (!scaledClipRect.isEmpty())
405 *outImage = outImage->copy(scaledClipRect);
406 return !outImage->isNull();
409 my_output_message(j_common_ptr(info));
504 JSAMPROW *row_pointer,
508 const QString &description,
513 bool success =
false;
514 const QList<QRgb> cmap = image.colorTable();
516 if (image.format() == QImage::Format_Invalid || image.format() == QImage::Format_Alpha8)
522 cinfo.err = jpeg_std_error(&jerr);
526 if (!setjmp(jerr.setjmp_buffer)) {
531 jpeg_create_compress(&cinfo);
533 cinfo.dest = iod_dest;
535 cinfo.image_width = image.width();
536 cinfo.image_height = image.height();
539 switch (image.format()) {
540 case QImage::Format_Mono:
541 case QImage::Format_MonoLSB:
542 case QImage::Format_Indexed8:
544 for (
int i = image.colorCount(); gray && i; i--) {
545 gray = gray & qIsGray(cmap[i-1]);
547 cinfo.input_components = gray ? 1 : 3;
548 cinfo.in_color_space = gray ? JCS_GRAYSCALE : JCS_RGB;
550 case QImage::Format_Grayscale8:
551 case QImage::Format_Grayscale16:
553 cinfo.input_components = 1;
554 cinfo.in_color_space = JCS_GRAYSCALE;
556 case QImage::Format_CMYK8888:
557 cinfo.input_components = 4;
558 cinfo.in_color_space = JCS_CMYK;
561 cinfo.input_components = 3;
562 cinfo.in_color_space = JCS_RGB;
565 jpeg_set_defaults(&cinfo);
567 qreal diffInch = qAbs(image.dotsPerMeterX()*2.54/100. - qRound(image.dotsPerMeterX()*2.54/100.))
568 + qAbs(image.dotsPerMeterY()*2.54/100. - qRound(image.dotsPerMeterY()*2.54/100.));
569 qreal diffCm = (qAbs(image.dotsPerMeterX()/100. - qRound(image.dotsPerMeterX()/100.))
570 + qAbs(image.dotsPerMeterY()/100. - qRound(image.dotsPerMeterY()/100.)))*2.54;
571 if (diffInch < diffCm) {
572 cinfo.density_unit = 1;
573 cinfo.X_density = qRound(image.dotsPerMeterX()*2.54/100.);
574 cinfo.Y_density = qRound(image.dotsPerMeterY()*2.54/100.);
576 cinfo.density_unit = 2;
577 cinfo.X_density = (image.dotsPerMeterX()+50) / 100;
578 cinfo.Y_density = (image.dotsPerMeterY()+50) / 100;
582 cinfo.optimize_coding =
true;
585 jpeg_simple_progression(&cinfo);
587 int quality = sourceQuality >= 0 ? qMin(
int(sourceQuality),100) : 75;
588 jpeg_set_quality(&cinfo, quality, TRUE );
592 cinfo.comp_info[0].v_samp_factor = 1;
593 cinfo.comp_info[0].h_samp_factor = 1;
596 jpeg_start_compress(&cinfo, TRUE);
598 set_text(image, &cinfo, description);
599 if (cinfo.in_color_space == JCS_RGB || cinfo.in_color_space == JCS_CMYK)
600 write_icc_profile(image, &cinfo);
602 row_pointer[0] =
new uchar[cinfo.image_width*cinfo.input_components];
603 int w = cinfo.image_width;
604 while (cinfo.next_scanline < cinfo.image_height) {
605 uchar *row = row_pointer[0];
606 switch (image.format()) {
607 case QImage::Format_Mono:
608 case QImage::Format_MonoLSB:
610 const uchar* data = image.constScanLine(cinfo.next_scanline);
611 if (image.format() == QImage::Format_MonoLSB) {
612 for (
int i=0; i<w; i++) {
613 bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
614 row[i] = qRed(cmap[bit]);
617 for (
int i=0; i<w; i++) {
618 bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
619 row[i] = qRed(cmap[bit]);
623 const uchar* data = image.constScanLine(cinfo.next_scanline);
624 if (image.format() == QImage::Format_MonoLSB) {
625 for (
int i=0; i<w; i++) {
626 bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
627 *row++ = qRed(cmap[bit]);
628 *row++ = qGreen(cmap[bit]);
629 *row++ = qBlue(cmap[bit]);
632 for (
int i=0; i<w; i++) {
633 bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
634 *row++ = qRed(cmap[bit]);
635 *row++ = qGreen(cmap[bit]);
636 *row++ = qBlue(cmap[bit]);
641 case QImage::Format_Indexed8:
643 const uchar* pix = image.constScanLine(cinfo.next_scanline);
644 for (
int i=0; i<w; i++) {
645 *row = qRed(cmap[*pix]);
649 const uchar* pix = image.constScanLine(cinfo.next_scanline);
650 for (
int i=0; i<w; i++) {
651 *row++ = qRed(cmap[*pix]);
652 *row++ = qGreen(cmap[*pix]);
653 *row++ = qBlue(cmap[*pix]);
658 case QImage::Format_Grayscale8:
659 memcpy(row, image.constScanLine(cinfo.next_scanline), w);
661 case QImage::Format_Grayscale16:
663 QImage rowImg = image.copy(0, cinfo.next_scanline, w, 1).convertToFormat(QImage::Format_Grayscale8);
664 memcpy(row, rowImg.constScanLine(0), w);
667 case QImage::Format_RGB888:
668 memcpy(row, image.constScanLine(cinfo.next_scanline), w * 3);
670 case QImage::Format_RGB32:
671 case QImage::Format_ARGB32:
672 case QImage::Format_ARGB32_Premultiplied:
674 const QRgb* rgb = (
const QRgb*)image.constScanLine(cinfo.next_scanline);
675 for (
int i=0; i<w; i++) {
677 *row++ = qGreen(*rgb);
678 *row++ = qBlue(*rgb);
683 case QImage::Format_CMYK8888: {
684 auto *cmykIn =
reinterpret_cast<
const quint32 *>(image.constScanLine(cinfo.next_scanline));
685 auto *cmykOut =
reinterpret_cast<quint32 *>(row);
687 for (
int i = 0; i < w; ++i)
688 cmykOut[i] = 0xffffffffu - cmykIn[i];
690 memcpy(cmykOut, cmykIn, w * 4);
697 QImage rowImg = image.copy(0, cinfo.next_scanline, w, 1).convertToFormat(QImage::Format_RGB32);
698 const QRgb* rgb = (
const QRgb*)rowImg.constScanLine(0);
699 for (
int i=0; i<w; i++) {
701 *row++ = qGreen(*rgb);
702 *row++ = qBlue(*rgb);
708 jpeg_write_scanlines(&cinfo, row_pointer, 1);
711 jpeg_finish_compress(&cinfo);
712 jpeg_destroy_compress(&cinfo);
716 jpeg_destroy_compress(&cinfo);
959 info.err = jpeg_std_error(&err);
960 err.error_exit = my_error_exit;
961 err.output_message = my_output_message;
963 jpeg_create_decompress(&
info);
965 if (
const int mbLimit = QImageReader::allocationLimit()) {
966 const qint64 bMax = 2 * qint64(mbLimit) * 1024 * 1024;
967 info.mem->max_memory_to_use =
long(qMin(bMax, qint64(
std::numeric_limits<
long>::max())));
970 if (!setjmp(err.setjmp_buffer)) {
971 jpeg_save_markers(&
info, JPEG_COM, 0xFFFF);
972 jpeg_save_markers(&
info, JPEG_APP0 + 1, 0xFFFF);
973 jpeg_save_markers(&
info, JPEG_APP0 + 2, 0xFFFF);
975 (
void) jpeg_read_header(&
info, TRUE);
980 size = QSize(width, height);
982 format = QImage::Format_Invalid;
983 read_jpeg_format(format, &
info);
987 for (jpeg_saved_marker_ptr marker =
info.marker_list; marker !=
nullptr; marker = marker->next) {
988 if (marker->marker == JPEG_COM) {
989#ifndef QT_NO_IMAGEIO_TEXT_LOADING
991 QString s = QString::fromUtf8((
const char *)marker->data, marker->data_length);
992 int index = s.indexOf(QLatin1String(
": "));
993 if (index == -1 || s.indexOf(QLatin1Char(
' ')) < index) {
994 key = QLatin1String(
"Description");
998 value = s.mid(index + 2);
1000 if (!description.isEmpty())
1001 description += QLatin1String(
"\n\n");
1002 description += key + QLatin1String(
": ") + value.simplified();
1003 readTexts.append(key);
1004 readTexts.append(value);
1006 }
else if (marker->marker == JPEG_APP0 + 1) {
1007 exifData.append((
const char*)marker->data, marker->data_length);
1008 }
else if (marker->marker == JPEG_APP0 + 2) {
1009 if (marker->data_length > 128 + 4 + 14 && strcmp((
const char *)marker->data,
"ICC_PROFILE") == 0) {
1010 iccProfile.append((
const char*)marker->data + 14, marker->data_length - 14);
1015 if (!exifData.isEmpty()) {
1017 int exifOrientation = getExifOrientation(exifData);
1018 if (exifOrientation > 0)
1019 transformation = exif2Qt(exifOrientation);