bool readPGFImageData(const QByteArray& data, QImage& img, bool verbose) { try { if (data.isEmpty()) { kDebug() << "PGF image data to decode : size is null"; return false; } CPGFMemoryStream stream((UINT8*)data.data(), (size_t)data.size()); if (verbose) kDebug() << "image data stream size is : " << stream.GetSize(); CPGFImage pgfImg; // NOTE: see bug #273765 : Loading PGF thumbs with OpenMP support through a separated thread do not work properlly with libppgf 6.11.24 pgfImg.ConfigureDecoder(false); pgfImg.Open(&stream); if (verbose) kDebug() << "PGF image is open"; if (pgfImg.Channels() != 4) { kDebug() << "PGF channels not supported"; return false; } img = QImage(pgfImg.Width(), pgfImg.Height(), QImage::Format_ARGB32); pgfImg.Read(); if (verbose) kDebug() << "PGF image is read"; if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { int map[] = {3, 2, 1, 0}; pgfImg.GetBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); } else { int map[] = {0, 1, 2, 3}; pgfImg.GetBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); } if (verbose) kDebug() << "PGF image is decoded"; } catch (IOException& e) { int err = e.error; if (err >= AppError) { err -= AppError; } kDebug() << "Error running libpgf (" << err << ")!"; return false; } return true; }
bool readPGFImageData(const QByteArray& data, QImage& img) { try { CPGFMemoryStream stream((UINT8*)data.data(), (size_t)data.size()); CPGFImage pgfImg; pgfImg.Open(&stream); if (pgfImg.Channels() != 4) { kDebug() << "PGF channels not supported"; return false; } img = QImage(pgfImg.Width(), pgfImg.Height(), QImage::Format_ARGB32); pgfImg.Read(); if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { int map[] = {3, 2, 1, 0}; pgfImg.GetBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); } else { int map[] = {0, 1, 2, 3}; pgfImg.GetBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); } } catch (IOException& e) { int err = e.error; if (err >= AppError) { err -= AppError; } kDebug() << "Error running libpgf (" << err << ")!"; return false; } return true; }
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 writePGFImageData(const QImage& img, QByteArray& data, int quality) { try { if (img.isNull()) { kDebug() << "Thumb image is null"; return false; } // No need Alpha to optimize space on DB. if (img.format() != QImage::Format_ARGB32) { img.convertToFormat(QImage::Format_ARGB32); } CPGFImage pgfImg; PGFHeader header; header.width = img.width(); header.height = img.height(); header.bpp = img.depth(); header.channels = 4; header.quality = quality; header.mode = ImageModeRGBA; header.background.rgbtBlue = header.background.rgbtGreen = header.background.rgbtRed = 0; pgfImg.SetHeader(header); if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { int map[] = {3, 2, 1, 0}; pgfImg.ImportBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); } else { int map[] = {0, 1, 2, 3}; pgfImg.ImportBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); } // TODO : optimize memory allocation... CPGFMemoryStream stream(256000); UINT32 nWrittenBytes = 0; pgfImg.Write(&stream, 0, NULL, &nWrittenBytes); data = QByteArray((const char*)stream.GetBuffer(), nWrittenBytes); } catch (IOException& e) { int err = e.error; if (err >= AppError) { err -= AppError; } kDebug() << "Error running libpgf (" << err << ")!"; return false; } return true; }
bool loadPGFScaled(QImage& img, const QString& path, int maximumSize) { FILE* file = fopen(QFile::encodeName(path), "rb"); if (!file) { kDebug() << "Error: Could not open source file."; return false; } unsigned char header[3]; if (fread(&header, 3, 1, file) != 1) { fclose(file); return false; } unsigned char pgfID[3] = { 0x50, 0x47, 0x46 }; if (memcmp(&header[0], &pgfID, 3) != 0) { // not a PGF file fclose(file); return false; } fclose(file); // ------------------------------------------------------------------- // Initialize PGF API. #ifdef WIN32 #ifdef UNICODE HANDLE fd = CreateFile((LPCWSTR)(QFile::encodeName(path).constData()), GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); #else HANDLE fd = CreateFile(QFile::encodeName(path), GENERIC_READ, 0, 0, OPEN_EXISTING, 0, 0); #endif if (fd == INVALID_HANDLE_VALUE) { return false; } #else int fd = open(QFile::encodeName(path), O_RDONLY); if (fd == -1) { return false; } #endif try { CPGFFileStream stream(fd); CPGFImage pgf; pgf.Open(&stream); // Try to find the right PGF level to get reduced image accordingly // with preview size wanted. int i=0; if (pgf.Levels() > 0) { for (i=pgf.Levels()-1 ; i>=0 ; --i) { if (qMin((int)pgf.Width(i), (int)pgf.Height(i)) >= maximumSize) { break; } } } if (i<0) { i=0; } pgf.Read(i); // Read PGF image at reduced level i. img = QImage(pgf.Width(i), pgf.Height(i), QImage::Format_RGB32); /* const PGFHeader* header = pgf.GetHeader(); kDebug() << "PGF width = " << header->width; kDebug() << "PGF height = " << header->height; kDebug() << "PGF bbp = " << header->bpp; kDebug() << "PGF channels = " << header->channels; kDebug() << "PGF quality = " << header->quality; kDebug() << "PGF mode = " << header->mode; kDebug() << "PGF levels = " << header->nLevels; kDebug() << "Level (w x h)= " << i << "(" << pgf.Width(i) << " x " << pgf.Height(i) << ")"; kDebug() << "QImage depth = " << img.depth(); */ if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { int map[] = {3, 2, 1, 0}; pgf.GetBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); } else { int map[] = {0, 1, 2, 3}; pgf.GetBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); } } catch (IOException& e) { int err = e.error; if (err >= AppError) { err -= AppError; } kDebug() << "Error running libpgf (" << err << ")!"; return false; } return true; }
bool writePGFImageDataToStream(const QImage& image, CPGFStream& stream, int quality, UINT32& nWrittenBytes, bool verbose) { try { if (image.isNull()) { kDebug() << "Thumb image is null"; return false; } QImage img; // Convert image with Alpha channel. if (image.format() != QImage::Format_ARGB32) { img = image.convertToFormat(QImage::Format_ARGB32); if (verbose) kDebug() << "RGB => ARGB"; } else { img = image; } CPGFImage pgfImg; PGFHeader header; header.width = img.width(); header.height = img.height(); header.nLevels = 0; // Auto. header.quality = quality; header.bpp = img.depth(); header.channels = 4; header.mode = ImageModeRGBA; header.usedBitsPerChannel = 0; // Auto #ifdef PGFCodecVersionID # if PGFCodecVersionID < 0x061142 header.background.rgbtBlue = 0; header.background.rgbtGreen = 0; header.background.rgbtRed = 0; # endif #endif pgfImg.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 pgfImg.ConfigureEncoder(false); if (verbose) { kDebug() << "PGF image settings:"; kDebug() << " width: " << header.width; kDebug() << " height: " << header.height; kDebug() << " nLevels: " << header.nLevels; kDebug() << " quality: " << header.quality; kDebug() << " bpp: " << header.bpp; kDebug() << " channels: " << header.channels; kDebug() << " mode: " << header.mode; kDebug() << " usedBitsPerChannel: " << header.usedBitsPerChannel; } if (QSysInfo::ByteOrder == QSysInfo::BigEndian) { int map[] = {3, 2, 1, 0}; pgfImg.ImportBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); } else { int map[] = {0, 1, 2, 3}; pgfImg.ImportBitmap(img.bytesPerLine(), (UINT8*)img.bits(), img.depth(), map); } nWrittenBytes = 0; #ifdef PGFCodecVersionID # if PGFCodecVersionID >= 0x061124 pgfImg.Write(&stream, &nWrittenBytes); # else pgfImg.Write(&stream, 0, 0, &nWrittenBytes); # endif #else pgfImg.Write(&stream, 0, 0, &nWrittenBytes); #endif } catch (IOException& e) { int err = e.error; if (err >= AppError) { err -= AppError; } kDebug() << "Error running libpgf (" << err << ")!"; return false; } return true; }