File indexing completed on 2024-04-28 15:40:25

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: