bool psdwrite_pascalstring(QIODevice* io, const QString &s) { Q_ASSERT(s.length() < 256); Q_ASSERT(s.length() >= 0); if (s.length() < 0 || s.length() > 255) return false; if (s.isNull()) { psdwrite(io, (quint8)0); psdwrite(io, (quint8)0); return true; } quint8 length = s.length(); psdwrite(io, length); QByteArray b = s.toLatin1(); char* str = b.data(); int written = io->write(str, length); if (written != length) return false; if ((length & 0x01) != 0) { return psdwrite(io, (quint8)0); } return true; }
bool psdwrite_pascalstring(QIODevice* io, const QString &s, int padding) { Q_ASSERT(s.length() < 256); Q_ASSERT(s.length() >= 0); if (s.length() < 0 || s.length() > 255) return false; if (s.isNull()) { psdwrite(io, (quint8)0); psdwrite(io, (quint8)0); return true; } quint8 length = s.length(); psdwrite(io, length); QByteArray b = s.toLatin1(); char* str = b.data(); int written = io->write(str, length); if (written != length) return false; // If the total length (length byte + content) is not a multiple of padding, add zeroes to pad length++; if ((length % padding) != 0) { for (int i = 0; i < (padding - (length %padding)); i++) { psdwrite(io, (quint8)0); } } return true; }
bool PSDResourceBlock::write(QIODevice* io) const { dbgFile << "Writing Resource Block" << PSDImageResourceSection::idToString((PSDImageResourceSection::PSDResourceID)identifier) << identifier; if (resource && !resource->valid()) { error = QString("Cannot write an invalid Resource Block"); return false; } if (identifier == PSDImageResourceSection::LAYER_STATE || identifier == PSDImageResourceSection::LAYER_GROUP || identifier == PSDImageResourceSection::LAYER_COMPS || identifier == PSDImageResourceSection::LAYER_GROUP_ENABLED_ID || identifier == PSDImageResourceSection::LAYER_SELECTION_ID) { /** * We can actually handle LAYER_SELECTION_ID. It consists * of a number of layers and a list of IDs to select, which * are retrieved from 'lyid' additional layer block. */ dbgFile << "Skip writing resource block" << identifier << displayText(); return true; } QByteArray ba; // createBlock returns true by default but does not change the data. if (resource && !resource->createBlock(ba)) { error = resource->error; return false; } else if (!resource) { // reconstruct from the data QBuffer buf(&ba); buf.open(QBuffer::WriteOnly); buf.write("8BIM", 4); psdwrite(&buf, identifier); psdwrite_pascalstring(&buf, name); psdwrite(&buf, dataSize); buf.write(data); buf.close(); } if (io->write(ba.constData(), ba.size()) != ba.size()) { error = QString("Could not write complete resource"); return false; } return true; }
void PSDUtilsTest::test_psdwrite_quint8() { QBuffer buf; buf.open(QBuffer::ReadWrite); quint8 i = 3; QVERIFY(psdwrite(&buf, i)); QCOMPARE(buf.buffer().size(), 1); QCOMPARE(buf.buffer().at(0), '\3'); }
void PSDUtilsTest::test_psdwrite_qstring() { QBuffer buf; buf.open(QBuffer::ReadWrite); QString s = "8BPS"; QVERIFY(psdwrite(&buf, s)); QCOMPARE(buf.buffer().size(), 4); QCOMPARE(buf.buffer().at(0), '8'); QCOMPARE(buf.buffer().at(1), 'B'); QCOMPARE(buf.buffer().at(2), 'P'); QCOMPARE(buf.buffer().at(3), 'S'); }
void PSDUtilsTest::test_psdwrite_quint32() { QBuffer buf; buf.open(QBuffer::ReadWrite); quint32 i = 100; QVERIFY(psdwrite(&buf, i)); QCOMPARE(buf.buffer().size(), 4); QCOMPARE(buf.buffer().at(0), '\0'); QCOMPARE(buf.buffer().at(1), '\0'); QCOMPARE(buf.buffer().at(2), '\0'); QCOMPARE(buf.buffer().at(3), 'd'); }
void PSDUtilsTest::test_psdread_quint32() { QBuffer buf; QVERIFY(buf.open(QBuffer::ReadWrite)); quint32 s = 300000; psdwrite(&buf, s); buf.close(); buf.open(QBuffer::ReadOnly); quint32 r; QVERIFY(psdread(&buf, &r)); QCOMPARE(r, s); }
void PSDUtilsTest::test_psdread_quint16() { QBuffer buf; buf.open(QBuffer::ReadWrite); quint16 s = 1024; QVERIFY(psdwrite(&buf, s)); buf.close(); buf.open(QBuffer::ReadOnly); quint16 r; QVERIFY(psdread(&buf, &r)); QCOMPARE(r, s); }
bool RESN_INFO_1005::createBlock(QByteArray & data) { dbgFile << "Writing RESN_INFO_1005"; QBuffer buf(&data); startBlock(buf, PSDImageResourceSection::RESN_INFO, 16); // Convert to 16.16 fixed point Fixed h = hRes * 65536.0 + 0.5; dbgFile << "h" << h << "hRes" << hRes; psdwrite(&buf, (quint32)h); psdwrite(&buf, hResUnit); psdwrite(&buf, widthUnit); // Convert to 16.16 fixed point Fixed v = vRes * 65536.0 + 0.5; dbgFile << "v" << v << "vRes" << vRes; psdwrite(&buf, (quint32)v); psdwrite(&buf, vResUnit); psdwrite(&buf, heightUnit); buf.close(); return true; }
bool PSDImageData::write(QIODevice *io, KisPaintDeviceSP dev, bool hasAlpha) { // XXX: make the compression settting configurable. For now, always use RLE. psdwrite(io, (quint16)Compression::RLE); // now write all the channels in display order // fill in the channel chooser, in the display order, but store the pixel index as well. QRect rc(0, 0, m_header->width, m_header->height); const int channelSize = m_header->channelDepth / 8; const psd_color_mode colorMode = m_header->colormode; QVector<PsdPixelUtils::ChannelWritingInfo> writingInfoList; bool writeAlpha = hasAlpha && dev->colorSpace()->channelCount() != dev->colorSpace()->colorChannelCount(); const int numChannels = writeAlpha ? dev->colorSpace()->channelCount() : dev->colorSpace()->colorChannelCount(); for (int i = 0; i < numChannels; i++) { const int rleOffset = io->pos(); int channelId = writeAlpha && i == numChannels - 1 ? -1 : i; writingInfoList << PsdPixelUtils::ChannelWritingInfo(channelId, -1, rleOffset); io->seek(io->pos() + rc.height() * sizeof(quint16)); } PsdPixelUtils::writePixelDataCommon(io, dev, rc, colorMode, channelSize, false, false, writingInfoList); return true; }
bool PSDImageData::write(QIODevice *io, KisPaintDeviceSP dev) { // XXX: make the compression settting configurable. For now, always use RLE. psdwrite(io, (quint16)Compression::RLE); // now write all the channels in display order // fill in the channel chooser, in the display order, but store the pixel index as well. QRect rc(0, 0, m_header->width, m_header->height); QVector<quint8* > tmp = dev->readPlanarBytes(0, 0, rc.width(), rc.height()); // then reorder the planes to fit the psd model -- alpha first, then display order QVector<quint8* > planes; QList<KoChannelInfo*> origChannels = dev->colorSpace()->channels(); quint8* alphaPlane = 0; foreach(KoChannelInfo *ch, KoChannelInfo::displayOrderSorted(origChannels)) { int channelIndex = KoChannelInfo::displayPositionToChannelIndex(ch->displayPosition(), origChannels); //qDebug() << ppVar(ch->name()) << ppVar(ch->pos()) << ppVar(ch->displayPosition()) << ppVar(channelIndex); if (ch->channelType() == KoChannelInfo::ALPHA) { alphaPlane = tmp[channelIndex]; } else { planes.append(tmp[channelIndex]); } }
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; }