File indexing completed on 2024-04-28 04:21:24
0001 /* SPDX-FileCopyrightText: 2003-2010 Jesper K. Pedersen <blackie@kde.org> 0002 0003 SPDX-License-Identifier: GPL-2.0-or-later 0004 */ 0005 0006 #include "FastJpeg.h" 0007 0008 #include "JpeglibWithFix.h" 0009 0010 #include <kpabase/FileName.h> 0011 #include <kpabase/Logging.h> 0012 0013 #include <QFileInfo> 0014 #include <QImageReader> 0015 #include <QVector> 0016 #include <csetjmp> 0017 extern "C" { 0018 #include <fcntl.h> 0019 #include <sys/stat.h> 0020 #include <sys/types.h> 0021 #include <unistd.h> 0022 } 0023 0024 struct myjpeg_error_mgr : public jpeg_error_mgr { 0025 jmp_buf setjmp_buffer; 0026 }; 0027 0028 extern "C" { 0029 static void myjpeg_error_exit(j_common_ptr cinfo) 0030 { 0031 auto *myerr = (myjpeg_error_mgr *)cinfo->err; 0032 0033 char buffer[JMSG_LENGTH_MAX]; 0034 (*cinfo->err->format_message)(cinfo, buffer); 0035 // kWarning() << buffer; 0036 longjmp(myerr->setjmp_buffer, 1); 0037 } 0038 } 0039 0040 namespace Utilities 0041 { 0042 static bool loadJPEGInternal(QImage *img, FILE *inputFile, QSize *fullSize, int dim, const char *membuf, size_t membuf_size); 0043 } 0044 0045 bool Utilities::loadJPEG(QImage *img, const DB::FileName &imageFile, QSize *fullSize, int dim, char *membuf, size_t membufSize) 0046 { 0047 bool ok; 0048 struct stat statbuf; 0049 if (stat(QFile::encodeName(imageFile.absolute()).constData(), &statbuf) == -1) 0050 return false; 0051 if (!membuf || statbuf.st_size > (int)membufSize) { 0052 qCDebug(UtilitiesLog) << "loadJPEG (slow path) " << imageFile.relative() << " " << statbuf.st_size << " " << membufSize; 0053 FILE *inputFile = fopen(QFile::encodeName(imageFile.absolute()).constData(), "rb"); 0054 if (!inputFile) 0055 return false; 0056 ok = loadJPEGInternal(img, inputFile, fullSize, dim, nullptr, 0); 0057 fclose(inputFile); 0058 } else { 0059 // May be more than is needed, but less likely to fragment memory this 0060 // way. 0061 int inputFD = open(QFile::encodeName(imageFile.absolute()).constData(), O_RDONLY); 0062 if (inputFD == -1) { 0063 return false; 0064 } 0065 unsigned bytesLeft = statbuf.st_size; 0066 unsigned offset = 0; 0067 while (bytesLeft > 0) { 0068 int bytes = read(inputFD, membuf + offset, bytesLeft); 0069 if (bytes <= 0) { 0070 (void)close(inputFD); 0071 return false; 0072 } 0073 offset += bytes; 0074 bytesLeft -= bytes; 0075 } 0076 ok = loadJPEGInternal(img, nullptr, fullSize, dim, membuf, statbuf.st_size); 0077 (void)close(inputFD); 0078 } 0079 return ok; 0080 } 0081 0082 bool Utilities::loadJPEG(QImage *img, const DB::FileName &imageFile, QSize *fullSize, int dim) 0083 { 0084 return loadJPEG(img, imageFile, fullSize, dim, nullptr, 0); 0085 } 0086 0087 bool Utilities::loadJPEG(QImage *img, const QByteArray &data, QSize *fullSize, int dim) 0088 { 0089 return loadJPEGInternal(img, nullptr, fullSize, dim, data.data(), data.size()); 0090 } 0091 0092 bool Utilities::loadJPEGInternal(QImage *img, FILE *inputFile, QSize *fullSize, int dim, const char *membuf, size_t membuf_size) 0093 { 0094 struct jpeg_decompress_struct cinfo; 0095 struct myjpeg_error_mgr jerr; 0096 0097 // JPEG error handling - thanks to Marcus Meissner 0098 cinfo.err = jpeg_std_error(&jerr); 0099 cinfo.err->error_exit = myjpeg_error_exit; 0100 0101 if (setjmp(jerr.setjmp_buffer)) { 0102 jpeg_destroy_decompress(&cinfo); 0103 return false; 0104 } 0105 0106 jpeg_create_decompress(&cinfo); 0107 if (inputFile) 0108 jpeg_stdio_src(&cinfo, inputFile); 0109 else 0110 jpeg_mem_src(&cinfo, (unsigned char *)membuf, membuf_size); 0111 jpeg_read_header(&cinfo, TRUE); 0112 *fullSize = QSize(cinfo.image_width, cinfo.image_height); 0113 0114 int imgSize = qMax(cinfo.image_width, cinfo.image_height); 0115 0116 // libjpeg supports a sort of scale-while-decoding which speeds up decoding 0117 int scale = 1; 0118 if (dim != -1) { 0119 while (dim * scale * 2 <= imgSize) { 0120 scale *= 2; 0121 } 0122 if (scale > 8) 0123 scale = 8; 0124 } 0125 0126 cinfo.scale_num = 1; 0127 cinfo.scale_denom = scale; 0128 0129 // Create QImage 0130 jpeg_start_decompress(&cinfo); 0131 0132 switch (cinfo.output_components) { 0133 case 3: 0134 case 4: 0135 *img = QImage( 0136 cinfo.output_width, cinfo.output_height, QImage::Format_RGB32); 0137 if (img->isNull()) 0138 return false; 0139 break; 0140 case 1: // B&W image 0141 *img = QImage( 0142 cinfo.output_width, cinfo.output_height, QImage::Format_Indexed8); 0143 if (img->isNull()) 0144 return false; 0145 img->setColorCount(256); 0146 for (int i = 0; i < 256; i++) 0147 img->setColor(i, qRgb(i, i, i)); 0148 break; 0149 default: 0150 return false; 0151 } 0152 0153 QVector<uchar *> linesVector; 0154 linesVector.reserve(img->height()); 0155 for (int i = 0; i < img->height(); ++i) 0156 linesVector.push_back(img->scanLine(i)); 0157 uchar **lines = linesVector.data(); 0158 while (cinfo.output_scanline < cinfo.output_height) 0159 jpeg_read_scanlines(&cinfo, lines + cinfo.output_scanline, 0160 cinfo.output_height); 0161 jpeg_finish_decompress(&cinfo); 0162 0163 // Expand 24->32 bpp 0164 if (cinfo.output_components == 3) { 0165 for (uint j = 0; j < cinfo.output_height; j++) { 0166 uchar *in = img->scanLine(j) + cinfo.output_width * 3; 0167 QRgb *out = reinterpret_cast<QRgb *>(img->scanLine(j)); 0168 0169 for (uint i = cinfo.output_width; i--;) { 0170 in -= 3; 0171 out[i] = qRgb(in[0], in[1], in[2]); 0172 } 0173 } 0174 } 0175 0176 /*int newMax = qMax(cinfo.output_width, cinfo.output_height); 0177 int newx = size_*cinfo.output_width / newMax; 0178 int newy = size_*cinfo.output_height / newMax;*/ 0179 0180 jpeg_destroy_decompress(&cinfo); 0181 0182 // image = img.smoothScale(newx,newy); 0183 return true; 0184 } 0185 0186 bool Utilities::isJPEG(const DB::FileName &fileName) 0187 { 0188 QString format = QString::fromLocal8Bit(QImageReader::imageFormat(fileName.absolute())); 0189 return format == QString::fromLocal8Bit("jpeg"); 0190 } 0191 0192 // vi:expandtab:tabstop=4 shiftwidth=4: