File indexing completed on 2024-05-12 04:33:19
0001 /* 0002 SPDX-FileCopyrightText: 2007-2018 Gilles Caulier <caulier dot gilles at gmail dot com> 0003 0004 SPDX-License-Identifier: GPL-2.0-or-later 0005 */ 0006 0007 #include "kipiwriteimage.h" 0008 #include "kipiwritehelp.h" 0009 0010 // C ANSI includes 0011 0012 extern "C" 0013 { 0014 #ifndef _WIN32 0015 #include <unistd.h> 0016 #include <sys/ipc.h> 0017 #include <sys/shm.h> 0018 #endif 0019 #include <sys/types.h> 0020 #include <tiffvers.h> 0021 } 0022 0023 // Qt includes 0024 0025 #include <QFile> 0026 #include <QDebug> 0027 #include <QDataStream> 0028 #include <QStandardPaths> 0029 0030 namespace KXMLKipiCmd 0031 { 0032 0033 class KIPIWriteImage::Private 0034 { 0035 public: 0036 0037 Private() 0038 { 0039 sixteenBit = false; 0040 hasAlpha = false; 0041 width = 0; 0042 height = 0; 0043 cancel = nullptr; 0044 } 0045 0046 bool* cancel; 0047 0048 bool sixteenBit; 0049 bool hasAlpha; 0050 0051 uint width; 0052 uint height; 0053 0054 QByteArray data; // BGR(A) image data 0055 // data[0] = blue, data[1] = green, data[2] = red, data[3] = alpha. 0056 }; 0057 0058 KIPIWriteImage::KIPIWriteImage() 0059 : d(new Private) 0060 { 0061 } 0062 0063 KIPIWriteImage::~KIPIWriteImage() 0064 { 0065 delete d; 0066 } 0067 0068 int KIPIWriteImage::bytesDepth() const 0069 { 0070 if (d->sixteenBit) 0071 { 0072 if (d->hasAlpha) 0073 return 8; 0074 else 0075 return 6; 0076 } 0077 0078 if (d->hasAlpha) 0079 return 4; 0080 0081 return 3; 0082 } 0083 0084 void KIPIWriteImage::setCancel(bool* const cancel) 0085 { 0086 d->cancel = cancel; 0087 } 0088 0089 bool KIPIWriteImage::cancel() const 0090 { 0091 if (d->cancel) 0092 return *d->cancel; 0093 0094 return false; 0095 } 0096 0097 void KIPIWriteImage::setImageData(const QByteArray& data, uint width, uint height, 0098 bool sixteenBit, bool hasAlpha) 0099 { 0100 d->data = data; 0101 d->width = width; 0102 d->height = height; 0103 d->sixteenBit = sixteenBit; 0104 d->hasAlpha = hasAlpha; 0105 } 0106 0107 bool KIPIWriteImage::write2JPEG(const QString& destPath) 0108 { 0109 QFile file(destPath); 0110 0111 if (!file.open(QIODevice::ReadWrite)) 0112 { 0113 qDebug() << "Failed to open JPEG file for writing" ; 0114 return false; 0115 } 0116 0117 struct jpeg_compress_struct cinfo; 0118 struct jpeg_error_mgr jerr; 0119 0120 // Init JPEG compressor. 0121 cinfo.err = jpeg_std_error(&jerr); 0122 jpeg_create_compress(&cinfo); 0123 kipi_jpeg_qiodevice_dest(&cinfo, &file); 0124 cinfo.image_width = d->width; 0125 cinfo.image_height = d->height; 0126 cinfo.input_components = 3; 0127 cinfo.in_color_space = JCS_RGB; 0128 jpeg_set_defaults(&cinfo); 0129 0130 // bug #149578: set encoder horizontal and vertical chroma subsampling 0131 // factor to 2x1, 1x1, 1x1 (4:2:2) : Medium subsampling. 0132 // See this page for details: http://en.wikipedia.org/wiki/Chroma_subsampling 0133 cinfo.comp_info[0].h_samp_factor = 2; 0134 cinfo.comp_info[0].v_samp_factor = 1; 0135 cinfo.comp_info[1].h_samp_factor = 1; 0136 cinfo.comp_info[1].v_samp_factor = 1; 0137 cinfo.comp_info[2].h_samp_factor = 1; 0138 cinfo.comp_info[2].v_samp_factor = 1; 0139 0140 // bug #154273: use 99 compression level instead 100 to reduce output JPEG file size. 0141 jpeg_set_quality(&cinfo, 99, boolean(true)); 0142 jpeg_start_compress(&cinfo, boolean(true)); 0143 0144 // Write image data 0145 uchar* line = new uchar[d->width*3]; 0146 uchar* dstPtr = nullptr; 0147 0148 if (!d->sixteenBit) // 8 bits image. 0149 { 0150 uchar* srcPtr = (uchar*)d->data.data(); 0151 0152 for (uint j=0; j < d->height; ++j) 0153 { 0154 if (cancel()) 0155 { 0156 delete [] line; 0157 jpeg_destroy_compress(&cinfo); 0158 file.close(); 0159 return false; 0160 } 0161 0162 dstPtr = line; 0163 0164 for (uint i = 0; i < d->width; ++i) 0165 { 0166 dstPtr[2] = srcPtr[0]; // Blue 0167 dstPtr[1] = srcPtr[1]; // Green 0168 dstPtr[0] = srcPtr[2]; // Red 0169 0170 d->hasAlpha ? srcPtr += 4 : srcPtr += 3; 0171 dstPtr += 3; 0172 } 0173 0174 jpeg_write_scanlines(&cinfo, &line, 1); 0175 } 0176 } 0177 else // 16 bits image 0178 { 0179 unsigned short* srcPtr = reinterpret_cast<unsigned short*>(d->data.data()); 0180 0181 for (uint j=0; j < d->height; ++j) 0182 { 0183 if (cancel()) 0184 { 0185 delete [] line; 0186 jpeg_destroy_compress(&cinfo); 0187 file.close(); 0188 return false; 0189 } 0190 0191 dstPtr = line; 0192 0193 for (uint i = 0; i < d->width; ++i) 0194 { 0195 dstPtr[2] = (srcPtr[0] * 255UL)/65535UL; // Blue 0196 dstPtr[1] = (srcPtr[1] * 255UL)/65535UL; // Green 0197 dstPtr[0] = (srcPtr[2] * 255UL)/65535UL; // Red 0198 0199 d->hasAlpha ? srcPtr += 4 : srcPtr += 3; 0200 dstPtr += 3; 0201 } 0202 0203 jpeg_write_scanlines(&cinfo, &line, 1); 0204 } 0205 } 0206 0207 delete [] line; 0208 0209 jpeg_finish_compress(&cinfo); 0210 jpeg_destroy_compress(&cinfo); 0211 file.close(); 0212 0213 return true; 0214 } 0215 0216 bool KIPIWriteImage::write2PPM(const QString& destPath) 0217 { 0218 FILE* const file = fopen((const char*)(QFile::encodeName(destPath)).constData(), "wb"); 0219 0220 if (!file) 0221 { 0222 qDebug() << "Failed to open ppm file for writing" ; 0223 return false; 0224 } 0225 0226 fprintf(file, "P6\n%d %d\n255\n", d->width, d->height); 0227 0228 // Write image data 0229 uchar* const line = new uchar[d->width*3]; 0230 uchar* dstPtr = nullptr; 0231 0232 if (!d->sixteenBit) // 8 bits image. 0233 { 0234 uchar* srcPtr = (uchar*)d->data.data(); 0235 0236 for (uint j=0; j < d->height; ++j) 0237 { 0238 if (cancel()) 0239 { 0240 delete [] line; 0241 fclose(file); 0242 return false; 0243 } 0244 0245 dstPtr = line; 0246 0247 for (uint i = 0; i < d->width; ++i) 0248 { 0249 dstPtr[2] = srcPtr[0]; // Blue 0250 dstPtr[1] = srcPtr[1]; // Green 0251 dstPtr[0] = srcPtr[2]; // Red 0252 0253 d->hasAlpha ? srcPtr += 4 : srcPtr += 3; 0254 dstPtr += 3; 0255 } 0256 0257 fwrite(line, 1, d->width*3, file); 0258 } 0259 } 0260 else // 16 bits image 0261 { 0262 unsigned short* srcPtr = reinterpret_cast<unsigned short*>(d->data.data()); 0263 0264 for (uint j=0; j < d->height; ++j) 0265 { 0266 if (cancel()) 0267 { 0268 delete [] line; 0269 fclose(file); 0270 return false; 0271 } 0272 0273 dstPtr = line; 0274 0275 for (uint i = 0; i < d->width; ++i) 0276 { 0277 dstPtr[2] = (srcPtr[0] * 255UL)/65535UL; // Blue 0278 dstPtr[1] = (srcPtr[1] * 255UL)/65535UL; // Green 0279 dstPtr[0] = (srcPtr[2] * 255UL)/65535UL; // Red 0280 0281 d->hasAlpha ? srcPtr += 4 : srcPtr += 3; 0282 dstPtr += 3; 0283 } 0284 0285 fwrite(line, 1, d->width*3, file); 0286 } 0287 } 0288 0289 delete [] line; 0290 fclose(file); 0291 0292 return true; 0293 } 0294 0295 bool KIPIWriteImage::write2PNG(const QString& destPath) 0296 { 0297 /* 0298 check this out for b/w support: 0299 http://lxr.kde.org/source/playground/graphics/krita-exp/kis_png_converter.cpp#607 0300 */ 0301 QFile file(destPath); 0302 0303 if (!file.open(QIODevice::ReadWrite)) 0304 { 0305 qDebug() << "Failed to open PNG file for writing" ; 0306 return false; 0307 } 0308 0309 uchar* data = nullptr; 0310 int bitsDepth = d->sixteenBit ? 16 : 8; 0311 png_color_8 sig_bit; 0312 png_bytep row_ptr; 0313 png_structp png_ptr = png_create_write_struct(PNG_LIBPNG_VER_STRING, nullptr, nullptr, nullptr); 0314 png_infop info_ptr = png_create_info_struct(png_ptr); 0315 0316 png_set_write_fn(png_ptr, (void*)&file, kipi_png_write_fn, kipi_png_flush_fn); 0317 0318 if (QSysInfo::ByteOrder == QSysInfo::LittleEndian) // Intel 0319 png_set_bgr(png_ptr); 0320 else // PPC 0321 png_set_swap_alpha(png_ptr); 0322 0323 if (d->hasAlpha) 0324 { 0325 png_set_IHDR(png_ptr, info_ptr, d->width, d->height, bitsDepth, 0326 PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, 0327 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); 0328 0329 if (d->sixteenBit) 0330 data = new uchar[d->width * 8 * sizeof(uchar)]; 0331 else 0332 data = new uchar[d->width * 4 * sizeof(uchar)]; 0333 } 0334 else 0335 { 0336 png_set_IHDR(png_ptr, info_ptr, d->width, d->height, bitsDepth, 0337 PNG_COLOR_TYPE_RGB, PNG_INTERLACE_NONE, 0338 PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); 0339 0340 if (d->sixteenBit) 0341 data = new uchar[d->width * 6 * sizeof(uchar)]; 0342 else 0343 data = new uchar[d->width * 3 * sizeof(uchar)]; 0344 } 0345 0346 sig_bit.red = bitsDepth; 0347 sig_bit.green = bitsDepth; 0348 sig_bit.blue = bitsDepth; 0349 sig_bit.alpha = bitsDepth; 0350 png_set_sBIT(png_ptr, info_ptr, &sig_bit); 0351 png_set_compression_level(png_ptr, 9); 0352 0353 // Write Software info. 0354 QString libpngver(QLatin1String(PNG_HEADER_VERSION_STRING)); 0355 libpngver.replace(QLatin1Char('\n'), QLatin1Char(' ')); 0356 QByteArray softAscii = libpngver.toLatin1(); 0357 png_text text; 0358 text.key = (png_charp)"Software"; 0359 text.text = softAscii.data(); 0360 text.compression = PNG_TEXT_COMPRESSION_zTXt; 0361 png_set_text(png_ptr, info_ptr, &(text), 1); 0362 0363 png_write_info(png_ptr, info_ptr); 0364 png_set_shift(png_ptr, &sig_bit); 0365 png_set_packing(png_ptr); 0366 0367 uchar* ptr = (uchar*)d->data.data(); 0368 uint x, y, j; 0369 0370 for (y = 0; y < d->height; ++y) 0371 { 0372 if (cancel()) 0373 { 0374 delete [] data; 0375 file.close(); 0376 png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr); 0377 png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr); 0378 return false; 0379 } 0380 0381 j = 0; 0382 0383 for (x = 0; x < d->width*bytesDepth(); x+=bytesDepth()) 0384 { 0385 if (d->sixteenBit) 0386 { 0387 if (d->hasAlpha) 0388 { 0389 data[j++] = ptr[x+1]; // Blue 0390 data[j++] = ptr[ x ]; 0391 data[j++] = ptr[x+3]; // Green 0392 data[j++] = ptr[x+2]; 0393 data[j++] = ptr[x+5]; // Red 0394 data[j++] = ptr[x+4]; 0395 data[j++] = ptr[x+7]; // Alpha 0396 data[j++] = ptr[x+6]; 0397 } 0398 else 0399 { 0400 data[j++] = ptr[x+1]; // Blue 0401 data[j++] = ptr[ x ]; 0402 data[j++] = ptr[x+3]; // Green 0403 data[j++] = ptr[x+2]; 0404 data[j++] = ptr[x+5]; // Red 0405 data[j++] = ptr[x+4]; 0406 } 0407 } 0408 else 0409 { 0410 if (d->hasAlpha) 0411 { 0412 data[j++] = ptr[ x ]; // Blue 0413 data[j++] = ptr[x+1]; // Green 0414 data[j++] = ptr[x+2]; // Red 0415 data[j++] = ptr[x+3]; // Alpha 0416 } 0417 else 0418 { 0419 data[j++] = ptr[ x ]; // Blue 0420 data[j++] = ptr[x+1]; // Green 0421 data[j++] = ptr[x+2]; // Red 0422 } 0423 } 0424 } 0425 0426 row_ptr = (png_bytep) data; 0427 0428 png_write_rows(png_ptr, &row_ptr, 1); 0429 ptr += (d->width * bytesDepth()); 0430 } 0431 0432 delete [] data; 0433 0434 png_write_end(png_ptr, info_ptr); 0435 png_destroy_write_struct(&png_ptr, (png_infopp) & info_ptr); 0436 png_destroy_info_struct(png_ptr, (png_infopp) & info_ptr); 0437 file.close(); 0438 0439 return true; 0440 } 0441 0442 bool KIPIWriteImage::write2TIFF(const QString& destPath) 0443 { 0444 uint32 w = d->width; 0445 uint32 h = d->height; 0446 uchar* const data = (uchar*)d->data.data(); 0447 0448 // TIFF error handling. If an errors/warnings occurs during reading, 0449 // libtiff will call these methods 0450 0451 TIFFSetWarningHandler(kipi_tiff_warning); 0452 TIFFSetErrorHandler(kipi_tiff_error); 0453 0454 // Open target file 0455 0456 TIFF* const tif = TIFFOpen((const char*)(QFile::encodeName(destPath)).constData(), "w"); 0457 0458 if (!tif) 0459 { 0460 qDebug() << "Failed to open TIFF file for writing" ; 0461 return false; 0462 } 0463 0464 int bitsDepth = d->sixteenBit ? 16 : 8; 0465 0466 TIFFSetField(tif, TIFFTAG_IMAGEWIDTH, w); 0467 TIFFSetField(tif, TIFFTAG_IMAGELENGTH, h); 0468 TIFFSetField(tif, TIFFTAG_PHOTOMETRIC, PHOTOMETRIC_RGB); 0469 TIFFSetField(tif, TIFFTAG_PLANARCONFIG, PLANARCONFIG_CONTIG); 0470 TIFFSetField(tif, TIFFTAG_ORIENTATION, ORIENTATION_TOPLEFT); 0471 TIFFSetField(tif, TIFFTAG_RESOLUTIONUNIT, RESUNIT_NONE); 0472 TIFFSetField(tif, TIFFTAG_COMPRESSION, COMPRESSION_ADOBE_DEFLATE); 0473 TIFFSetField(tif, TIFFTAG_ZIPQUALITY, 9); 0474 // NOTE : this tag values aren't defined in libtiff 3.6.1. '2' is PREDICTOR_HORIZONTAL. 0475 // Use horizontal differencing for images which are 0476 // likely to be continuous tone. The TIFF spec says that this 0477 // usually leads to better compression. 0478 // See this url for more details: 0479 // http://www.awaresystems.be/imaging/tiff/tifftags/predictor.html 0480 TIFFSetField(tif, TIFFTAG_PREDICTOR, 2); 0481 0482 uint16 sampleinfo[1]; 0483 0484 if (d->hasAlpha) 0485 { 0486 sampleinfo[0] = EXTRASAMPLE_ASSOCALPHA; 0487 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 4); 0488 TIFFSetField(tif, TIFFTAG_EXTRASAMPLES, 1, sampleinfo); 0489 } 0490 else 0491 { 0492 TIFFSetField(tif, TIFFTAG_SAMPLESPERPIXEL, 3); 0493 } 0494 0495 TIFFSetField(tif, TIFFTAG_BITSPERSAMPLE, (uint16)bitsDepth); 0496 TIFFSetField(tif, TIFFTAG_ROWSPERSTRIP, TIFFDefaultStripSize(tif, 0)); 0497 0498 QString libtiffver(QLatin1String(TIFFLIB_VERSION_STR)); 0499 libtiffver.replace(QLatin1Char('\n'), QLatin1Char(' ')); 0500 TIFFSetField(tif, TIFFTAG_SOFTWARE, (const char*)libtiffver.toLatin1().data()); 0501 0502 // Write full image data in tiff directory IFD0 0503 0504 uchar* pixel = nullptr; 0505 double alpha_factor; 0506 uint32 x, y; 0507 uint8 r8, g8, b8, a8=0; 0508 uint16 r16, g16, b16, a16=0; 0509 int i=0; 0510 0511 uint8* const buf = (uint8 *)_TIFFmalloc(TIFFScanlineSize(tif)); 0512 0513 if (!buf) 0514 { 0515 qDebug() << "Cannot allocate memory buffer for main TIFF image." ; 0516 TIFFClose(tif); 0517 return false; 0518 } 0519 0520 for (y = 0; y < h; ++y) 0521 { 0522 if (cancel()) 0523 { 0524 _TIFFfree(buf); 0525 TIFFClose(tif); 0526 return false; 0527 } 0528 0529 i = 0; 0530 0531 for (x = 0; x < w; ++x) 0532 { 0533 pixel = &data[((y * w) + x) * bytesDepth()]; 0534 0535 if ( d->sixteenBit ) // 16 bits image. 0536 { 0537 b16 = (uint16)(pixel[0]+256*pixel[1]); 0538 g16 = (uint16)(pixel[2]+256*pixel[3]); 0539 r16 = (uint16)(pixel[4]+256*pixel[5]); 0540 0541 if (d->hasAlpha) 0542 { 0543 // TIFF makes you pre-multiply the rgb components by alpha 0544 0545 a16 = (uint16)(pixel[6]+256*pixel[7]); 0546 alpha_factor = ((double)a16 / 65535.0); 0547 r16 = (uint16)(r16*alpha_factor); 0548 g16 = (uint16)(g16*alpha_factor); 0549 b16 = (uint16)(b16*alpha_factor); 0550 } 0551 0552 // This might be endian dependent 0553 0554 buf[i++] = (uint8)(r16); 0555 buf[i++] = (uint8)(r16 >> 8); 0556 buf[i++] = (uint8)(g16); 0557 buf[i++] = (uint8)(g16 >> 8); 0558 buf[i++] = (uint8)(b16); 0559 buf[i++] = (uint8)(b16 >> 8); 0560 0561 if (d->hasAlpha) 0562 { 0563 buf[i++] = (uint8)(a16) ; 0564 buf[i++] = (uint8)(a16 >> 8) ; 0565 } 0566 } 0567 else // 8 bits image. 0568 { 0569 b8 = (uint8)pixel[0]; 0570 g8 = (uint8)pixel[1]; 0571 r8 = (uint8)pixel[2]; 0572 0573 if (d->hasAlpha) 0574 { 0575 // TIFF makes you pre-multiply the rgb components by alpha 0576 0577 a8 = (uint8)(pixel[3]); 0578 alpha_factor = ((double)a8 / 255.0); 0579 r8 = (uint8)(r8*alpha_factor); 0580 g8 = (uint8)(g8*alpha_factor); 0581 b8 = (uint8)(b8*alpha_factor); 0582 } 0583 0584 // This might be endian dependent 0585 0586 buf[i++] = r8; 0587 buf[i++] = g8; 0588 buf[i++] = b8; 0589 0590 if (d->hasAlpha) 0591 buf[i++] = a8; 0592 } 0593 } 0594 0595 if (!TIFFWriteScanline(tif, buf, y, 0)) 0596 { 0597 qDebug() << "Cannot write main TIFF image to target file." ; 0598 _TIFFfree(buf); 0599 TIFFClose(tif); 0600 return false; 0601 } 0602 } 0603 0604 _TIFFfree(buf); 0605 TIFFWriteDirectory(tif); 0606 TIFFClose(tif); 0607 0608 return true; 0609 } 0610 0611 } // namespace KXMLKipiCmd