void PSDHeaderTest::testRoundTripping() { QString filename = "test.psd"; QFile f(filename); f.open(QIODevice::ReadWrite); PSDHeader header; Q_ASSERT(!header.valid()); header.signature = "8BPS"; header.version = 1; header.nChannels = 3; header.width = 1000; header.height = 1000; header.channelDepth = 8; header.colormode = RGB; Q_ASSERT(header.valid()); bool retval = header.write(&f); Q_ASSERT(retval); f.close(); f.open(QIODevice::ReadOnly); PSDHeader header2; retval = header2.read(&f); Q_ASSERT(retval); QCOMPARE(header.signature, header2.signature); QVERIFY(header.version == header2.version); QVERIFY(header.nChannels == header2.nChannels); QVERIFY(header.width == header2.width); QVERIFY(header.height == header2.height); QVERIFY(header.channelDepth == header2.channelDepth); QVERIFY(header.colormode == header2.colormode); }
KisImageBuilder_Result PSDSaver::buildFile(QIODevice *io) { if (!m_image) return KisImageBuilder_RESULT_EMPTY; const bool haveLayers = m_image->rootLayer()->childCount() > 1 || checkIfHasTransparency(m_image->rootLayer()->firstChild()->projection()); // HEADER PSDHeader header; header.signature = "8BPS"; header.version = 1; header.nChannels = haveLayers ? m_image->colorSpace()->channelCount() : m_image->colorSpace()->colorChannelCount(); header.width = m_image->width(); header.height = m_image->height(); QPair<psd_color_mode, quint16> colordef = colormodelid_to_psd_colormode(m_image->colorSpace()->colorModelId().id(), m_image->colorSpace()->colorDepthId().id()); if (colordef.first == COLORMODE_UNKNOWN || colordef.second == 0 || colordef.second == 32) { return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } header.colormode = colordef.first; header.channelDepth = colordef.second; dbgFile << "header" << header << io->pos(); if (!header.write(io)) { dbgFile << "Failed to write header. Error:" << header.error << io->pos(); return KisImageBuilder_RESULT_FAILURE; } // COLORMODE BlOCK PSDColorModeBlock colorModeBlock(header.colormode); // XXX: check for annotations that contain the duotone spec KisAnnotationSP annotation = m_image->annotation("DuotoneColormodeBlock"); if (annotation) { colorModeBlock.duotoneSpecification = annotation->annotation(); } dbgFile << "colormode block" << io->pos(); if (!colorModeBlock.write(io)) { dbgFile << "Failed to write colormode block. Error:" << colorModeBlock.error << io->pos(); return KisImageBuilder_RESULT_FAILURE; } // IMAGE RESOURCES SECTION PSDImageResourceSection resourceSection; vKisAnnotationSP_it it = m_image->beginAnnotations(); vKisAnnotationSP_it endIt = m_image->endAnnotations(); while (it != endIt) { KisAnnotationSP annotation = (*it); if (!annotation || annotation->type().isEmpty()) { dbgFile << "Warning: empty annotation"; it++; continue; } dbgFile << "Annotation:" << annotation->type() << annotation->description(); if (annotation->type().startsWith(QString("PSD Resource Block:"))) { // PSDResourceBlock *resourceBlock = dynamic_cast<PSDResourceBlock*>(annotation.data()); if (resourceBlock) { dbgFile << "Adding PSD Resource Block" << resourceBlock->identifier; resourceSection.resources[(PSDImageResourceSection::PSDResourceID)resourceBlock->identifier] = resourceBlock; } } it++; } // Add resolution block { RESN_INFO_1005 *resInfo = new RESN_INFO_1005; resInfo->hRes = INCH_TO_POINT(m_image->xRes()); resInfo->vRes = INCH_TO_POINT(m_image->yRes()); PSDResourceBlock *block = new PSDResourceBlock; block->identifier = PSDImageResourceSection::RESN_INFO; block->resource = resInfo; resourceSection.resources[PSDImageResourceSection::RESN_INFO] = block; } // Add icc block { ICC_PROFILE_1039 *profileInfo = new ICC_PROFILE_1039; profileInfo->icc = m_image->profile()->rawData(); PSDResourceBlock *block = new PSDResourceBlock; block->identifier = PSDImageResourceSection::ICC_PROFILE; block->resource = profileInfo; resourceSection.resources[PSDImageResourceSection::ICC_PROFILE] = block; } dbgFile << "resource section" << io->pos(); if (!resourceSection.write(io)) { dbgFile << "Failed to write resource section. Error:" << resourceSection.error << io->pos(); return KisImageBuilder_RESULT_FAILURE; } // LAYER AND MASK DATA // Only save layers and masks if there is more than one layer dbgFile << "m_image->rootLayer->childCount" << m_image->rootLayer()->childCount() << io->pos(); if (haveLayers) { PSDLayerMaskSection layerSection(header); layerSection.hasTransparency = true; if (!layerSection.write(io, m_image->rootLayer())) { dbgFile << "failed to write layer section. Error:" << layerSection.error << io->pos(); return KisImageBuilder_RESULT_FAILURE; } } else { // else write a zero length block dbgFile << "No layers, saving empty layers/mask block" << io->pos(); psdwrite(io, (quint32)0); } // IMAGE DATA dbgFile << "Saving composited image" << io->pos(); PSDImageData imagedata(&header); if (!imagedata.write(io, m_image->projection(), haveLayers)) { dbgFile << "Failed to write image data. Error:" << imagedata.error; return KisImageBuilder_RESULT_FAILURE; } return KisImageBuilder_RESULT_OK; }
KisImageBuilder_Result PSDSaver::buildFile(const KUrl& uri) { if (!m_image) return KisImageBuilder_RESULT_EMPTY; if (uri.isEmpty()) return KisImageBuilder_RESULT_NO_URI; if (!uri.isLocalFile()) return KisImageBuilder_RESULT_NOT_LOCAL; // Open file for writing QFile f(uri.toLocalFile()); if (!f.open(QIODevice::WriteOnly)) { return KisImageBuilder_RESULT_NOT_LOCAL; } // HEADER PSDHeader header; header.signature = "8BPS"; header.version = 1; header.nChannels = m_image->colorSpace()->channelCount(); header.width = m_image->width(); header.height = m_image->height(); QPair<PSDColorMode, quint16> colordef = colormodelid_to_psd_colormode(m_image->colorSpace()->colorModelId().id(), m_image->colorSpace()->colorDepthId().id()); if (colordef.first == UNKNOWN || colordef.second == 0 || colordef.second == 32) { return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE; } header.colormode = colordef.first; header.channelDepth = colordef.second; dbgFile << "header" << header << f.pos(); if (!header.write(&f)) { dbgFile << "Failed to write header. Error:" << header.error << f.pos(); return KisImageBuilder_RESULT_FAILURE; } // COLORMODE BlOCK PSDColorModeBlock colorModeBlock(header.colormode); // XXX: check for annotations that contain the duotone spec dbgFile << "colormode block" << f.pos(); if (!colorModeBlock.write(&f)) { dbgFile << "Failed to write colormode block. Error:" << colorModeBlock.error << f.pos(); return KisImageBuilder_RESULT_FAILURE; } // IMAGE RESOURCES SECTION PSDResourceSection resourceSection; // Add resolution block { RESN_INFO_1005 *resInfo = new RESN_INFO_1005; resInfo->hRes = INCH_TO_POINT(m_image->xRes()); resInfo->vRes = INCH_TO_POINT(m_image->yRes()); PSDResourceBlock *block = new PSDResourceBlock; block->resource = resInfo; resourceSection.resources[PSDResourceSection::RESN_INFO] = block; } // Add icc block { ICC_PROFILE_1039 *profileInfo = new ICC_PROFILE_1039; profileInfo->icc = m_image->profile()->rawData(); PSDResourceBlock *block = new PSDResourceBlock; block->resource = profileInfo; resourceSection.resources[PSDResourceSection::ICC_PROFILE] = block; } // XXX: Add other blocks... dbgFile << "resource section" << f.pos(); if (!resourceSection.write(&f)) { dbgFile << "Failed to write resource section. Error:" << resourceSection.error << f.pos(); return KisImageBuilder_RESULT_FAILURE; } // LAYER AND MASK DATA // Only save layers and masks if there is more than one layer dbgFile << "m_image->rootLayer->childCount" << m_image->rootLayer()->childCount() << f.pos(); if (m_image->rootLayer()->childCount() > 1) { PSDLayerSection layerSection(header); layerSection.hasTransparency = true; if (!layerSection.write(&f, m_image->rootLayer())) { dbgFile << "failed to write layer section. Error:" << layerSection.error << f.pos(); return KisImageBuilder_RESULT_FAILURE; } } else { // else write a zero length block dbgFile << "No layers, saving empty layers/mask block" << f.pos(); psdwrite(&f, (quint32)0); } // IMAGE DATA dbgFile << "Saving composited image" << f.pos(); PSDImageData imagedata(&header); if (!imagedata.write(&f, m_image->projection())) { dbgFile << "Failed to write image data. Error:" << imagedata.error; return KisImageBuilder_RESULT_FAILURE; } f.close(); return KisImageBuilder_RESULT_OK; }