bool QImageLoader::save(const QString& filePath, DImgLoaderObserver* const observer) { QVariant qualityAttr = imageGetAttribute(QLatin1String("quality")); int quality = qualityAttr.isValid() ? qualityAttr.toInt() : 90; if (quality < 0) { quality = 90; } if (quality > 100) { quality = 100; } QVariant formatAttr = imageGetAttribute(QLatin1String("format")); QByteArray format = formatAttr.toByteArray(); QImage image = m_image->copyQImage(); if (observer) { observer->progressInfo(m_image, 0.1F); } // Saving is opaque to us. No support for stopping from observer, // progress info are only pseudo values bool success = image.save(filePath, format.toUpper().constData(), quality); if (observer && success) { observer->progressInfo(m_image, 1.0F); } imageSetAttribute(QLatin1String("format"), format.toUpper()); saveMetadata(filePath); return success; }
bool PGFLoader::load(const QString& filePath, DImgLoaderObserver* const observer) { m_observer = observer; readMetadata(filePath, DImg::PGF); FILE* file = fopen(QFile::encodeName(filePath).constData(), "rb"); if (!file) { qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Error: Could not open source file."; loadingFailed(); return false; } unsigned char header[3]; if (fread(&header, 3, 1, file) != 1) { fclose(file); loadingFailed(); return false; } unsigned char pgfID[3] = { 0x50, 0x47, 0x46 }; if (memcmp(&header[0], &pgfID, 3) != 0) { // not a PGF file fclose(file); loadingFailed(); return false; } fclose(file); // ------------------------------------------------------------------- // Initialize PGF API. #ifdef WIN32 #ifdef UNICODE HANDLE fd = CreateFile((LPCWSTR)(QFile::encodeName(filePath).constData()), GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); #else HANDLE fd = CreateFile(QFile::encodeName(filePath).constData(), GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); #endif if (fd == INVALID_HANDLE_VALUE) { loadingFailed(); return false; } #else int fd = open(QFile::encodeName(filePath).constData(), O_RDONLY); if (fd == -1) { loadingFailed(); return false; } #endif CPGFFileStream stream(fd); CPGFImage pgf; int colorModel = DImg::COLORMODELUNKNOWN; try { // open pgf image pgf.Open(&stream); switch (pgf.Mode()) { case ImageModeRGBColor: case ImageModeRGB48: m_hasAlpha = false; colorModel = DImg::RGB; break; case ImageModeRGBA: m_hasAlpha = true; colorModel = DImg::RGB; break; default: qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Cannot load PGF image: color mode not supported (" << pgf.Mode() << ")"; loadingFailed(); return false; break; } switch (pgf.Channels()) { case 3: case 4: break; default: qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Cannot load PGF image: color channels number not supported (" << pgf.Channels() << ")"; loadingFailed(); return false; break; } int bitDepth = pgf.BPP(); switch (bitDepth) { case 24: // RGB 8 bits. case 32: // RGBA 8 bits. m_sixteenBit = false; break; case 48: // RGB 16 bits. case 64: // RGBA 16 bits. m_sixteenBit = true; break; default: qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Cannot load PGF image: color bits depth not supported (" << bitDepth << ")"; loadingFailed(); return false; break; } if(DIGIKAM_DIMG_LOG_PGF().isDebugEnabled()) { const PGFHeader* header = pgf.GetHeader(); qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF width = " << header->width; qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF height = " << header->height; qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF bbp = " << header->bpp; qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF channels = " << header->channels; qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF quality = " << header->quality; qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF mode = " << header->mode; qCDebug(DIGIKAM_DIMG_LOG_PGF) << "Has Alpha = " << m_hasAlpha; qCDebug(DIGIKAM_DIMG_LOG_PGF) << "Is 16 bits = " << m_sixteenBit; } // NOTE: see bug #273765 : Loading PGF thumbs with OpenMP support through a separated thread do not work properlly with libppgf 6.11.24 pgf.ConfigureDecoder(false); int width = pgf.Width(); int height = pgf.Height(); uchar* data = 0; QSize originalSize(width, height); if (m_loadFlags & LoadImageData) { // ------------------------------------------------------------------- // Find out if we do the fast-track loading with reduced size. PGF specific. int level = 0; QVariant attribute = imageGetAttribute(QLatin1String("scaledLoadingSize")); if (attribute.isValid() && pgf.Levels() > 0) { int scaledLoadingSize = attribute.toInt(); int i, w, h; for (i = pgf.Levels() - 1 ; i >= 0 ; --i) { w = pgf.Width(i); h = pgf.Height(i); if (qMin(w, h) >= scaledLoadingSize) { break; } } if (i >= 0) { width = w; height = h; level = i; qCDebug(DIGIKAM_DIMG_LOG_PGF) << "Loading PGF scaled version at level " << i << " (" << w << " x " << h << ") for size " << scaledLoadingSize; } } if (m_sixteenBit) { data = new_failureTolerant(width, height, 8); // 16 bits/color/pixel } else { data = new_failureTolerant(width, height, 4); // 8 bits/color/pixel } // Fill all with 255 including alpha channel. memset(data, 0xFF, width * height * (m_sixteenBit ? 8 : 4)); pgf.Read(level, CallbackForLibPGF, this); pgf.GetBitmap(m_sixteenBit ? width * 8 : width * 4, (UINT8*)data, m_sixteenBit ? 64 : 32, NULL, CallbackForLibPGF, this); if (observer) { observer->progressInfo(m_image, 1.0); } } // ------------------------------------------------------------------- // Get ICC color profile. if (m_loadFlags & LoadICCData) { // TODO: Implement proper storage in PGF for color profiles checkExifWorkingColorSpace(); } imageWidth() = width; imageHeight() = height; imageData() = data; imageSetAttribute(QLatin1String("format"), QLatin1String("PGF")); imageSetAttribute(QLatin1String("originalColorModel"), colorModel); imageSetAttribute(QLatin1String("originalBitDepth"), bitDepth); imageSetAttribute(QLatin1String("originalSize"), originalSize); #ifdef WIN32 CloseHandle(fd); #else close(fd); #endif return true; } catch (IOException& e) { int err = e.error; if (err >= AppError) { err -= AppError; } qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Error: Opening and reading PGF image failed (" << err << ")!"; #ifdef WIN32 CloseHandle(fd); #else close(fd); #endif loadingFailed(); return false; } catch (std::bad_alloc& e) { qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Failed to allocate memory for loading" << filePath << e.what(); #ifdef WIN32 CloseHandle(fd); #else close(fd); #endif loadingFailed(); return false; } return true; }
bool PGFLoader::save(const QString& filePath, DImgLoaderObserver* const observer) { m_observer = observer; #ifdef WIN32 #ifdef UNICODE HANDLE fd = CreateFile((LPCWSTR)(QFile::encodeName(filePath).constData()), GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); #else HANDLE fd = CreateFile(QFile::encodeName(filePath).constData(), GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); #endif if (fd == INVALID_HANDLE_VALUE) { qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Error: Could not open destination file."; return false; } #elif defined(__POSIX__) int fd = open(QFile::encodeName(filePath).constData(), O_RDWR | O_CREAT | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH); if (fd == -1) { qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Error: Could not open destination file."; return false; } #endif try { QVariant qualityAttr = imageGetAttribute(QLatin1String("quality")); int quality = qualityAttr.isValid() ? qualityAttr.toInt() : 3; qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF quality: " << quality; CPGFFileStream stream(fd); CPGFImage pgf; PGFHeader header; header.width = imageWidth(); header.height = imageHeight(); header.quality = quality; if (imageHasAlpha()) { if (imageSixteenBit()) { // NOTE : there is no PGF color mode in 16 bits with alpha. header.channels = 3; header.bpp = 48; header.mode = ImageModeRGB48; } else { header.channels = 4; header.bpp = 32; header.mode = ImageModeRGBA; } } else { if (imageSixteenBit()) { header.channels = 3; header.bpp = 48; header.mode = ImageModeRGB48; } else { header.channels = 3; header.bpp = 24; header.mode = ImageModeRGBColor; } } #ifdef PGFCodecVersionID # if PGFCodecVersionID < 0x061142 header.background.rgbtBlue = 0; header.background.rgbtGreen = 0; header.background.rgbtRed = 0; # endif #endif pgf.SetHeader(header); // NOTE: see bug #273765 : Loading PGF thumbs with OpenMP support through a separated thread do not work properlly with libppgf 6.11.24 pgf.ConfigureEncoder(false); pgf.ImportBitmap(4 * imageWidth() * (imageSixteenBit() ? 2 : 1), (UINT8*)imageData(), imageBitsDepth() * 4, NULL, CallbackForLibPGF, this); UINT32 nWrittenBytes = 0; #ifdef PGFCodecVersionID # if PGFCodecVersionID >= 0x061124 pgf.Write(&stream, &nWrittenBytes, CallbackForLibPGF, this); # endif #else pgf.Write(&stream, 0, CallbackForLibPGF, &nWrittenBytes, this); #endif qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF width = " << header.width; qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF height = " << header.height; qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF bbp = " << header.bpp; qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF channels = " << header.channels; qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF quality = " << header.quality; qCDebug(DIGIKAM_DIMG_LOG_PGF) << "PGF mode = " << header.mode; qCDebug(DIGIKAM_DIMG_LOG_PGF) << "Bytes Written = " << nWrittenBytes; #ifdef WIN32 CloseHandle(fd); #else close(fd); #endif // TODO: Store ICC profile in an appropriate place in the image storeColorProfileInMetadata(); if (observer) { observer->progressInfo(m_image, 1.0); } imageSetAttribute(QLatin1String("savedformat"), QLatin1String("PGF")); saveMetadata(filePath); return true; } catch (IOException& e) { int err = e.error; if (err >= AppError) { err -= AppError; } qCWarning(DIGIKAM_DIMG_LOG_PGF) << "Error: Opening and saving PGF image failed (" << err << ")!"; #ifdef WIN32 CloseHandle(fd); #else close(fd); #endif return false; } return true; }
bool JP2KLoader::save(const QString& filePath, DImgLoaderObserver* observer) { FILE* file = fopen(QFile::encodeName(filePath), "wb"); if (!file) { return false; } fclose(file); // ------------------------------------------------------------------- // Initialize JPEG 2000 API. register long i, x, y; unsigned long number_components; jas_image_t* jp2_image = 0; jas_stream_t* jp2_stream = 0; jas_matrix_t* pixels[4]; jas_image_cmptparm_t component_info[4]; int init = jas_init(); if (init != 0) { kDebug() << "Unable to init JPEG2000 decoder"; return false; } jp2_stream = jas_stream_fopen(QFile::encodeName(filePath), "wb"); if (jp2_stream == 0) { kDebug() << "Unable to open JPEG2000 stream"; return false; } number_components = imageHasAlpha() ? 4 : 3; for (i = 0 ; i < (long)number_components ; ++i) { component_info[i].tlx = 0; component_info[i].tly = 0; component_info[i].hstep = 1; component_info[i].vstep = 1; component_info[i].width = imageWidth(); component_info[i].height = imageHeight(); component_info[i].prec = imageBitsDepth(); component_info[i].sgnd = false; } jp2_image = jas_image_create(number_components, component_info, JAS_CLRSPC_UNKNOWN); if (jp2_image == 0) { jas_stream_close(jp2_stream); kDebug() << "Unable to create JPEG2000 image"; return false; } if (observer) { observer->progressInfo(m_image, 0.1F); } // ------------------------------------------------------------------- // Check color space. if (number_components >= 3 ) // RGB & RGBA { // Alpha Channel if (number_components == 4 ) { jas_image_setcmpttype(jp2_image, 3, JAS_IMAGE_CT_OPACITY); } jas_image_setclrspc(jp2_image, JAS_CLRSPC_SRGB); jas_image_setcmpttype(jp2_image, 0, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_R)); jas_image_setcmpttype(jp2_image, 1, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_G)); jas_image_setcmpttype(jp2_image, 2, JAS_IMAGE_CT_COLOR(JAS_CLRSPC_CHANIND_RGB_B)); } // ------------------------------------------------------------------- // Set ICC color profile. // FIXME : doesn't work yet! jas_cmprof_t* cm_profile = 0; jas_iccprof_t* icc_profile = 0; QByteArray profile_rawdata = m_image->getIccProfile().data(); icc_profile = jas_iccprof_createfrombuf((uchar*)profile_rawdata.data(), profile_rawdata.size()); if (icc_profile != 0) { cm_profile = jas_cmprof_createfromiccprof(icc_profile); if (cm_profile != 0) { jas_image_setcmprof(jp2_image, cm_profile); } } // ------------------------------------------------------------------- // Convert to JPEG 2000 pixels. for (i = 0 ; i < (long)number_components ; ++i) { pixels[i] = jas_matrix_create(1, (unsigned int)imageWidth()); if (pixels[i] == 0) { for (x = 0 ; x < i ; ++x) { jas_matrix_destroy(pixels[x]); } jas_image_destroy(jp2_image); kDebug() << "Error encoding JPEG2000 image data : Memory Allocation Failed"; return false; } } unsigned char* data = imageData(); unsigned char* pixel; unsigned short r, g, b, a=0; uint checkpoint = 0; for (y = 0 ; y < (long)imageHeight() ; ++y) { if (observer && y == (long)checkpoint) { checkpoint += granularity(observer, imageHeight(), 0.8F); if (!observer->continueQuery(m_image)) { jas_image_destroy(jp2_image); for (i = 0 ; i < (long)number_components ; ++i) { jas_matrix_destroy(pixels[i]); } jas_cleanup(); return false; } observer->progressInfo(m_image, 0.1 + (0.8 * ( ((float)y)/((float)imageHeight()) ))); } for (x = 0 ; x < (long)imageWidth() ; ++x) { pixel = &data[((y * imageWidth()) + x) * imageBytesDepth()]; if ( imageSixteenBit() ) // 16 bits image. { b = (unsigned short)(pixel[0]+256*pixel[1]); g = (unsigned short)(pixel[2]+256*pixel[3]); r = (unsigned short)(pixel[4]+256*pixel[5]); if (imageHasAlpha()) { a = (unsigned short)(pixel[6]+256*pixel[7]); } } else // 8 bits image. { b = (unsigned short)pixel[0]; g = (unsigned short)pixel[1]; r = (unsigned short)pixel[2]; if (imageHasAlpha()) { a = (unsigned short)(pixel[3]); } } jas_matrix_setv(pixels[0], x, r); jas_matrix_setv(pixels[1], x, g); jas_matrix_setv(pixels[2], x, b); if (number_components > 3) { jas_matrix_setv(pixels[3], x, a); } } for (i = 0 ; i < (long)number_components ; ++i) { int ret = jas_image_writecmpt(jp2_image, (short) i, 0, (unsigned int)y, (unsigned int)imageWidth(), 1, pixels[i]); if (ret != 0) { kDebug() << "Error encoding JPEG2000 image data"; jas_image_destroy(jp2_image); for (i = 0 ; i < (long)number_components ; ++i) { jas_matrix_destroy(pixels[i]); } jas_cleanup(); return false; } } } QVariant qualityAttr = imageGetAttribute("quality"); int quality = qualityAttr.isValid() ? qualityAttr.toInt() : 90; if (quality < 0) { quality = 90; } if (quality > 100) { quality = 100; } QString rate; QTextStream ts( &rate, QIODevice::WriteOnly ); // NOTE: to have a lossless compression use quality=100. // jp2_encode()::optstr: // - rate=#B => the resulting file size is about # bytes // - rate=0.0 .. 1.0 => the resulting file size is about the factor times // the uncompressed size ts << "rate=" << ( quality / 100.0F ); kDebug() << "JPEG2000 quality: " << quality; kDebug() << "JPEG2000 " << rate; int ret = jp2_encode(jp2_image, jp2_stream, rate.toUtf8().data()); if (ret != 0) { kDebug() << "Unable to encode JPEG2000 image"; jas_image_destroy(jp2_image); jas_stream_close(jp2_stream); for (i = 0 ; i < (long)number_components ; ++i) { jas_matrix_destroy(pixels[i]); } jas_cleanup(); return false; } if (observer) { observer->progressInfo(m_image, 1.0); } imageSetAttribute("savedformat", "JP2K"); saveMetadata(filePath); jas_image_destroy(jp2_image); jas_stream_close(jp2_stream); for (i = 0 ; i < (long)number_components ; ++i) { jas_matrix_destroy(pixels[i]); } jas_cleanup(); return true; }