File indexing completed on 2024-12-22 04:15:42

0001 /*
0002  *  SPDX-FileCopyrightText: 2019 Aaron Boxer <boxerab@gmail.com>
0003  *
0004  *  SPDX-License-Identifier: LGPL-2.1-only
0005  */
0006 
0007 #include "jp2_converter.h"
0008 
0009 #include <openjpeg.h>
0010 
0011 #include <QFileInfo>
0012 #include <QApplication>
0013 
0014 #include <QMessageBox>
0015 
0016 #include <KoColorSpaceRegistry.h>
0017 #include <KoColorSpaceTraits.h>
0018 #include <KoColorSpaceConstants.h>
0019 #include <KisImportExportManager.h>
0020 #include <KoColorSpace.h>
0021 #include <KoColorModelStandardIds.h>
0022 
0023 #include <KisDocument.h>
0024 #include <kis_image.h>
0025 #include <kis_group_layer.h>
0026 #include <kis_paint_layer.h>
0027 #include <kis_paint_device.h>
0028 #include <kis_transaction.h>
0029 #include "kis_iterator_ng.h"
0030 #include <QThread>
0031 #include <plugins/impex/xcf/3rdparty/xcftools/xcftools.h>
0032 
0033 #include <iostream>
0034 #include <sstream>
0035 #include <cstring>
0036 #include <list> 
0037 #include <utility>
0038 
0039 #define J2K_CFMT 0
0040 #define JP2_CFMT 1
0041 
0042 JP2Converter::JP2Converter(KisDocument *doc) {
0043     m_doc = doc;
0044     m_stop = false;
0045 }
0046 
0047 JP2Converter::~JP2Converter() {
0048 }
0049 
0050 /**
0051  * sample error callback expecting a FILE* client object
0052  * */
0053 static void error_callback(const char *msg, void *client_data) {
0054     JP2Converter *converter = (JP2Converter*) client_data;
0055     converter->addErrorString(msg);
0056 }
0057 
0058 /**
0059  * sample warning callback expecting a FILE* client object
0060  * */
0061 static void warning_callback(const char *msg, void *client_data) {
0062     JP2Converter *converter = (JP2Converter*) client_data;
0063     converter->addWarningString(msg);
0064 }
0065 
0066 /**
0067  * sample debug callback expecting no client object
0068  * */
0069 static void info_callback(const char *msg, void *client_data) {
0070     JP2Converter *converter = (JP2Converter*) client_data;
0071     converter->addInfoString(msg);
0072 }
0073 
0074 static int getFileFormat(const char *filename) {
0075     static const std::list<std::pair<const char*, int>> formats= {
0076         {"j2k", J2K_CFMT},
0077         {"jp2", JP2_CFMT},
0078         {"j2c", J2K_CFMT},
0079         {"jpc", J2K_CFMT},
0080         {"jpx", J2K_CFMT},
0081         {"jpf", J2K_CFMT}
0082     };
0083     const char *ext = strrchr(filename, '.');
0084     if (ext == NULL) {
0085         return -1;
0086     }
0087     ext++;
0088     if (*ext) {
0089         for (const auto &format : formats) {
0090             if (strcasecmp(ext, format.first) == 0) {
0091                 return format.second;
0092             }
0093         }
0094     }
0095 
0096     return -1;
0097 }
0098 
0099 #define JP2_RFC3745_MAGIC    "\x00\x00\x00\x0c\x6a\x50\x20\x20\x0d\x0a\x87\x0a"
0100 #define JP2_MAGIC            "\x0d\x0a\x87\x0a"
0101 #define J2K_CODESTREAM_MAGIC "\xff\x4f\xff\x51"
0102 
0103 int JP2Converter::infile_format(const char *fname) {
0104     FILE *reader;
0105     const char *s, *magic_s;
0106     int ext_format, magic_format;
0107     unsigned char buf[12];
0108     OPJ_SIZE_T l_nb_read;
0109 
0110     reader = fopen(fname, "rb");
0111 
0112     if (reader == NULL) {
0113         return -2;
0114     }
0115 
0116     memset(buf, 0, 12);
0117     l_nb_read = fread(buf, 1, 12, reader);
0118     fclose(reader);
0119     if (l_nb_read != 12) {
0120         return -1;
0121     }
0122     ext_format = getFileFormat(fname);
0123     if (memcmp(buf, JP2_RFC3745_MAGIC, 12) == 0
0124             || memcmp(buf, JP2_MAGIC, 4) == 0) {
0125         magic_format = JP2_CFMT;
0126         magic_s = ".jp2";
0127     } else if (memcmp(buf, J2K_CODESTREAM_MAGIC, 4) == 0) {
0128         magic_format = J2K_CFMT;
0129         magic_s = ".j2k, .j2c, .jpc, .jpx, or .jpf";
0130     } else {
0131         return -1;
0132     }
0133 
0134     if (magic_format == ext_format) {
0135         return ext_format;
0136     }
0137 
0138     if (strlen(fname) >= 4) {
0139         s = fname + strlen(fname) - 4;
0140         std::ostringstream buffer;
0141         buffer << "The extension of this file is incorrect.\n"
0142             << "Found " << s << " while it should be " << magic_s << ".";
0143         addErrorString(buffer.str());
0144     }
0145     return magic_format;
0146 }
0147 
0148 KisImportExportErrorCode JP2Converter::buildImage(const QString &filename) {
0149     KisImportExportErrorCode res = ImportExportCodes::OK;
0150     const QByteArray file_str = filename.toUtf8();
0151     opj_codec_t *l_codec = 0;
0152     opj_dparameters_t parameters;
0153     bool hasColorSpaceInfo = false;
0154     opj_stream_t *l_stream = NULL;
0155     opj_image_t *image = NULL;
0156     int pos = 0;
0157     KisHLineIteratorSP it = NULL;
0158     unsigned int numComponents = 0;
0159     unsigned int precision = 0;
0160     const KoColorSpace *colorSpace = 0;
0161     QVector<int> channelorder;
0162     KisPaintLayerSP layer;
0163     bool isSigned;
0164     int32_t signedCorrection = 0;
0165     uint32_t w=0, h=0;
0166 
0167     // decompression parameters
0168     opj_set_default_decoder_parameters(&parameters);
0169     // Determine the type
0170     parameters.decod_format = infile_format(file_str.constData());
0171     if (parameters.decod_format == -1) {
0172         addErrorString("Not a JPEG 2000 file.");
0173         res = ImportExportCodes::FileFormatIncorrect;
0174         goto beach;
0175     }
0176 
0177     // Decode the file
0178     /* get a decoder handle */
0179     switch (parameters.decod_format) {
0180     case J2K_CFMT: {
0181         l_codec = opj_create_decompress(OPJ_CODEC_J2K);
0182         break;
0183     }
0184     case JP2_CFMT: {
0185         l_codec = opj_create_decompress(OPJ_CODEC_JP2);
0186         hasColorSpaceInfo = true;
0187         break;
0188     }
0189     }
0190     Q_ASSERT(l_codec);
0191 
0192     opj_codec_set_threads( l_codec,QThread::idealThreadCount() );
0193 
0194     /* setup the decoder decoding parameters using user parameters */
0195     opj_setup_decoder(l_codec, &parameters);
0196 
0197     l_stream = opj_stream_create_default_file_stream(file_str.constData(), 1);
0198     if (!l_stream) {
0199         addErrorString("Failed to create the stream");
0200         res = ImportExportCodes::ErrorWhileReading;
0201         goto beach;
0202     }
0203 
0204     // Setup an event handling
0205     opj_set_info_handler(l_codec, info_callback, this);
0206     opj_set_error_handler(l_codec, error_callback, this);
0207     opj_set_warning_handler(l_codec, warning_callback, this);
0208 
0209     if (!opj_read_header(l_stream, l_codec, &image)) {
0210         addErrorString("Failed to read the header");
0211         res = ImportExportCodes::ErrorWhileReading;
0212         goto beach;
0213     }
0214 
0215     /* Get the decoded image */
0216     if (!(opj_decode(l_codec, l_stream, image)
0217             && opj_end_decompress(l_codec, l_stream))) {
0218         addErrorString("Failed to decode image");
0219         res = ImportExportCodes::ErrorWhileReading;
0220         goto beach;
0221     }
0222 
0223     // Look for the colorspace
0224     numComponents = image->numcomps;
0225     if (image->numcomps == 0) {
0226         addErrorString("Image must have at least one component");
0227         res = ImportExportCodes::Failure;
0228         goto beach;
0229     }
0230     precision = image->comps[0].prec;
0231     for (uint32_t i = 1; i < numComponents; ++i) {
0232         if (image->comps[i].prec != precision) {
0233             std::ostringstream buffer;
0234             buffer << "All components must have the same bit depth "
0235                     << precision;
0236             addErrorString(buffer.str());
0237             res = ImportExportCodes::FormatFeaturesUnsupported;
0238             goto beach;
0239         }
0240     }
0241     isSigned = false;
0242     for (uint32_t i = 0; i < numComponents; ++i) {
0243         if ((image->comps[i].dx != 1) || (image->comps[i].dy != 1)) {
0244             addErrorString("Sub-sampling not supported");
0245             res = ImportExportCodes::FormatFeaturesUnsupported;
0246             goto beach;
0247         }
0248         isSigned = isSigned || (image->comps[0].sgnd);
0249     }
0250     if (isSigned)
0251         signedCorrection = 1 << (precision - 1);
0252 
0253     dbgFile
0254     << "Image has " << numComponents << " numComponents and a bit depth of "
0255             << precision << " for color space " << image->color_space;
0256     channelorder = QVector<int>(numComponents);
0257     if (!hasColorSpaceInfo) {
0258         if (numComponents == 3) {
0259             image->color_space = OPJ_CLRSPC_SRGB;
0260         } else if (numComponents == 1) {
0261             image->color_space = OPJ_CLRSPC_GRAY;
0262         }
0263     }
0264     switch (image->color_space) {
0265     case OPJ_CLRSPC_UNKNOWN:
0266     case OPJ_CLRSPC_UNSPECIFIED:
0267         break;
0268     case OPJ_CLRSPC_SRGB: {
0269         if (precision == 16 || precision == 12) {
0270             colorSpace = KoColorSpaceRegistry::instance()->rgb16();
0271         } else if (precision == 8) {
0272             colorSpace = KoColorSpaceRegistry::instance()->rgb8();
0273         }
0274         if (numComponents != 3) {
0275             std::ostringstream buffer;
0276             buffer << "sRGB: number of numComponents " << numComponents
0277                     << " does not equal 3";
0278             addErrorString(buffer.str());
0279             res = ImportExportCodes::FormatFeaturesUnsupported;
0280             goto beach;
0281         }
0282         channelorder[0] = KoBgrU16Traits::red_pos;
0283         channelorder[1] = KoBgrU16Traits::green_pos;
0284         channelorder[2] = KoBgrU16Traits::blue_pos;
0285         break;
0286     }
0287     case OPJ_CLRSPC_GRAY: {
0288         if (precision == 16 || precision == 12) {
0289             colorSpace = KoColorSpaceRegistry::instance()->colorSpace(
0290                     GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), "");
0291         } else if (precision == 8) {
0292             colorSpace = KoColorSpaceRegistry::instance()->colorSpace(
0293                     GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), "");
0294         }
0295         if (numComponents != 1) {
0296             std::ostringstream buffer;
0297             buffer << "Grayscale: number of numComponents " << numComponents
0298                     << " greater than 1";
0299             addErrorString(buffer.str());
0300             res = ImportExportCodes::FormatFeaturesUnsupported;
0301             goto beach;
0302         }
0303         channelorder[0] = 0;
0304         break;
0305     }
0306     case OPJ_CLRSPC_SYCC:
0307         addErrorString("YUV color space not supported");
0308         res = ImportExportCodes::FormatColorSpaceUnsupported;
0309         goto beach;
0310         break;
0311     case OPJ_CLRSPC_EYCC:
0312         addErrorString("eYCC color space not supported");
0313         res = ImportExportCodes::FormatColorSpaceUnsupported;
0314         goto beach;
0315         break;
0316     case OPJ_CLRSPC_CMYK:
0317         addErrorString("CMYK color space not supported");
0318         res = ImportExportCodes::FormatColorSpaceUnsupported;
0319         goto beach;
0320         break;
0321     default:
0322         break;
0323     }
0324 
0325     if (!colorSpace) {
0326         addErrorString("No color space found for image");
0327         res = ImportExportCodes::FormatColorSpaceUnsupported;
0328         goto beach;
0329     }
0330 
0331     // Create the image
0332     w = (uint32_t)(image->x1 - image->x0);
0333     h = (uint32_t)(image->y1 - image->y0);
0334     if (m_image == 0) {
0335         m_image = new KisImage(m_doc->createUndoStore(), w, h,
0336                 colorSpace, "built image");
0337     }
0338 
0339     // Create the layer
0340     layer = new KisPaintLayer(m_image, m_image->nextLayerName(),
0341             OPACITY_OPAQUE_U8);
0342     m_image->addNode(layer);
0343 
0344     // Set the data
0345     it = layer->paintDevice()->createHLineIteratorNG(0, 0, w);
0346     for (OPJ_UINT32 v = 0; v < image->y1; ++v) {
0347         if (precision == 16 || precision == 12) {
0348             do {
0349                 quint16 *px = reinterpret_cast<quint16*>(it->rawData());
0350                 for (uint32_t i = 0; i < numComponents; ++i) {
0351                     px[channelorder[i]] = image->comps[i].data[pos]
0352                             + signedCorrection;
0353                 }
0354                 colorSpace->setOpacity(it->rawData(), OPACITY_OPAQUE_U8, 1);
0355                 ++pos;
0356 
0357             } while (it->nextPixel());
0358         } else if (precision == 8) {
0359             do {
0360                 quint8 *px = it->rawData();
0361                 for (uint32_t i = 0; i < numComponents; ++i) {
0362                     px[channelorder[i]] = image->comps[i].data[pos]
0363                             + signedCorrection;
0364                 }
0365                 colorSpace->setOpacity(px, OPACITY_OPAQUE_U8, 1);
0366                 ++pos;
0367 
0368             } while (it->nextPixel());
0369         }
0370         it->nextRow();
0371     }
0372 
0373 beach:
0374     if (l_stream)
0375         opj_stream_destroy(l_stream);
0376     if (l_codec)
0377         opj_destroy_codec(l_codec);
0378     if (image)
0379         opj_image_destroy(image);
0380     if (!err.empty())
0381         m_doc->setErrorMessage(i18n(err.c_str()));
0382     if (!warn.empty())
0383         m_doc->setWarningMessage(i18n(warn.c_str()));
0384     return res;
0385 }
0386 
0387 KisImageWSP JP2Converter::image() {
0388     return m_image;
0389 }
0390 
0391 KisImportExportErrorCode JP2Converter::buildFile(const QString &filename,
0392         KisPaintLayerSP layer, const JP2ConvertOptions &options) {
0393     (void) layer;
0394     (void) filename;
0395     (void) options;
0396     return ImportExportCodes::Failure;
0397 }
0398 
0399 void JP2Converter::cancel() {
0400     m_stop = true;
0401 }
0402 
0403 void JP2Converter::addWarningString(const std::string &str) {
0404     if (!warn.empty())
0405         warn += "\n";
0406     warn += str;
0407 }
0408 void JP2Converter::addInfoString(const std::string &str) {
0409     dbgFile
0410     << str.c_str();
0411 }
0412 
0413 void JP2Converter::addErrorString(const std::string &str) {
0414     if (!err.empty())
0415         err += "\n";
0416     err += str;
0417 }
0418