File indexing completed on 2024-06-16 04:16:35

0001 /*
0002  * This file is part of Krita
0003  *
0004  * SPDX-FileCopyrightText: 2005 Michael Thaler <michael.thaler@physik.tu-muenchen.de>
0005  *
0006  * ported from Gimp, SPDX-FileCopyrightText: 1997 Eiichi Takamori <taka@ma1.seikyou.ne.jp>
0007  * original pixelize.c for GIMP 0.54 by Tracy Scott
0008  *
0009  *  SPDX-License-Identifier: GPL-2.0-or-later
0010  */
0011 
0012 #include "kis_channel_separator.h"
0013 
0014 #include <limits.h>
0015 #include <stdlib.h>
0016 #include <vector>
0017 
0018 #include <QStandardPaths>
0019 
0020 #include <klocalizedstring.h>
0021 #include <kis_debug.h>
0022 #include <kpluginfactory.h>
0023 
0024 
0025 #include <KisImportExportManager.h>
0026 #include <KoUpdater.h>
0027 #include <KoFileDialog.h>
0028 #include <KoColorSpace.h>
0029 #include <KoColorSpaceRegistry.h>
0030 #include <KoChannelInfo.h>
0031 #include <KoColorModelStandardIds.h>
0032 
0033 #include <KisDocument.h>
0034 #include <kis_image.h>
0035 #include <kis_layer.h>
0036 #include <kis_paint_layer.h>
0037 #include <kis_group_layer.h>
0038 #include <kis_transaction.h>
0039 #include <kis_undo_adapter.h>
0040 #include <kis_global.h>
0041 #include <kis_types.h>
0042 #include "kis_iterator_ng.h"
0043 #include <KisPart.h>
0044 #include <KisViewManager.h>
0045 #include <kis_paint_device.h>
0046 #include <kis_node_manager.h>
0047 #include <kis_node_commands_adapter.h>
0048 #include <KisMimeDatabase.h>
0049 #include "KisImageBarrierLock.h"
0050 
0051 KisChannelSeparator::KisChannelSeparator(KisViewManager * view)
0052     : m_viewManager(view)
0053 {
0054 }
0055 
0056 void KisChannelSeparator::separate(KoUpdater * progressUpdater, enumSepAlphaOptions alphaOps, enumSepSource sourceOps, bool downscale, bool toColor, bool activateCurrentChannel)
0057 {
0058     KisImageSP image = m_viewManager->image();
0059     if (!image) return;
0060 
0061     KisPaintDeviceSP src;
0062 
0063     // Use the flattened image, if required
0064     switch (sourceOps) {
0065     case ALL_LAYERS:
0066         // the content will be locked later
0067         src = image->projection();
0068         break;
0069     case CURRENT_LAYER:
0070         src = m_viewManager->activeDevice();
0071         break;
0072     default:
0073         break;
0074     }
0075 
0076     if (!src) return;
0077 
0078     progressUpdater->setProgress(1);
0079 
0080     const KoColorSpace * dstCs = 0;
0081 
0082     quint32 numberOfChannels = src->channelCount();
0083     const KoColorSpace * srcCs  = src->colorSpace();
0084     const QList<KoChannelInfo *> channels = srcCs->channels();
0085     vKisPaintDeviceSP paintDevices;
0086 
0087     /**
0088      * We should process the entire image, even when its pixels are
0089      * transparent, because we might be pulling colors from under a
0090      * zero-alpha channel.
0091      */
0092     const QRect rect = image->bounds();
0093 
0094     KisImageBarrierLock lock(image);
0095     int i = 0;
0096     for (QList<KoChannelInfo *>::const_iterator it =  channels.constBegin(); it != channels.constEnd(); ++it) {
0097 
0098 
0099         KoChannelInfo *ch = (*it);
0100 
0101         bool channelToColor = (toColor && ch->channelType() != KoChannelInfo::ALPHA);
0102 
0103         if (ch->channelType() == KoChannelInfo::ALPHA && alphaOps != CREATE_ALPHA_SEPARATION) {
0104             continue;
0105         }
0106 
0107         qint32 channelSize = ch->size();
0108         qint32 channelPos = ch->pos();
0109         qint32 destSize = 1;
0110 
0111         KisPaintDeviceSP dev;
0112         if (channelToColor) {
0113             // We don't downscale if we separate to color channels
0114             dev = new KisPaintDevice(srcCs);
0115         } else {
0116             if (channelSize == 1 || downscale) {
0117                 dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer8BitsColorDepthID.id(), 0));
0118             } else {
0119                 dev = new KisPaintDevice(KoColorSpaceRegistry::instance()->colorSpace(GrayAColorModelID.id(), Integer16BitsColorDepthID.id(), 0));
0120                 destSize = 2;
0121             }
0122         }
0123 
0124         dstCs = dev->colorSpace();
0125 
0126         paintDevices.push_back(dev);
0127 
0128         KisHLineConstIteratorSP srcIt = src->createHLineConstIteratorNG(rect.x(), rect.y(), rect.width());
0129         KisHLineIteratorSP dstIt = dev->createHLineIteratorNG(rect.x(), rect.y(), rect.width());
0130 
0131         for (qint32 row = 0; row < rect.height(); ++row) {
0132             do {
0133                 if (channelToColor) {
0134                     dstCs->singleChannelPixel(dstIt->rawData(), srcIt->oldRawData(), channelPos);
0135 
0136                     if (alphaOps == COPY_ALPHA_TO_SEPARATIONS) {
0137                         dstCs->setOpacity(dstIt->rawData(), srcCs->opacityU8(srcIt->oldRawData()), 1);
0138                     } else {
0139                         dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1);
0140                     }
0141                 } else {
0142 
0143                     // To grayscale
0144 
0145                     // Decide whether we need downscaling
0146                     if (channelSize == 1 && destSize == 1) {
0147 
0148                         // Both 8-bit channels
0149                         dstIt->rawData()[0] = srcIt->oldRawData()[channelPos];
0150 
0151                         if (alphaOps == COPY_ALPHA_TO_SEPARATIONS) {
0152                             dstCs->setOpacity(dstIt->rawData(), srcCs->opacityU8(srcIt->oldRawData()), 1);
0153                         } else {
0154                             dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1);
0155                         }
0156                     } else if (channelSize == 2 && destSize == 2) {
0157 
0158                         // Both 16-bit
0159                         dstIt->rawData()[0] = srcIt->oldRawData()[channelPos];
0160                         dstIt->rawData()[1] = srcIt->oldRawData()[channelPos + 1];
0161 
0162                         if (alphaOps == COPY_ALPHA_TO_SEPARATIONS) {
0163                             dstCs->setOpacity(dstIt->rawData(), srcCs->opacityU8(srcIt->oldRawData()), 1);
0164                         } else {
0165                             dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1);
0166                         }
0167                     } else if (channelSize != 1 && destSize == 1) {
0168                         // Downscale
0169                         memset(dstIt->rawData(), srcCs->scaleToU8(srcIt->oldRawData(), channelPos), 1);
0170 
0171                         dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1);
0172                     } else if (channelSize != 2 && destSize == 2) {
0173                         // Upscale
0174                         dstIt->rawData()[0] = srcCs->scaleToU8(srcIt->oldRawData(), channelPos);
0175 
0176                         dstCs->setOpacity(dstIt->rawData(), OPACITY_OPAQUE_U8, 1);
0177 
0178                     }
0179                 }
0180 
0181             } while (dstIt->nextPixel() && srcIt->nextPixel());
0182             dstIt->nextRow();
0183             srcIt->nextRow();
0184         }
0185         ++i;
0186 
0187         progressUpdater->setProgress((i * 100) / numberOfChannels);
0188         if (progressUpdater->interrupted()) {
0189             break;
0190         }
0191     }
0192 
0193     vKisPaintDeviceSP::const_iterator paintDeviceIterator = paintDevices.cbegin();
0194 
0195     if (!progressUpdater->interrupted()) {
0196         KisNodeCommandsAdapter adapter(m_viewManager);
0197         adapter.beginMacro(kundo2_i18n("Separate Image"));
0198 
0199         for (QList<KoChannelInfo *>::const_iterator it =  channels.constBegin(); it != channels.constEnd(); ++it) {
0200 
0201             KoChannelInfo *ch = (*it);
0202             if (ch->channelType() == KoChannelInfo::ALPHA && alphaOps != CREATE_ALPHA_SEPARATION) {
0203                 // Don't make an separate separation of the alpha channel if the user didn't ask for it.
0204                 continue;
0205             }
0206 
0207             KisPaintLayerSP l = new KisPaintLayer(image, ch->name(), OPACITY_OPAQUE_U8, *paintDeviceIterator);
0208 
0209             if (toColor && ch->channelType() != KoChannelInfo::ALPHA && activateCurrentChannel) {
0210                 QBitArray channelFlags(channels.count());
0211                 int i = 0;
0212                 for (QList<KoChannelInfo *>::const_iterator it2 =  channels.constBegin(); it2 != channels.constEnd(); ++it2) {
0213                     channelFlags.setBit(i, (it == it2));
0214                     i++;
0215                 }
0216                 l->setChannelFlags(channelFlags);
0217             }
0218 
0219             adapter.addNode(l.data(), image->rootLayer(), 0);
0220             ++paintDeviceIterator;
0221         }
0222 
0223         adapter.endMacro();
0224     }
0225 
0226     progressUpdater->setProgress(100);
0227 
0228 
0229 }