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(¶meters); 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, ¶meters); 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