217 QSize scaledSize, QRect scaledClipRect,
218 QRect clipRect,
int quality,
219 Rgb888ToRgb32Converter converter,
220 j_decompress_ptr info,
struct my_error_mgr* err,
bool invertCMYK)
222 if (!setjmp(err->setjmp_buffer)) {
230 if (!scaledClipRect.isEmpty()) {
231 if (scaledSize.isEmpty() && clipRect.isEmpty()) {
233 clipRect = scaledClipRect;
234 scaledClipRect = QRect();
235 }
else if (scaledSize.isEmpty()) {
237 scaledClipRect.translate(clipRect.topLeft());
238 clipRect = scaledClipRect.intersected(clipRect);
239 scaledClipRect = QRect();
240 }
else if (clipRect.isEmpty()) {
243 if ((info->image_width % scaledSize.width()) == 0 &&
244 (info->image_height % scaledSize.height()) == 0) {
245 int x = scaledClipRect.x() * info->image_width /
247 int y = scaledClipRect.y() * info->image_height /
249 int width = (scaledClipRect.right() + 1) *
250 info->image_width / scaledSize.width() - x;
251 int height = (scaledClipRect.bottom() + 1) *
252 info->image_height / scaledSize.height() - y;
253 clipRect = QRect(x, y, width, height);
254 scaledSize = scaledClipRect.size();
255 scaledClipRect = QRect();
264 if (!scaledSize.isEmpty() && info->image_width && info->image_height) {
265 if (clipRect.isEmpty()) {
266 double f = qMin(
double(info->image_width) / scaledSize.width(),
267 double(info->image_height) / scaledSize.height());
271 info->scale_num = qBound(1, qCeil(8/f), 8);
272 info->scale_denom = 8;
274 info->scale_denom = qMin(clipRect.width() / scaledSize.width(),
275 clipRect.height() / scaledSize.height());
279 if (info->scale_denom < 2)
280 info->scale_denom = 1;
281 else if (info->scale_denom < 4)
282 info->scale_denom = 2;
283 else if (info->scale_denom < 8)
284 info->scale_denom = 4;
286 info->scale_denom = 8;
292 while (info->scale_denom > 1 &&
293 ((clipRect.x() % info->scale_denom) != 0 ||
294 (clipRect.y() % info->scale_denom) != 0 ||
295 (clipRect.width() % info->scale_denom) != 0 ||
296 (clipRect.height() % info->scale_denom) != 0)) {
297 info->scale_denom /= 2;
304 info->dct_method = JDCT_IFAST;
305 info->do_fancy_upsampling = FALSE;
308 (
void) jpeg_calc_output_dimensions(info);
311 QRect imageRect(0, 0, info->output_width, info->output_height);
313 if (clipRect.isEmpty()) {
315 }
else if (info->scale_denom == info->scale_num) {
316 clip = clipRect.intersected(imageRect);
320 clip = QRect(clipRect.x() /
int(info->scale_denom),
321 clipRect.y() /
int(info->scale_denom),
322 clipRect.width() /
int(info->scale_denom),
323 clipRect.height() /
int(info->scale_denom));
324 clip = clip.intersected(imageRect);
328 if (!ensureValidImage(outImage, info, clip.size()))
332 bool quickGray = (info->output_components == 1 &&
341 JSAMPARRAY rows = (info->mem->alloc_sarray)
342 ((j_common_ptr)info, JPOOL_IMAGE,
343 info->output_width * info->output_components, 1);
345 (
void) jpeg_start_decompress(info);
347 while (info->output_scanline < info->output_height) {
348 int y =
int(info->output_scanline) - clip.y();
349 if (y >= clip.height())
352 (
void) jpeg_read_scanlines(info, rows, 1);
357 if (info->output_components == 3) {
358 uchar *in = rows[0] + clip.x() * 3;
359 QRgb *out = (QRgb*)outImage->scanLine(y);
360 converter(out, in, clip.width());
361 }
else if (info->out_color_space == JCS_CMYK) {
362 uchar *in = rows[0] + clip.x() * 4;
363 quint32 *out = (quint32*)outImage->scanLine(y);
365 for (
int i = 0; i < clip.width(); ++i) {
366 *out++ = 0xffffffffu - (in[0] | in[1] << 8 | in[2] << 16 | in[3] << 24);
370 memcpy(out, in, clip.width() * 4);
372 }
else if (info->output_components == 1) {
374 memcpy(outImage->scanLine(y),
375 rows[0] + clip.x(), clip.width());
380 (
void) jpeg_start_decompress(info);
381 while (info->output_scanline < info->output_height) {
382 uchar *row = outImage->scanLine(info->output_scanline);
383 (
void) jpeg_read_scanlines(info, &row, 1);
387 if (info->output_scanline == info->output_height)
388 (
void) jpeg_finish_decompress(info);
390 if (info->density_unit == 1) {
391 outImage->setDotsPerMeterX(
int(100. * info->X_density / 2.54));
392 outImage->setDotsPerMeterY(
int(100. * info->Y_density / 2.54));
393 }
else if (info->density_unit == 2) {
394 outImage->setDotsPerMeterX(
int(100. * info->X_density));
395 outImage->setDotsPerMeterY(
int(100. * info->Y_density));
398 if (scaledSize.isValid() && scaledSize != clip.size()) {
399 *outImage = outImage->scaled(scaledSize, Qt::IgnoreAspectRatio, quality >=
HIGH_QUALITY_THRESHOLD ? Qt::SmoothTransformation : Qt::FastTransformation);
402 if (!scaledClipRect.isEmpty())
403 *outImage = outImage->copy(scaledClipRect);
404 return !outImage->isNull();
407 my_output_message(j_common_ptr(info));
502 JSAMPROW *row_pointer,
506 const QString &description,
511 bool success =
false;
512 const QList<QRgb> cmap = image.colorTable();
514 if (image.format() == QImage::Format_Invalid || image.format() == QImage::Format_Alpha8)
520 cinfo.err = jpeg_std_error(&jerr);
524 if (!setjmp(jerr.setjmp_buffer)) {
529 jpeg_create_compress(&cinfo);
531 cinfo.dest = iod_dest;
533 cinfo.image_width = image.width();
534 cinfo.image_height = image.height();
537 switch (image.format()) {
538 case QImage::Format_Mono:
539 case QImage::Format_MonoLSB:
540 case QImage::Format_Indexed8:
542 for (
int i = image.colorCount(); gray && i; i--) {
543 gray = gray & qIsGray(cmap[i-1]);
545 cinfo.input_components = gray ? 1 : 3;
546 cinfo.in_color_space = gray ? JCS_GRAYSCALE : JCS_RGB;
548 case QImage::Format_Grayscale8:
549 case QImage::Format_Grayscale16:
551 cinfo.input_components = 1;
552 cinfo.in_color_space = JCS_GRAYSCALE;
554 case QImage::Format_CMYK8888:
555 cinfo.input_components = 4;
556 cinfo.in_color_space = JCS_CMYK;
559 cinfo.input_components = 3;
560 cinfo.in_color_space = JCS_RGB;
563 jpeg_set_defaults(&cinfo);
565 qreal diffInch = qAbs(image.dotsPerMeterX()*2.54/100. - qRound(image.dotsPerMeterX()*2.54/100.))
566 + qAbs(image.dotsPerMeterY()*2.54/100. - qRound(image.dotsPerMeterY()*2.54/100.));
567 qreal diffCm = (qAbs(image.dotsPerMeterX()/100. - qRound(image.dotsPerMeterX()/100.))
568 + qAbs(image.dotsPerMeterY()/100. - qRound(image.dotsPerMeterY()/100.)))*2.54;
569 if (diffInch < diffCm) {
570 cinfo.density_unit = 1;
571 cinfo.X_density = qRound(image.dotsPerMeterX()*2.54/100.);
572 cinfo.Y_density = qRound(image.dotsPerMeterY()*2.54/100.);
574 cinfo.density_unit = 2;
575 cinfo.X_density = (image.dotsPerMeterX()+50) / 100;
576 cinfo.Y_density = (image.dotsPerMeterY()+50) / 100;
580 cinfo.optimize_coding =
true;
583 jpeg_simple_progression(&cinfo);
585 int quality = sourceQuality >= 0 ? qMin(
int(sourceQuality),100) : 75;
586 jpeg_set_quality(&cinfo, quality, TRUE );
590 cinfo.comp_info[0].v_samp_factor = 1;
591 cinfo.comp_info[0].h_samp_factor = 1;
594 jpeg_start_compress(&cinfo, TRUE);
596 set_text(image, &cinfo, description);
597 if (cinfo.in_color_space == JCS_RGB || cinfo.in_color_space == JCS_CMYK)
598 write_icc_profile(image, &cinfo);
600 row_pointer[0] =
new uchar[cinfo.image_width*cinfo.input_components];
601 int w = cinfo.image_width;
602 while (cinfo.next_scanline < cinfo.image_height) {
603 uchar *row = row_pointer[0];
604 switch (image.format()) {
605 case QImage::Format_Mono:
606 case QImage::Format_MonoLSB:
608 const uchar* data = image.constScanLine(cinfo.next_scanline);
609 if (image.format() == QImage::Format_MonoLSB) {
610 for (
int i=0; i<w; i++) {
611 bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
612 row[i] = qRed(cmap[bit]);
615 for (
int i=0; i<w; i++) {
616 bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
617 row[i] = qRed(cmap[bit]);
621 const uchar* data = image.constScanLine(cinfo.next_scanline);
622 if (image.format() == QImage::Format_MonoLSB) {
623 for (
int i=0; i<w; i++) {
624 bool bit = !!(*(data + (i >> 3)) & (1 << (i & 7)));
625 *row++ = qRed(cmap[bit]);
626 *row++ = qGreen(cmap[bit]);
627 *row++ = qBlue(cmap[bit]);
630 for (
int i=0; i<w; i++) {
631 bool bit = !!(*(data + (i >> 3)) & (1 << (7 -(i & 7))));
632 *row++ = qRed(cmap[bit]);
633 *row++ = qGreen(cmap[bit]);
634 *row++ = qBlue(cmap[bit]);
639 case QImage::Format_Indexed8:
641 const uchar* pix = image.constScanLine(cinfo.next_scanline);
642 for (
int i=0; i<w; i++) {
643 *row = qRed(cmap[*pix]);
647 const uchar* pix = image.constScanLine(cinfo.next_scanline);
648 for (
int i=0; i<w; i++) {
649 *row++ = qRed(cmap[*pix]);
650 *row++ = qGreen(cmap[*pix]);
651 *row++ = qBlue(cmap[*pix]);
656 case QImage::Format_Grayscale8:
657 memcpy(row, image.constScanLine(cinfo.next_scanline), w);
659 case QImage::Format_Grayscale16:
661 QImage rowImg = image.copy(0, cinfo.next_scanline, w, 1).convertToFormat(QImage::Format_Grayscale8);
662 memcpy(row, rowImg.constScanLine(0), w);
665 case QImage::Format_RGB888:
666 memcpy(row, image.constScanLine(cinfo.next_scanline), w * 3);
668 case QImage::Format_RGB32:
669 case QImage::Format_ARGB32:
670 case QImage::Format_ARGB32_Premultiplied:
672 const QRgb* rgb = (
const QRgb*)image.constScanLine(cinfo.next_scanline);
673 for (
int i=0; i<w; i++) {
675 *row++ = qGreen(*rgb);
676 *row++ = qBlue(*rgb);
681 case QImage::Format_CMYK8888: {
682 auto *cmykIn =
reinterpret_cast<
const quint32 *>(image.constScanLine(cinfo.next_scanline));
683 auto *cmykOut =
reinterpret_cast<quint32 *>(row);
685 for (
int i = 0; i < w; ++i)
686 cmykOut[i] = 0xffffffffu - cmykIn[i];
688 memcpy(cmykOut, cmykIn, w * 4);
695 QImage rowImg = image.copy(0, cinfo.next_scanline, w, 1).convertToFormat(QImage::Format_RGB32);
696 const QRgb* rgb = (
const QRgb*)rowImg.constScanLine(0);
697 for (
int i=0; i<w; i++) {
699 *row++ = qGreen(*rgb);
700 *row++ = qBlue(*rgb);
706 jpeg_write_scanlines(&cinfo, row_pointer, 1);
709 jpeg_finish_compress(&cinfo);
710 jpeg_destroy_compress(&cinfo);
714 jpeg_destroy_compress(&cinfo);
957 info.err = jpeg_std_error(&err);
958 err.error_exit = my_error_exit;
959 err.output_message = my_output_message;
961 jpeg_create_decompress(&info);
964 if (!setjmp(err.setjmp_buffer)) {
965 jpeg_save_markers(&
info, JPEG_COM, 0xFFFF);
966 jpeg_save_markers(&
info, JPEG_APP0 + 1, 0xFFFF);
967 jpeg_save_markers(&
info, JPEG_APP0 + 2, 0xFFFF);
969 (
void) jpeg_read_header(&
info, TRUE);
974 size = QSize(width, height);
976 format = QImage::Format_Invalid;
977 read_jpeg_format(format, &info);
981 for (jpeg_saved_marker_ptr marker =
info.marker_list; marker !=
nullptr; marker = marker->next) {
982 if (marker->marker == JPEG_COM) {
983#ifndef QT_NO_IMAGEIO_TEXT_LOADING
985 QString s = QString::fromUtf8((
const char *)marker->data, marker->data_length);
986 int index = s.indexOf(QLatin1String(
": "));
987 if (index == -1 || s.indexOf(QLatin1Char(
' ')) < index) {
988 key = QLatin1String(
"Description");
992 value = s.mid(index + 2);
994 if (!description.isEmpty())
995 description += QLatin1String(
"\n\n");
996 description += key + QLatin1String(
": ") + value.simplified();
997 readTexts.append(key);
998 readTexts.append(value);
1000 }
else if (marker->marker == JPEG_APP0 + 1) {
1001 exifData.append((
const char*)marker->data, marker->data_length);
1002 }
else if (marker->marker == JPEG_APP0 + 2) {
1003 if (marker->data_length > 128 + 4 + 14 && strcmp((
const char *)marker->data,
"ICC_PROFILE") == 0) {
1004 iccProfile.append((
const char*)marker->data + 14, marker->data_length - 14);
1009 if (!exifData.isEmpty()) {
1011 int exifOrientation = getExifOrientation(exifData);
1012 if (exifOrientation > 0)
1013 transformation = exif2Qt(exifOrientation);