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;
}
Exemple #2
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;
}
Exemple #3
0
KisImageBuilder_Result PSDLoader::decode(const KUrl& uri)
{
    // open the file
    QFile f(uri.toLocalFile());
    if (!f.exists()) {
        return KisImageBuilder_RESULT_NOT_EXIST;
    }
    if (!f.open(QIODevice::ReadOnly)) {
        return KisImageBuilder_RESULT_FAILURE;
    }

    dbgFile << "pos:" << f.pos();

    PSDHeader header;
    if (!header.read(&f)) {
        dbgFile << "failed reading header: " << header.error;
        return KisImageBuilder_RESULT_FAILURE;
    }

    dbgFile << header;
    dbgFile << "Read header. pos:" << f.pos();

    PSDColorModeBlock colorModeBlock(header.colormode);
    if (!colorModeBlock.read(&f)) {
        dbgFile << "failed reading colormode block: " << colorModeBlock.error;
        return KisImageBuilder_RESULT_FAILURE;
    }

    dbgFile << "Read color mode block. pos:" << f.pos();

    PSDResourceSection resourceSection;
    if (!resourceSection.read(&f)) {
        dbgFile << "failed reading resource section: " << resourceSection.error;
        return KisImageBuilder_RESULT_FAILURE;
    }

    dbgFile << "Read resource section. pos:" << f.pos();

    PSDLayerSection layerSection(header);
    if (!layerSection.read(&f)) {
        dbgFile << "failed reading layer section: " << layerSection.error;
        return KisImageBuilder_RESULT_FAILURE;
    }
    // XXX: add all the image resource blocks as annotations to the image

    dbgFile << "Read layer section. " << layerSection.nLayers << "layers. pos:" << f.pos();

    // Get the right colorspace
    QPair<QString, QString> colorSpaceId = psd_colormode_to_colormodelid(header.colormode,
                                                                         header.channelDepth);
    if (colorSpaceId.first.isNull()) return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;

    // Get the icc profile!
    const KoColorProfile* profile = 0;
    if (resourceSection.resources.contains(PSDResourceSection::ICC_PROFILE)) {
        ICC_PROFILE_1039 *iccProfileData = dynamic_cast<ICC_PROFILE_1039*>(resourceSection.resources[PSDResourceSection::ICC_PROFILE]->resource);
        if (iccProfileData ) {
            profile = KoColorSpaceRegistry::instance()->createColorProfile(colorSpaceId.first,
                                                                       colorSpaceId.second,
                                                                       iccProfileData->icc);
            dbgFile  << "Loaded ICC profile" << profile->name();
        }

    }

    // Create the colorspace
    const KoColorSpace* cs = KoColorSpaceRegistry::instance()->colorSpace(colorSpaceId.first, colorSpaceId.second, profile);
    if (!cs) {
        return KisImageBuilder_RESULT_UNSUPPORTED_COLORSPACE;
    }

    // Creating the KisImageWSP
    m_image = new KisImage(m_doc->createUndoStore(),  header.width, header.height, cs, "built image");
    Q_CHECK_PTR(m_image);
    m_image->lock();

    // set the correct resolution
    if (resourceSection.resources.contains(PSDResourceSection::RESN_INFO)) {
        RESN_INFO_1005 *resInfo = dynamic_cast<RESN_INFO_1005*>(resourceSection.resources[PSDResourceSection::RESN_INFO]->resource);
        if (resInfo) {
            m_image->setResolution(POINT_TO_INCH(resInfo->hRes), POINT_TO_INCH(resInfo->vRes));
            // let's skip the unit for now; we can only set that on the KoDocument, and krita doesn't use it.
        }
    }
    // Preserve the duotone colormode block for saving back to psd
    if (header.colormode == DuoTone) {
        KisAnnotationSP annotation = new KisAnnotation("DuotoneColormodeBlock",
                                                       i18n("Duotone Colormode Block"),
                                                       colorModeBlock.data);
        m_image->addAnnotation(annotation);
    }

    // read the projection into our single layer
    if (layerSection.nLayers == 0) {
        dbgFile << "Position" << f.pos() << "Going to read the projection into the first layer, which Photoshop calls 'Background'";

        KisPaintLayerSP layer = new KisPaintLayer(m_image, i18n("Background"), OPACITY_OPAQUE_U8);
        KisTransaction("", layer -> paintDevice());

        PSDImageData imageData(&header);
        imageData.read(&f, layer->paintDevice());

        //readLayerData(&f, layer->paintDevice(), f.pos(), QRect(0, 0, header.width, header.height));
        m_image->addNode(layer, m_image->rootLayer());

    }
    else {

        // read the channels for the various layers
        for(int i = 0; i < layerSection.nLayers; ++i) {

            // XXX: work out the group layer structure in Photoshop, as well as the adjustment layers

            PSDLayerRecord* layerRecord = layerSection.layers.at(i);
            dbgFile << "Going to read channels for layer" << i << layerRecord->layerName;

            KisPaintLayerSP layer = new KisPaintLayer(m_image, layerRecord->layerName, layerRecord->opacity);
            layer->setCompositeOp(psd_blendmode_to_composite_op(layerRecord->blendModeKey));
            if (!layerRecord->readPixelData(&f, layer->paintDevice())) {
                dbgFile << "failed reading channels for layer: " << layerRecord->layerName << layerRecord->error;
                return KisImageBuilder_RESULT_FAILURE;
            }

            m_image->addNode(layer, m_image->rootLayer());
            layer->setVisible(layerRecord->visible);
        }
    }

    m_image->unlock();
    return KisImageBuilder_RESULT_OK;
}