Beispiel #1
0
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;
}
Beispiel #2
0
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;
}
Beispiel #3
0
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;
}
Beispiel #4
0
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');
}
Beispiel #5
0
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');
}
Beispiel #6
0
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');
}
Beispiel #7
0
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);
}
Beispiel #8
0
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);
}
Beispiel #9
0
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;
}
Beispiel #10
0
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;
}
Beispiel #11
0
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]);
        }
    }
Beispiel #12
0
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;
}
Beispiel #13
0
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;
}